├── LICENSE ├── README.md ├── config ├── dependencies_config.json ├── dependency_cache.json └── python_environments.json ├── css ├── main.css └── modules │ ├── base.css │ ├── components.css │ └── utilities.css ├── index.html ├── js ├── main.js └── modules │ ├── core-api.js │ ├── dependency-manager.js │ ├── ui-components.js │ └── visualization.js ├── main.py ├── py ├── __init__.py ├── api.py ├── core.py └── dependency.py ├── requirements.txt └── 启动.bat /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Dontdrunk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI环境管理工具 2 | 3 | https://github.com/user-attachments/assets/87a2be3f-c9d3-46be-9526-ace0223d7330 4 | 5 | 一个简洁高效的 Python AI 开发环境管理工具,提供可视化界面进行 Python 依赖包的管理和维护。 6 | 7 | ## 特色功能 8 | 9 | 1. **全面的依赖概览** 10 | - 清晰展示所有已安装的 Python 依赖 11 | - 实时检查并显示最新版本状态 12 | - 智能分类标记系统、AI模型和数据科学相关依赖 13 | - 自动获取并显示依赖的详细描述信息 14 | 15 | 2. **高效的依赖管理** 16 | - 一键安装、卸载和更新依赖 17 | - 支持版本历史浏览和快速切换 18 | - 批量操作多个依赖 19 | - 支持上传安装 wheel 文件和 requirements.txt 20 | 21 | 3. **优化的用户体验** 22 | - 实时搜索和多条件筛选 23 | - 明暗主题切换 24 | - 响应式界面设计 25 | - 操作进度可视化 26 | 27 | ## 使用方法 28 | 29 | 1. 双击 `启动.bat` 文件运行应用 30 | 2. 在浏览器中访问 `http://127.0.0.1:8282` 打开管理界面 31 | 3. 应用将自动加载所有已安装的 Python 依赖 32 | 33 | ### 依赖搜索和筛选 34 | 35 | - 使用顶部搜索框快速查找依赖 36 | - 使用下拉菜单按类别筛选(全部、核心、软件、数据科学、人工智能、其他) 37 | 38 | ### 依赖安装 39 | 40 | - 在底部安装区域输入包名称直接安装 41 | - 支持指定版本号(例如:`tensorflow==2.15.0`) 42 | - 上传 .whl 文件进行本地安装 43 | - 上传 requirements.txt 批量安装 44 | 45 | ### 批量操作 46 | 47 | - 使用复选框选择多个依赖 48 | - 点击"批量卸载"一次性移除多个依赖 49 | - 点击"一键更新所选依赖"将所选依赖更新到最新版本 50 | - 点击"清理PIP缓存"快速释放存储空间 51 | 52 | ## 多环境管理 53 | - 可设置指定目录环境进行管理,支持虚拟环境和便携式环境 54 | 55 | ## 系统要求 56 | 57 | - Windows 操作系统 58 | - Python 3.7+ 59 | - 现代浏览器(推荐 Chrome、Edge 或 Firefox 最新版本) 60 | 61 | ## 故障排除 62 | 63 | 如果遇到问题: 64 | 65 | 1. 确保您的Python环境正常工作 66 | 2. 检查是否有防火墙阻止了应用程序 67 | 3. 确保端口8282未被其他应用占用 68 | 4. 如有问题,请查看控制台输出的错误信息 69 | 5. 如果一直停留在加载界面,请检查自己的网络,确保魔法畅通。 70 | 71 | ## 更新日志 72 | 73 | ### V2.1.0-正式版 (2025-05-27) 74 | 75 | **问题修复** 🐛 76 | - 修复前后端通信BUG,解决依赖更新后前端界面无法刷新的问题 77 | - 优化网络并发请求机制,大幅提高批量操作时的稳定性和响应速度 78 | - 完善错误处理机制,提升系统整体稳定性 79 | 80 | ### V2.0.0-Bata (2025-05-24) 81 | 82 | **新特性** ✨ 83 | - 全新的用户界面设计,提供更直观的操作体验 84 | - 多环境管理,支持便携式环境丶虚拟环境丶系统环境管理(支持添加多个环境) 85 | 86 | **优化改进** 🔨 87 | - 优化依赖安装流程,提升安装成功率 88 | - 优化批量操作功能,提供更清晰的进度展示 89 | - 提升整体性能和响应速度 90 | 91 | **问题修复** 🐛 92 | - 修复某些依赖无法正确显示版本信息的问题 93 | - 修复在特定情况下安装wheel文件失败的问题 94 | - 解决requirements.txt导入时的编码问题 95 | - 修复批量更新时可能出现的并发问题 96 | - 修复无法正确获取描述的问题 97 | 98 | **待修复问题** (该问题将在下个版本修复) 99 | - 前后端通信存在一定BUG,更新依赖后前端界面有时会出现无法刷新的情况,需要刷新浏览器才能查看最新结果。 100 | - 网络并发请求还有近一步优化的空间 101 | 102 | ### V1.0.0 (2025-04-02) 103 | 104 | - 最初版本上线,支持管理本地Python环境 105 | 106 | ## 支持作者 107 | 108 | 🎭 我用代码垒起一座围栏,把Bug都关在了里面... 109 | 但是!Bug总会顽皮地翻墙逃出来,我需要你的支持来买更多的砖头补墙! 110 | 111 | 以下是我的"防Bug补墙基金"募捐通道 👇 112 | 113 | 💡 捐赠小贴士: 114 | - ¥6.66 - 助我喝杯咖啡,让Bug没机会钻空子 115 | - ¥66.6 - 够我吃顿好的,代码写得更欢乐 116 | - ¥666 - 我要给Bug们建豪华监狱! 117 | 118 | 🎯 声明:本项目开源免费,打赏纯自愿。 119 | 不打赏也没关系,但你的程序可能会遇到一些"不经意的小惊喜"... 120 | 121 |  122 | 123 | (开玩笑的,绝对不会有Bug😉) 124 | 125 | -------------------------------------------------------------------------------- /config/dependencies_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "systemDependencies": [ 3 | "pip", "setuptools", "wheel", "distlib", "virtualenv", "blinker", 4 | "six", "soupsieve", "webencodings", "colorama", "certifi", 5 | "cffi", "chardet", "idna", "urllib3", "charset-normalizer", 6 | "pycparser", "cryptography", "pyopenssl", "pygments", "pyparsing", 7 | "packaging", "MarkupSafe", "Jinja2", "pytz", "toml", "platformdirs", 8 | "importlib-metadata", "zipp", "click", "werkzeug", "flask", "flask-cors", 9 | "itsdangerous", "filelock", "typing-extensions", "decorator", "entrypoints", 10 | "pywin32", "pythonnet", "psutil", "pyyaml", "atomicwrites", "attrs", "distro", 11 | "pluggy", "jsonschema", "pyrsistent", "more-itertools", "python-dateutil", "requests", 12 | "torch", "torchvision", "torchaudio" 13 | ], 14 | "softwareDependencies": [ 15 | "flask", "flask-cors", "requests", "packaging", "importlib-metadata", "werkzeug", 16 | "jinja2", "itsdangerous", "click", "markupsafe", "colorama", "pipdeptree" 17 | ], 18 | "dataScienceDependencies": [ 19 | "numpy", "scipy", "pandas", "scikit-learn", "statsmodels", "joblib", 20 | "numba", "dask", "vaex", "pillow", "matplotlib", "seaborn", "plotly", 21 | "bokeh", "altair", "graphviz", "networkx", 22 | "sqlalchemy", "psycopg2", "pymysql", "pymongo", "redis", "elasticsearch", 23 | "cassandra-driver", "neo4j", "clickhouse-driver", "mariadb", 24 | "polars", "modin", "datatable", "koalas", "cudf", "cupy", "datashader", 25 | "holoviews", "pygwalker", "sweetviz", "pandas-profiling", "dtale", 26 | "lux-api", "hiplot", "mplfinance", "cufflinks", "yellowbrick", "eli5", 27 | "dalex", "pycebox", "scikit-optimize", "hyperopt", "nevergrad", 28 | "ax-platform", "bayesian-optimization", "scikit-multilearn", "river", 29 | "creme", "imbalanced-learn", "tsfel", "tsfresh", "cesium", "featuretools", 30 | "feature-engine", "category_encoders", "patsy", "kmodes", "sompy", 31 | "hdbscan", "umap-learn", "pacmap", "alluvial-diagrams", "spectral-clustering", 32 | "dbscan", "opentsne", "interpret", "interpret-ml", "pdpbox", "fairlearn", 33 | "fairness-indicators", "optuna-dashboard", "mlemory-profiler", "bandit", 34 | "skll", "dvc", "great_expectations", "cerberus", "pandera", "arctic", 35 | "ibis-framework", "pyarrow", "duckdb", "modin", "daft", "lancedb", 36 | "milvus", "vespa", "qdrant", "scylladb", "tidb", "druid", 37 | "clickhouse-sqlalchemy", "influxdb", "timescaledb", "monetdb", "teradata", 38 | "phoenixdb", "kinetica", "skdb", "qlib", "pyfolio", "finmarketpy", 39 | "backtrader", "zipline", "alphalens", "ta-lib", "ta", "mlfinlab", 40 | "empyrical", "pyalgotrade", "backtesting", "quantstats", 41 | "adtk", "altair_ally", "anndata", "arviz", "autoimpute", "autoviz", 42 | "bamboolib", "bioinfokit", "biopython", "blaze", "bonobo", "bqplot", 43 | "dagster", "darts-forecast", "datacompy", "dataprep", "datasette", 44 | "deepchecks", "deepnog", "deltalake", "evidently", "factor_analyzer", 45 | "fastparquet", "feast", "geoalchemy2", "geopy", "glom", "gplearn", 46 | "gspread", "ipywidgets", "janitor", "kedro", "keplergl", "klib", 47 | "lifelines", "luigi", "lux", "mlxtend", "momepy", "nbconvert", 48 | "nilearn", "nlpaug", "omegaconf", "ortools", "osmnx", "prefect", 49 | "prov", "pyjanitor", "pymc-marketing", "pymde", "pymongo-pandas", 50 | "pytensor", "pyviz", "pyxll", "qgrid", "shiny", "skimpy", 51 | "statsforecast", "sympy", "tableone", "tabulate", "trubrics", 52 | "tune-sklearn", "ucimlrepo", "upath", "vaex-viz", "visidata", 53 | "voila-gridstack", "vtk", "xarray-spatial", "xarray", "xgboost-ray", 54 | "xlrd", "xlwings", "ydata-profiling", "zarr" 55 | ], 56 | "aiDependencies": [ 57 | "accelerate", "bitsandbytes", "triton", "peft", "deepspeed", 58 | "optimum", "llama-cpp-python", "AutoGPTQ", "flash-attn", 59 | "vllm", "ctransformers", "exllama", "gradio", "streamlit", 60 | "fastapi", "sentence-transformers", "langchain", "llama-index", 61 | "transformers", "diffusers", "pytorch-lightning", "einops", 62 | "safetensors", "tokenizers", "datasets", "evaluate", "loralib", 63 | "optimum", "timm", "wandb", "mlflow", "optuna", "hydra-core", 64 | "onnx", "onnxruntime", "onnxruntime-gpu", "tensorboardX", 65 | "huggingface_hub", "model-index", "hf-transfer", "accelerator", 66 | "torch", "torchvision", "torchaudio", 67 | "tensorflow", "tensorflow-gpu", 68 | "keras", "theano", "jax", "flax", "mxnet", "caffe", "paddlepaddle", 69 | "nltk", "spacy", "gensim", "pattern", "textblob", "polyglot", 70 | "stanza", "huggingface_hub", "tokenizers", 71 | "transformers-interpret", "jieba", "fasttext", "allennlp", "fairseq", 72 | "flair", "bert-serving-server", "bert-serving-client", "rasa", 73 | "adapter-transformers", "sentence-splitter", "text2vec", "keybert", 74 | "opencv-python", "scikit-image", "mahotas", "imageio", "albumentations", 75 | "kornia", "pytesseract", "pywavelets", 76 | "opencv-python-headless", "detectron2", "mmcv", "mmdet", "mmseg", 77 | "supervision", "roboflow", "yolov5", "ultralytics", "facenet-pytorch", 78 | "dlib", "mediapipe", "face-recognition", "imgaug", "kornia", 79 | "xgboost", "lightgbm", "catboost", "pymc", "pystan", "prophet", 80 | "ray", "tune", "horovod", "fairscale", "thinc", 81 | "dash", "streamlit", "panel", "voila", "gradio", "pydantic", "starlette", 82 | "stable-diffusion-webui", "audioldm", "audiocraft", "bark", 83 | "comfyui", "taming-transformers", "coqui-tts", "open-clip", 84 | "whisper", "speechbrain", "moviepy", 85 | "gymnasium", "gym", "stable-baselines3", "tianshou", "rlgym", "spinningup", 86 | "langflow", "llm", "modelscope", "text-generation-webui", "fastchat", 87 | "mosaicml", "trl", "megatron-lm", "nanotron", "llamaindex", "bentoml", 88 | "mlflow", "ray[tune]", 89 | "auto-gptq", "exllama", "gptq-for-llama", "llama.cpp", "flashattention", 90 | "onnx-runtime", "tvm", "tensorrt", 91 | "autokeras", "fastai", "segmentation-models", "yolact", "efficientnet", 92 | "resnest", "deepspeech", "librosa", "spleeter", "demucs", "pyannote-audio", 93 | "autoencoders", "openai-whisper", "rl-games", "mindsdb", "pyro-ppl", 94 | "edward", "bambi", "numpyro", "pymc3", "bert-extractive-summarizer", 95 | "summarizer", "sumy", "txtai", "lexnlp", "snorkel", "flyingsquid", 96 | "cleanlab", "focal-loss", "pix2pix", "stylegan", "dcgan", "cycleGAN", 97 | "nerfstudio", "instant-ngp", "habitat-sim", "mujoco-py", "dm_control", 98 | "opensim", "neuralmonkey", "lime", "shap", "alibi", "aif360", "deepchecks", 99 | "evidently", "autogluon", "pycaret", "sktime", "neuralprophet", "gluonts", 100 | "kats", "tslearn", "pyts", "darts", "pmdarima", "orbit-ml", "etna", "tempo", 101 | "torchani", "deepchem", "rdkit", "pymatgen", "psi4", "ase", "matscipy", 102 | "openmm", "botfront", "botbuilder-core", "chatterbot", "nemoguardrails", 103 | "autogluon-tabular", "biobert", "cleanvision", "cleverhans", "cylp", 104 | "deeplake", "deeplip", "deeppavlov", "dgl", "distilbert", "ecco", 105 | "efficientnet-pytorch", "eleuther-ai", "espnet", "fewshot-learning", 106 | "flaml", "frankenstein", "gluon", "gorilla", "gradsim", "grimoireml", 107 | "h2o", "haystack", "hummingbird", "hyperband", "hyperparameter-hunter", 108 | "ignite", "imgviz", "interpret-community", "judge", "kubeflow", 109 | "labml", "laion", "lark-parser", "ludwig", "metaflow", "miniai", 110 | "mmocr", "modAL", "nebuly", "nemo", "nnabla", "onnxmltools", 111 | "opacus", "openvino", "petastorm", "piccolotto", "pytorch3d", 112 | "pywick", "qiskit", "quantus", "recommenders", "replay", "rllib", 113 | "robustdg", "sagemaker", "scikeras", "seldon-core", "shapash", 114 | "skorch", "snips-nlu", "spago", "splade", "tensorflow-addons", 115 | "tensorflow-decision-forests", "tensorflow-probability", "tensorflow-serving", 116 | "torchserve", "trax", "vectorhub", "vowpal-wabbit", "xformers", "zenml","tqwen-vl-utils", 117 | "huggingface-hub","gradio_client" 118 | ] 119 | } 120 | -------------------------------------------------------------------------------- /config/dependency_cache.json: -------------------------------------------------------------------------------- 1 | { 2 | "typing_extensions": "Backported and Experimental Type Hints for Python 3.8+", 3 | "accelerate": "Accelerate", 4 | "typing-inspection": "Runtime typing introspection tools", 5 | "tzdata": "Provider of IANA time zone data", 6 | "aiofiles": "File support for asyncio.", 7 | "urllib3": "HTTP library with thread-safe connection pooling, file post, and more.", 8 | "uvicorn": "The lightning-fast ASGI server.", 9 | "annotated-types": "Reusable constraint types to use with typing.Annotated", 10 | "anyio": "High level compatibility layer for multiple asynchronous event loop implementations", 11 | "attrs": "Classes Without Boilerplate", 12 | "beautifulsoup4": "Screen-scraping library", 13 | "webcolors": "A library for working with the color formats defined by HTML and CSS.", 14 | "webencodings": "Character encoding aliases for legacy web content", 15 | "websockets": "An implementation of the WebSocket Protocol (RFC 6455 & 7692)", 16 | "wget": "pure python download utility", 17 | "blinker": "Fast, simple object-to-object and broadcast signaling", 18 | "certifi": "Python package for providing Mozilla's CA Bundle.", 19 | "zipp": "Backport of pathlib-compatible object wrapper for zip files", 20 | "cffi": "Foreign Function Interface for Python calling C code.", 21 | "chardet": "Universal encoding detector for Python 3", 22 | "charset-normalizer": "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet.", 23 | "click": "Composable command line interface toolkit", 24 | "cmake": "CMake is an open-source, cross-platform family of tools designed to build, test and package software", 25 | "colorama": "Cross-platform colored terminal text.", 26 | "coloredlogs": "Colored terminal output for Python's logging module", 27 | "cryptography": "cryptography is a package which provides cryptographic recipes and primitives to Python developers.", 28 | "cycler": "Composable style cycles", 29 | "datasets": "HuggingFace community-driven open-source library of datasets", 30 | "diffusers": "State-of-the-art diffusion in PyTorch and JAX.", 31 | "distro": "Distro - an OS platform information API", 32 | "einops": "A new flavour of deep learning operations", 33 | "fastapi": "FastAPI framework, high performance, easy to learn, fast to code, ready for production", 34 | "ffmpy": "A simple Python wrapper for FFmpeg", 35 | "filelock": "A platform independent file lock.", 36 | "flatbuffers": "The FlatBuffers serialization format for Python", 37 | "fonttools": "Tools to manipulate font files", 38 | "frozenlist": "A list-like structure which implements collections.abc.MutableSequence", 39 | "fsspec": "File-system specification", 40 | "gradio": "Python library for easily interacting with trained machine learning models", 41 | "gradio_client": "Python library for easily interacting with trained machine learning models", 42 | "groovy": "A small Python library created to help developers protect their applications from Server Side Request Forgery (SSRF) attacks.", 43 | "h11": "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1", 44 | "httpcore": "A minimal low-level HTTP client.", 45 | "httpx": "The next generation HTTP client.", 46 | "httpx-sse": "Consume Server-Sent Event (SSE) messages with HTTPX.", 47 | "huggingface-hub": "Client library to download and publish models, datasets and other repos on the huggingface.co hub", 48 | "humanfriendly": "Human friendly output for text interfaces using Python", 49 | "idna": "Internationalized Domain Names in Applications (IDNA)", 50 | "importlib_metadata": "Read metadata from Python packages", 51 | "jinja2": "A very fast and expressive template engine.", 52 | "jiter": "Fast iterable JSON parser.", 53 | "joblib": "Lightweight pipelining with Python functions", 54 | "kiwisolver": "A fast implementation of the Cassowary constraint solver", 55 | "lxml": "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API.", 56 | "markdown-it-py": "Python port of markdown-it. Markdown parsing, done right!", 57 | "markupsafe": "Safely add untrusted strings to HTML/XML markup.", 58 | "matplotlib": "Python plotting package", 59 | "mdurl": "Markdown URL utilities", 60 | "mpmath": "Python library for arbitrary-precision floating-point arithmetic", 61 | "multidict": "multidict implementation", 62 | "networkx": "Python package for creating and manipulating graphs and networks", 63 | "numpy": "Fundamental package for array computing in Python", 64 | "ollama": "The official Python client for Ollama.", 65 | "onnxruntime": "ONNX Runtime is a runtime accelerator for Machine Learning models", 66 | "opencv-python": "Wrapper package for OpenCV python bindings.", 67 | "orjson": "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy", 68 | "packaging": "Core utilities for Python packages", 69 | "pandas": "Powerful data structures for data analysis, time series, and statistics", 70 | "pillow": "Python Imaging Library (Fork)", 71 | "pip": "The PyPA recommended tool for installing Python packages.", 72 | "platformdirs": "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`.", 73 | "propcache": "Accelerated property cache", 74 | "protobuf": null, 75 | "psutil": "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7.", 76 | "pyarrow": "Python library for Apache Arrow", 77 | "pycparser": "C parser in Python", 78 | "pydantic": "Data validation using Python type hints", 79 | "pydantic_core": "Core functionality for Pydantic validation and serialization", 80 | "pydub": "Manipulate audio with an simple and easy high level interface", 81 | "pygments": "Pygments is a syntax highlighting package written in Python.", 82 | "pymupdf": "A high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents.", 83 | "pyparsing": "pyparsing module - Classes and methods to define and execute parsing grammars", 84 | "pyreadline3": "A python implementation of GNU readline.", 85 | "python-dateutil": "Extensions to the standard Python datetime module", 86 | "python-dotenv": "Read key-value pairs from a .env file and set them as environment variables", 87 | "python-multipart": "A streaming multipart parser for Python", 88 | "pytz": "World timezone definitions, modern and historical", 89 | "pywin32": "Python for Window Extensions", 90 | "pyyaml": "YAML parser and emitter for Python", 91 | "regex": "Alternative regular expression module, to replace re.", 92 | "requests": "Python HTTP for Humans.", 93 | "rich": "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal", 94 | "ruff": "An extremely fast Python linter and code formatter, written in Rust.", 95 | "safehttpx": "A small Python library created to help developers protect their applications from Server Side Request Forgery (SSRF) attacks.", 96 | "safetensors": null, 97 | "scikit-learn": "A set of python modules for machine learning and data mining", 98 | "scipy": "Fundamental algorithms for scientific computing in Python", 99 | "semantic-version": "A library implementing the 'SemVer' scheme.", 100 | "sentencepiece": "SentencePiece python wrapper", 101 | "setuptools": "Easily download, build, install, upgrade, and uninstall Python packages", 102 | "shellingham": "Tool to Detect Surrounding Shell", 103 | "six": "Python 2 and 3 compatibility utilities", 104 | "sniffio": "Sniff out which async library your code is running under", 105 | "soupsieve": "A modern CSS selector implementation for Beautiful Soup.", 106 | "starlette": "The little ASGI library that shines.", 107 | "sympy": "Computer algebra system (CAS) in Python", 108 | "tokenizers": null, 109 | "tomlkit": "Style preserving TOML library", 110 | "torch": "Tensors and Dynamic neural networks in Python with strong GPU acceleration", 111 | "torchaudio": "An audio package for PyTorch", 112 | "torchvision": "image and video datasets and models for torch deep learning", 113 | "tqdm": "Fast, Extensible Progress Meter", 114 | "transformers": "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow", 115 | "typer": "Typer, build great CLIs. Easy to code. Based on Python type hints.", 116 | "antlr4-python3-runtime": "ANTLR 4.13.2 runtime for Python 3", 117 | "av": "Pythonic bindings for FFmpeg's libraries.", 118 | "absl-py": "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py.", 119 | "addict": "Addict is a dictionary whose items can be set using both attribute and item syntax.", 120 | "aggdraw": "High quality drawing interface for PIL.", 121 | "aiohappyeyeballs": "Happy Eyeballs for asyncio", 122 | "aiohttp": "Async http client/server framework (asyncio)", 123 | "aiosignal": "aiosignal: a list of registered asynchronous callbacks", 124 | "albucore": "High-performance image processing functions for deep learning and computer vision.", 125 | "albumentations": "Fast, flexible, and advanced augmentation library for deep learning, computer vision, and medical imaging. Albumentations offers a wide range of transformations for both 2D (images, masks, bboxes, keypoints) and 3D (volumes, volumetric masks, keypoints) data, with optimized performance and seamless integration into ML workflows.", 126 | "altair": "Vega-Altair: A declarative statistical visualization library for Python.", 127 | "audioread": "Multi-library, cross-platform audio decoding.", 128 | "bert-score": "PyTorch implementation of BERT score", 129 | "bitsandbytes": "k-bit optimizers and matrix multiplication routines.", 130 | "bizyengine": "[a/BizyAir](https://github.com/siliconflow/BizyAir) Comfy Nodes that can run in any environment.", 131 | "bizyui": "Web resources for [a/BizyAir](https://github.com/siliconflow/BizyAir) which contains Comfy Nodes that can run in any environment.", 132 | "blend_modes": "Image processing blend modes", 133 | "blind-watermark": "Blind Watermark in Python", 134 | "blis": "The Blis BLAS-like linear algebra library, as a self-contained C-extension.", 135 | "braceexpand": "Bash-style brace expansion for Python", 136 | "cachetools": "Extensible memoizing collections and decorators", 137 | "catalogue": "Super lightweight function registries for your library", 138 | "clip": "A CLI clipboard manager", 139 | "clip-interrogator": "Generate a prompt from an image", 140 | "cloudpathlib": "pathlib-style classes for cloud storage services.", 141 | "colorlog": "Add colours to the output of Python's logging module.", 142 | "color-matcher": "Package enabling color transfer across images", 143 | "colour-science": "Colour Science for Python", 144 | "comfyui_frontend_package": null, 145 | "comfyui_workflow_templates": "ComfyUI workflow templates package", 146 | "confection": "The sweetest config system for Python", 147 | "contourpy": "Python library for calculating contours of 2D quadrilateral grids", 148 | "cssselect2": "CSS selectors for Python ElementTree", 149 | "cupy-cuda12x": "CuPy: NumPy & SciPy for GPU", 150 | "cymem": "Manage calls to calloc/free through Cython", 151 | "cython": "The Cython compiler for writing C extensions in the Python language.", 152 | "ddt": "Data-Driven/Decorated Tests", 153 | "decorator": "Decorators for Humans", 154 | "deepdiff": "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other.", 155 | "deprecated": "Python @deprecated decorator to deprecate old python classes, functions or methods.", 156 | "dill": "serialize all of Python", 157 | "diskcache": "Disk Cache -- Disk and file backed persistent cache.", 158 | "dlib": "A toolkit for making real world machine learning and data analysis applications", 159 | "docopt": "Pythonic argument parser, that will make you smile", 160 | "docstring_parser": "Parse Python docstrings in reST, Google and Numpydoc format", 161 | "docutils": "Docutils -- Python Documentation Utilities", 162 | "easydict": "Access dict values as attributes (works recursively).", 163 | "eval_type_backport": "Like `typing._eval_type`, but lets older Python versions use newer typing features.", 164 | "facexlib": "Basic face library", 165 | "fairscale": "FairScale: A PyTorch library for large-scale and high-performance training.", 166 | "fal_client": "Python client for fal.ai", 167 | "fastrlock": "Fast, re-entrant optimistic lock implemented in Cython", 168 | "filterpy": "Kalman filtering and optimal estimation library", 169 | "flet": "Flet for Python - easily build interactive multi-platform apps in Python", 170 | "freetype-py": "Freetype python bindings", 171 | "ftfy": "Fixes mojibake and other problems with Unicode, after the fact", 172 | "fvcore": "Collection of common code shared among different research projects in FAIR computer vision team", 173 | "gdown": "Google Drive Public File/Folder Downloader", 174 | "gguf": "Read and write ML models in GGUF for GGML", 175 | "gitdb": "Git Object Database", 176 | "gitpython": "GitPython is a Python library used to interact with Git repositories", 177 | "glfw": "A ctypes-based wrapper for GLFW3.", 178 | "glitch_this": "A package to glitch images and GIFs, with highly customizable options!", 179 | "googleapis-common-protos": "Common protobufs used in Google APIs", 180 | "google-ai-generativelanguage": "Google Ai Generativelanguage API client library", 181 | "google-api-core": "Google API client core library", 182 | "google-api-python-client": "Google API Client Library for Python", 183 | "google-auth": "Google Authentication Library", 184 | "google-auth-httplib2": "Google Authentication Library: httplib2 transport", 185 | "google-cloud-core": "Google Cloud API client core library", 186 | "google-cloud-storage": "Google Cloud Storage API client library", 187 | "google-crc32c": "A python wrapper of the C library 'Google CRC32C'", 188 | "google-genai": "GenAI Python SDK", 189 | "google-generativeai": "Google Generative AI High level API client library and tools.", 190 | "google-resumable-media": "Utilities for Google Media Downloads and Resumable Uploads", 191 | "grpcio": "HTTP/2-based RPC framework", 192 | "grpcio-status": "Status proto mapping for gRPC", 193 | "h5py": "Read and write HDF5 files from Python", 194 | "httplib2": "A comprehensive HTTP client library.", 195 | "hydra-core": "A framework for elegantly configuring complex applications", 196 | "imageio": "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats.", 197 | "imageio-ffmpeg": "FFMPEG wrapper for Python", 198 | "image-reward": "ImageReward", 199 | "img2texture": "Command line utility for converting images to seamless tiles.", 200 | "inputimeout": "Multi platform standard input with timeout", 201 | "insightface": "InsightFace Python Library", 202 | "iopath": "A library for providing I/O abstraction.", 203 | "jax": "Differentiate, compile, and transform Numpy code.", 204 | "jaxlib": "XLA library for JAX", 205 | "jsonschema": "An implementation of JSON Schema validation for Python", 206 | "jsonschema-specifications": "The JSON Schema meta-schemas and vocabularies, exposed as a Registry", 207 | "kornia": "Open Source Differentiable Computer Vision Library for PyTorch", 208 | "kornia_rs": "Low level implementations for computer vision in Rust", 209 | "langcodes": "Tools for labeling human languages with IETF language tags", 210 | "language_data": "Supplementary data about languages used by the langcodes module", 211 | "lark": "a modern parsing library", 212 | "lazy_loader": "Makes it easy to load subpackages and functions on demand.", 213 | "librosa": "Python module for audio and music processing", 214 | "lightning-utilities": "Lightning toolbox for across the our ecosystem.", 215 | "litelama": "A lightweight LAMA model inference wrapper", 216 | "llama_cpp_python": "Python bindings for the llama.cpp library", 217 | "llvmlite": "lightweight wrapper around basic LLVM functionality", 218 | "lmdb": "Universal Python binding for the LMDB 'Lightning' Database", 219 | "loguru": "Python logging made (stupidly) simple", 220 | "manifold3d": "Library for geometric robustness", 221 | "mapbox_earcut": "Python bindings for the mapbox earcut C++ polygon triangulation library", 222 | "marisa-trie": "Static memory-efficient and fast Trie-like structures for Python.", 223 | "matrix-client": "Client-Server SDK for Matrix", 224 | "mediapipe": "MediaPipe is the simplest way for researchers and developers to build world-class ML solutions and applications for mobile, edge, cloud and the web.", 225 | "ml_dtypes": null, 226 | "moviepy": "Video editing with Python", 227 | "msgpack": "MessagePack serializer", 228 | "mss": "An ultra fast cross-platform multiple screenshots module in pure python using ctypes.", 229 | "multiprocess": "better multiprocessing and multithreading in Python", 230 | "murmurhash": "Cython bindings for MurmurHash", 231 | "narwhals": "Extremely lightweight compatibility layer between dataframe libraries", 232 | "numba": "compiling Python code using LLVM", 233 | "nvidia-ml-py": "Python Bindings for the NVIDIA Management Library", 234 | "oauthlib": "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic", 235 | "omegaconf": "A flexible configuration library", 236 | "onnx": "Open Neural Network Exchange", 237 | "onnxruntime-gpu": "ONNX Runtime is a runtime accelerator for Machine Learning models", 238 | "openai": "The official Python library for the openai API", 239 | "opencv-contrib-python": "Wrapper package for OpenCV python bindings.", 240 | "open_clip_torch": "Open reproduction of consastive language-image pretraining (CLIP) and related.", 241 | "opt_einsum": "Path optimization of einsum functions.", 242 | "orderly-set": "Orderly set", 243 | "pdf2image": "A wrapper around the pdftoppm and pdftocairo command line tools to convert PDF to a PIL Image list.", 244 | "peft": "Parameter-Efficient Fine-Tuning (PEFT)", 245 | "piexif": "To simplify exif manipulations with python. Writing, reading, and more...", 246 | "pilgram": "library for instagram filters", 247 | "pixeloe": "Detail-Oriented Pixelization based on Contrast-Aware Outline Expansion.", 248 | "pooch": "A friend to fetch your data files", 249 | "portalocker": "Wraps the portalocker recipe for easy usage", 250 | "preshed": "Cython hash table that trusts the keys are pre-hashed", 251 | "prettytable": "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format", 252 | "proglog": "Log and progress bar manager for console, notebooks, web...", 253 | "proto-plus": "Beautiful, Pythonic protocol buffers", 254 | "psd-tools": "Python package for working with Adobe Photoshop PSD files", 255 | "pyasn1": "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)", 256 | "pyasn1_modules": "A collection of ASN.1-based protocols modules", 257 | "pycollada": "python library for reading and writing collada documents", 258 | "pydeck": "Widget for deck.gl maps", 259 | "pygit2": "Python bindings for libgit2.", 260 | "pygithub": "Use the full Github API v3", 261 | "pyglet": "pyglet is a cross-platform games and multimedia package.", 262 | "pyjwt": "JSON Web Token implementation in Python", 263 | "pymatting": "Python package for alpha matting.", 264 | "pynacl": "Python binding to the Networking and Cryptography (NaCl) library", 265 | "pynvml": "Python utilities for the NVIDIA Management Library", 266 | "pyopengl": "Standard OpenGL bindings for Python", 267 | "pypdf2": "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files", 268 | "pyrender": "Easy-to-use Python renderer for 3D visualization", 269 | "pysocks": "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information.", 270 | "pytorch-lightning": "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers. Scale your models. Write less boilerplate.", 271 | "pywavelets": "PyWavelets, wavelet transform module", 272 | "pyzbar": "Read one-dimensional barcodes and QR codes from Python 2 and 3.", 273 | "py-cpuinfo": "Get CPU info with pure Python", 274 | "qrcode": "QR Code image generator", 275 | "referencing": "JSON Referencing + Python", 276 | "rembg": "Remove image background", 277 | "repath": "Generate regular expressions form ExpressJS path patterns", 278 | "reportlab": "The Reportlab Toolkit", 279 | "requirements-parser": "This is a small Python module for parsing Pip requirement files.", 280 | "rich-argparse": "Rich help formatters for argparse and optparse", 281 | "rpds-py": "Python bindings to Rust's persistent data structures (rpds)", 282 | "rsa": "Pure-Python RSA implementation", 283 | "rtree": "R-Tree spatial index for Python GIS", 284 | "runwayml": "The official Python library for the runwayml API", 285 | "sageattention": "Accurate and efficient 8-bit plug-and-play attention.", 286 | "scikit-image": "Image processing in Python", 287 | "seaborn": "Statistical data visualization", 288 | "segment-anything": "", 289 | "shapely": "Manipulation and analysis of geometric objects", 290 | "shtab": "Automagic shell tab completion for Python CLI applications", 291 | "simpleeval": "A simple, safe single expression evaluator library.", 292 | "simsimd": "Portable mixed-precision BLAS-like vector math library for x86 and ARM", 293 | "smart-open": "Utils for streaming large files (S3, HDFS, GCS, Azure Blob Storage, gzip, bz2...)", 294 | "smmap": "A pure Python implementation of a sliding window memory map manager", 295 | "smplx": "PyTorch module for loading the SMPLX body model", 296 | "sounddevice": "Play and Record Sound with Python", 297 | "soundfile": "An audio library based on libsndfile, CFFI and NumPy", 298 | "soxr": "High quality, one-dimensional sample-rate conversion library", 299 | "spacy": "Industrial-strength Natural Language Processing (NLP) in Python", 300 | "spacy-legacy": "Legacy registered functions for spaCy backwards compatibility", 301 | "spacy-loggers": "Logging utilities for SpaCy", 302 | "spandrel": "Give your project support for a variety of PyTorch model architectures, including auto-detecting model architecture from just .pth files. spandrel gives you arch support.", 303 | "srsly": "Modern high-performance serialization utilities for Python", 304 | "streamlit": "A faster way to build and share data apps", 305 | "stringzilla": "SIMD-accelerated string search, sort, hashes, fingerprints, & edit distances", 306 | "svg.path": "SVG path objects and parser", 307 | "svglib": "A pure-Python library for reading and converting SVG", 308 | "tabulate": "Pretty-print tabular data", 309 | "tenacity": "Retry code until it succeeds", 310 | "termcolor": "ANSI color formatting for output in terminal", 311 | "thinc": "A refreshing functional take on deep learning, compatible with your favorite libraries", 312 | "threadpoolctl": "threadpoolctl", 313 | "tifffile": "Read and write TIFF files", 314 | "timm": "PyTorch Image Models", 315 | "tinycss2": "A tiny CSS parser", 316 | "tipo-kgen": "TIPO: Text to Image with text Presampling for Optimal prompting", 317 | "toml": "Python Library for Tom's Obvious, Minimal Language", 318 | "torchmetrics": "PyTorch native Metrics", 319 | "torchscale": "Transformers at any scale", 320 | "torchsde": "SDE solvers and stochastic adjoint sensitivity analysis in PyTorch.", 321 | "tornado": "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.", 322 | "trampoline": "Simple and tiny yield-based trampoline implementation.", 323 | "transparent-background": "Make images with transparent background", 324 | "trimesh": "Import, export, process, analyze and view triangular meshes.", 325 | "triton-windows": "A language and compiler for custom Deep Learning operations", 326 | "typer-config": "Utilities for working with configuration files in typer CLIs. ", 327 | "types-setuptools": "Typing stubs for setuptools", 328 | "tyro": "CLI interfaces & config objects, from types", 329 | "ultralytics": "Ultralytics YOLO 🚀 for SOTA object detection, multi-object tracking, instance segmentation, pose estimation and image classification.", 330 | "ultralytics-thop": "Ultralytics THOP package for fast computation of PyTorch model FLOPs and parameters.", 331 | "uritemplate": "Implementation of RFC 6570 URI Templates", 332 | "uv": "An extremely fast Python package and project manager, written in Rust.", 333 | "vhacdx": "Python bindings for VHACD", 334 | "wand": "Ctypes-based simple MagickWand API binding for Python", 335 | "wasabi": "A lightweight console printing and formatting toolkit", 336 | "watchdog": "Filesystem events monitoring", 337 | "wcwidth": "Measures the displayed width of unicode strings in a terminal", 338 | "weasel": "Weasel: A small and easy workflow system", 339 | "webdataset": "High performance storage and I/O for deep learning and data processing.", 340 | "win32_setctime": "A small Python utility to set file creation time on Windows", 341 | "wrapt": "Module for decorators, wrappers and monkey patching.", 342 | "xformers": "XFormers: A collection of composable Transformer building blocks.", 343 | "xxhash": "Python binding for xxHash", 344 | "yacs": "Yet Another Configuration System", 345 | "yapf": "A formatter for Python code", 346 | "yarl": "Yet another URL library", 347 | "zhipuai": "A SDK library for accessing big model apis from ZhipuAI", 348 | "asyncio": "reference implementation of PEP 3156", 349 | "bottle": "Fast and simple WSGI-framework for small web-applications.", 350 | "clr_loader": "Generic pure Python loader for .NET runtimes", 351 | "customtkinter": "Create modern looking GUIs with Python", 352 | "darkdetect": "Detect OS Dark Mode from Python", 353 | "ebooklib": "Ebook library which can handle EPUB2/EPUB3 and Kindle format", 354 | "elasticsearch": "Python client for Elasticsearch", 355 | "et_xmlfile": "An implementation of lxml.xmlfile for the standard library", 356 | "flash_attn": "Flash Attention: Fast and Memory-Efficient Exact Attention", 357 | "flask": "A simple framework for building complex web applications.", 358 | "flask-cors": "A Flask extension simplifying CORS support", 359 | "iniconfig": "brain-dead simple config-ini parsing", 360 | "ipex-llm": "Large Language Model Develop Toolkit", 361 | "itsdangerous": "Safely pass data to untrusted environments and back.", 362 | "langchain": "Building applications with LLMs through composability", 363 | "lit": "A Software Testing Tool", 364 | "mcp": "Model Context Protocol SDK", 365 | "mcp-server-time": "A Model Context Protocol server providing tools for time queries and timezone conversions for LLMs", 366 | "ninja": "Ninja is a small build system with a focus on speed", 367 | "nltk": "Natural Language Toolkit", 368 | "opencv-python-headless": "Wrapper package for OpenCV python bindings.", 369 | "openpyxl": "A Python library to read/write Excel 2010 xlsx/xlsm files", 370 | "pefile": "Python PE parsing module", 371 | "pipdeptree": "Command line utility to show dependency tree of packages.", 372 | "pluggy": "plugin and hook calling mechanisms for python", 373 | "prometheus_client": "Python client for the Prometheus monitoring system.", 374 | "prov": "A library for W3C Provenance Data Model supporting PROV-JSON, PROV-XML and PROV-O (RDF)", 375 | "proxy_tools": "Proxy Implementation", 376 | "pybind11": "Seamless operability between C++11 and Python", 377 | "pydantic-settings": "Settings management using Pydantic", 378 | "pyinstaller": "PyInstaller bundles a Python application and all its dependencies into a single package.", 379 | "pyinstaller-hooks-contrib": "Community maintained hooks for PyInstaller", 380 | "pymongo": "PyMongo - the Official MongoDB Python driver", 381 | "pymysql": "Pure Python MySQL Driver", 382 | "pytesseract": "Python-tesseract is a python wrapper for Google's Tesseract-OCR", 383 | "pytest": "pytest: simple powerful testing with Python", 384 | "pythonnet": ".NET and Mono integration for Python", 385 | "python-docx": "Create, read, and update Microsoft Word .docx files.", 386 | "pywebview": "Build GUI for your Python program with JavaScript, HTML, and CSS", 387 | "pywin32-ctypes": "A (partial) reimplementation of pywin32 using ctypes/cffi", 388 | "redis": "Python client for Redis database and key-value store", 389 | "sentence-transformers": "Embeddings, Retrieval, and Reranking", 390 | "sqlalchemy": "Database Abstraction Library", 391 | "sse-starlette": "SSE plugin for Starlette", 392 | "werkzeug": "The comprehensive WSGI web application library.", 393 | "wheel": "A built-package format for Python", 394 | "xlrd": "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files", 395 | "altgraph": "Python graph (network) package" 396 | } -------------------------------------------------------------------------------- /config/python_environments.json: -------------------------------------------------------------------------------- 1 | { 2 | "environments": [ 3 | { 4 | "id": "system", 5 | "name": "系统环境", 6 | "path": "C:\\Users\\Dontdrunk\\AppData\\Local\\Programs\\Python\\Python312\\python.exe", 7 | "type": "system", 8 | "version": "3.12.9" 9 | }, 10 | { 11 | "id": "4e4d80b2-828f-40ce-ad17-dd2ea787e963", 12 | "name": "ComfyUI环境", 13 | "path": "D:\\Projects\\Procedure\\ComfyUI-X\\python_embeded\\python.exe", 14 | "type": "custom", 15 | "version": "3.12.9" 16 | } 17 | ], 18 | "current": "system" 19 | } -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /* 主CSS文件 - 仅用于导入其他模块 */ 2 | 3 | /* 基础样式 */ 4 | @import 'modules/base.css'; 5 | 6 | /* 功能组件 */ 7 | @import 'modules/components.css'; 8 | 9 | /* 工具与动画 */ 10 | @import 'modules/utilities.css'; 11 | -------------------------------------------------------------------------------- /css/modules/base.css: -------------------------------------------------------------------------------- 1 | /* Base.css - 基础核心模块 */ 2 | 3 | /* ====================================================== 4 | 设计系统变量 - 全局变量定义 5 | ====================================================== */ 6 | :root { 7 | /* 扩展色板 - 主色 */ 8 | --primary-100: #e3f2fd; 9 | --primary-200: #bbdefb; 10 | --primary-300: #90caf9; 11 | --primary-400: #64b5f6; 12 | --primary-500: #42a5f5; 13 | --primary-600: #2196f3; 14 | --primary-700: #1e88e5; 15 | --primary-800: #1976d2; 16 | --primary-900: #1565c0; 17 | 18 | /* 中性色 */ 19 | --neutral-50: #fafafa; 20 | --neutral-100: #f5f5f5; 21 | --neutral-200: #eeeeee; 22 | --neutral-300: #e0e0e0; 23 | --neutral-400: #bdbdbd; 24 | --neutral-500: #9e9e9e; 25 | --neutral-600: #757575; 26 | --neutral-700: #616161; 27 | --neutral-800: #424242; 28 | --neutral-900: #212121; 29 | 30 | /* 功能色 */ 31 | --success-color: #00b894; 32 | --warning-color: #f39c12; 33 | --error-color: #e74c3c; 34 | --info-color: #3498db; 35 | 36 | /* 应用变量 */ 37 | --primary-color: var(--primary-600); 38 | --primary-light: var(--primary-400); 39 | --primary-dark: var(--primary-800); 40 | --secondary-color: var(--primary-800); 41 | 42 | --background-color: var(--neutral-100); 43 | --card-color: #ffffff; 44 | --text-color: var(--neutral-800); 45 | --secondary-text: var(--neutral-600); 46 | --border-color: var(--neutral-300); 47 | 48 | /* 阴影 */ 49 | --shadow-sm: 0 1px 2px rgba(0,0,0,0.05); 50 | --shadow-md: 0 4px 6px rgba(0,0,0,0.07); 51 | --shadow-lg: 0 10px 15px rgba(0,0,0,0.1); 52 | 53 | /* 间距 */ 54 | --space-xs: 4px; 55 | --space-sm: 8px; 56 | --space-md: 16px; 57 | --space-lg: 24px; 58 | --space-xl: 32px; 59 | 60 | /* 边框圆角 */ 61 | --radius-sm: 4px; 62 | --radius-md: 8px; 63 | --radius-lg: 12px; 64 | --radius-xl: 20px; 65 | 66 | /* 排版 */ 67 | --font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, system-ui, Roboto, 'Helvetica Neue', sans-serif; 68 | } 69 | 70 | /* ====================================================== 71 | 基础元素样式 72 | ====================================================== */ 73 | * { 74 | margin: 0; 75 | padding: 0; 76 | box-sizing: border-box; 77 | } 78 | 79 | body { 80 | font-family: var(--font-family); 81 | background-color: var(--background-color); 82 | color: var(--text-color); 83 | line-height: 1.6; 84 | font-size: 16px; 85 | } 86 | 87 | .container { 88 | max-width: 1200px; 89 | margin: 0 auto; 90 | padding: var(--space-lg); 91 | } 92 | 93 | /* 标题样式 */ 94 | h1, h2, h3 { 95 | color: var(--primary-color); 96 | font-weight: 600; 97 | } 98 | 99 | h1 { 100 | margin-right: var(--space-lg); 101 | font-size: 1.75rem; 102 | } 103 | 104 | h2 { 105 | font-size: 1.5rem; 106 | margin-bottom: var(--space-md); 107 | } 108 | 109 | h3 { 110 | font-size: 1.25rem; 111 | margin-bottom: var(--space-sm); 112 | } 113 | 114 | /* 按钮基础样式 */ 115 | button { 116 | background-color: var(--primary-color); 117 | color: white; 118 | border: none; 119 | padding: 8px 16px; 120 | border-radius: var(--radius-sm); 121 | font-weight: 500; 122 | cursor: pointer; 123 | transition: all 0.2s ease; 124 | font-size: 0.9rem; 125 | display: inline-flex; 126 | align-items: center; 127 | justify-content: center; 128 | gap: var(--space-xs); 129 | } 130 | 131 | button:hover { 132 | background-color: var(--primary-dark); 133 | transform: translateY(-1px); 134 | box-shadow: var(--shadow-sm); 135 | } 136 | 137 | button:active { 138 | transform: translateY(0); 139 | } 140 | 141 | button:disabled { 142 | opacity: 0.6; 143 | cursor: not-allowed; 144 | transform: none; 145 | box-shadow: none; 146 | } 147 | 148 | /* 表单元素基础样式 */ 149 | input[type="text"], 150 | input[type="file"], 151 | select { 152 | padding: 10px 12px; 153 | border: 1px solid var(--border-color); 154 | border-radius: var(--radius-sm); 155 | font-family: var(--font-family); 156 | font-size: 0.95rem; 157 | transition: all 0.2s; 158 | } 159 | 160 | input[type="text"]:focus, 161 | select:focus { 162 | outline: none; 163 | border-color: var(--primary-color); 164 | box-shadow: 0 0 0 3px var(--primary-100); 165 | } 166 | 167 | /* ====================================================== 168 | 布局组件 169 | ====================================================== */ 170 | /* 头部样式 */ 171 | header { 172 | display: flex; 173 | flex-wrap: wrap; 174 | justify-content: space-between; 175 | align-items: center; 176 | margin-bottom: var(--space-xl); 177 | padding-bottom: var(--space-md); 178 | border-bottom: 1px solid var(--border-color); 179 | } 180 | 181 | /* 状态栏样式 */ 182 | .status-bar { 183 | display: flex; 184 | justify-content: space-between; 185 | align-items: center; 186 | font-size: 0.875rem; 187 | color: var(--secondary-text); 188 | margin-left: auto; 189 | padding-left: var(--space-lg); 190 | } 191 | 192 | .system-info { 193 | display: flex; 194 | gap: var(--space-lg); 195 | align-items: center; 196 | } 197 | 198 | /* 作者链接样式 */ 199 | .author-links { 200 | display: flex; 201 | align-items: center; 202 | gap: var(--space-sm); 203 | } 204 | 205 | .author-btn { 206 | background: transparent; 207 | border: none; 208 | color: var(--secondary-text); 209 | padding: var(--space-xs); 210 | cursor: pointer; 211 | border-radius: 50%; 212 | width: 36px; 213 | height: 36px; 214 | display: flex; 215 | align-items: center; 216 | justify-content: center; 217 | transition: all 0.2s; 218 | } 219 | 220 | .author-btn:hover { 221 | background-color: var(--neutral-200); 222 | color: var(--primary-color); 223 | transform: scale(1.1); 224 | } 225 | 226 | /* 页脚样式 */ 227 | footer { 228 | margin-top: var(--space-xl); 229 | padding-top: var(--space-lg); 230 | text-align: center; 231 | font-size: 0.875rem; 232 | color: var(--secondary-text); 233 | border-top: 1px solid var(--border-color); 234 | } 235 | 236 | /* ====================================================== 237 | 通知和加载状态 238 | ====================================================== */ 239 | /* 通知样式 */ 240 | .notification { 241 | position: fixed; 242 | top: var(--space-lg); 243 | right: var(--space-lg); 244 | padding: var(--space-md) var(--space-lg); 245 | border-radius: var(--radius-md); 246 | background-color: white; 247 | color: var(--text-primary); 248 | font-weight: 500; 249 | box-shadow: var(--shadow-md); 250 | z-index: 1000; 251 | opacity: 0; 252 | transform: translateY(-20px); 253 | transition: all 0.3s ease; 254 | max-width: 350px; 255 | border-left: 4px solid var(--primary-color); 256 | display: flex; 257 | align-items: center; 258 | gap: 0.75rem; 259 | } 260 | 261 | .notification.success { 262 | border-left-color: var(--success-color); 263 | } 264 | 265 | .notification.error { 266 | border-left-color: var(--error-color); 267 | } 268 | 269 | .notification.warning { 270 | border-left-color: var(--warning-color); 271 | } 272 | 273 | .notification.show { 274 | opacity: 1; 275 | transform: translateY(0); 276 | } 277 | 278 | .notification.hidden { 279 | display: none; 280 | } 281 | 282 | .notification-icon { 283 | display: flex; 284 | align-items: center; 285 | justify-content: center; 286 | width: 20px; 287 | height: 20px; 288 | flex-shrink: 0; 289 | } 290 | 291 | .notification-content { 292 | flex: 1; 293 | } 294 | 295 | .notification-close { 296 | background: none; 297 | border: none; 298 | color: var(--text-secondary); 299 | cursor: pointer; 300 | padding: 4px; 301 | border-radius: 50%; 302 | display: flex; 303 | align-items: center; 304 | justify-content: center; 305 | transition: background-color 0.2s; 306 | } 307 | 308 | .notification-close:hover { 309 | background-color: rgba(0, 0, 0, 0.05); 310 | color: var(--text-primary); 311 | } 312 | 313 | /* 加载状态样式 */ 314 | .loading { 315 | text-align: center; 316 | padding: var(--space-xl); 317 | color: var(--secondary-text); 318 | font-style: italic; 319 | } 320 | 321 | /* ====================================================== 322 | 模态框基础样式 323 | ====================================================== */ 324 | .modal { 325 | display: none; 326 | position: fixed; 327 | top: 0; 328 | left: 0; 329 | right: 0; 330 | bottom: 0; 331 | width: 100%; 332 | height: 100%; 333 | background-color: rgba(0, 0, 0, 0.5); 334 | z-index: 1000; 335 | align-items: center; 336 | justify-content: center; 337 | overflow-y: auto; 338 | padding: var(--space-lg) 0; 339 | } 340 | 341 | .modal-content { 342 | background-color: var(--card-color); 343 | margin: auto; 344 | padding: var(--space-xl); 345 | border-radius: var(--radius-lg); 346 | width: 500px; 347 | max-width: 95%; 348 | max-height: 90vh; 349 | box-shadow: var(--shadow-lg); 350 | position: relative; 351 | overflow-y: auto; 352 | animation: modalFadeIn 0.3s ease; 353 | } 354 | 355 | @keyframes modalFadeIn { 356 | from { opacity: 0; transform: translateY(-20px); } 357 | to { opacity: 1; transform: translateY(0); } 358 | } 359 | 360 | .close-btn { 361 | position: absolute; 362 | top: var(--space-md); 363 | right: var(--space-md); 364 | font-size: 1.5rem; 365 | font-weight: bold; 366 | cursor: pointer; 367 | color: var(--neutral-500); 368 | transition: color 0.2s; 369 | width: 32px; 370 | height: 32px; 371 | display: flex; 372 | align-items: center; 373 | justify-content: center; 374 | border-radius: 50%; 375 | } 376 | 377 | .close-btn:hover { 378 | color: var(--error-color); 379 | background-color: var(--neutral-100); 380 | } 381 | 382 | /* 错误详情模态框样式 */ 383 | .error-modal-content { 384 | width: 600px; 385 | max-width: 90%; 386 | max-height: 80vh; 387 | } 388 | 389 | .error-list { 390 | max-height: 300px; 391 | overflow-y: auto; 392 | margin: 15px 0; 393 | border: 1px solid var(--border-color); 394 | border-radius: 4px; 395 | } 396 | 397 | .error-item { 398 | padding: 10px; 399 | border-bottom: 1px solid var(--border-color); 400 | } 401 | 402 | .error-item:last-child { 403 | border-bottom: none; 404 | } 405 | 406 | .error-package { 407 | font-weight: bold; 408 | color: var(--error-color); 409 | margin-bottom: 5px; 410 | } 411 | 412 | .error-message { 413 | margin-bottom: 5px; 414 | } 415 | 416 | .error-details { 417 | font-size: 0.9em; 418 | background-color: #f5f5f5; 419 | padding: 8px; 420 | border-radius: 4px; 421 | white-space: pre-wrap; 422 | overflow-x: auto; 423 | color: #666; 424 | } 425 | 426 | .modal-footer { 427 | display: flex; 428 | justify-content: flex-end; 429 | margin-top: 15px; 430 | } 431 | 432 | /* ====================================================== 433 | 初始加载界面 434 | ====================================================== */ 435 | #initial-loading-screen { 436 | position: fixed; 437 | top: 0; 438 | left: 0; 439 | width: 100%; 440 | height: 100%; 441 | background-color: var(--background-color); 442 | z-index: 10000; 443 | display: flex; 444 | justify-content: center; 445 | align-items: center; 446 | transition: opacity 0.5s ease; 447 | } 448 | 449 | #initial-loading-screen.fade-out { 450 | opacity: 0; 451 | } 452 | 453 | .initial-loading-content { 454 | background-color: var(--card-color); 455 | border-radius: var(--radius-lg); 456 | box-shadow: var(--shadow-lg); 457 | padding: var(--space-xl); 458 | width: 90%; 459 | max-width: 500px; 460 | text-align: center; 461 | } 462 | 463 | .initial-loading-content h2 { 464 | color: var(--primary-color); 465 | margin-bottom: var(--space-lg); 466 | } 467 | 468 | .initial-progress-container { 469 | height: 10px; 470 | background-color: var(--neutral-200); 471 | border-radius: 5px; 472 | margin-bottom: var(--space-sm); 473 | overflow: hidden; 474 | } 475 | 476 | .initial-progress-bar { 477 | height: 100%; 478 | width: 0; 479 | background-color: var(--primary-color); 480 | transition: width 0.3s ease; 481 | border-radius: 5px; 482 | } 483 | 484 | .initial-progress-text { 485 | font-size: 0.875rem; 486 | color: var(--secondary-text); 487 | font-weight: 500; 488 | margin-bottom: var(--space-md); 489 | } 490 | 491 | .initial-loading-info { 492 | color: var(--secondary-text); 493 | font-size: 0.9rem; 494 | margin-bottom: var(--space-lg); 495 | } 496 | -------------------------------------------------------------------------------- /css/modules/components.css: -------------------------------------------------------------------------------- 1 | /* Components.css - 功能组件模块 */ 2 | 3 | /* ====================================================== 4 | 依赖管理组件 5 | ====================================================== */ 6 | 7 | /* 依赖容器卡片样式 */ 8 | .dependency-container { 9 | background-color: var(--card-color); 10 | border-radius: var(--radius-lg); 11 | box-shadow: var(--shadow-md); 12 | margin-bottom: var(--space-xl); 13 | overflow: hidden; 14 | border: 1px solid var(--border-color); 15 | } 16 | 17 | /* 批量操作面板 */ 18 | .batch-actions { 19 | display: flex; 20 | flex-wrap: wrap; 21 | justify-content: space-between; 22 | align-items: center; 23 | padding: var(--space-md) var(--space-lg); 24 | background-color: var(--neutral-50); 25 | border-bottom: 1px solid var(--border-color); 26 | gap: var(--space-md); 27 | } 28 | 29 | .batch-left { 30 | display: flex; 31 | align-items: center; 32 | gap: var(--space-md); 33 | flex-wrap: wrap; 34 | } 35 | 36 | /* 筛选器容器 */ 37 | .filter-container { 38 | display: flex; 39 | align-items: center; 40 | position: relative; 41 | } 42 | 43 | #dependency-filter { 44 | padding: 8px 12px; 45 | border-radius: var(--radius-sm); 46 | border: 1px solid var(--border-color); 47 | font-size: 0.875rem; 48 | background-color: var(--card-color); 49 | min-width: 120px; 50 | height: 38px; 51 | appearance: none; 52 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23757575' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); 53 | background-repeat: no-repeat; 54 | background-position: right 8px center; 55 | padding-right: 32px; 56 | } 57 | 58 | /* 清理缓存按钮 */ 59 | #clean-cache-btn { 60 | background-color: var(--warning-color); 61 | height: 38px; 62 | } 63 | 64 | #clean-cache-btn:hover { 65 | background-color: #d35400; 66 | } 67 | 68 | /* 全选容器 */ 69 | .select-all-container { 70 | display: flex; 71 | align-items: center; 72 | margin-right: var(--space-md); 73 | padding: var(--space-xs) var(--space-sm); 74 | background-color: var(--neutral-100); 75 | border-radius: var(--radius-sm); 76 | border: 1px solid var(--border-color); 77 | height: 38px; 78 | } 79 | 80 | .select-all-container input[type="checkbox"] { 81 | margin-right: var(--space-sm); 82 | width: 18px; 83 | height: 18px; 84 | cursor: pointer; 85 | } 86 | 87 | .select-all-container label { 88 | cursor: pointer; 89 | user-select: none; 90 | font-weight: 500; 91 | font-size: 0.9rem; 92 | } 93 | 94 | /* 批量按钮 */ 95 | .batch-buttons { 96 | display: flex; 97 | flex-wrap: wrap; 98 | gap: var(--space-sm); 99 | align-items: center; 100 | } 101 | 102 | .batch-btn { 103 | font-size: 0.875rem; 104 | padding: 8px 12px; 105 | height: 38px; 106 | display: flex; 107 | align-items: center; 108 | white-space: nowrap; 109 | border-radius: var(--radius-sm); 110 | } 111 | 112 | /* 操作按钮 */ 113 | .action-btn { 114 | padding: 4px 8px; 115 | font-size: 0.75rem; 116 | border-radius: var(--radius-sm); 117 | transition: all 0.2s; 118 | margin-left: 3px; 119 | min-width: 0; 120 | width: auto; 121 | white-space: nowrap; 122 | flex-shrink: 0; 123 | } 124 | 125 | .action-btn:hover { 126 | transform: translateY(-1px); 127 | box-shadow: var(--shadow-sm); 128 | } 129 | 130 | /* 卸载按钮 - 红色 */ 131 | .action-btn.uninstall { 132 | background-color: var(--error-color); 133 | } 134 | 135 | .action-btn.uninstall:hover { 136 | background-color: #c0392b; 137 | } 138 | 139 | /* 版本切换按钮 - 蓝色 */ 140 | .action-btn.version { 141 | background-color: var(--info-color); 142 | } 143 | 144 | .action-btn.version:hover { 145 | background-color: #2980b9; 146 | } 147 | 148 | /* 更新按钮 - 黄色 */ 149 | .action-btn.update { 150 | background-color: var(--warning-color); 151 | } 152 | 153 | .action-btn.update:hover { 154 | background-color: #d35400; 155 | } 156 | 157 | /* 最新版本按钮 - 绿色 */ 158 | .action-btn.update.latest { 159 | background-color: var(--success-color); 160 | cursor: default; 161 | } 162 | 163 | .action-btn.update.latest:hover { 164 | background-color: #27ae60; 165 | transform: none; 166 | } 167 | 168 | /* 刚更新的依赖项高亮效果 */ 169 | .dependency-item.just-updated { 170 | background-color: var(--primary-100); 171 | border-left: 4px solid var(--primary-color); 172 | transition: all 0.3s ease; 173 | } 174 | 175 | .dependency-item.just-updated:hover { 176 | background-color: var(--primary-200); 177 | } 178 | 179 | /* 依赖列表头部 */ 180 | .dependency-header { 181 | display: flex; 182 | padding: var(--space-md) var(--space-lg); 183 | background-color: var(--neutral-100); 184 | font-weight: 600; 185 | font-size: 0.875rem; 186 | color: var(--secondary-text); 187 | border-bottom: 1px solid var(--border-color); 188 | letter-spacing: 0.5px; 189 | text-transform: uppercase; 190 | } 191 | 192 | /* 依赖列表滚动容器 */ 193 | .dependency-list { 194 | max-height: 450px; 195 | overflow-y: auto; 196 | scrollbar-width: thin; 197 | scrollbar-color: var(--neutral-400) transparent; 198 | } 199 | 200 | .dependency-list::-webkit-scrollbar { 201 | width: 6px; 202 | } 203 | 204 | .dependency-list::-webkit-scrollbar-track { 205 | background: transparent; 206 | } 207 | 208 | .dependency-list::-webkit-scrollbar-thumb { 209 | background-color: var(--neutral-400); 210 | border-radius: 3px; 211 | } 212 | 213 | /* 依赖项样式 */ 214 | .dependency-item { 215 | display: flex; 216 | padding: var(--space-md) var(--space-lg); 217 | border-bottom: 1px solid var(--border-color); 218 | align-items: center; 219 | transition: background-color 0.2s; 220 | } 221 | 222 | .dependency-item:hover { 223 | background-color: var(--neutral-50); 224 | } 225 | 226 | /* 依赖项动画效果 */ 227 | @keyframes updatePulse { 228 | 0% { background-color: rgba(52, 152, 219, 0.2); } 229 | 50% { background-color: rgba(52, 152, 219, 0.05); } 230 | 100% { background-color: rgba(52, 152, 219, 0); } 231 | } 232 | 233 | .dependency-item.just-updated { 234 | animation: updatePulse 3s ease-out; 235 | } 236 | 237 | /* 依赖项颜色分类 */ 238 | .dependency-item.core { 239 | background-color: rgba(52, 152, 219, 0.08); 240 | } 241 | 242 | .dependency-item.ai-model { 243 | background-color: rgba(46, 204, 113, 0.08); 244 | } 245 | 246 | .dependency-item.selected { 247 | background-color: var(--primary-100); 248 | } 249 | 250 | .dependency-item.system { 251 | background-color: rgba(153, 102, 255, 0.08); 252 | } 253 | 254 | .dependency-item.app-required { 255 | background-color: rgba(233, 30, 99, 0.08); 256 | } 257 | 258 | /* 依赖项淡出效果 */ 259 | .dependency-item.fading-out { 260 | opacity: 0.5; 261 | transition: opacity 0.5s; 262 | } 263 | 264 | /* 刷新中的依赖项 */ 265 | .dependency-item.refreshing { 266 | background-color: rgba(255, 255, 204, 0.3); 267 | transition: background-color 0.5s; 268 | } 269 | 270 | /* 依赖项列宽 */ 271 | .col-checkbox { 272 | width: 30px; 273 | display: flex; 274 | align-items: center; 275 | justify-content: center; 276 | } 277 | 278 | .col-checkbox input[type="checkbox"] { 279 | width: 18px; 280 | height: 18px; 281 | cursor: pointer; 282 | } 283 | 284 | .col-name { 285 | width: 20%; 286 | font-weight: 500; 287 | overflow: hidden; 288 | text-overflow: ellipsis; 289 | padding-right: var(--space-md); 290 | } 291 | 292 | .col-version { 293 | width: 10%; 294 | color: var(--secondary-text); 295 | font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; 296 | font-size: 0.875rem; 297 | } 298 | 299 | .col-description { 300 | width: 45%; 301 | color: var(--secondary-text); 302 | overflow: hidden; 303 | text-overflow: ellipsis; 304 | font-size: 0.9rem; 305 | padding-right: var(--space-md); 306 | } 307 | 308 | .col-actions { 309 | width: 25%; 310 | display: flex; 311 | justify-content: flex-end; 312 | flex-wrap: nowrap; 313 | gap: 2px; 314 | } 315 | 316 | /* 标签样式 */ 317 | .tag { 318 | display: inline-block; 319 | padding: 2px 6px; 320 | border-radius: var(--radius-sm); 321 | font-size: 0.75rem; 322 | font-weight: 500; 323 | margin-left: 8px; 324 | } 325 | 326 | .tag.core { 327 | background-color: #3498db; 328 | color: white; 329 | } 330 | 331 | .tag.ai-model { 332 | background-color: #2ecc71; 333 | color: white; 334 | } 335 | 336 | .tag.system { 337 | background-color: #9966ff; 338 | color: white; 339 | } 340 | 341 | .tag.app-required { 342 | background-color: #e91e63; 343 | color: white; 344 | } 345 | 346 | /* 依赖图按钮 */ 347 | .action-btn.dependency-graph { 348 | background-color: #9c27b0; 349 | position: relative; 350 | overflow: hidden; 351 | z-index: 1; 352 | width: auto; 353 | min-width: 0; 354 | padding: 4px 8px; 355 | } 356 | 357 | .action-btn.dependency-graph::before { 358 | content: ""; 359 | position: absolute; 360 | top: -2px; 361 | left: -2px; 362 | right: -2px; 363 | bottom: -2px; 364 | background: linear-gradient( 365 | 124deg, 366 | #ff2400, #e81d1d, #e8b71d, #e3e81d, #1de840, 367 | #1ddde8, #2b1de8, #dd00f3, #dd00f3 368 | ); 369 | background-size: 1800% 1800%; 370 | z-index: -1; 371 | animation: rainbow-pulse 6s linear infinite; 372 | border-radius: calc(var(--radius-sm) + 2px); 373 | opacity: 0.7; 374 | } 375 | 376 | @keyframes rainbow-pulse { 377 | 0% { background-position: 0% 82% } 378 | 50% { background-position: 100% 19% } 379 | 100% { background-position: 0% 82% } 380 | } 381 | 382 | .action-btn.dependency-graph:hover { 383 | transform: translateY(-1px) scale(1.05); 384 | box-shadow: 0 0 8px rgba(156, 39, 176, 0.6); 385 | } 386 | 387 | .action-btn.dependency-graph:hover::before { 388 | animation-duration: 2s; 389 | opacity: 0.9; 390 | } 391 | 392 | /* 搜索框 */ 393 | .search-container { 394 | margin-bottom: var(--space-xl); 395 | width: 100%; 396 | } 397 | 398 | .search-input-wrapper { 399 | position: relative; 400 | width: 100%; 401 | display: flex; 402 | align-items: center; 403 | } 404 | 405 | #dependency-search { 406 | width: 100%; 407 | padding: 12px 40px 12px 16px; 408 | border: 1px solid var(--border-color); 409 | border-radius: var(--radius-xl); 410 | font-size: 1rem; 411 | transition: all 0.3s; 412 | box-shadow: var(--shadow-sm); 413 | } 414 | 415 | #dependency-search:focus { 416 | outline: none; 417 | border-color: var(--primary-color); 418 | box-shadow: 0 0 0 3px var(--primary-100); 419 | } 420 | 421 | .clear-search-btn { 422 | position: absolute; 423 | right: 12px; 424 | background: transparent; 425 | border: none; 426 | color: var(--neutral-500); 427 | font-size: 20px; 428 | cursor: pointer; 429 | padding: 5px; 430 | line-height: 1; 431 | display: none; 432 | transition: color 0.2s; 433 | } 434 | 435 | .clear-search-btn:hover { 436 | color: var(--error-color); 437 | } 438 | 439 | #dependency-search:not(:placeholder-shown) + .clear-search-btn { 440 | display: block; 441 | } 442 | 443 | .highlight-match { 444 | background-color: rgba(255, 193, 7, 0.35); 445 | padding: 0 2px; 446 | border-radius: 2px; 447 | font-weight: 500; 448 | } 449 | 450 | /* ====================================================== 451 | 安装区域 452 | ====================================================== */ 453 | .package-install-container { 454 | background-color: var(--card-color); 455 | border-radius: var(--radius-lg); 456 | box-shadow: var(--shadow-md); 457 | margin-top: var(--space-xl); 458 | margin-bottom: var(--space-xl); 459 | padding: var(--space-xl); 460 | border: 1px solid var(--border-color); 461 | } 462 | 463 | .package-install-container h2 { 464 | margin-bottom: var(--space-md); 465 | font-size: 1.25rem; 466 | color: var(--primary-color); 467 | display: flex; 468 | align-items: center; 469 | gap: var(--space-sm); 470 | } 471 | 472 | .package-install-container h2::before { 473 | content: ""; 474 | display: inline-block; 475 | width: 4px; 476 | height: 18px; 477 | background-color: var(--primary-color); 478 | border-radius: 2px; 479 | } 480 | 481 | .package-input-group { 482 | display: flex; 483 | flex-wrap: wrap; 484 | gap: var(--space-sm); 485 | align-items: center; 486 | margin-bottom: var(--space-sm); 487 | } 488 | 489 | #package-input { 490 | flex: 1; 491 | min-width: 300px; 492 | padding: 10px 16px; 493 | border-radius: var(--radius-md); 494 | } 495 | 496 | .file-select-btn { 497 | background-color: var(--neutral-200); 498 | color: var(--text-color); 499 | border: 1px solid var(--border-color); 500 | white-space: nowrap; 501 | font-weight: normal; 502 | } 503 | 504 | .file-select-btn:hover { 505 | background-color: var(--neutral-300); 506 | } 507 | 508 | .install-btn { 509 | background-color: var(--success-color); 510 | color: white; 511 | white-space: nowrap; 512 | } 513 | 514 | .install-btn:hover { 515 | background-color: #27ae60; 516 | } 517 | 518 | .file-info { 519 | font-size: 0.875rem; 520 | color: var(--secondary-text); 521 | margin-top: var(--space-xs); 522 | min-height: 20px; 523 | } 524 | 525 | /* ====================================================== 526 | 进度条 527 | ====================================================== */ 528 | .progress-container { 529 | position: fixed; 530 | top: 0; 531 | left: 0; 532 | right: 0; 533 | background-color: var(--card-color); 534 | padding: var(--space-md) var(--space-lg); 535 | box-shadow: var(--shadow-md); 536 | z-index: 1100; 537 | text-align: center; 538 | border-bottom: 1px solid var(--border-color); 539 | animation: slideDown 0.3s ease; 540 | display: flex; 541 | flex-direction: column; 542 | gap: var(--space-xs); 543 | } 544 | 545 | @keyframes slideDown { 546 | from { transform: translateY(-100%); } 547 | to { transform: translateY(0); } 548 | } 549 | 550 | .progress-container.hidden { 551 | display: none; 552 | } 553 | 554 | .progress-title { 555 | font-weight: 600; 556 | margin-bottom: var(--space-md); 557 | color: var(--primary-color); 558 | } 559 | 560 | .progress-bar-container { 561 | height: 10px; 562 | background-color: var(--neutral-200); 563 | border-radius: 5px; 564 | margin-bottom: var(--space-sm); 565 | overflow: hidden; 566 | } 567 | 568 | .progress-bar { 569 | height: 100%; 570 | width: 0; 571 | background-color: var(--primary-color); 572 | transition: width 0.3s ease; 573 | border-radius: 5px; 574 | background-image: linear-gradient( 575 | 45deg, 576 | rgba(255, 255, 255, 0.15) 25%, 577 | transparent 25%, 578 | transparent 50%, 579 | rgba(255, 255, 255, 0.15) 50%, 580 | rgba(255, 255, 255, 0.15) 75%, 581 | transparent 75%, 582 | transparent 583 | ); 584 | background-size: 20px 20px; 585 | animation: progress-bar-stripes 1s linear infinite; 586 | } 587 | 588 | @keyframes progress-bar-stripes { 589 | from { background-position: 20px 0; } 590 | to { background-position: 0 0; } 591 | } 592 | 593 | .progress-text { 594 | font-size: 0.875rem; 595 | color: var(--secondary-text); 596 | font-weight: 500; 597 | } 598 | 599 | /* ====================================================== 600 | 更新确认对话框 601 | ====================================================== */ 602 | .update-confirm-content { 603 | width: 650px; 604 | max-width: 95%; 605 | max-height: 80vh; 606 | } 607 | 608 | .update-confirm-info { 609 | background-color: var(--primary-100); 610 | border-radius: var(--radius-md); 611 | padding: var(--space-md); 612 | margin-bottom: var(--space-lg); 613 | border-left: 4px solid var(--primary-color); 614 | } 615 | 616 | .update-confirm-info p { 617 | margin: 0; 618 | color: var(--primary-dark); 619 | font-weight: 500; 620 | } 621 | 622 | .update-confirm-info span { 623 | font-weight: 700; 624 | color: var(--primary-color); 625 | } 626 | 627 | .update-packages-container, 628 | .latest-packages-container { 629 | margin-bottom: var(--space-lg); 630 | } 631 | 632 | .update-packages-container h3, 633 | .latest-packages-container h3 { 634 | display: flex; 635 | align-items: center; 636 | font-size: 1rem; 637 | margin-bottom: var(--space-sm); 638 | } 639 | 640 | .update-packages-container h3::before, 641 | .latest-packages-container h3::before { 642 | content: ""; 643 | display: inline-block; 644 | width: 3px; 645 | height: 14px; 646 | margin-right: var(--space-sm); 647 | border-radius: 2px; 648 | } 649 | 650 | .update-packages-container h3::before { 651 | background-color: var(--warning-color); 652 | } 653 | 654 | .latest-packages-container h3::before { 655 | background-color: var(--success-color); 656 | } 657 | 658 | .packages-list { 659 | max-height: 200px; 660 | overflow-y: auto; 661 | border: 1px solid var(--border-color); 662 | border-radius: var(--radius-md); 663 | background-color: var(--card-color); 664 | scrollbar-width: thin; 665 | scrollbar-color: var(--neutral-400) transparent; 666 | } 667 | 668 | .packages-list::-webkit-scrollbar { 669 | width: 6px; 670 | } 671 | 672 | .packages-list::-webkit-scrollbar-track { 673 | background: transparent; 674 | } 675 | 676 | .packages-list::-webkit-scrollbar-thumb { 677 | background-color: var(--neutral-400); 678 | border-radius: 3px; 679 | } 680 | 681 | .package-item { 682 | display: flex; 683 | padding: var(--space-md); 684 | border-bottom: 1px solid var(--border-color); 685 | align-items: center; 686 | transition: background-color 0.2s; 687 | } 688 | 689 | .package-item:last-child { 690 | border-bottom: none; 691 | } 692 | 693 | .package-item:hover { 694 | background-color: var(--neutral-50); 695 | } 696 | 697 | .package-name { 698 | flex: 1; 699 | font-weight: 500; 700 | overflow: hidden; 701 | text-overflow: ellipsis; 702 | white-space: nowrap; 703 | } 704 | 705 | .package-version, 706 | .package-latest-version { 707 | padding: 3px 8px; 708 | border-radius: var(--radius-sm); 709 | font-size: 0.75rem; 710 | font-family: "SFMono-Regular", Consolas, monospace; 711 | margin-left: var(--space-sm); 712 | background-color: var(--neutral-100); 713 | color: var(--text-color); 714 | } 715 | 716 | .package-latest-version { 717 | background-color: var(--success-color); 718 | color: white; 719 | } 720 | 721 | .no-packages { 722 | padding: var(--space-lg); 723 | text-align: center; 724 | color: var(--secondary-text); 725 | font-style: italic; 726 | } 727 | 728 | .update-confirm-actions { 729 | display: flex; 730 | justify-content: flex-end; 731 | margin-top: var(--space-lg); 732 | gap: var(--space-md); 733 | border-top: 1px solid var(--border-color); 734 | padding-top: var(--space-md); 735 | } 736 | 737 | #update-confirm-proceed { 738 | background-color: var(--warning-color); 739 | } 740 | 741 | #update-confirm-proceed:hover { 742 | background-color: #e67e22; 743 | } 744 | 745 | /* ====================================================== 746 | 版本切换模态框 747 | ====================================================== */ 748 | .version-modal-content { 749 | width: 550px; 750 | max-width: 95%; 751 | } 752 | 753 | /* 标签页导航 */ 754 | .version-tabs { 755 | display: flex; 756 | border-bottom: 1px solid var(--border-color); 757 | margin: var(--space-lg) 0; 758 | } 759 | 760 | .version-tab-btn { 761 | background: transparent; 762 | color: var(--text-color); 763 | border: none; 764 | padding: var(--space-sm) var(--space-md); 765 | margin-right: var(--space-md); 766 | position: relative; 767 | cursor: pointer; 768 | font-weight: 500; 769 | transition: all 0.2s; 770 | } 771 | 772 | .version-tab-btn:hover { 773 | color: var(--primary-color); 774 | background: transparent; 775 | transform: none; 776 | box-shadow: none; 777 | } 778 | 779 | .version-tab-btn.active { 780 | color: var(--primary-color); 781 | } 782 | 783 | .version-tab-btn.active::after { 784 | content: ""; 785 | position: absolute; 786 | bottom: -1px; 787 | left: 0; 788 | width: 100%; 789 | height: 2px; 790 | background-color: var(--primary-color); 791 | border-radius: 2px 2px 0 0; 792 | } 793 | 794 | /* 标签内容 */ 795 | .version-tab-content { 796 | display: none; 797 | margin-top: var(--space-md); 798 | } 799 | 800 | /* 版本列表头部 */ 801 | .version-list-header { 802 | display: flex; 803 | padding: var(--space-sm) var(--space-md); 804 | background-color: var(--neutral-100); 805 | border-radius: var(--radius-sm) var(--radius-sm) 0 0; 806 | border: 1px solid var(--border-color); 807 | font-weight: 600; 808 | font-size: 0.875rem; 809 | color: var(--secondary-text); 810 | } 811 | 812 | /* 版本列表样式 */ 813 | .version-list { 814 | max-height: 300px; 815 | overflow-y: auto; 816 | border: 1px solid var(--border-color); 817 | border-top: none; 818 | border-radius: 0 0 var(--radius-sm) var(--radius-sm); 819 | } 820 | 821 | .version-item { 822 | display: flex; 823 | padding: var(--space-sm) var(--space-md); 824 | border-bottom: 1px solid var(--border-color); 825 | align-items: center; 826 | transition: background-color 0.2s; 827 | } 828 | 829 | .version-item:last-child { 830 | border-bottom: none; 831 | } 832 | 833 | .version-item:hover { 834 | background-color: var(--neutral-50); 835 | } 836 | 837 | .version-name { 838 | flex: 1; 839 | font-weight: 500; 840 | display: flex; 841 | align-items: center; 842 | gap: var(--space-sm); 843 | } 844 | 845 | .version-date { 846 | width: 100px; 847 | color: var(--secondary-text); 848 | font-size: 0.875rem; 849 | } 850 | 851 | .version-action { 852 | width: 120px; 853 | text-align: right; 854 | } 855 | 856 | /* 版本徽章 */ 857 | .version-badge { 858 | display: inline-block; 859 | padding: 2px 6px; 860 | border-radius: var(--radius-sm); 861 | font-size: 0.7rem; 862 | font-weight: 500; 863 | } 864 | 865 | .version-badge.current { 866 | background-color: var(--primary-color); 867 | color: white; 868 | } 869 | 870 | .version-badge.latest { 871 | background-color: var(--success-color); 872 | color: white; 873 | } 874 | 875 | /* 特殊版本项样式 */ 876 | .version-item.current { 877 | background-color: rgba(33, 150, 243, 0.1); 878 | } 879 | 880 | .version-item.latest { 881 | background-color: rgba(0, 184, 148, 0.08); 882 | } 883 | 884 | /* 版本使用按钮 */ 885 | .version-use-btn { 886 | font-size: 0.8rem; 887 | padding: 4px 8px; 888 | background-color: var(--primary-color); 889 | color: white; 890 | border: none; 891 | border-radius: var(--radius-sm); 892 | cursor: pointer; 893 | transition: all 0.2s; 894 | } 895 | 896 | .version-use-btn:disabled { 897 | background-color: var(--neutral-400); 898 | cursor: not-allowed; 899 | opacity: 0.7; 900 | } 901 | 902 | .version-use-btn:not(:disabled):hover { 903 | background-color: var(--primary-dark); 904 | transform: translateY(-1px); 905 | } 906 | 907 | /* 手动切换版本输入 */ 908 | .version-input { 909 | display: flex; 910 | gap: var(--space-md); 911 | margin-top: var(--space-lg); 912 | } 913 | 914 | .version-input input { 915 | flex: 1; 916 | padding: var(--space-sm) var(--space-md); 917 | border: 1px solid var(--border-color); 918 | border-radius: var(--radius-sm); 919 | } 920 | 921 | .version-input input:focus { 922 | outline: none; 923 | border-color: var(--primary-color); 924 | box-shadow: 0 0 0 2px var(--primary-100); 925 | } 926 | 927 | /* 加载和错误状态 */ 928 | .version-loading, .version-error { 929 | padding: var(--space-lg); 930 | text-align: center; 931 | color: var(--secondary-text); 932 | } 933 | 934 | .version-error { 935 | color: var(--error-color); 936 | } 937 | 938 | /* ====================================================== 939 | 环境管理组件 940 | ====================================================== */ 941 | 942 | /* 环境选择按钮 */ 943 | .env-selector { 944 | position: relative; 945 | background-color: var(--primary-color); 946 | color: white; 947 | border: none; 948 | border-radius: var(--radius-md); 949 | padding: 6px 16px; 950 | font-weight: 600; 951 | display: flex; 952 | align-items: center; 953 | justify-content: center; 954 | gap: 8px; 955 | cursor: pointer; 956 | transition: all 0.3s ease; 957 | height: 36px; 958 | min-width: fit-content; 959 | text-align: center; 960 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 961 | } 962 | 963 | .env-selector:hover { 964 | background-color: var(--primary-dark); 965 | transform: translateY(-1px); 966 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 967 | } 968 | 969 | .env-selector:active { 970 | transform: translateY(0); 971 | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1); 972 | } 973 | 974 | .env-selector .env-icon { 975 | width: 18px; 976 | height: 18px; 977 | fill: currentColor; 978 | transition: transform 0.3s ease; 979 | } 980 | 981 | .env-selector:hover .env-icon { 982 | transform: rotate(180deg); 983 | } 984 | 985 | /* 环境模态框 */ 986 | .environments-modal-content { 987 | width: 90%; 988 | max-width: 800px; 989 | max-height: 85vh; 990 | overflow-y: auto; 991 | } 992 | 993 | .environment-list { 994 | margin: 20px 0; 995 | border: 1px solid var(--border-color); 996 | border-radius: 4px; 997 | overflow: hidden; 998 | } 999 | 1000 | .environment-item { 1001 | padding: 12px; 1002 | display: flex; 1003 | align-items: center; 1004 | border-bottom: 1px solid var(--border-color); 1005 | transition: background-color 0.2s; 1006 | } 1007 | 1008 | .environment-item:last-child { 1009 | border-bottom: none; 1010 | } 1011 | 1012 | .environment-item:hover { 1013 | background-color: var(--hover-bg-color); 1014 | } 1015 | 1016 | .environment-item.active { 1017 | background-color: var(--active-bg-color); 1018 | } 1019 | 1020 | .environment-info { 1021 | flex: 1; 1022 | } 1023 | 1024 | .environment-name { 1025 | font-weight: bold; 1026 | margin-bottom: 4px; 1027 | } 1028 | 1029 | .environment-details { 1030 | font-size: 0.9em; 1031 | color: var(--text-secondary); 1032 | display: flex; 1033 | flex-wrap: wrap; 1034 | gap: 8px; 1035 | } 1036 | 1037 | .environment-detail { 1038 | display: flex; 1039 | align-items: center; 1040 | gap: 4px; 1041 | } 1042 | 1043 | .environment-actions { 1044 | display: flex; 1045 | gap: 8px; 1046 | } 1047 | 1048 | .add-environment-section { 1049 | margin: 20px 0; 1050 | padding: 15px; 1051 | border: 1px solid var(--border-color); 1052 | border-radius: 4px; 1053 | background-color: var(--card-bg-color); 1054 | } 1055 | 1056 | .add-environment-form { 1057 | display: grid; 1058 | grid-template-columns: 1fr 1fr; 1059 | gap: 15px; 1060 | } 1061 | 1062 | .form-field { 1063 | display: flex; 1064 | flex-direction: column; 1065 | gap: 6px; 1066 | } 1067 | 1068 | .form-field label { 1069 | font-weight: bold; 1070 | font-size: 0.9em; 1071 | } 1072 | 1073 | .form-field input { 1074 | padding: 8px; 1075 | border: 1px solid var(--border-color); 1076 | border-radius: 4px; 1077 | background-color: var(--input-bg-color); 1078 | color: var(--text-primary); 1079 | } 1080 | 1081 | .form-actions { 1082 | grid-column: span 2; 1083 | display: flex; 1084 | justify-content: flex-end; 1085 | gap: 10px; 1086 | margin-top: 10px; 1087 | } 1088 | 1089 | .path-input-group { 1090 | display: flex; 1091 | gap: 8px; 1092 | } 1093 | 1094 | .path-input-group input { 1095 | flex: 1; 1096 | } 1097 | 1098 | .path-input-group button { 1099 | white-space: nowrap; 1100 | } 1101 | 1102 | .browse-btn { 1103 | background-color: var(--secondary-button-bg); 1104 | color: var(--secondary-button-text); 1105 | border: 1px solid var(--secondary-button-border); 1106 | border-radius: 4px; 1107 | padding: 0 10px; 1108 | cursor: pointer; 1109 | transition: background-color 0.2s; 1110 | } 1111 | 1112 | .browse-btn:hover { 1113 | background-color: var(--secondary-button-hover-bg); 1114 | } 1115 | 1116 | .no-environments { 1117 | padding: 20px; 1118 | text-align: center; 1119 | color: var(--text-secondary); 1120 | font-style: italic; 1121 | } 1122 | 1123 | .badge { 1124 | display: inline-block; 1125 | padding: 2px 6px; 1126 | border-radius: 10px; 1127 | font-size: 0.7em; 1128 | font-weight: bold; 1129 | text-transform: uppercase; 1130 | color: white; 1131 | } 1132 | 1133 | .badge.current { 1134 | background-color: var(--primary-color); 1135 | } 1136 | 1137 | /* 环境搜索结果 */ 1138 | .search-results { 1139 | margin-top: 15px; 1140 | max-height: 300px; 1141 | overflow-y: auto; 1142 | border: 1px solid var(--border-color); 1143 | border-radius: 4px; 1144 | } 1145 | 1146 | .search-results-header { 1147 | padding: 10px 15px; 1148 | background-color: var(--header-bg-color); 1149 | border-bottom: 1px solid var(--border-color); 1150 | font-weight: bold; 1151 | } 1152 | 1153 | .search-result-item { 1154 | padding: 10px 15px; 1155 | border-bottom: 1px solid var(--border-color); 1156 | display: flex; 1157 | justify-content: space-between; 1158 | align-items: center; 1159 | cursor: pointer; 1160 | transition: background-color 0.2s; 1161 | } 1162 | 1163 | .search-result-item:last-child { 1164 | border-bottom: none; 1165 | } 1166 | 1167 | .search-result-item:hover { 1168 | background-color: var(--hover-bg-color); 1169 | } 1170 | 1171 | .search-result-info { 1172 | flex: 1; 1173 | } 1174 | 1175 | .search-result-name { 1176 | font-weight: bold; 1177 | } 1178 | 1179 | .search-result-path { 1180 | font-size: 0.8em; 1181 | color: var(--text-secondary); 1182 | margin-top: 2px; 1183 | } 1184 | 1185 | .refresh-icon { 1186 | cursor: pointer; 1187 | margin-left: 10px; 1188 | display: inline-flex; 1189 | align-items: center; 1190 | justify-content: center; 1191 | color: var(--text-secondary); 1192 | transition: transform 0.3s; 1193 | } 1194 | 1195 | .refresh-icon:hover { 1196 | color: var(--primary-color); 1197 | } 1198 | 1199 | .refresh-icon.spinning { 1200 | animation: spin 1s linear infinite; 1201 | } 1202 | 1203 | @keyframes spin { 1204 | 0% { transform: rotate(0deg); } 1205 | 100% { transform: rotate(360deg); } 1206 | } 1207 | 1208 | /* ====================================================== 1209 | 依赖关系图 1210 | ====================================================== */ 1211 | .dependency-graph-modal-content { 1212 | width: 800px; 1213 | max-width: 95%; 1214 | max-height: 80vh; 1215 | } 1216 | 1217 | .graph-container { 1218 | height: 500px; 1219 | width: 100%; 1220 | border: 1px solid var(--border-color); 1221 | border-radius: var(--radius-md); 1222 | margin: var(--space-md) 0; 1223 | position: relative; 1224 | overflow: hidden; 1225 | background-color: var(--card-color); 1226 | } 1227 | 1228 | .dependency-graph { 1229 | width: 100%; 1230 | height: 100%; 1231 | } 1232 | 1233 | .graph-loading, .graph-error { 1234 | position: absolute; 1235 | top: 50%; 1236 | left: 50%; 1237 | transform: translate(-50%, -50%); 1238 | text-align: center; 1239 | color: var(--secondary-text); 1240 | font-size: 1rem; 1241 | } 1242 | 1243 | .graph-error { 1244 | color: var(--error-color); 1245 | display: none; 1246 | } 1247 | 1248 | .graph-legend { 1249 | display: flex; 1250 | justify-content: center; 1251 | gap: var(--space-md); 1252 | flex-wrap: wrap; 1253 | margin-top: var(--space-md); 1254 | padding: var(--space-sm) var(--space-lg); 1255 | background-color: var(--neutral-50); 1256 | border-radius: var(--radius-md); 1257 | } 1258 | 1259 | .legend-item { 1260 | display: flex; 1261 | align-items: center; 1262 | gap: var(--space-xs); 1263 | } 1264 | 1265 | .legend-color { 1266 | width: 16px; 1267 | height: 16px; 1268 | border-radius: 50%; 1269 | display: inline-block; 1270 | border: 1px solid var(--border-color); 1271 | } 1272 | 1273 | .legend-color.main-package { 1274 | background-color: #e74c3c; 1275 | } 1276 | 1277 | .legend-color.direct-dependency { 1278 | background-color: #3498db; 1279 | } 1280 | 1281 | .legend-color.optional-dependency { 1282 | background-color: #95a5a6; 1283 | } 1284 | 1285 | .legend-label { 1286 | font-size: 0.875rem; 1287 | color: var(--text-color); 1288 | } 1289 | 1290 | /* D3.js样式 */ 1291 | .node circle { 1292 | stroke-width: 2px; 1293 | } 1294 | 1295 | .node text { 1296 | font-size: 12px; 1297 | font-family: "Segoe UI", sans-serif; 1298 | } 1299 | 1300 | .link { 1301 | fill: none; 1302 | stroke: #999; 1303 | stroke-width: 1.5px; 1304 | stroke-opacity: 0.6; 1305 | } 1306 | 1307 | .link.optional { 1308 | stroke-dasharray: 4; 1309 | } 1310 | 1311 | /* 节点悬停提示框 */ 1312 | .node-tooltip { 1313 | position: fixed; 1314 | padding: var(--space-sm); 1315 | background-color: var(--card-color); 1316 | border: 1px solid var(--border-color); 1317 | border-radius: var(--radius-sm); 1318 | box-shadow: var(--shadow-md); 1319 | font-size: 0.875rem; 1320 | z-index: 1500; 1321 | max-width: 250px; 1322 | pointer-events: none; 1323 | word-wrap: break-word; 1324 | overflow: hidden; 1325 | } 1326 | 1327 | .node-tooltip h4 { 1328 | margin: 0 0 5px 0; 1329 | font-size: 0.95rem; 1330 | color: var(--primary-color); 1331 | } 1332 | 1333 | .node-tooltip p { 1334 | margin: 0; 1335 | color: var(--text-color); 1336 | max-height: 100px; 1337 | overflow-y: auto; 1338 | } 1339 | 1340 | .node-tooltip .version { 1341 | color: var(--secondary-text); 1342 | font-family: monospace; 1343 | font-size: 0.8rem; 1344 | } 1345 | 1346 | /* ====================================================== 1347 | 响应式布局 1348 | ====================================================== */ 1349 | @media (max-width: 768px) { 1350 | /* 优化小屏幕布局 */ 1351 | 1352 | .select-all-container { 1353 | margin-bottom: var(--space-sm); 1354 | margin-right: 0; 1355 | width: 100%; 1356 | justify-content: center; 1357 | } 1358 | } 1359 | -------------------------------------------------------------------------------- /css/modules/utilities.css: -------------------------------------------------------------------------------- 1 | /* Utilities.css - 工具与动画模块 */ 2 | 3 | /* ====================================================== 4 | 动画和过渡效果 5 | ====================================================== */ 6 | /* 基础动画关键帧 */ 7 | @keyframes fadeIn { 8 | from { opacity: 0; } 9 | to { opacity: 1; } 10 | } 11 | 12 | @keyframes fadeOut { 13 | from { opacity: 1; } 14 | to { opacity: 0; } 15 | } 16 | 17 | @keyframes slideDown { 18 | from { transform: translateY(-20px); opacity: 0; } 19 | to { transform: translateY(0); opacity: 1; } 20 | } 21 | 22 | @keyframes slideUp { 23 | from { transform: translateY(0); opacity: 1; } 24 | to { transform: translateY(-20px); opacity: 0; } 25 | } 26 | 27 | @keyframes pulse { 28 | 0% { transform: scale(1); } 29 | 50% { transform: scale(1.05); } 30 | 100% { transform: scale(1); } 31 | } 32 | 33 | @keyframes spin { 34 | from { transform: rotate(0deg); } 35 | to { transform: rotate(360deg); } 36 | } 37 | 38 | @keyframes bounce { 39 | 0%, 100% { transform: translateY(0); } 40 | 50% { transform: translateY(-10px); } 41 | } 42 | 43 | /* 应用动画的工具类 */ 44 | .animate-fade-in { 45 | animation: fadeIn 0.3s ease forwards; 46 | } 47 | 48 | .animate-fade-out { 49 | animation: fadeOut 0.3s ease forwards; 50 | } 51 | 52 | .animate-slide-down { 53 | animation: slideDown 0.4s ease forwards; 54 | } 55 | 56 | .animate-slide-up { 57 | animation: slideUp 0.4s ease forwards; 58 | } 59 | 60 | .animate-pulse { 61 | animation: pulse 1.5s infinite; 62 | } 63 | 64 | .animate-spin { 65 | animation: spin 1s linear infinite; 66 | } 67 | 68 | .animate-bounce { 69 | animation: bounce 1s infinite; 70 | } 71 | 72 | /* 过渡效果 */ 73 | .transition-all { 74 | transition: all 0.3s ease; 75 | } 76 | 77 | .transition-transform { 78 | transition: transform 0.3s ease; 79 | } 80 | 81 | .transition-opacity { 82 | transition: opacity 0.3s ease; 83 | } 84 | 85 | .transition-colors { 86 | transition: background-color 0.3s ease, color 0.3s ease; 87 | } 88 | 89 | /* ====================================================== 90 | 布局辅助类 91 | ====================================================== */ 92 | /* Flexbox 工具类 */ 93 | .flex { 94 | display: flex; 95 | } 96 | 97 | .flex-col { 98 | display: flex; 99 | flex-direction: column; 100 | } 101 | 102 | .flex-wrap { 103 | flex-wrap: wrap; 104 | } 105 | 106 | .flex-nowrap { 107 | flex-wrap: nowrap; 108 | } 109 | 110 | .items-center { 111 | align-items: center; 112 | } 113 | 114 | .items-start { 115 | align-items: flex-start; 116 | } 117 | 118 | .items-end { 119 | align-items: flex-end; 120 | } 121 | 122 | .justify-center { 123 | justify-content: center; 124 | } 125 | 126 | .justify-between { 127 | justify-content: space-between; 128 | } 129 | 130 | .justify-start { 131 | justify-content: flex-start; 132 | } 133 | 134 | .justify-end { 135 | justify-content: flex-end; 136 | } 137 | 138 | .flex-1 { 139 | flex: 1; 140 | } 141 | 142 | .flex-auto { 143 | flex: 1 1 auto; 144 | } 145 | 146 | .flex-none { 147 | flex: none; 148 | } 149 | 150 | /* Grid 工具类 */ 151 | .grid { 152 | display: grid; 153 | } 154 | 155 | .grid-cols-2 { 156 | grid-template-columns: repeat(2, 1fr); 157 | } 158 | 159 | .grid-cols-3 { 160 | grid-template-columns: repeat(3, 1fr); 161 | } 162 | 163 | .grid-cols-4 { 164 | grid-template-columns: repeat(4, 1fr); 165 | } 166 | 167 | .gap-xs { 168 | gap: var(--space-xs); 169 | } 170 | 171 | .gap-sm { 172 | gap: var(--space-sm); 173 | } 174 | 175 | .gap-md { 176 | gap: var(--space-md); 177 | } 178 | 179 | .gap-lg { 180 | gap: var(--space-lg); 181 | } 182 | 183 | /* ====================================================== 184 | 间距和尺寸工具类 185 | ====================================================== */ 186 | /* Margin 工具类 */ 187 | .m-0 { margin: 0; } 188 | .mx-auto { margin-left: auto; margin-right: auto; } 189 | .mt-xs { margin-top: var(--space-xs); } 190 | .mt-sm { margin-top: var(--space-sm); } 191 | .mt-md { margin-top: var(--space-md); } 192 | .mt-lg { margin-top: var(--space-lg); } 193 | 194 | .mb-xs { margin-bottom: var(--space-xs); } 195 | .mb-sm { margin-bottom: var(--space-sm); } 196 | .mb-md { margin-bottom: var(--space-md); } 197 | .mb-lg { margin-bottom: var(--space-lg); } 198 | 199 | .ml-xs { margin-left: var(--space-xs); } 200 | .ml-sm { margin-left: var(--space-sm); } 201 | .ml-md { margin-left: var(--space-md); } 202 | .ml-lg { margin-left: var(--space-lg); } 203 | 204 | .mr-xs { margin-right: var(--space-xs); } 205 | .mr-sm { margin-right: var(--space-sm); } 206 | .mr-md { margin-right: var(--space-md); } 207 | .mr-lg { margin-right: var(--space-lg); } 208 | 209 | /* Padding 工具类 */ 210 | .p-0 { padding: 0; } 211 | .p-xs { padding: var(--space-xs); } 212 | .p-sm { padding: var(--space-sm); } 213 | .p-md { padding: var(--space-md); } 214 | .p-lg { padding: var(--space-lg); } 215 | 216 | .px-xs { padding-left: var(--space-xs); padding-right: var(--space-xs); } 217 | .px-sm { padding-left: var(--space-sm); padding-right: var(--space-sm); } 218 | .px-md { padding-left: var(--space-md); padding-right: var(--space-md); } 219 | .px-lg { padding-left: var(--space-lg); padding-right: var(--space-lg); } 220 | 221 | .py-xs { padding-top: var(--space-xs); padding-bottom: var(--space-xs); } 222 | .py-sm { padding-top: var(--space-sm); padding-bottom: var(--space-sm); } 223 | .py-md { padding-top: var(--space-md); padding-bottom: var(--space-md); } 224 | .py-lg { padding-top: var(--space-lg); padding-bottom: var(--space-lg); } 225 | 226 | /* 尺寸工具类 */ 227 | .w-full { width: 100%; } 228 | .w-auto { width: auto; } 229 | .h-full { height: 100%; } 230 | .h-auto { height: auto; } 231 | .max-w-xs { max-width: 320px; } 232 | .max-w-sm { max-width: 480px; } 233 | .max-w-md { max-width: 640px; } 234 | .max-w-lg { max-width: 800px; } 235 | .max-w-xl { max-width: 1024px; } 236 | 237 | /* ====================================================== 238 | 文本样式工具类 239 | ====================================================== */ 240 | .text-xs { font-size: 0.75rem; } 241 | .text-sm { font-size: 0.875rem; } 242 | .text-md { font-size: 1rem; } 243 | .text-lg { font-size: 1.125rem; } 244 | .text-xl { font-size: 1.25rem; } 245 | .text-2xl { font-size: 1.5rem; } 246 | .text-3xl { font-size: 1.875rem; } 247 | .text-4xl { font-size: 2.25rem; } 248 | 249 | .font-normal { font-weight: 400; } 250 | .font-medium { font-weight: 500; } 251 | .font-semibold { font-weight: 600; } 252 | .font-bold { font-weight: 700; } 253 | 254 | .text-left { text-align: left; } 255 | .text-center { text-align: center; } 256 | .text-right { text-align: right; } 257 | 258 | .text-primary { color: var(--primary-color); } 259 | .text-secondary { color: var(--secondary-text); } 260 | .text-success { color: var(--success-color); } 261 | .text-warning { color: var(--warning-color); } 262 | .text-error { color: var(--error-color); } 263 | .text-info { color: var(--info-color); } 264 | 265 | .line-clamp-1 { 266 | display: -webkit-box; 267 | -webkit-line-clamp: 1; 268 | -webkit-box-orient: vertical; 269 | overflow: hidden; 270 | } 271 | 272 | .line-clamp-2 { 273 | display: -webkit-box; 274 | -webkit-line-clamp: 2; 275 | -webkit-box-orient: vertical; 276 | overflow: hidden; 277 | } 278 | 279 | .line-clamp-3 { 280 | display: -webkit-box; 281 | -webkit-line-clamp: 3; 282 | -webkit-box-orient: vertical; 283 | overflow: hidden; 284 | } 285 | 286 | .truncate { 287 | overflow: hidden; 288 | text-overflow: ellipsis; 289 | white-space: nowrap; 290 | } 291 | 292 | /* ====================================================== 293 | 样式辅助类 294 | ====================================================== */ 295 | /* Display工具类 */ 296 | .block { display: block; } 297 | .inline-block { display: inline-block; } 298 | .inline { display: inline; } 299 | .hidden { display: none; } 300 | .invisible { visibility: hidden; } 301 | 302 | /* Position工具类 */ 303 | .relative { position: relative; } 304 | .absolute { position: absolute; } 305 | .fixed { position: fixed; } 306 | .sticky { position: sticky; } 307 | 308 | .top-0 { top: 0; } 309 | .bottom-0 { bottom: 0; } 310 | .left-0 { left: 0; } 311 | .right-0 { right: 0; } 312 | 313 | /* 边框和圆角工具类 */ 314 | .rounded-sm { border-radius: var(--radius-sm); } 315 | .rounded-md { border-radius: var(--radius-md); } 316 | .rounded-lg { border-radius: var(--radius-lg); } 317 | .rounded-full { border-radius: 9999px; } 318 | 319 | .border { border: 1px solid var(--border-color); } 320 | .border-t { border-top: 1px solid var(--border-color); } 321 | .border-b { border-bottom: 1px solid var(--border-color); } 322 | .border-l { border-left: 1px solid var(--border-color); } 323 | .border-r { border-right: 1px solid var(--border-color); } 324 | 325 | /* 阴影工具类 */ 326 | .shadow-none { box-shadow: none; } 327 | .shadow-sm { box-shadow: var(--shadow-sm); } 328 | .shadow-md { box-shadow: var(--shadow-md); } 329 | .shadow-lg { box-shadow: var(--shadow-lg); } 330 | 331 | /* 交互状态工具类 */ 332 | .pointer { cursor: pointer; } 333 | .select-none { user-select: none; } 334 | .select-text { user-select: text; } 335 | 336 | /* 透明度工具类 */ 337 | .opacity-0 { opacity: 0; } 338 | .opacity-25 { opacity: 0.25; } 339 | .opacity-50 { opacity: 0.5; } 340 | .opacity-75 { opacity: 0.75; } 341 | .opacity-100 { opacity: 1; } 342 | 343 | /* ====================================================== 344 | 背景颜色工具类 345 | ====================================================== */ 346 | .bg-transparent { background-color: transparent; } 347 | .bg-primary { background-color: var(--primary-color); } 348 | .bg-primary-light { background-color: var(--primary-light); } 349 | .bg-primary-dark { background-color: var(--primary-dark); } 350 | .bg-card { background-color: var(--card-color); } 351 | .bg-success { background-color: var(--success-color); } 352 | .bg-warning { background-color: var(--warning-color); } 353 | .bg-error { background-color: var(--error-color); } 354 | .bg-info { background-color: var(--info-color); } 355 | 356 | /* ====================================================== 357 | 响应式设计辅助工具 358 | ====================================================== */ 359 | /* 显示/隐藏工具类 */ 360 | @media (max-width: 640px) { 361 | .hidden-xs { display: none; } 362 | .block-xs { display: block; } 363 | .flex-xs { display: flex; } 364 | } 365 | 366 | @media (min-width: 641px) and (max-width: 768px) { 367 | .hidden-sm { display: none; } 368 | .block-sm { display: block; } 369 | .flex-sm { display: flex; } 370 | } 371 | 372 | @media (min-width: 769px) and (max-width: 1024px) { 373 | .hidden-md { display: none; } 374 | .block-md { display: block; } 375 | .flex-md { display: flex; } 376 | } 377 | 378 | @media (min-width: 1025px) { 379 | .hidden-lg { display: none; } 380 | .block-lg { display: block; } 381 | .flex-lg { display: flex; } 382 | } 383 | 384 | /* 响应式栅格 */ 385 | @media (max-width: 640px) { 386 | .grid-cols-xs-1 { grid-template-columns: 1fr; } 387 | .grid-cols-xs-2 { grid-template-columns: repeat(2, 1fr); } 388 | } 389 | 390 | @media (min-width: 641px) and (max-width: 768px) { 391 | .grid-cols-sm-2 { grid-template-columns: repeat(2, 1fr); } 392 | .grid-cols-sm-3 { grid-template-columns: repeat(3, 1fr); } 393 | } 394 | 395 | @media (min-width: 769px) and (max-width: 1024px) { 396 | .grid-cols-md-3 { grid-template-columns: repeat(3, 1fr); } 397 | .grid-cols-md-4 { grid-template-columns: repeat(4, 1fr); } 398 | } 399 | 400 | /* 打印样式辅助 */ 401 | @media print { 402 | .print-hidden { display: none !important; } 403 | .print-break-before { page-break-before: always; } 404 | .print-break-after { page-break-after: always; } 405 | .print-no-break { page-break-inside: avoid; } 406 | } 407 | 408 | /* ====================================================== 409 | 辅助内容样式 410 | ====================================================== */ 411 | .sr-only { 412 | position: absolute; 413 | width: 1px; 414 | height: 1px; 415 | padding: 0; 416 | margin: -1px; 417 | overflow: hidden; 418 | clip: rect(0, 0, 0, 0); 419 | white-space: nowrap; 420 | border-width: 0; 421 | } 422 | 423 | .focus-outline { 424 | outline: 2px solid var(--primary-color); 425 | outline-offset: 2px; 426 | } 427 | 428 | .clearfix::after { 429 | content: ""; 430 | display: table; 431 | clear: both; 432 | } 433 | 434 | /* 实用ARIA辅助类 */ 435 | [aria-busy="true"] { 436 | cursor: progress; 437 | } 438 | 439 | [aria-disabled="true"] { 440 | cursor: not-allowed; 441 | opacity: 0.7; 442 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |