adobe 系列最新版破解器 genp
使用过后觉得挺不错的,分享一下吧,虽然这里能用上这个软件的佬友不太多,现在还有 canva 在平替。
GenP.v3.7.1-CGP.zip
先安装官方的 creative cloud,然后想装啥装啥,再用 genp 破解即可。
实际体验下来 Windows 上 ps 最新版 27.2 破解后会打不开(闪退),27.1 版本正常使用,其他软件最新版均能成功破解。
顺便附上 genp 发布网址:https://gen.paramore.su/
xiaohack博客专注前沿科技动态与实用技术干货分享,涵盖 AI 代理、大模型应用、编程工具、文档解析、SEO 实战、自动化部署等内容,提供开源项目教程、科技资讯日报、工具使用指南,助力开发者、AI 爱好者获取前沿技术与实战经验。
使用过后觉得挺不错的,分享一下吧,虽然这里能用上这个软件的佬友不太多,现在还有 canva 在平替。
GenP.v3.7.1-CGP.zip
先安装官方的 creative cloud,然后想装啥装啥,再用 genp 破解即可。
实际体验下来 Windows 上 ps 最新版 27.2 破解后会打不开(闪退),27.1 版本正常使用,其他软件最新版均能成功破解。
顺便附上 genp 发布网址:https://gen.paramore.su/
没有付费方案
并且模型全是顶级大模型
测试注册帐户后只有记忆对话而已
不过缺点是只有 web 对话端

