style: move activity chart to bottom and use vega-lite for better visuals
This commit is contained in:
10
README.md
10
README.md
@@ -7,7 +7,7 @@ A collection of enhancements, plugins, and prompts for [OpenWebUI](https://githu
|
|||||||
|
|
||||||
<!-- STATS_START -->
|
<!-- STATS_START -->
|
||||||
## 📊 Community Stats
|
## 📊 Community Stats
|
||||||
> 🕐 Auto-updated: 2026-02-11 12:41
|
> 🕐 Auto-updated: 2026-02-11 12:46
|
||||||
|
|
||||||
| 👤 Author | 👥 Followers | ⭐ Points | 🏆 Contributions |
|
| 👤 Author | 👥 Followers | ⭐ Points | 🏆 Contributions |
|
||||||
| :---: | :---: | :---: | :---: |
|
| :---: | :---: | :---: | :---: |
|
||||||
@@ -17,8 +17,6 @@ A collection of enhancements, plugins, and prompts for [OpenWebUI](https://githu
|
|||||||
| :---: | :---: | :---: | :---: | :---: |
|
| :---: | :---: | :---: | :---: | :---: |
|
||||||
|  |  |  |  |  |
|
|  |  |  |  |  |
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 🔥 Top 6 Popular Plugins
|
### 🔥 Top 6 Popular Plugins
|
||||||
| Rank | Plugin | Version | Downloads | Views | Updated |
|
| Rank | Plugin | Version | Downloads | Views | Updated |
|
||||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
@@ -156,6 +154,12 @@ If you have great prompts or plugins to share:
|
|||||||
|
|
||||||
[Contributing](./CONTRIBUTING.md)
|
[Contributing](./CONTRIBUTING.md)
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ACTIVITY_CHART_START -->
|
||||||
|
### 📈 Total Downloads Trend (14 Days)
|
||||||
|

|
||||||
|
<!-- ACTIVITY_CHART_END -->
|
||||||
|
|
||||||
## Contributors ✨
|
## Contributors ✨
|
||||||
|
|
||||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|||||||
10
README_CN.md
10
README_CN.md
@@ -6,7 +6,7 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
|
|||||||
|
|
||||||
<!-- STATS_START -->
|
<!-- STATS_START -->
|
||||||
## 📊 社区统计
|
## 📊 社区统计
|
||||||
> 🕐 自动更新于 2026-02-11 12:41
|
> 🕐 自动更新于 2026-02-11 12:46
|
||||||
|
|
||||||
| 👤 作者 | 👥 粉丝 | ⭐ 积分 | 🏆 贡献 |
|
| 👤 作者 | 👥 粉丝 | ⭐ 积分 | 🏆 贡献 |
|
||||||
| :---: | :---: | :---: | :---: |
|
| :---: | :---: | :---: | :---: |
|
||||||
@@ -16,8 +16,6 @@ OpenWebUI 增强功能集合。包含个人开发与收集的插件、提示词
|
|||||||
| :---: | :---: | :---: | :---: | :---: |
|
| :---: | :---: | :---: | :---: | :---: |
|
||||||
|  |  |  |  |  |
|
|  |  |  |  |  |
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 🔥 热门插件 Top 6
|
### 🔥 热门插件 Top 6
|
||||||
| 排名 | 插件 | 版本 | 下载 | 浏览 | 更新日期 |
|
| 排名 | 插件 | 版本 | 下载 | 浏览 | 更新日期 |
|
||||||
| :---: | :--- | :---: | :---: | :---: | :---: |
|
| :---: | :--- | :---: | :---: | :---: | :---: |
|
||||||
@@ -134,3 +132,9 @@ Open WebUI 的前端增强扩展:
|
|||||||
本项目是一个资源集合,无需安装 Python 环境。你只需要下载对应的文件并导入到你的 OpenWebUI 实例中即可。
|
本项目是一个资源集合,无需安装 Python 环境。你只需要下载对应的文件并导入到你的 OpenWebUI 实例中即可。
|
||||||
|
|
||||||
[贡献指南](./CONTRIBUTING_CN.md) | [更新日志](./CHANGELOG.md)
|
[贡献指南](./CONTRIBUTING_CN.md) | [更新日志](./CHANGELOG.md)
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ACTIVITY_CHART_START -->
|
||||||
|
### 📈 总下载量累计趋势 (14天)
|
||||||
|

|
||||||
|
<!-- ACTIVITY_CHART_END -->
|
||||||
@@ -999,39 +999,6 @@ class OpenWebUIStats:
|
|||||||
)
|
)
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
# 插入总下载量趋势图 (仅 README 使用)
|
|
||||||
history = self.load_history()
|
|
||||||
if len(history) >= 3:
|
|
||||||
# 辅助函数:Kroki 渲染
|
|
||||||
def kroki_render(mermaid_code: str) -> str:
|
|
||||||
try:
|
|
||||||
compressed = zlib.compress(mermaid_code.encode("utf-8"), level=9)
|
|
||||||
encoded = base64.urlsafe_b64encode(compressed).decode("utf-8")
|
|
||||||
return f"https://kroki.io/mermaid/svg/{encoded}"
|
|
||||||
except:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
data = history[-14:] # 取最近14天
|
|
||||||
dates = [item["date"][-5:] for item in data]
|
|
||||||
dates_str = ", ".join([f'"{d}"' for d in dates])
|
|
||||||
dls = [str(item["total_downloads"]) for item in data]
|
|
||||||
|
|
||||||
# 多语言标题
|
|
||||||
chart_titles = {
|
|
||||||
"zh": "总下载量累计趋势 (14天)",
|
|
||||||
"en": "Total Downloads Trend (14 Days)",
|
|
||||||
}
|
|
||||||
c_title = chart_titles.get(lang, chart_titles["en"])
|
|
||||||
|
|
||||||
mm = f"""xychart-beta
|
|
||||||
title "{c_title}"
|
|
||||||
x-axis [{dates_str}]
|
|
||||||
y-axis "Total Downloads"
|
|
||||||
line [{', '.join(dls)}]"""
|
|
||||||
|
|
||||||
lines.append(f"})")
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
# Top 6 热门插件
|
# Top 6 热门插件
|
||||||
lines.append(t["top6_title"])
|
lines.append(t["top6_title"])
|
||||||
lines.append(t["top6_header"])
|
lines.append(t["top6_header"])
|
||||||
@@ -1077,50 +1044,134 @@ class OpenWebUIStats:
|
|||||||
pattern = r"<!-- STATS_START -->.*?<!-- STATS_END -->"
|
pattern = r"<!-- STATS_START -->.*?<!-- STATS_END -->"
|
||||||
if re.search(pattern, content, re.DOTALL):
|
if re.search(pattern, content, re.DOTALL):
|
||||||
# 替换现有区域
|
# 替换现有区域
|
||||||
new_content = re.sub(pattern, new_stats, content, flags=re.DOTALL)
|
content = re.sub(pattern, new_stats, content, flags=re.DOTALL)
|
||||||
else:
|
else:
|
||||||
# 在简介段落之后插入统计区域
|
# 在简介段落之后插入统计区域
|
||||||
# 查找模式:标题 -> 语言切换行 -> 简介段落 -> 插入位置
|
|
||||||
lines = content.split("\n")
|
lines = content.split("\n")
|
||||||
insert_pos = 0
|
insert_pos = 0
|
||||||
found_intro = False
|
found_intro = False
|
||||||
|
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
# 跳过标题
|
|
||||||
if line.startswith("# "):
|
if line.startswith("# "):
|
||||||
continue
|
continue
|
||||||
# 跳过空行
|
|
||||||
if line.strip() == "":
|
if line.strip() == "":
|
||||||
continue
|
continue
|
||||||
# 跳过语言切换行 (如 "English | [中文]" 或 "[English] | 中文")
|
|
||||||
if ("English" in line or "中文" in line) and "|" in line:
|
if ("English" in line or "中文" in line) and "|" in line:
|
||||||
continue
|
continue
|
||||||
# 找到第一个非空、非标题、非语言切换的段落(简介)
|
|
||||||
if not found_intro:
|
if not found_intro:
|
||||||
found_intro = True
|
found_intro = True
|
||||||
# 继续到这个段落结束
|
|
||||||
continue
|
continue
|
||||||
# 简介段落后的空行或下一个标题就是插入位置
|
|
||||||
if line.strip() == "" or line.startswith("#"):
|
if line.strip() == "" or line.startswith("#"):
|
||||||
insert_pos = i
|
insert_pos = i
|
||||||
break
|
break
|
||||||
|
|
||||||
# 如果没找到合适位置,就放在第3行(标题和语言切换后)
|
|
||||||
if insert_pos == 0:
|
if insert_pos == 0:
|
||||||
insert_pos = 3
|
insert_pos = 3
|
||||||
|
|
||||||
# 在适当位置插入
|
|
||||||
lines.insert(insert_pos, "")
|
lines.insert(insert_pos, "")
|
||||||
lines.insert(insert_pos + 1, new_stats)
|
lines.insert(insert_pos + 1, new_stats)
|
||||||
lines.insert(insert_pos + 2, "")
|
lines.insert(insert_pos + 2, "")
|
||||||
new_content = "\n".join(lines)
|
content = "\n".join(lines)
|
||||||
|
|
||||||
|
# 生成并插入/更新底部趋势图 (Vega-Lite)
|
||||||
|
activity_chart = self.generate_activity_chart(lang)
|
||||||
|
if activity_chart:
|
||||||
|
chart_pattern = (
|
||||||
|
r"<!-- ACTIVITY_CHART_START -->.*?<!-- ACTIVITY_CHART_END -->"
|
||||||
|
)
|
||||||
|
chart_section = f"<!-- ACTIVITY_CHART_START -->\n{activity_chart}\n<!-- ACTIVITY_CHART_END -->"
|
||||||
|
|
||||||
|
if re.search(chart_pattern, content, re.DOTALL):
|
||||||
|
content = re.sub(chart_pattern, chart_section, content, flags=re.DOTALL)
|
||||||
|
else:
|
||||||
|
# 尝试插入到 Contributors 之前
|
||||||
|
contributors_pattern = r"(## .*Contributors.*)"
|
||||||
|
match = re.search(contributors_pattern, content, re.IGNORECASE)
|
||||||
|
if match:
|
||||||
|
content = (
|
||||||
|
content[: match.start()]
|
||||||
|
+ f"\n{chart_section}\n\n"
|
||||||
|
+ content[match.start() :]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
content += f"\n\n{chart_section}"
|
||||||
|
|
||||||
# 写回文件
|
# 写回文件
|
||||||
with open(readme_path, "w", encoding="utf-8") as f:
|
with open(readme_path, "w", encoding="utf-8") as f:
|
||||||
f.write(new_content)
|
f.write(content)
|
||||||
|
|
||||||
print(f"✅ README 已更新: {readme_path}")
|
print(f"✅ README 已更新: {readme_path}")
|
||||||
|
|
||||||
|
def generate_activity_chart(self, lang: str = "zh") -> str:
|
||||||
|
"""生成 Vega-Lite 趋势图 (更美观)"""
|
||||||
|
history = self.load_history()
|
||||||
|
if len(history) < 3:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
data = history[-14:]
|
||||||
|
|
||||||
|
# 准备数据点
|
||||||
|
values = []
|
||||||
|
for item in data:
|
||||||
|
values.append({"date": item["date"], "downloads": item["total_downloads"]})
|
||||||
|
|
||||||
|
title = (
|
||||||
|
"Total Downloads Trend (14 Days)"
|
||||||
|
if lang == "en"
|
||||||
|
else "总下载量累计趋势 (14天)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Vega-Lite Spec
|
||||||
|
vl_spec = {
|
||||||
|
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
||||||
|
"description": title,
|
||||||
|
"width": 800,
|
||||||
|
"height": 200,
|
||||||
|
"padding": 5,
|
||||||
|
"background": "transparent",
|
||||||
|
"config": {
|
||||||
|
"view": {"stroke": "transparent"},
|
||||||
|
"axis": {"domain": False, "grid": False},
|
||||||
|
},
|
||||||
|
"data": {"values": values},
|
||||||
|
"mark": {
|
||||||
|
"type": "area",
|
||||||
|
"line": {"color": "#2563eb"},
|
||||||
|
"color": {
|
||||||
|
"x1": 1,
|
||||||
|
"y1": 1,
|
||||||
|
"x2": 1,
|
||||||
|
"y2": 0,
|
||||||
|
"gradient": "linear",
|
||||||
|
"stops": [
|
||||||
|
{"offset": 0, "color": "white"},
|
||||||
|
{"offset": 1, "color": "#2563eb"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "date",
|
||||||
|
"type": "temporal",
|
||||||
|
"axis": {"format": "%m-%d", "title": None, "labelColor": "#666"},
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "downloads",
|
||||||
|
"type": "quantitative",
|
||||||
|
"axis": {"title": None, "labelColor": "#666"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Kroki encoding for Vega-Lite
|
||||||
|
json_spec = json.dumps(vl_spec)
|
||||||
|
compressed = zlib.compress(json_spec.encode("utf-8"), level=9)
|
||||||
|
encoded = base64.urlsafe_b64encode(compressed).decode("utf-8")
|
||||||
|
url = f"https://kroki.io/vegalite/svg/{encoded}"
|
||||||
|
return f"### 📈 {title}\n"
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
|
|||||||
Reference in New Issue
Block a user