支持生图
感觉渠道有点 2api 的意思
暂未测试模型是否保真,欢迎佬们测试
从 开源项目推广困境 这个帖子继续聊起
感谢佬友们上次对项目的建议,学习到了很多,我进一步梳理了 PocketMocker 的思路。
特别感谢 @YougLin 大佬的建议
前端开发中,一个长期痛点是:调试网络边缘场景非常麻烦。
比如,我想测试 UI 如何处理 500 内部服务器错误或异常的 JSON 数据,通常有三种低效方式:
if (true) throw new Error()
不优雅,而且容易忘记清理。
Chrome DevTools 虽然可以观察流量,但无法直接干预请求。
Pocket Mocker 的设计理念是:“在浏览器层拦截请求,快速调试 UI 对异常网络响应的处理 ”。
特点:
它不是要替代 MSW 或全套 Mock 方案,而是专注于加强前端对于 http 的控制。
在我的日常工作中,Pocket Mocker 大大提升了调试效率,尤其是复现生产环境 bug 或者测试边界条件时,节省了大量时间。
如果你佬友们曾为前端网络调试头疼,不妨试试 Pocket Mocker。
它让网络调试像手术一样精准、可控,而不是依赖笨重的 Mock 或改后台数据。
GitHub 地址:
研究了几个月的 BS-RoFormer 音乐分轨分离,训练出了效果让自己都大吃一惊的模型。
模型和推理代码上传到了 HuggingFace Hub: HiDolen/Mini-BS-RoFormer-V2-46.8M · Hugging Face
用两块 4090 在 MUSDB18-HQ 数据集上训练了两天。比同体积模型效果更好、运算量更低。
分离贝斯、鼓、其他和人声四个轨道,可以把人声以外的三轨合并起来当伴奏。适配 transformers 库,一行 Python 代码就能拉下来使用。
model = AutoModel.from_pretrained(
"HiDolen/Mini-BS-RoFormer-V2-46.8M",
trust_remote_code=True
).to("cuda") # 非常推荐用 GPU 推理,速度快得多 详细使用方式在 HuggingFace 查看。
感谢佬友们的公益,跌跌撞撞 vibe 出了个试听页面: Mini-BS-RoFormer-V2
那么可能你需要 EnsoAI 来重构你的 AI 辅助开发流
一个分支,一个工作区(Agent+Terminal + 编辑器 + Git 管理)
EnsoAI 将 Git Worktree 与 AI Agent 绑定。每个 worktree 都是独立的工作空间,拥有:
可以前往官网: https://enso.j3.do/ 在首页演示区中试用体验核心理念
macOS (Homebrew)
brew tap j3n5en/ensoai
brew install --cask ensoai
Windows (Scoop)
scoop bucket add ensoai https://github.com/J3n5en/scoop-ensoai
scoop install ensoai
Github 地址: GitHub - J3n5en/EnsoAI: Multiple Agents, Parallel Flow
官网地址: https://enso.j3.do
无缝切换 Claude、Codex、Gemini 或自定义 Agent。每个 Worktree 都有独立的持久化 AI 会话
优雅的可视化 Git 面板。通过键盘即可完成差异对比、暂存修改和提交代码。
基于 Monaco 构建的轻量级编辑器。支持 50+ 种语言高亮,Markdown 实时同步预览
自动生成高质量的 Commit Message,并利用 AI 助手对代码变更进行深度审查与优化。
内置 Jetbrains 式的三栏合并编辑器。清晰展示冲突来源,支持一键采纳变更与实时结果预览,让解决冲突变得轻松愉悦。
Fiat24 常见问题中总结的方案美国家宽,但触发概率较低日本节点触发零元购的概率较高实测使用几家 15 块 / 月的机场也可以触发
PayPal 不能是.cn 结尾的国区
日本改成法国别忘记取消第二个月的自动扣款
教程中用的
BitgetWallet与Fiat24合作的 U 卡
根据实测与佬友经历分享,YPT/Wise/Bybit/N26均可,招商那个万事达借记卡好像也行 (?
风控是有点严重,但都零元购了,要什么自行车
就刚刚 KatCoder 发布 CodingPlan 计划
基础定价首月 48 元,次月 70 元
认真说,就这个价格实在很难与其他两家国产做比较,基本和 KIMI 一桌。
该模型声浪太小,没见过谁讨论,具体效果不详,只知道不是多模态模型,他家还有个 IDE 叫 CodeFilcker 吧,感觉也没什么人提起
这到底是个认真做的产品,还是个 KPI 产品难说
https://aidrouter.qzz.io
sk-Eai7R0PiXX96XC2Mk6wZiYHHJn6xIC4wYjNRsgUkwZkE7oj1
zai-org/GLM-4.5
google/gemini-3-flash-preview
claude-sonnet-4
claude 应该也可以用
不支持高并发,code(渠道有风控)
当前限制 3 分钟 10 次请求
仅限合法用途,禁止二次倒卖
同时原站点支持 ldc 积分,可使用 claude 等其他模型
已修复 ldc 积分支付后无法到账问题
自己在 windows 上装总是有各种问题 干脆用出来 CC 写了个脚本 源都换成国内源了 默认屏蔽了官方登录 直接使用第三方 api 即可
ClaudeCode-Windows-Installer
之前书签管理用的是 raindrop,但是 raindrop 的搜索和没有一样。就试了下 karakeep,虽然不轻量,但是界面美观,配合 singlefile 可以持久化大部分网页(避免网页失效的问题),还有 ai 自动打标签和移动端,总体还是挺好用的。
但是 raindrop 有个很好用的功能在 karakeep 没有:地址栏搜索,在浏览器的地址栏输入”rd xxx” 就可以搜索书签。karakeep 不仅没有这个功能,chrome 插件甚至不提供 sidebar,必须打开 karakeep 网页才能查看 or 搜索书签。
于是,我让 gpt 写了一个插件,来实现 karakeep 的地址栏搜索功能,地址栏输入”kk xxx” 搜索书签

使用 karakeep 的佬友可以试试这个插件。
karakeep 是自搭建服务,所有有想试试的佬友也可以找我加个账号试试
网页挂后台方便点
Bilibili 推荐区随机播放
关注露米 Lumi 谢谢喵~:
露米 Lumi https://space.bilibili.com/2000609327
可以完成节点按照地区分区并且 AI 服务分流到 AI 分组,流媒体分流到流媒体分组
// 按地区分组脚本 - 支持负载均衡、自动选择、故障转移、流媒体和AI服务
function main(config) {
// 国内直连规则
const directRules = [
"DOMAIN-SUFFIX,cn,DIRECT",
"DOMAIN-SUFFIX,baidu.com,DIRECT",
"DOMAIN-SUFFIX,bdstatic.com,DIRECT",
"DOMAIN-SUFFIX,qq.com,DIRECT",
"DOMAIN-SUFFIX,weixin.qq.com,DIRECT",
"DOMAIN-SUFFIX,wechat.com,DIRECT",
"DOMAIN-SUFFIX,taobao.com,DIRECT",
"DOMAIN-SUFFIX,tmall.com,DIRECT",
"DOMAIN-SUFFIX,alicdn.com,DIRECT",
"DOMAIN-SUFFIX,alipay.com,DIRECT",
"DOMAIN-SUFFIX,aliyun.com,DIRECT",
"DOMAIN-SUFFIX,jd.com,DIRECT",
"DOMAIN-SUFFIX,163.com,DIRECT",
"DOMAIN-SUFFIX,126.com,DIRECT",
"DOMAIN-SUFFIX,netease.com,DIRECT",
"DOMAIN-SUFFIX,weibo.com,DIRECT",
"DOMAIN-SUFFIX,sina.com.cn,DIRECT",
"DOMAIN-SUFFIX,douyin.com,DIRECT",
"DOMAIN-SUFFIX,tiktokv.com,DIRECT",
"DOMAIN-SUFFIX,bytedance.com,DIRECT",
"DOMAIN-SUFFIX,zhihu.com,DIRECT",
"DOMAIN-SUFFIX,bilibili.com,DIRECT",
"DOMAIN-SUFFIX,bilivideo.com,DIRECT",
"DOMAIN-SUFFIX,hdslb.com,DIRECT",
"DOMAIN-SUFFIX,douban.com,DIRECT",
"DOMAIN-SUFFIX,xiaohongshu.com,DIRECT",
"DOMAIN-SUFFIX,meituan.com,DIRECT",
"DOMAIN-SUFFIX,dianping.com,DIRECT",
"DOMAIN-SUFFIX,ctrip.com,DIRECT",
"DOMAIN-SUFFIX,csdn.net,DIRECT",
"DOMAIN-SUFFIX,jianshu.com,DIRECT",
"DOMAIN-SUFFIX,iqiyi.com,DIRECT",
"DOMAIN-SUFFIX,youku.com,DIRECT",
"DOMAIN-SUFFIX,sohu.com,DIRECT",
"DOMAIN-SUFFIX,sogou.com,DIRECT",
"DOMAIN-SUFFIX,360.cn,DIRECT",
"DOMAIN-SUFFIX,huawei.com,DIRECT",
"DOMAIN-SUFFIX,xiaomi.com,DIRECT",
"DOMAIN-SUFFIX,mi.com,DIRECT",
"DOMAIN-SUFFIX,oppo.com,DIRECT",
"DOMAIN-SUFFIX,vivo.com,DIRECT",
"GEOIP,CN,DIRECT",
];
// 流媒体和AI服务规则
const serviceRules = [
// AI 服务
"DOMAIN-SUFFIX,openai.com,🤖 AI服务",
"DOMAIN-SUFFIX,ai.com,🤖 AI服务",
"DOMAIN-SUFFIX,chatgpt.com,🤖 AI服务",
"DOMAIN-SUFFIX,oaistatic.com,🤖 AI服务",
"DOMAIN-SUFFIX,oaiusercontent.com,🤖 AI服务",
"DOMAIN-SUFFIX,gemini.google.com,🤖 AI服务",
"DOMAIN-SUFFIX,bard.google.com,🤖 AI服务",
"DOMAIN-SUFFIX,generativelanguage.googleapis.com,🤖 AI服务",
"DOMAIN-SUFFIX,anthropic.com,🤖 AI服务",
"DOMAIN-SUFFIX,claude.ai,🤖 AI服务",
"DOMAIN-SUFFIX,x.ai,🤖 AI服务",
"DOMAIN-SUFFIX,grok.x.ai,🤖 AI服务",
"DOMAIN-SUFFIX,perplexity.ai,🤖 AI服务",
"DOMAIN-SUFFIX,poe.com,🤖 AI服务",
"DOMAIN-SUFFIX,cohere.ai,🤖 AI服务",
"DOMAIN-SUFFIX,mistral.ai,🤖 AI服务",
// 流媒体
"DOMAIN-SUFFIX,netflix.com,🎬 流媒体",
"DOMAIN-SUFFIX,netflix.net,🎬 流媒体",
"DOMAIN-SUFFIX,nflxvideo.net,🎬 流媒体",
"DOMAIN-SUFFIX,nflximg.net,🎬 流媒体",
"DOMAIN-SUFFIX,nflxext.com,🎬 流媒体",
"DOMAIN-SUFFIX,disneyplus.com,🎬 流媒体",
"DOMAIN-SUFFIX,disney-plus.net,🎬 流媒体",
"DOMAIN-SUFFIX,hbomax.com,🎬 流媒体",
"DOMAIN-SUFFIX,max.com,🎬 流媒体",
"DOMAIN-SUFFIX,hulu.com,🎬 流媒体",
"DOMAIN-SUFFIX,primevideo.com,🎬 流媒体",
"DOMAIN-SUFFIX,youtube.com,🎬 流媒体",
"DOMAIN-SUFFIX,googlevideo.com,🎬 流媒体",
"DOMAIN-SUFFIX,ytimg.com,🎬 流媒体",
"DOMAIN-SUFFIX,spotify.com,🎬 流媒体",
"DOMAIN-SUFFIX,scdn.co,🎬 流媒体",
"DOMAIN-SUFFIX,tvb.com,🎬 流媒体",
"DOMAIN-SUFFIX,viu.com,🎬 流媒体",
];
// 地区分组正则 - 增强匹配规则
const regionMap = {
"🇭🇰 港澳台": /香港|HK|HongKong|Hong\s*Kong|港|🇭🇰|澳门|澳門|MO|Macao|Macau|🇲🇴|台湾|台灣|TW|Taiwan|臺灣|🇹🇼|高雄|台北/i,
"🌎 美洲": /美国|美國|US|USA|United\s*States|🇺🇸|洛杉矶|洛杉磯|Los\s*Angeles|纽约|紐約|New\s*York|西雅图|Seattle|芝加哥|Chicago|达拉斯|Dallas|凤凰城|Phoenix|加拿大|CA|Canada|🇨🇦|多伦多|Toronto|温哥华|Vancouver|巴西|BR|Brazil|🇧🇷|圣保罗|Sao\s*Paulo|墨西哥|MX|Mexico|🇲🇽|墨西哥城|Mexico\s*City|阿根廷|AR|Argentina|🇦🇷|布宜诺斯艾利斯|Buenos\s*Aires|智利|CL|Chile|🇨🇱|圣地亚哥|Santiago|哥伦比亚|CO|Colombia|🇨🇴|波哥大|Bogota|秘鲁|PE|Peru|🇵🇪|利马|Lima/i,
"🌍 欧洲": /英国|英國|GB|UK|United\s*Kingdom|Britain|🇬🇧|伦敦|倫敦|London|法国|法國|FR|France|🇫🇷|巴黎|Paris|德国|德國|DE|Germany|🇩🇪|法兰克福|Frankfurt|柏林|Berlin|意大利|IT|Italy|🇮🇹|罗马|Roma|米兰|Milan|西班牙|ES|Spain|🇪🇸|马德里|Madrid|巴塞罗那|Barcelona|荷兰|荷蘭|NL|Netherlands|🇳🇱|阿姆斯特丹|Amsterdam|瑞士|CH|Switzerland|🇨🇭|苏黎世|Zurich|瑞典|SE|Sweden|🇸🇪|斯德哥尔摩|Stockholm|波兰|波蘭|PL|Poland|🇵🇱|华沙|Warsaw|乌克兰|烏克蘭|UA|Ukraine|🇺🇦|基辅|Kiev|俄罗斯|俄羅斯|RU|Russia|🇷🇺|莫斯科|Moscow|葡萄牙|PT|Portugal|🇵🇹|里斯本|Lisbon|比利时|比利時|BE|Belgium|🇧🇪|布鲁塞尔|Brussels|奥地利|奧地利|AT|Austria|🇦🇹|维也纳|Vienna|挪威|NO|Norway|🇳🇴|奥斯陆|Oslo|丹麦|丹麥|DK|Denmark|🇩🇰|哥本哈根|Copenhagen|芬兰|芬蘭|FI|Finland|🇫🇮|赫尔辛基|Helsinki|爱尔兰|愛爾蘭|IE|Ireland|🇮🇪|都柏林|Dublin|捷克|CZ|Czech|🇨🇿|布拉格|Prague|罗马尼亚|羅馬尼亞|RO|Romania|🇷🇴|布加勒斯特|Bucharest|保加利亚|保加利亞|BG|Bulgaria|🇧🇬|索非亚|Sofia|希腊|希臘|GR|Greece|🇬🇷|雅典|Athens/i,
"🌏 亚太": /日本|日|JP|Japan|🇯🇵|东京|東京|Tokyo|大阪|Osaka|新加坡|狮城|SG|Singapore|🇸🇬|韩国|韓國|KR|Korea|🇰🇷|首尔|首爾|Seoul|印度|IN|India|🇮🇳|孟买|Mumbai|新德里|New\s*Delhi|班加罗尔|Bangalore|澳大利亚|澳洲|AU|Australia|🇦🇺|悉尼|Sydney|墨尔本|Melbourne|新西兰|新西蘭|NZ|New\s*Zealand|🇳🇿|奥克兰|Auckland|马来西亚|馬來西亞|MY|Malaysia|🇲🇾|吉隆坡|Kuala\s*Lumpur|印度尼西亚|印尼|ID|Indonesia|🇮🇩|雅加达|Jakarta|菲律宾|菲律賓|PH|Philippines|🇵🇭|马尼拉|Manila|泰国|泰國|TH|Thailand|🇹🇭|曼谷|Bangkok|越南|VN|Vietnam|🇻🇳|胡志明|Ho\s*Chi\s*Minh|河内|Hanoi|巴基斯坦|PK|Pakistan|🇵🇰|卡拉奇|Karachi|孟加拉|孟加拉国|BD|Bangladesh|🇧🇩|达卡|Dhaka|斯里兰卡|LK|Sri\s*Lanka|🇱🇰|科伦坡|Colombo|缅甸|緬甸|MM|Myanmar|Burma|🇲🇲|仰光|Yangon|柬埔寨|KH|Cambodia|🇰🇭|金边|Phnom\s*Penh|老挝|老撾|LA|Laos|🇱🇦|万象|Vientiane/i,
"🌍 中东非洲": /阿联酋|阿聯酋|UAE|Dubai|迪拜|🇦🇪|阿布扎比|Abu\s*Dhabi|以色列|IL|Israel|🇮🇱|特拉维夫|Tel\s*Aviv|南非|ZA|South\s*Africa|🇿🇦|开普敦|Cape\s*Town|约翰内斯堡|Johannesburg|尼日利亚|尼日利亞|NG|Nigeria|🇳🇬|拉各斯|Lagos|土耳其|TR|Turkey|Türkiye|🇹🇷|伊斯坦布尔|Istanbul|安卡拉|Ankara|沙特|沙特阿拉伯|SA|Saudi|🇸🇦|利雅得|Riyadh|吉达|Jeddah|卡塔尔|卡塔爾|QA|Qatar|🇶🇦|多哈|Doha|科威特|KW|Kuwait|🇰🇼|巴林|BH|Bahrain|🇧🇭|麦纳麦|Manama|阿曼|OM|Oman|🇴🇲|马斯喀特|Muscat|约旦|JO|Jordan|🇯🇴|安曼|Amman|埃及|EG|Egypt|🇪🇬|开罗|Cairo|肯尼亚|肯尼亞|KE|Kenya|🇰🇪|内罗毕|Nairobi|埃塞俄比亚|埃塞俄比亞|ET|Ethiopia|🇪🇹|亚的斯亚贝巴|Addis\s*Ababa|摩洛哥|MA|Morocco|🇲🇦|卡萨布兰卡|Casablanca|阿尔及利亚|阿爾及利亞|DZ|Algeria|🇩🇿|阿尔及尔|Algiers/i,
};
const excludePattern = /剩余|重置|更新|订阅|失联|超时|卡顿|飞行|续费/;
const proxies = config.proxies || [];
const validProxies = proxies.filter(p => !excludePattern.test(p.name));
const proxyNames = validProxies.map(p => p.name);
const regionGroups = [];
const regionNames = [];
for (const [name, regex] of Object.entries(regionMap)) {
const matched = proxyNames.filter(n => regex.test(n));
if (matched.length > 0) {
const baseName = name.split(" ")[1];
const autoName = `⚡ ${baseName}自动`;
const lbName = `⚖️ ${baseName}均衡`;
const fbName = `🔄 ${baseName}故障转移`;
regionGroups.push(
{ name, type: "select", proxies: [autoName, lbName, fbName, ...matched] },
{ name: autoName, type: "url-test", proxies: matched, url: "http://www.gstatic.com/generate_204", interval: 300, tolerance: 50 },
{ name: lbName, type: "load-balance", proxies: matched, url: "http://www.gstatic.com/generate_204", interval: 300, strategy: "consistent-hashing" },
{ name: fbName, type: "fallback", proxies: matched, url: "http://www.gstatic.com/generate_204", interval: 180 }
);
regionNames.push(name);
}
}
// AI服务分组 - 美洲+亚太节点
const aiRegex = /美国|US|洛杉矶|纽约|日本|JP|Japan|新加坡|SG/i;
const aiProxies = proxyNames.filter(n => aiRegex.test(n)).slice(0, 30);
// 流媒体分组 - 亚太节点
const streamRegex = /香港|HK|港|台湾|TW|新加坡|SG|日本|JP|Japan/i;
const streamProxies = proxyNames.filter(n => streamRegex.test(n)).slice(0, 30);
// 在节点选择组中添加分组(AI和流媒体在最前面)
const groups = config["proxy-groups"] || [];
const selectGroup = groups.find(g => g.name === "🚀 节点选择");
if (selectGroup) {
const directIdx = selectGroup.proxies.indexOf("DIRECT");
const newGroups = ["🤖 AI服务", "🎬 流媒体", ...regionNames];
if (directIdx > 0) {
selectGroup.proxies.splice(directIdx, 0, ...newGroups);
} else {
selectGroup.proxies.push(...newGroups);
}
}
// 添加所有分组
config["proxy-groups"].push(...regionGroups);
if (aiProxies.length > 0) {
config["proxy-groups"].push(
{ name: "🤖 AI服务", type: "select", proxies: ["⚡ AI自动", "⚖️ AI均衡", "🔄 AI故障转移", ...aiProxies] },
{ name: "⚡ AI自动", type: "url-test", proxies: aiProxies, url: "http://www.gstatic.com/generate_204", interval: 300, tolerance: 100 },
{ name: "⚖️ AI均衡", type: "load-balance", proxies: aiProxies, url: "http://www.gstatic.com/generate_204", interval: 300, strategy: "consistent-hashing" },
{ name: "🔄 AI故障转移", type: "fallback", proxies: aiProxies, url: "http://www.gstatic.com/generate_204", interval: 180 }
);
}
if (streamProxies.length > 0) {
config["proxy-groups"].push(
{ name: "🎬 流媒体", type: "select", proxies: ["⚡ 流媒体自动", "⚖️ 流媒体均衡", "🔄 流媒体故障转移", ...streamProxies] },
{ name: "⚡ 流媒体自动", type: "url-test", proxies: streamProxies, url: "http://www.gstatic.com/generate_204", interval: 300, tolerance: 100 },
{ name: "⚖️ 流媒体均衡", type: "load-balance", proxies: streamProxies, url: "http://www.gstatic.com/generate_204", interval: 300, strategy: "consistent-hashing" },
{ name: "🔄 流媒体故障转移", type: "fallback", proxies: streamProxies, url: "http://www.gstatic.com/generate_204", interval: 180 }
);
}
// 插入规则:服务规则在前,直连规则在后
const rules = config.rules || [];
config.rules = [...serviceRules, ...rules, ...directRules];
return config;
}
免费短链接,通过 Cloudflare 驱动・基于 KV 持久化存储・后缀链接永久有效
所有前缀链接通用,后缀永久有效。
小工具 短链接生成
如果喜欢,麻烦给个 star
Notable changes
Implemented support for SSO with OpenID Connect, https://github.com/dani-garcia/vaultwarden/wiki/Enabling-SSO-support-using-OpenId-Connect
Updated web vault to 2025.12.0
Added support for future mobile apps with versions 2026.1.0+
This is the first vaultwarden release using immutable releases and release attestation!
更多信息参照:
机翻版本
Google 向来低调,即便是 Gemini 3 这种顶级产品的发布,也不像其他公司一样搞得那么隆重,有一些小产品更是低调到发了很久都没人知道,比如这个面向程序员的产品 CodeWiki
和 Code Wiki 属于同一类产品,功能完全一致。
DeepWiki 底层可能采用了多家大模型,具体是哪个不太清楚,但是 CodeWiki 是基于 Gemini 模型的,Gemini 具有超长上下文,也就决定了 CodeWiki 非常适合做大型项目的代码分析
你只需要把 GitHub 仓库地址扔给它,它自动帮你写好这个项目的详细解析文档,甚至超过绝大多数仓库自带的 README 文档
最近闲的没事,找了个项目(github 上的 Hello-Agents)看看,太久没看书了,都快阅读障碍了,还好有 Gemini 陪伴
Gemini 的活人感还是很足的,情绪价值也给的蛮足滴。课后习题也通通交给 Gemini
此 docker 是在 perplexity-ai 基础上,增加了 mcp 功能,具体看上一个帖子。目前原项目没做自动更新 token 功能,token 一个月过期。
除了 mcp 之外的问题,提 issue 请找原项目
从这里继续讨论,https://linux.do/t/topic/1371904 增加 http 远程调用,因此封装了个 docker 自用,发出来给需要的人。
github workflow 自动化构建还没时间搞(让 ai 写了一版,没改变量,也没测)
docker compose 一键部署
注意,socks 代理没有测试过
services: perplexity-mcp: shancw/perplexity-mcp:latest container_name: perplexity-mcp ports: - "${MCP_PORT:-8000}:8000" environment: # MCP 认证密钥 - MCP_TOKEN=${MCP_TOKEN:-sk-123456} # Perplexity 账户凭证 (可选,用于高级功能) - PPLX_NEXT_AUTH_CSRF_TOKEN=${PPLX_NEXT_AUTH_CSRF_TOKEN:-} - PPLX_SESSION_TOKEN=${PPLX_SESSION_TOKEN:-} # SOCKS 代理配置 (可选) # 格式: socks5://[user[:pass]@]host[:port][#remark] # 示例: socks5://127.0.0.1:1080 或 socks5://user:pass@proxy.example.com:1080 # - SOCKS_PROXY=${SOCKS_PROXY:-} restart: unless-stopped .env 环境变量
# Perplexity MCP Server 环境变量配置 # 复制此文件为 .env 并填入实际值 # ============================================ # MCP 服务配置 # ============================================ # MCP 服务端口
MCP_PORT=8000
# MCP API 认证密钥 (客户端需要在 Authorization header 中携带此密钥)
MCP_TOKEN=sk-123456
# ============================================ # Perplexity 账户凭证 (可选) # 用于解锁高级功能: Pro 模式、Reasoning 模式、Deep Research # 不配置则只能使用 auto 模式 # ============================================ # 从 Perplexity 网站 Cookie 中获取 # 打开 perplexity.ai -> F12 开发者工具 -> Application -> Cookies
PPLX_NEXT_AUTH_CSRF_TOKEN=
PPLX_SESSION_TOKEN=

mcp 配置
{ "perplexity": { "type": "http", "url": "http://127.0.0.1:8000/mcp", "headers": { "Authorization": "Bearer sk-123456" } } } github
这是一本讲网页排版书籍,书中内容有在线示例与代码,是这么多年来对我极其有用的平面设计书籍之一。原书是英文,翻译了在 L 站分享给大家。
此书为 90% 的 AI 翻译,完全保留原书排版,由个人粗略校对确保大部分用词统一。
书名:Better Web Typography for a Better Web
译名:更好的网页排版,造就更好的互联网
2018 年,我还在大学社团做海报,那时候对排版的理解更多是靠 “直觉”。直到接触到了 Matej Latin 的网页排版公开课,也就是这本书的起源。课和中提出 “排版的完美等边三角形”—— 即字号、行高与行宽之间的动态平衡,以及 “模块化比例” 的概念,彻底改变了我观察网页的视角。我至今铭记在心:“要获得完美的段落,需要三样东西:字体大小、行高和行宽。它们需要达到平衡。”
从那以后,这三要素就成了我做阅读文字时绕不开的底层直觉。每当我看到一个网页,总会下意识地去拆解它的文字比例是否协调。尽管此书以英文为基准,其中的排版规律对多种语言均适用。
虽然这本书写在八年前,在互联网平面设计风格迭代如此之快的今天,它讨论的核心规则却依然稳固。甚至可以说,如今主流的网页排版方向,就如书中的所言。如果你想摆脱 “凭感觉” 排版,真正理解排版的美学逻辑,倾情您阅读推荐这本书。
Better Web Typography for a Better Web 是一本源自高分在线课程的著作,旨在向网页设计师和开发人员等网站构建者讲解排版。作者 Matej Latin 将垂直节奏(vertical rhythm)、模块化比例(modular scale)和页面构成(page composition)等复杂概念,以通俗易懂的方式进行了深入浅出的解析。本书配有实时代码示例,读者在阅读过程中将亲历设计并构建一个示例网站的完整流程。这是一本针对新媒介的排印新书:基本规则虽未改变,但除此之外的一切都已焕然一新。
CloudFlare - R2 直链下载:
标签: 排版,字体排印,平面设计,网页设计
又看到站内大佬的一款,太棒了!~
佬们,还有别的推荐吗?
给个赞呗~
全自动看网课 ((支持倍速))
这才是真正得解放了我的生产力,解决了某些网站没有脚本自动完成的难题
另外补一个 timer hooker 脚本(源自油猴某大佬,适用于所有网站加速)
// ==UserScript==
// @name 计时器掌控者|视频广告跳过|视频广告加速器
// @name:en TimerHooker
// @namespace https://gitee.com/HGJing/everthing-hook/
// @version 1.0.62
// @description 控制网页计时器速度|加速跳过页面计时广告|视频快进(慢放)|跳过广告|支持几乎所有网页.
// @description:en it can hook the timer speed to change.
// @include *
// @require https://greasyfork.org/scripts/372672-everything-hook/code/Everything-Hook.js?version=881251
// @author Cangshi
// @match http://*/*
// @run-at document-start
// @grant none
// @license GPL-3.0-or-later
// @downloadURL https://update.greasyfork.org/scripts/372673/%E8%AE%A1%E6%97%B6%E5%99%A8%E6%8E%8C%E6%8E%A7%E8%80%85%7C%E8%A7%86%E9%A2%91%E5%B9%BF%E5%91%8A%E8%B7%B3%E8%BF%87%7C%E8%A7%86%E9%A2%91%E5%B9%BF%E5%91%8A%E5%8A%A0%E9%80%9F%E5%99%A8.user.js
// @updateURL https://update.greasyfork.org/scripts/372673/%E8%AE%A1%E6%97%B6%E5%99%A8%E6%8E%8C%E6%8E%A7%E8%80%85%7C%E8%A7%86%E9%A2%91%E5%B9%BF%E5%91%8A%E8%B7%B3%E8%BF%87%7C%E8%A7%86%E9%A2%91%E5%B9%BF%E5%91%8A%E5%8A%A0%E9%80%9F%E5%99%A8.meta.js
// ==/UserScript==
/**
* ---------------------------
* Time: 2017/11/20 19:28.
* Author: Cangshi
* View: http://palerock.cn
* ---------------------------
*/
/**
* 1. hook Object.defineProperty | Object.defineProperties
* 2. set configurable: true
* 3. delete property
* 4. can set property for onxx event method
*/
window.isDOMLoaded = false;
window.isDOMRendered = false;
document.addEventListener('readystatechange', function () {
if (document.readyState === "interactive" || document.readyState === "complete") {
window.isDOMLoaded = true;
}
});
~function (global) {
var workerURLs = [];
var extraElements = [];
var suppressEvents = {};
var helper = function (eHookContext, timerContext, util) {
return {
applyUI: function () {
var style = '._th-container ._th-item{margin-bottom:3px;position:relative;width:0;height:0;cursor:pointer;opacity:.3;background-color:aquamarine;border-radius:100%;text-align:center;line-height:30px;-webkit-transition:all .35s;-o-transition:all .35s;transition:all .35s;right:30px}._th-container ._th-item,._th-container ._th-click-hover,._th_cover-all-show-times ._th_times{-webkit-box-shadow:-3px 4px 12px -5px black;box-shadow:-3px 4px 12px -5px black}._th-container:hover ._th-item._item-x2{margin-left:18px;width:40px;height:40px;line-height:40px}._th-container:hover ._th-item._item-x-2{margin-left:17px;width:38px;height:38px;line-height:38px}._th-container:hover ._th-item._item-xx2{width:36px;height:36px;margin-left:16px;line-height:36px}._th-container:hover ._th-item._item-xx-2{width:32px;height:32px;line-height:32px;margin-left:14px}._th-container:hover ._th-item._item-reset{width:30px;line-height:30px;height:30px;margin-left:10px}._th-click-hover{position:relative;-webkit-transition:all .5s;-o-transition:all .5s;transition:all .5s;height:45px;width:45px;cursor:pointer;opacity:.3;border-radius:100%;background-color:aquamarine;text-align:center;line-height:45px;right:0}._th-container:hover{left:-5px}._th-container{font-size:12px;-webkit-transition:all .5s;-o-transition:all .5s;transition:all .5s;left:-35px;top:20%;position:fixed;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:100000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}._th-container ._th-item:hover{opacity:.8;background-color:#5fb492;color:aliceblue}._th-container ._th-item:active{opacity:.9;background-color:#1b3a26;color:aliceblue}._th-container:hover ._th-click-hover{opacity:.8}._th-container:hover ._th-item{opacity:.6;right:0}._th-container ._th-click-hover:hover{opacity:.8;background-color:#5fb492;color:aliceblue}._th_cover-all-show-times{position:fixed;top:0;right:0;width:100%;height:100%;z-index:99999;opacity:1;font-weight:900;font-size:30px;color:#4f4f4f;background-color:rgba(0,0,0,0.1)}._th_cover-all-show-times._th_hidden{z-index:-99999;opacity:0;-webkit-transition:1s all;-o-transition:1s all;transition:1s all}._th_cover-all-show-times ._th_times{width:300px;height:300px;border-radius:50%;background-color:rgba(127,255,212,0.51);text-align:center;line-height:300px;position:absolute;top:50%;right:50%;margin-top:-150px;margin-right:-150px}';
var displayNum = (1 / timerContext._percentage).toFixed(2);
// 在页面左边添加一个半圆便于修改
var html = '<div class="_th-container">\n' +
' <div class="_th-click-hover _item-input">\n' +
' x' + displayNum + '\n' +
' </div>\n' +
' <div class="_th-item _item-x2">></div>\n' +
' <div class="_th-item _item-x-2"><</div>\n' +
' <div class="_th-item _item-xx2">>></div>\n' +
' <div class="_th-item _item-xx-2"><<</div>\n' +
' <div class="_th-item _item-reset">O</div>\n' +
'</div>\n' +
'<div class="_th_cover-all-show-times _th_hidden">\n' +
' <div class="_th_times">x' + displayNum + '</div>\n' +
'</div>' +
'';
var stylenode = document.createElement('style');
stylenode.setAttribute("type", "text/css");
if (stylenode.styleSheet) {// IE
stylenode.styleSheet.cssText = style;
} else {// w3c
var cssText = document.createTextNode(style);
stylenode.appendChild(cssText);
}
var node = document.createElement('div');
node.innerHTML = html;
var clickMapper = {
'_item-input': function () {
changeTime();
},
'_item-x2': function () {
changeTime(2, 0, true);
},
'_item-x-2': function () {
changeTime(-2, 0, true);
},
'_item-xx2': function () {
changeTime(0, 2);
},
'_item-xx-2': function () {
changeTime(0, -2);
},
'_item-reset': function () {
changeTime(0, 0, false, true);
}
};
Object.keys(clickMapper).forEach(function (className) {
var exec = clickMapper[className];
var targetEle = node.getElementsByClassName(className)[0];
if (targetEle) {
targetEle.onclick = exec;
}
});
if (!global.isDOMLoaded) {
document.addEventListener('readystatechange', function () {
if ((document.readyState === "interactive" || document.readyState === "complete") && !global.isDOMRendered) {
document.head.appendChild(stylenode);
document.body.appendChild(node);
global.isDOMRendered = true;
console.log('Time Hooker Works!');
}
});
} else {
document.head.appendChild(stylenode);
document.body.appendChild(node);
global.isDOMRendered = true;
console.log('Time Hooker Works!');
}
},
applyGlobalAction: function (timer) {
// 界面半圆按钮点击的方法
timer.changeTime = function (anum, cnum, isa, isr) {
if (isr) {
global.timer.change(1);
return;
}
if (!global.timer) {
return;
}
var result;
if (!anum && !cnum) {
var t = prompt("输入欲改变计时器变化倍率(当前:" + 1 / timerContext._percentage + ")");
if (t == null) {
return;
}
if (isNaN(parseFloat(t))) {
alert("请输入正确的数字");
timer.changeTime();
return;
}
if (parseFloat(t) <= 0) {
alert("倍率不能小于等于0");
timer.changeTime();
return;
}
result = 1 / parseFloat(t);
} else {
if (isa && anum) {
if (1 / timerContext._percentage <= 1 && anum < 0) {
return;
}
result = 1 / (1 / timerContext._percentage + anum);
} else {
if (cnum <= 0) {
cnum = 1 / -cnum
}
result = 1 / ((1 / timerContext._percentage) * cnum);
}
}
timer.change(result);
};
global.changeTime = timer.changeTime;
},
applyHooking: function () {
var _this = this;
// 劫持循环计时器
eHookContext.hookReplace(window, 'setInterval', function (setInterval) {
return _this.getHookedTimerFunction('interval', setInterval);
});
// 劫持单次计时
eHookContext.hookReplace(window, 'setTimeout', function (setTimeout) {
return _this.getHookedTimerFunction('timeout', setTimeout)
});
// 劫持循环计时器的清除方法
eHookContext.hookBefore(window, 'clearInterval', function (method, args) {
_this.redirectNewestId(args);
});
// 劫持循环计时器的清除方法
eHookContext.hookBefore(window, 'clearTimeout', function (method, args) {
_this.redirectNewestId(args);
});
var newFunc = this.getHookedDateConstructor();
eHookContext.hookClass(window, 'Date', newFunc, '_innerDate', ['now']);
Date.now = function () {
return new Date().getTime();
};
eHookContext.hookedToString(timerContext._Date.now, Date.now);
var objToString = Object.prototype.toString;
Object.prototype.toString = function toString() {
'use strict';
if (this instanceof timerContext._mDate) {
return '[object Date]';
} else {
return objToString.call(this);
}
};
eHookContext.hookedToString(objToString, Object.prototype.toString);
eHookContext.hookedToString(timerContext._setInterval, setInterval);
eHookContext.hookedToString(timerContext._setTimeout, setTimeout);
eHookContext.hookedToString(timerContext._clearInterval, clearInterval);
timerContext._mDate = window.Date;
this.hookShadowRoot();
},
getHookedDateConstructor: function () {
return function () {
if (arguments.length === 1) {
Object.defineProperty(this, '_innerDate', {
configurable: false,
enumerable: false,
value: new timerContext._Date(arguments[0]),
writable: false
});
return;
} else if (arguments.length > 1) {
var definedValue;
switch (arguments.length) {
case 2:
definedValue = new timerContext._Date(
arguments[0],
arguments[1]
);
break;
case 3:
definedValue = new timerContext._Date(
arguments[0],
arguments[1],
arguments[2],
);
break;
case 4:
definedValue = new timerContext._Date(
arguments[0],
arguments[1],
arguments[2],
arguments[3],
);
break;
case 5:
definedValue = new timerContext._Date(
arguments[0],
arguments[1],
arguments[2],
arguments[3],
arguments[4]
);
break;
case 6:
definedValue = new timerContext._Date(
arguments[0],
arguments[1],
arguments[2],
arguments[3],
arguments[4],
arguments[5]
);
break;
default:
case 7:
definedValue = new timerContext._Date(
arguments[0],
arguments[1],
arguments[2],
arguments[3],
arguments[4],
arguments[5],
arguments[6]
);
break;
}
Object.defineProperty(this, '_innerDate', {
configurable: false,
enumerable: false,
value: definedValue,
writable: false
});
return;
}
var now = timerContext._Date.now();
var passTime = now - timerContext.__lastDatetime;
var hookPassTime = passTime * (1 / timerContext._percentage);
// console.log(__this.__lastDatetime + hookPassTime, now,__this.__lastDatetime + hookPassTime - now);
Object.defineProperty(this, '_innerDate', {
configurable: false,
enumerable: false,
value: new timerContext._Date(timerContext.__lastMDatetime + hookPassTime),
writable: false
});
};
},
getHookedTimerFunction: function (type, timer) {
var property = '_' + type + 'Ids';
return function () {
var uniqueId = timerContext.genUniqueId();
var callback = arguments[0];
if (typeof callback === 'string') {
callback += ';timer.notifyExec(' + uniqueId + ')';
arguments[0] = callback;
}
if (typeof callback === 'function') {
arguments[0] = function () {
var returnValue = callback.apply(this, arguments);
timerContext.notifyExec(uniqueId);
return returnValue;
}
}
// 储存原始时间间隔
var originMS = arguments[1];
// 获取变速时间间隔
arguments[1] *= timerContext._percentage;
var resultId = timer.apply(window, arguments);
// 保存每次使用计时器得到的id以及参数等
timerContext[property][resultId] = {
args: arguments,
originMS: originMS,
originId: resultId,
nowId: resultId,
uniqueId: uniqueId,
oldPercentage: timerContext._percentage,
exceptNextFireTime: timerContext._Date.now() + originMS
};
return resultId;
};
},
redirectNewestId: function (args) {
var id = args[0];
if (timerContext._intervalIds[id]) {
args[0] = timerContext._intervalIds[id].nowId;
// 清除该记录id
delete timerContext._intervalIds[id];
}
if (timerContext._timeoutIds[id]) {
args[0] = timerContext._timeoutIds[id].nowId;
// 清除该记录id
delete timerContext._timeoutIds[id];
}
},
registerShortcutKeys: function (timer) {
// 快捷键注册
addEventListener('keydown', function (e) {
switch (e.keyCode) {
case 57:
if (e.ctrlKey || e.altKey) {
// custom
timer.changeTime();
}
break;
// [=]
case 190:
case 187: {
if (e.ctrlKey) {
// console.log('+2');
timer.changeTime(2, 0, true);
} else if (e.altKey) {
// console.log('xx2');
timer.changeTime(0, 2);
}
break;
}
// [-]
case 188:
case 189: {
if (e.ctrlKey) {
// console.log('-2');
timer.changeTime(-2, 0, true);
} else if (e.altKey) {
// console.log('xx-2');
timer.changeTime(0, -2);
}
break;
}
// [0]
case 48: {
if (e.ctrlKey || e.altKey) {
// console.log('reset');
timer.changeTime(0, 0, false, true);
}
break;
}
default:
// console.log(e);
}
});
},
/**
* 当计时器速率被改变时调用的回调方法
* @param percentage
* @private
*/
percentageChangeHandler: function (percentage) {
// 改变所有的循环计时
util.ergodicObject(timerContext, timerContext._intervalIds, function (idObj, id) {
idObj.args[1] = Math.floor((idObj.originMS || 1) * percentage);
// 结束原来的计时器
this._clearInterval.call(window, idObj.nowId);
// 新开一个计时器
idObj.nowId = this._setInterval.apply(window, idObj.args);
});
// 改变所有的延时计时
util.ergodicObject(timerContext, timerContext._timeoutIds, function (idObj, id) {
var now = this._Date.now();
var exceptTime = idObj.exceptNextFireTime;
var oldPercentage = idObj.oldPercentage;
var time = exceptTime - now;
if (time < 0) {
time = 0;
}
var changedTime = Math.floor(percentage / oldPercentage * time);
idObj.args[1] = changedTime;
// 重定下次执行时间
idObj.exceptNextFireTime = now + changedTime;
idObj.oldPercentage = percentage;
// 结束原来的计时器
this._clearTimeout.call(window, idObj.nowId);
// 新开一个计时器
idObj.nowId = this._setTimeout.apply(window, idObj.args);
});
},
hookShadowRoot: function () {
var origin = Element.prototype.attachShadow;
eHookContext.hookAfter(Element.prototype, 'attachShadow',
function (m, args, result) {
extraElements.push(result);
return result;
}, false);
eHookContext.hookedToString(origin, Element.prototype.attachShadow);
},
hookDefine: function () {
const _this = this;
eHookContext.hookBefore(Object, 'defineProperty', function (m, args) {
var option = args[2];
var ele = args[0];
var key = args[1];
var afterArgs = _this.hookDefineDetails(ele, key, option);
afterArgs.forEach((arg, i) => {
args[i] = arg;
})
});
eHookContext.hookBefore(Object, 'defineProperties', function (m, args) {
var option = args[1];
var ele = args[0];
if (ele && ele instanceof Element) {
Object.keys(option).forEach(key => {
var o = option[key];
var afterArgs = _this.hookDefineDetails(ele, key, o);
args[0] = afterArgs[0];
delete option[key];
option[afterArgs[1]] = afterArgs[2]
})
}
})
},
hookDefineDetails: function (target, key, option) {
if (option && target && target instanceof Element && typeof key === 'string' && key.indexOf('on') >= 0) {
option.configurable = true;
}
if (target instanceof HTMLVideoElement && key === 'playbackRate') {
option.configurable = true;
console.warn('[Timer Hook]', '已阻止默认操作视频倍率');
key = 'playbackRate_hooked'
}
return [target, key, option];
},
suppressEvent: function (ele, eventName) {
if (ele) {
delete ele['on' + eventName];
delete ele['on' + eventName];
delete ele['on' + eventName];
ele['on' + eventName] = undefined;
}
if (!suppressEvents[eventName]) {
eHookContext.hookBefore(EventTarget.prototype, 'addEventListener',
function (m, args) {
var eName = args[0];
if (eventName === eName) {
console.warn(eventName, 'event suppressed.')
args[0] += 'suppressed';
}
}, false);
suppressEvents[eventName] = true;
}
},
changePlaybackRate: function (ele, rate) {
delete ele.playbackRate;
delete ele.playbackRate;
delete ele.playbackRate;
ele.playbackRate = rate
if (rate !== 1) {
timerContext.defineProperty.call(Object, ele, 'playbackRate', {
configurable: true,
get: function () {
return 1;
},
set: function () {
}
});
}
}
}
};
var normalUtil = {
isInIframe: function () {
let is = global.parent !== global;
try {
is = is && global.parent.document.body.tagName !== 'FRAMESET'
} catch (e) {
// ignore
}
return is;
},
listenParentEvent: function (handler) {
global.addEventListener('message', function (e) {
var data = e.data;
var type = data.type || '';
if (type === 'changePercentage') {
handler(data.percentage || 0);
}
})
},
sentChangesToIframe: function (percentage) {
var iframes = document.querySelectorAll('iframe') || [];
var frames = document.querySelectorAll('frame');
if (iframes.length) {
for (var i = 0; i < iframes.length; i++) {
iframes[i].contentWindow.postMessage(
{type: 'changePercentage', percentage: percentage}, '*');
}
}
if (frames.length) {
for (var j = 0; j < frames.length; j++) {
frames[j].contentWindow.postMessage(
{type: 'changePercentage', percentage: percentage}, '*');
}
}
}
};
var querySelectorAll = function (ele, selector, includeExtra) {
var elements = ele.querySelectorAll(selector);
elements = Array.prototype.slice.call(elements || []);
if (includeExtra) {
extraElements.forEach(function (element) {
elements = elements.concat(querySelectorAll(element, selector, false));
})
}
return elements;
};
var generate = function () {
return function (util) {
// disable worker
workerURLs.forEach(function (url) {
if (util.urlMatching(location.href, 'http.*://.*' + url + '.*')) {
window['Worker'] = undefined;
console.log('Worker disabled');
}
});
var eHookContext = this;
var timerHooker = {
// 用于储存计时器的id和参数
_intervalIds: {},
_timeoutIds: {},
_auoUniqueId: 1,
// 计时器速率
__percentage: 1.0,
// 劫持前的原始的方法
_setInterval: window['setInterval'],
_clearInterval: window['clearInterval'],
_clearTimeout: window['clearTimeout'],
_setTimeout: window['setTimeout'],
_Date: window['Date'],
__lastDatetime: new Date().getTime(),
__lastMDatetime: new Date().getTime(),
videoSpeedInterval: 1000,
defineProperty: Object.defineProperty,
defineProperties: Object.defineProperties,
genUniqueId: function () {
return this._auoUniqueId++;
},
notifyExec: function (uniqueId) {
var _this = this;
if (uniqueId) {
// 清除 timeout 所储存的记录
var timeoutInfos = Object.values(this._timeoutIds).filter(
function (info) {
return info.uniqueId === uniqueId;
}
);
timeoutInfos.forEach(function (info) {
_this._clearTimeout.call(window, info.nowId);
delete _this._timeoutIds[info.originId]
})
}
// console.log(uniqueId, 'called')
},
/**
* 初始化方法
*/
init: function () {
var timerContext = this;
var h = helper(eHookContext, timerContext, util);
h.hookDefine();
h.applyHooking();
// 设定百分比属性被修改的回调
Object.defineProperty(timerContext, '_percentage', {
get: function () {
return timerContext.__percentage;
},
set: function (percentage) {
if (percentage === timerContext.__percentage) {
return percentage;
}
h.percentageChangeHandler(percentage);
timerContext.__percentage = percentage;
return percentage;
}
});
if (!normalUtil.isInIframe()) {
console.log('[TimeHooker]', 'loading outer window...');
h.applyUI();
h.applyGlobalAction(timerContext);
h.registerShortcutKeys(timerContext);
} else {
console.log('[TimeHooker]', 'loading inner window...');
normalUtil.listenParentEvent((function (percentage) {
console.log('[TimeHooker]', 'Inner Changed', percentage)
this.change(percentage);
}).bind(this))
}
},
/**
* 调用该方法改变计时器速率
* @param percentage
*/
change: function (percentage) {
this.__lastMDatetime = this._mDate.now();
this.__lastDatetime = this._Date.now();
this._percentage = percentage;
var oldNode = document.getElementsByClassName('_th-click-hover');
var oldNode1 = document.getElementsByClassName('_th_times');
var displayNum = (1 / this._percentage).toFixed(2);
(oldNode[0] || {}).innerHTML = 'x' + displayNum;
(oldNode1[0] || {}).innerHTML = 'x' + displayNum;
var a = document.getElementsByClassName('_th_cover-all-show-times')[0] || {};
a.className = '_th_cover-all-show-times';
this._setTimeout.bind(window)(function () {
a.className = '_th_cover-all-show-times _th_hidden';
}, 100);
this.changeVideoSpeed();
normalUtil.sentChangesToIframe(percentage);
},
changeVideoSpeed: function () {
var timerContext = this;
var h = helper(eHookContext, timerContext, util);
var rate = 1 / this._percentage;
rate > 16 && (rate = 16);
rate < 0.065 && (rate = 0.065);
var videos = querySelectorAll(document, 'video', true) || [];
if (videos.length) {
for (var i = 0; i < videos.length; i++) {
h.changePlaybackRate(videos[i], rate);
}
}
}
};
// 默认初始化
timerHooker.init();
return timerHooker;
}
};
if (global.eHook) {
global.eHook.plugins({
name: 'timer',
/**
* 插件装载
* @param util
*/
mount: generate()
});
}
}(window);
已经优化的部分镜像:DockerHub - CandyMuj
现在部署项目基本都依赖于 Docker 了,但是国内的网络环境导致使用 Docker 很不方便
看了下目前安装 Docker 的方式多种多样,但似乎都挺麻烦的,其实可以直接用如下简洁的命令一键安装(基于阿里云,这个似乎是很多年前在菜鸟教程看见的安装方式,我就一直用到了现在)
不知为啥现在好多教程都搜不到这个命令了,刚刚看了下菜鸟教程的安装方式也不是这个命令了,但是这个命令确实还可以用的
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
docker pull docker login docker push 直接用不了
当上面的问题解决后,在使用 Docker 镜像的时候也会遇到问题,比如 apt pip maven npm 等都存在网络问题
并且我希望这个东西更通用,除了可以用于 Docker,也可以直接用于优化国内的服务器
执行优化可以高度自定义(自己想怎么优化就怎么优化,相当于提供一个框架,可以自定义实现)
我不希望每次使用的时候还要手动处理镜像源相关的问题,我就在想有没有可能直接开箱即用,镜像拉下来就可以使用,想了想,自己造一个吧,于是就花了两天实际造了这个项目
具体的使用及优化细节,可以看看 README,自认为写的还是比较详细的(甚至可能有点啰嗦)
希望对佬友们有用!减少不必要的时间,提升效率!
旨在解决在中国使用 Docker 及 Docker 镜像时的网络问题!加速镜像构建和依赖下载,节省使用 Docker 的时间,让 Docker 更丝滑!
pull push login 无法使用包管理器(apt、apk) Python Pip Java Maven 等工具或环境的默认源缓慢或被墙导致无法使用的问题其中关于 Docker 代理的配置方法参考自这位佬友的帖子 请教下佬们 docker login 登录报错问题 - #10,来自 yhp666
我也是试了很多方法去配置代理,一直没成功,然后在站内搜了下发现了这个佬友的帖子
佬友们,分享一下我最近开发的 Excel 智能数据分析软件–ExcelMind
算是在 L 站第一次分享我的开源项目,希望佬友们多多提 Issue,多多 Star。
下面是演示视频:
这个项目是基于 LangGraph 开发的,支持自然语言查询、多轮对话、流式输出、ECharts 图表可视化 和可视化思考过程。
GitHub 地址:GitHub - stark-456/ExcelMind: AI 智能分析 Excel 文件,对话式完成多场景 Excel 分析任务,解决 Excel 报表分析复杂、效率低等痛点
上传 Excel 文件后,我们可以用自然语言跟 AI 对话,AI 会自主决策,自主调用工具,完成 Excel 的分析任务。
原理呢,其实是只给 AI 看一部分 Excel 的局部,让 AI 了解表结构之后,调用十个工具来完成分析任务,避免 AI 直接看数据做分析带来幻觉,是我觉得做分析必须要考虑到的。
AI 的分析过程,我都尽可能做了显式的输出,并做了前端优化让工具调用更易于阅读,目的是让 AI 做的每一步都是易于追溯的,这样可以让分析过程摆脱黑盒,让我们对分析过程掌控度更高,即使是出错了,也容易改正。
这里我先加入了 bar (柱状图), line (折线图), pie (饼图),scatter (散点图), radar (雷达图), funnel (漏斗图)。你可以指定 AI 输出什么图,如果不指定,AI 会自主决策输出什么图
考虑到有时候我们的 Excel 文档有很多 AI 不易理解的字段或信息,我加入了知识库功能,会在每次问答前进行召回,这样,有一些特殊需求我们就可以放在知识库里
这个是考虑到有时候需要多表联查,但是很多朋友没有数据库基础,这里选定两张表,可以触发 AI 推荐联表的外键跟连接方式,实测,还是很准的,基本上不用自己去考虑怎么联表方式。
实测,模型能力越强,回答越精准,所以推荐佬们用 Sota 模型,站里很多公益站的模型就很不错!
大概功能就是这些,希望有建议的佬随时互动,我会认真看并改进。
以后会不断开发 AI 智能体项目,并开源给大家,希望多多支持!
GitHub 地址:GitHub - stark-456/ExcelMind: AI 智能分析 Excel 文件,对话式完成多场景 Excel 分析任务,解决 Excel 报表分析复杂、效率低等痛点