mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Merge branch 'preview-fixes'
This commit is contained in:
commit
3d78bf7c9e
@ -46,6 +46,7 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
'vue/html-self-closing': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"id": "feature",
|
||||
"attributes": {
|
||||
"label": "对应功能",
|
||||
"description": "(指本脚本的某项具体功能, 不是指 b 站的) 可以在设置中尝试开启 / 关闭某项功能来排查. 最好能附上功能的 Commit 值 (在设置里功能选项的底部).\n<img src=\"https://user-images.githubusercontent.com/26504152/152666017-8a5acabc-2ad1-47ef-b0a2-cd5c54c867ab.jpg\" width=\"400\">\n请注意: 以下功能已不再维护 (不再修复 bug 和添加新功能), 但是你仍然可以为其提供 Pull Request.\n- 直播间自动抽奖\n- 默认播放器模式\n- 播放器置顶\n- 播放器定位\n",
|
||||
"description": "(指本脚本的某项具体功能, 不是指 b 站的) 可以在设置中尝试开启 / 关闭某项功能来排查. 最好能附上功能的 Commit 值 (在设置里功能选项的底部).\n<img src=\"https://user-images.githubusercontent.com/26504152/152666017-8a5acabc-2ad1-47ef-b0a2-cd5c54c867ab.jpg\" width=\"400\">\n如果组件包含作者的信息 (by @xxx), 请在这里 at 他们\n请注意: 以下功能已不再维护 (不再修复 bug 和添加新功能), 但是你仍然可以为其提供 Pull Request.\n- 直播间自动抽奖\n- 默认播放器模式\n- 播放器置顶\n- 播放器定位\n",
|
||||
"placeholder": "请输入"
|
||||
},
|
||||
"validations": {
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -13,6 +13,7 @@ body:
|
||||
description: |
|
||||
(指本脚本的某项具体功能, 不是指 b 站的) 可以在设置中尝试开启 / 关闭某项功能来排查. 最好能附上功能的 Commit 值 (在设置里功能选项的底部).
|
||||
<img src="https://user-images.githubusercontent.com/26504152/152666017-8a5acabc-2ad1-47ef-b0a2-cd5c54c867ab.jpg" width="400">
|
||||
如果组件包含作者的信息 (by @xxx), 请在这里 at 他们
|
||||
请注意: 以下功能已不再维护 (不再修复 bug 和添加新功能), 但是你仍然可以为其提供 Pull Request.
|
||||
- 直播间自动抽奖
|
||||
- 默认播放器模式
|
||||
|
||||
91
CHANGELOG.md
91
CHANGELOG.md
@ -1,5 +1,96 @@
|
||||
# 更新日志
|
||||
|
||||
## v2.8.5 / v2.8.5-preview
|
||||
`2023-09-24`
|
||||
|
||||
✨新增
|
||||
- 新增组件 `自定义字体`. (PR #4406 by [Tinhone](https://github.com/Tinhone))
|
||||
> 使用组件提供的字体设置覆盖原版的主站字体,并使主站字体可被自定义。字体设置写法请参考 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family) 、默认设置与设置说明
|
||||
|
||||
- `简化直播间` 适配了新版 PK 条. (PR #4439 by [Pencil](https://github.com/pencilqaq))
|
||||
|
||||
- `下载视频` 支持配置 DASH 格式下的文件扩展名. (PR #4449 by [小傅Fox](https://github.com/xfoxfu))
|
||||
|
||||
|
||||
<details>
|
||||
<summary>正式版用户将获得 v2.8.0-preview ~ v2.8.4-preview 的所有新功能, 点击展开查看</summary>
|
||||
|
||||
- `清爽首页` 改进:
|
||||
- 支持自定义板块的布局和可见性. (操作方式和自定义顶栏那个差不多)
|
||||
- 番剧时间表优化样式, 增加翻页按钮.
|
||||
|
||||
<div>
|
||||
<img height="400" src="https://user-images.githubusercontent.com/26504152/242905984-895cb72c-b344-40c3-91a0-2a6b20d5f783.png">
|
||||
<img height="400" src="https://user-images.githubusercontent.com/26504152/242905640-cbc948f1-734e-46f2-96a7-d57787b7cf47.png">
|
||||
</div>
|
||||
|
||||
- 新增功能 `首页净化`. (PR #4153 by [RieN](https://github.com/rien7))
|
||||
> 删除首页特定类型的卡片.
|
||||
|
||||
- `自定义顶栏` 增加 `创作中心` 入口, `排行` 新增 `全站音乐榜` 和 `短剧榜` 的入口. (#4101, PR #4154 by [星海](https://github.com/lovelyCARDINAL), PR #4155 by [星海](https://github.com/lovelyCARDINAL))
|
||||
- `播放器置顶(新)` 增加选项 `顶部留白`. (#4152, PR #4165 by [Ziu](https://github.com/ZiuChen))
|
||||
- `播放器置顶(新)` 的标题改为放置到播放器和点赞中间. (PR #4208 by [LockRim](https://github.com/LockRim))
|
||||
- `显示视频投稿时间` 支持转义字符. (#4160, PR #4167 by [呼乎户](https://github.com/wisokey))
|
||||
- `隐藏视频推荐` 支持稍后再看和收藏夹播放页, 并适配 bpx 播放器. (#4174)
|
||||
- 新增功能 `隐藏记笔记` 和 `隐藏稿件投诉`. (#4124)
|
||||
- 优化了 `自定义顶栏` 中订阅在筛选时的 API 调用. (#4120)
|
||||
- 优化粗体的显示: 优先调用 Semibold 字重, 若字体不支持则使用 Bold 字重.
|
||||
- `自定义顶栏` 增加选项:
|
||||
- 支持自定义消息提醒样式, 可设置为 `数字`, `点状` 或 `隐藏`. (#4125)
|
||||
- 支持自定义搜索栏宽度. (#4112)
|
||||
- 搜索在纯数字输入时, 增加对 UID 和直播间的搜索. (#677)
|
||||
- `网址参数清理` 更新对直播间的支持. (#1459)
|
||||
- `自动点赞` 转为由 [CrazyboyQCD](https://github.com/CrazyboyQCD) 维护, 支持手动控制点赞和黑名单功能. (PR #4343, PR #4358 by [CrazyboyQCD](https://github.com/CrazyboyQCD))
|
||||
|
||||
- `简化直播间` 支持隐藏荣耀等级勋章. (PR #4348 by [CrunchyShark](https://github.com/CrunchyShark944))
|
||||
- 在设置面板中的组件管理等面板中, 搜索框移动至已安装列表的上方, 更符合直觉. (#3806)
|
||||
- 支持从 Tampermonkey 的菜单中唤起功能和设置面板. (#4170)
|
||||
- 搜索栏中添加了导入 / 导出设置的操作. (#4170)
|
||||
- `自定义顶栏` 新增选项 `链接对齐样式`, 可以自定义纯链接弹窗内链接文字的对齐样式. (PR #4365 by [Tinhone](https://github.com/Tinhone))
|
||||
- `简化评论区` 支持独立控制简化选项, 并支持隐藏粉丝勋章和活动横幅. (仅支持新版评论区) (#2381)
|
||||
- `清爽首页` 设置为隐藏的版块现在将彻底销毁, 减少资源占用.
|
||||
- `禁用特殊弹幕样式` 支持禁用大会员弹幕. (#4227)
|
||||
- `下载视频` 在批量下载番剧时, 支持下载多个不同板块的选集. (#2834)
|
||||
- 新增功能 `全屏直播礼物简化`. (PR #4306 by [TimmyOVO](https://github.com/TimmyOVO))
|
||||
|
||||
> 移除全屏观看直播时的底部礼物栏
|
||||
|
||||
- 新增功能 `评论区 IP 属地显示`. (PR #4331, PR #4334 by [Light_Quanta](https://github.com/LightQuanta))
|
||||
|
||||
> 在评论区显示评论的IP属地信息
|
||||
|
||||
- 新增功能 `直播间网页全屏自适应`. (#4216)
|
||||
|
||||
> 在直播网页全屏时, 自动调整侧边栏的宽度, 使得视频区域的比例和视频源相匹配, 达到无黑边的效果. 如果在侧边栏的边缘拖动, 可以自定义侧边栏的固定宽度, 双击边缘可以还原到自动宽度.
|
||||
>
|
||||
> - `侧边栏最大宽度 (px)`: 限制侧边栏可被拉伸到的最大宽度. (最小宽度固定为 190px, 再小的话布局就要出问题了)
|
||||
>
|
||||
> > 注意, 由于有最大宽度和最小宽度的限制, 部分窗口尺寸下仍然无法做到无黑边.
|
||||
|
||||
- 新增功能 `关注时间显示`. (PR #4352 by [Light_Quanta](https://github.com/LightQuanta))
|
||||
|
||||
> 在个人空间的粉丝/关注列表显示关注的具体时间
|
||||
|
||||
- 新增功能 `自动移出稍后再看`.
|
||||
|
||||
> 在稍后再看页面播放结束时, 自动将当前视频移出稍后再看.
|
||||
> 注意:
|
||||
> - 一定要播放结束, 快结束时手动切走不算
|
||||
> - b 站的稍后再看列表不会实时刷新
|
||||
|
||||
- 新增功能 `相簿发布时间显示`. (PR #4362 by [Light_Quanta](https://github.com/LightQuanta))
|
||||
|
||||
> 在个人空间的相簿界面显示相簿的发布时间
|
||||
|
||||
</details>
|
||||
|
||||
🐛修复
|
||||
- 修复 `禁止滚轮调音量` 在网页全屏下失效. (#4381).
|
||||
- 修复 Feeds API 无法解析新版图文动态的文字内容. (主要是动态过滤器会受影响) (#4409)
|
||||
- 修复 `选集区域优化` 在部分视频合集页面上无法使用 <kbd>Alt</kbd> 来切换折叠. (#4429)
|
||||
- 修复被转发图文动态的 `详情 >` 功能失效. (PR #4452 by [sunfkny](https://github.com/sunfkny))
|
||||
|
||||
|
||||
## v2.8.4-preview
|
||||
`2023-09-07`
|
||||
|
||||
|
||||
@ -21,6 +21,8 @@ pnpm install
|
||||
- [配置 VS Code 插件](https://code.visualstudio.com/docs/editor/extension-marketplace):
|
||||
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), 用于格式化 TypeScript 和 Vue 文件.
|
||||
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode), 用于格式化 Scss 和其他文件.
|
||||
- [Vue Language Features (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.volar), 为 *.vue 文件提供支持.
|
||||
- [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin), 让 TS Server 识别 *.vue 文件.(建议启用性能更好的 [Takeover 模式](https://vuejs.org/guide/typescript/overview.html#volar-takeover-mode))
|
||||
|
||||
### 本体
|
||||
需要说明的是, 脚本本体和功能是分开的两个项目. 本体的代码在 `src/` 下, 开发时产生 `dist/bilibili-evolved.dev.user.js` 文件. 功能的代码位于 `registry/` 下, 开发时在 `registry/dist/` 下产生文件.
|
||||
@ -153,9 +155,7 @@ pnpm install
|
||||
### 全局
|
||||
全局变量, 无需 `import` 就可以直接使用. (Tampermonkey API 这里不再列出了, 可根据代码提示使用)
|
||||
|
||||
- `Vue`: Vue 库的主对象, 在创建 `.vue` 组件时, 其中的 `<script>` 可以直接使用 `Vue.extend()`
|
||||
> 出于历史原因, 项目中用的还是 Vue 2, 由于其糟糕的 TypeScript 支持, 在 VS Code + Vetur 的环境下浏览 `.vue` 文件可能会报各种奇奇怪怪的类型错误, 无视就好. (类型是否正确以 `pnpm run type` 的结果为准)
|
||||
|
||||
- `Vue`: Vue 2 提供的主要对象. 不再推荐使用. 如果需要以选项式方式定义 Vue 组件, 请使用 `defineComponent` 而非 `Vue.extend` 或 `new Vue`.
|
||||
- `lodash`: 包含所有 Lodash 库提供的方法
|
||||
- `dq` / `dqa`: `document.querySelector` 和 `document.querySelectorAll` 的简写, `dqa` 会返回真实数组
|
||||
> 在 `bwp-video` 出现后, 这两个查询函数还会自动将对 `video` 的查询扩展到 `bwp-video`
|
||||
@ -241,6 +241,11 @@ pnpm install
|
||||
- `ui/VLoading.vue`: 表示数据加载中, 界面可被插件更改
|
||||
- `ui/AsyncButton.vue`: `click` 事件为异步函数时, 执行期间自动使 `Button` 禁用, 其他和 `Button` 相同.
|
||||
|
||||
## 代码类型检查
|
||||
提交 Pull Request 前, 请确保代码通过类型检查. 类型检查以 VS Code 任务: `生产:类型检查 prod:type` 为准.
|
||||
|
||||
> 项目有[计划](https://github.com/the1812/Bilibili-Evolved/discussions/3939)从 Vue 2 迁移到 Vue 3, 因此虽然我们[启用](https://github.com/the1812/Bilibili-Evolved/pull/4337)了 Volar 对 *.vue 文件进行类型检查, 却未完全修复 Volar 报告的类型错误. 因此,开发时 VS Code 报错属正常现象. Pull Request 的类型检查标准仍以上述内容为准.
|
||||
|
||||
## 代码风格检查
|
||||
项目中含有 ESLint, 不通过 ESLint 是无法进行 Pull Request 的.
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ https://afdian.net/@the1812?tab=sponsor
|
||||
|
||||
| 时间 | 用户名 | 单号后4位 | 金额 |
|
||||
| ------------------- | --------------------- | --------- | ------- |
|
||||
| 2023.09.22 04:41:59 | S*y | 7595 | ¥2.00 |
|
||||
| 2023.08.29 12:38:40 | 匿名 | 3584 | ¥2.33 |
|
||||
| 2023.08.27 05:41:58 | 匿名 | 3000 | ¥20.00 |
|
||||
| 2023.08.27 05:41:35 | 匿名 | 2195 | ¥20.00 |
|
||||
|
||||
@ -91,10 +91,18 @@
|
||||
"type": "component",
|
||||
"name": "badgeHelper",
|
||||
"displayName": "直播勋章快速更换",
|
||||
"description": "在直播区中, 可从功能面板中直接切换勋章和头衔. 默认显示 256 个 (同时也是上限), 可在选项中修改.",
|
||||
"description": "在直播区中, 可从功能面板中直接切换勋章和头衔. 默认显示 256 个 (同时也是上限), 可在选项中修改.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/live/badge-helper.js",
|
||||
"fullAbsolutePath": "registry/dist/components/live/badge-helper.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "liveChatPanelFit",
|
||||
"displayName": "直播间网页全屏自适应",
|
||||
"description": "在直播网页全屏时, 自动调整侧边栏的宽度, 使得视频区域的比例和视频源相匹配, 达到无黑边的效果.\r\n如果在侧边栏的边缘拖动, 可以自定义侧边栏的固定宽度, 双击边缘可以还原到自动宽度.\r\n\r\n- `侧边栏最大宽度 (px)`: 限制侧边栏可被拉伸到的最大宽度. (最小宽度固定为 190px, 再小的话布局就要出问题了)\r\n\r\n> 注意, 由于有最大宽度和最小宽度的限制, 部分窗口尺寸下仍然无法做到无黑边.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/live/chat-panel-fit.js",
|
||||
"fullAbsolutePath": "registry/dist/components/live/chat-panel-fit.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "liveDanmakuSendbar",
|
||||
@ -111,6 +119,14 @@
|
||||
"fullRelativePath": "../../registry/dist/components/live/gift-box.js",
|
||||
"fullAbsolutePath": "registry/dist/components/live/gift-box.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "hide-fullscreen-gift-bar",
|
||||
"displayName": "全屏直播礼物简化",
|
||||
"description": "by [@TimmyOVO](https://github.com/TimmyOVO)\n\n移除全屏观看直播时的底部礼物栏",
|
||||
"fullRelativePath": "../../registry/dist/components/live/hide-gift-fullscreen.js",
|
||||
"fullAbsolutePath": "registry/dist/components/live/hide-gift-fullscreen.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "liveHomeMute",
|
||||
@ -167,11 +183,27 @@
|
||||
"fullRelativePath": "../../registry/dist/components/style/auto-hide-sidebar.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/auto-hide-sidebar.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "clear-home",
|
||||
"displayName": "首页净化",
|
||||
"description": "by [@RieN7](https://github.com/rien7)\n\n删除首页特定类型的卡片",
|
||||
"fullRelativePath": "../../registry/dist/components/style/clear-home.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/clear-home.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "customFontFamily",
|
||||
"displayName": "自定义字体",
|
||||
"description": "by [@Tinhone](https://github.com/Tinhone)\n\n使用组件提供的字体设置覆盖原版的主站字体,并使主站字体可被自定义。字体设置写法请参考 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family) 、默认设置与设置说明",
|
||||
"fullRelativePath": "../../registry/dist/components/style/custom-font-family.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/custom-font-family.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "customNavbar",
|
||||
"displayName": "自定义顶栏",
|
||||
"description": "使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换), 启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:\r\n- *://message.bilibili.com/pages/nav/index_new_pc_sync\r\n- *://message.bilibili.com/pages/nav/index_new_sync\r\n",
|
||||
"description": "使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换).\r\n\r\n启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:\r\n- *://message.bilibili.com/pages/nav/index_new_pc_sync\r\n- *://message.bilibili.com/pages/nav/index_new_sync\r\n\r\n选项说明:\r\n- `全局固定`: 将顶栏固定显示, 即使页面向下滚动也仍然可见. (除了一部分有 b 站定制顶栏的页面会忽略此选项)\r\n- `主题色填充`: 使用通用设置中的主题颜色填充顶栏的背景.\r\n- `透明填充`: 在首页和主站的部分页面中, 当存在顶部横幅图片时, 顶栏背景将变为透明 + 阴影的样式.\r\n- `背景模糊`: 启用背景模糊效果, 会使背景颜色变得半透明, 注意这个效果非常非常消耗图形性能, 慎用.\r\n- `投影`: 在顶栏下方增加一小段投影效果.\r\n- `使用季节 Logo`: 使用 b 站首页的特殊 Logo 图片替换顶栏的 Logo 图, 通常会对应当前季节, 特殊活动期间可能会有变化导致看不清. (比如搞了个全白的 Logo, 而顶栏正好也是白色的)\r\n- `触摸模式`: 启用后顶栏的一级入口点击时不进行跳转, 方便触屏使用.\r\n- `新标签页打开`: 控制顶栏内链接是否在新标签页打开.\r\n- `自动刷新数据`: 启用后, 部分有弹窗的顶栏入口在鼠标经过时会自动刷新数据.\r\n- `顶栏高度`: 自定义顶栏的高度 (b 站原版的为 64px)\r\n- `消息提醒样式`: 自定义顶栏入口的消息提醒样式.\r\n- `搜索栏宽度`: 自定义搜索栏占顶栏的比例.\r\n- `链接对齐样式`: 自定义纯链接弹窗内链接文字的对齐样式.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/custom-navbar.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/custom-navbar.js"
|
||||
},
|
||||
@ -239,6 +271,14 @@
|
||||
"fullRelativePath": "../../registry/dist/components/style/hide/user-card.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/hide/user-card.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "hideVideoNotes",
|
||||
"displayName": "隐藏记笔记",
|
||||
"description": "隐藏视频页面中的 \"记笔记\" 按钮.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/hide/video/notes.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/hide/video/notes.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "hideRecommendedLive",
|
||||
@ -251,10 +291,18 @@
|
||||
"type": "component",
|
||||
"name": "hideRelatedVideos",
|
||||
"displayName": "隐藏视频推荐",
|
||||
"description": "隐藏番剧和视频页面右侧的推荐视频列表. 注意: 如果你想关闭 b 站的自动连播 (自动播放下一个推荐视频) 功能, 需要先取消隐藏视频推荐才能看到开关.",
|
||||
"description": "隐藏番剧和视频页面右侧的推荐视频列表. 注意: 如果你想关闭 b 站的自动连播 (自动播放下一个推荐视频) 功能, 需要先取消隐藏视频推荐才能看到开关.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/hide/video/related-videos.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/hide/video/related-videos.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "hideVideoReport",
|
||||
"displayName": "隐藏稿件投诉",
|
||||
"description": "隐藏视频页面中的 \"稿件投诉\" 按钮.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/hide/video/report.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/hide/video/report.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "hideVideoShare",
|
||||
@ -291,7 +339,7 @@
|
||||
"type": "component",
|
||||
"name": "playerOnTopNew",
|
||||
"displayName": "播放器置顶(新)",
|
||||
"description": "by [@RieN7](https://github.com/rien7)\n\n原来的播放器置顶插件,现在已经不可用了,这是一个新的版本,可以在视频页面中将播放器放在页面最上方.",
|
||||
"description": "by [@RieN7](https://github.com/rien7),[@ZiuChen](https://github.com/ZiuChen),[@LockRim](https://github.com/LockRim)\n\n原来的播放器置顶插件,现在已经不可用了,这是一个新的版本,可以在视频页面中将播放器放在页面最上方.",
|
||||
"fullRelativePath": "../../registry/dist/components/style/player-on-top-new.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/player-on-top-new.js"
|
||||
},
|
||||
@ -331,7 +379,7 @@
|
||||
"type": "component",
|
||||
"name": "simplifyComments",
|
||||
"displayName": "简化评论区",
|
||||
"description": "- 删除热评头像下方的关注按钮\r\n- 删除用户的等级标识\r\n- 删除发送源信息(\\`来自安卓客户端\\` 这种)\r\n- 删除用户名右边的勋章\r\n- 删除评论区顶部的横幅\r\n- 发送时间移动到右上角\r\n- 位图图标全部换用矢量图标, 高分屏不会模糊\r\n- 投票仅显示链接, 隐藏下面的大框.\r\n\r\n> 注: 关注和等级可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看.\r\n",
|
||||
"description": "去除或优化评论区内的元素, 可配置以下选项 (描述的是勾选时的效果):\r\n\r\n> 配置项仅对新版评论区有效\r\n\r\n- `用户等级`: 隐藏用户等级标识. (隐藏后, 可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看)\r\n- `装扮 & 时间`: 隐藏装扮图片, 并把发送时间移动到装扮的位置.\r\n- `头像框`: 隐藏头像框.\r\n- `回复换行`:\r\n - 楼中楼回复也另起一行显示, 和一级回复保持一致.\r\n - `热评`, `UP 主点赞` 等标记和点赞栏放在同一行.\r\n- `编辑框`: 将提示文本居上, 符合用户实际输入文字的位置, 并将发布按钮的字号略微调小.\r\n- `粉丝勋章`: 隐藏用户的粉丝勋章.\r\n- `活动横幅`: 隐藏评论区顶部的活动横幅\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/simplify/comments.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/simplify/comments.js"
|
||||
},
|
||||
@ -355,7 +403,7 @@
|
||||
"type": "component",
|
||||
"name": "disableSpecialDanmaku",
|
||||
"displayName": "禁用特殊弹幕样式",
|
||||
"description": "移除高赞弹幕或 UP 主弹幕的特殊样式, 弹幕内容不会移除.",
|
||||
"description": "移除高赞弹幕或 UP 主弹幕的特殊样式, 弹幕内容不会移除.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/special-danmaku.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/special-danmaku.js"
|
||||
},
|
||||
@ -407,11 +455,19 @@
|
||||
"fullRelativePath": "../../registry/dist/components/touch/player-gestures.js",
|
||||
"fullAbsolutePath": "registry/dist/components/touch/player-gestures.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "albumPubTimeShow",
|
||||
"displayName": "相簿发布时间显示",
|
||||
"description": "by [@Light_Quanta](https://github.com/LightQuanta)\n\n在相簿界面显示相簿的发布时间",
|
||||
"fullRelativePath": "../../registry/dist/components/utils/album-time-show.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/album-time-show.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "autoLike",
|
||||
"displayName": "自动点赞",
|
||||
"description": "进入视频 / 查看动态时, 自动点赞.\r\n",
|
||||
"description": "by [@CrazyboyQCD](https://github.com/CrazyboyQCD)\n\n进入视频 / 查看动态时, 自动点赞\\\r\n无法触发未加载动态的点赞,当启用手动对动态点赞后可手动触发,启用后不会触发动态自动点赞\\\r\n安装快捷键扩展后可以点击【l】键或【L】键触发\\\r\n还可以添加动态点赞的黑名单\\\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/utils/auto-like.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/auto-like.js"
|
||||
},
|
||||
@ -503,6 +559,14 @@
|
||||
"fullRelativePath": "../../registry/dist/components/utils/import-series.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/import-series.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "ipShow",
|
||||
"displayName": "评论区IP属地显示",
|
||||
"description": "by [@Light_Quanta](https://github.com/LightQuanta)\n\n在评论区显示评论的IP属地信息",
|
||||
"fullRelativePath": "../../registry/dist/components/utils/ip-show.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/ip-show.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "keymap",
|
||||
@ -519,11 +583,19 @@
|
||||
"fullRelativePath": "../../registry/dist/components/utils/remove-promotions.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/remove-promotions.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "subscribeTimeShow",
|
||||
"displayName": "关注时间显示",
|
||||
"description": "by [@Light_Quanta](https://github.com/LightQuanta)\n\n在粉丝/关注列表显示关注的具体时间",
|
||||
"fullRelativePath": "../../registry/dist/components/utils/subscribe-time-show.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/subscribe-time-show.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "urlParamsClean",
|
||||
"displayName": "网址参数清理",
|
||||
"description": "自动删除网址中的多余跟踪参数. 请注意这会导致浏览器历史记录出现重复的标题 (分别是转换前后的网址), 并可能导致后退要多退几次.\r\n",
|
||||
"description": "自动删除网址中的多余跟踪参数。请注意这会导致浏览器历史记录出现重复的标题(分别是转换前后的网址),并可能导致后退要多退几次。\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/utils/url-params-clean.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/url-params-clean.js"
|
||||
},
|
||||
@ -551,6 +623,14 @@
|
||||
"fullRelativePath": "../../registry/dist/components/utils/watchlater-redirect.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/watchlater-redirect.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "autoRemoveWatchlater",
|
||||
"displayName": "自动移出稍后再看",
|
||||
"description": "在稍后再看页面播放结束时, 自动将当前视频移出稍后再看.\r\n注意:\r\n- 一定要播放结束, 快结束时手动切走不算\r\n- b 站的稍后再看列表不会实时刷新\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/video/auto-remove-watchlater.js",
|
||||
"fullAbsolutePath": "registry/dist/components/video/auto-remove-watchlater.js"
|
||||
},
|
||||
{
|
||||
"type": "component",
|
||||
"name": "avUrl",
|
||||
@ -779,7 +859,7 @@
|
||||
"type": "component",
|
||||
"name": "showUploadTime",
|
||||
"displayName": "显示视频投稿时间",
|
||||
"description": "by [@wisokey](https://github.com/wisokey)\n\n为视频播放页面的推荐列表中的视频添加显示视频投稿时间.\r\n\r\n`时间格式` 替换up名的文本格式 (默认为'up · yyyy-MM-dd'):\r\n - y: 年\r\n - M: 月\r\n - d: 日\r\n - h: 时\r\n - m: 分\r\n - s: 秒\r\n - q: 季度\r\n - up: up名\r\n",
|
||||
"description": "by [@wisokey](https://github.com/wisokey)\n\n为视频播放页面的推荐列表中的视频添加显示视频投稿时间.\r\n\r\n`时间格式` 替换up名的文本格式 (默认为'up · yyyy-MM-dd'):\r\n - y: 年\r\n - M: 月\r\n - d: 日\r\n - h: 时\r\n - m: 分\r\n - s: 秒\r\n - q: 季度\r\n - up: up名\r\n - \\r: 回车符\r\n - \\n:换行符\r\n - \\t:制表符",
|
||||
"fullRelativePath": "../../registry/dist/components/video/player/show-upload-time.js",
|
||||
"fullAbsolutePath": "registry/dist/components/video/player/show-upload-time.js"
|
||||
},
|
||||
|
||||
@ -116,6 +116,20 @@
|
||||
|
||||
在直播区中, 可从功能面板中直接切换勋章和头衔. 默认显示 256 个 (同时也是上限), 可在选项中修改.
|
||||
|
||||
### [直播间网页全屏自适应](../../registry/dist/components/live/chat-panel-fit.js)
|
||||
`liveChatPanelFit`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/live/chat-panel-fit.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/live/chat-panel-fit.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/live/chat-panel-fit.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/live/chat-panel-fit.js)
|
||||
|
||||
在直播网页全屏时, 自动调整侧边栏的宽度, 使得视频区域的比例和视频源相匹配, 达到无黑边的效果.
|
||||
如果在侧边栏的边缘拖动, 可以自定义侧边栏的固定宽度, 双击边缘可以还原到自动宽度.
|
||||
|
||||
- `侧边栏最大宽度 (px)`: 限制侧边栏可被拉伸到的最大宽度. (最小宽度固定为 190px, 再小的话布局就要出问题了)
|
||||
|
||||
> 注意, 由于有最大宽度和最小宽度的限制, 部分窗口尺寸下仍然无法做到无黑边.
|
||||
|
||||
### [直播弹幕发送栏](../../registry/dist/components/live/danmaku-sendbar.js)
|
||||
`liveDanmakuSendbar`
|
||||
|
||||
@ -134,6 +148,17 @@
|
||||
|
||||
在直播的网页全屏(不能是全屏)模式下往控制栏添加包裹按钮.
|
||||
|
||||
### [全屏直播礼物简化](../../registry/dist/components/live/hide-gift-fullscreen.js)
|
||||
`hide-fullscreen-gift-bar`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/live/hide-gift-fullscreen.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/live/hide-gift-fullscreen.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/live/hide-gift-fullscreen.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/live/hide-gift-fullscreen.js)
|
||||
|
||||
by [@TimmyOVO](https://github.com/TimmyOVO)
|
||||
|
||||
移除全屏观看直播时的底部礼物栏
|
||||
|
||||
### [直播首页静音](../../registry/dist/components/live/home-mute.js)
|
||||
`liveHomeMute`
|
||||
|
||||
@ -197,6 +222,28 @@
|
||||
|
||||
自动隐藏脚本的侧栏 (功能和设置图标). 设置面板停靠在右侧时不建议使用, 因为网页的滚动条会占用右边缘的触发区域.
|
||||
|
||||
### [首页净化](../../registry/dist/components/style/clear-home.js)
|
||||
`clear-home`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/style/clear-home.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/style/clear-home.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/clear-home.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/clear-home.js)
|
||||
|
||||
by [@RieN7](https://github.com/rien7)
|
||||
|
||||
删除首页特定类型的卡片
|
||||
|
||||
### [自定义字体](../../registry/dist/components/style/custom-font-family.js)
|
||||
`customFontFamily`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/style/custom-font-family.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/style/custom-font-family.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/custom-font-family.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/custom-font-family.js)
|
||||
|
||||
by [@Tinhone](https://github.com/Tinhone)
|
||||
|
||||
使用组件提供的字体设置覆盖原版的主站字体,并使主站字体可被自定义。字体设置写法请参考 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family) 、默认设置与设置说明
|
||||
|
||||
### [自定义顶栏](../../registry/dist/components/style/custom-navbar.js)
|
||||
`customNavbar`
|
||||
|
||||
@ -204,10 +251,27 @@
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/custom-navbar.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/custom-navbar.js)
|
||||
|
||||
使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换), 启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:
|
||||
使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换).
|
||||
|
||||
启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:
|
||||
- *://message.bilibili.com/pages/nav/index_new_pc_sync
|
||||
- *://message.bilibili.com/pages/nav/index_new_sync
|
||||
|
||||
选项说明:
|
||||
- `全局固定`: 将顶栏固定显示, 即使页面向下滚动也仍然可见. (除了一部分有 b 站定制顶栏的页面会忽略此选项)
|
||||
- `主题色填充`: 使用通用设置中的主题颜色填充顶栏的背景.
|
||||
- `透明填充`: 在首页和主站的部分页面中, 当存在顶部横幅图片时, 顶栏背景将变为透明 + 阴影的样式.
|
||||
- `背景模糊`: 启用背景模糊效果, 会使背景颜色变得半透明, 注意这个效果非常非常消耗图形性能, 慎用.
|
||||
- `投影`: 在顶栏下方增加一小段投影效果.
|
||||
- `使用季节 Logo`: 使用 b 站首页的特殊 Logo 图片替换顶栏的 Logo 图, 通常会对应当前季节, 特殊活动期间可能会有变化导致看不清. (比如搞了个全白的 Logo, 而顶栏正好也是白色的)
|
||||
- `触摸模式`: 启用后顶栏的一级入口点击时不进行跳转, 方便触屏使用.
|
||||
- `新标签页打开`: 控制顶栏内链接是否在新标签页打开.
|
||||
- `自动刷新数据`: 启用后, 部分有弹窗的顶栏入口在鼠标经过时会自动刷新数据.
|
||||
- `顶栏高度`: 自定义顶栏的高度 (b 站原版的为 64px)
|
||||
- `消息提醒样式`: 自定义顶栏入口的消息提醒样式.
|
||||
- `搜索栏宽度`: 自定义搜索栏占顶栏的比例.
|
||||
- `链接对齐样式`: 自定义纯链接弹窗内链接文字的对齐样式.
|
||||
|
||||
### [夜间模式跟随系统](../../registry/dist/components/style/dark-mode/follow-system.js)
|
||||
`darkModeFollowSystem`
|
||||
|
||||
@ -284,6 +348,15 @@ by [@WakelessSloth56](https://github.com/WakelessSloth56)
|
||||
|
||||
隐藏鼠标指向用户名或用户头像时弹出的浮动用户信息卡片
|
||||
|
||||
### [隐藏记笔记](../../registry/dist/components/style/hide/video/notes.js)
|
||||
`hideVideoNotes`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/style/hide/video/notes.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/style/hide/video/notes.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/hide/video/notes.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/hide/video/notes.js)
|
||||
|
||||
隐藏视频页面中的 "记笔记" 按钮.
|
||||
|
||||
### [隐藏直播推荐](../../registry/dist/components/style/hide/video/recommended-live.js)
|
||||
`hideRecommendedLive`
|
||||
|
||||
@ -302,6 +375,15 @@ by [@WakelessSloth56](https://github.com/WakelessSloth56)
|
||||
|
||||
隐藏番剧和视频页面右侧的推荐视频列表. 注意: 如果你想关闭 b 站的自动连播 (自动播放下一个推荐视频) 功能, 需要先取消隐藏视频推荐才能看到开关.
|
||||
|
||||
### [隐藏稿件投诉](../../registry/dist/components/style/hide/video/report.js)
|
||||
`hideVideoReport`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/style/hide/video/report.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/style/hide/video/report.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/hide/video/report.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/hide/video/report.js)
|
||||
|
||||
隐藏视频页面中的 "稿件投诉" 按钮.
|
||||
|
||||
### [隐藏视频分享](../../registry/dist/components/style/hide/video/share.js)
|
||||
`hideVideoShare`
|
||||
|
||||
@ -355,7 +437,7 @@ by [@WakelessSloth56](https://github.com/WakelessSloth56)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/player-on-top-new.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/player-on-top-new.js)
|
||||
|
||||
by [@RieN7](https://github.com/rien7)
|
||||
by [@RieN7](https://github.com/rien7),[@ZiuChen](https://github.com/ZiuChen),[@LockRim](https://github.com/LockRim)
|
||||
|
||||
原来的播放器置顶插件,现在已经不可用了,这是一个新的版本,可以在视频页面中将播放器放在页面最上方.
|
||||
|
||||
@ -402,16 +484,19 @@ by [@RieN7](https://github.com/rien7)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/style/simplify/comments.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/style/simplify/comments.js)
|
||||
|
||||
- 删除热评头像下方的关注按钮
|
||||
- 删除用户的等级标识
|
||||
- 删除发送源信息(\`来自安卓客户端\` 这种)
|
||||
- 删除用户名右边的勋章
|
||||
- 删除评论区顶部的横幅
|
||||
- 发送时间移动到右上角
|
||||
- 位图图标全部换用矢量图标, 高分屏不会模糊
|
||||
- 投票仅显示链接, 隐藏下面的大框.
|
||||
去除或优化评论区内的元素, 可配置以下选项 (描述的是勾选时的效果):
|
||||
|
||||
> 注: 关注和等级可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看.
|
||||
> 配置项仅对新版评论区有效
|
||||
|
||||
- `用户等级`: 隐藏用户等级标识. (隐藏后, 可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看)
|
||||
- `装扮 & 时间`: 隐藏装扮图片, 并把发送时间移动到装扮的位置.
|
||||
- `头像框`: 隐藏头像框.
|
||||
- `回复换行`:
|
||||
- 楼中楼回复也另起一行显示, 和一级回复保持一致.
|
||||
- `热评`, `UP 主点赞` 等标记和点赞栏放在同一行.
|
||||
- `编辑框`: 将提示文本居上, 符合用户实际输入文字的位置, 并将发布按钮的字号略微调小.
|
||||
- `粉丝勋章`: 隐藏用户的粉丝勋章.
|
||||
- `活动横幅`: 隐藏评论区顶部的活动横幅
|
||||
|
||||
### [简化首页](../../registry/dist/components/style/simplify/home.js)
|
||||
`simplifyHome`
|
||||
@ -498,6 +583,17 @@ by [@RieN7](https://github.com/rien7)
|
||||
- 进度调整可在左上角和右上角取消
|
||||
- 进度调整时在不同位置滑动, 可以使用3档不同的灵敏度.
|
||||
|
||||
### [相簿发布时间显示](../../registry/dist/components/utils/album-time-show.js)
|
||||
`albumPubTimeShow`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/utils/album-time-show.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/utils/album-time-show.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/utils/album-time-show.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/utils/album-time-show.js)
|
||||
|
||||
by [@Light_Quanta](https://github.com/LightQuanta)
|
||||
|
||||
在相簿界面显示相簿的发布时间
|
||||
|
||||
### [自动点赞](../../registry/dist/components/utils/auto-like.js)
|
||||
`autoLike`
|
||||
|
||||
@ -505,7 +601,12 @@ by [@RieN7](https://github.com/rien7)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/utils/auto-like.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/utils/auto-like.js)
|
||||
|
||||
进入视频 / 查看动态时, 自动点赞.
|
||||
by [@CrazyboyQCD](https://github.com/CrazyboyQCD)
|
||||
|
||||
进入视频 / 查看动态时, 自动点赞\
|
||||
无法触发未加载动态的点赞,当启用手动对动态点赞后可手动触发,启用后不会触发动态自动点赞\
|
||||
安装快捷键扩展后可以点击【l】键或【L】键触发\
|
||||
还可以添加动态点赞的黑名单\
|
||||
|
||||
### [屏蔽黑名单up主](../../registry/dist/components/utils/black-list.js)
|
||||
`blackList`
|
||||
@ -636,6 +737,17 @@ by [@aiden](https://github.com/swhoro)
|
||||
|
||||
在合集页面制作一个批量导入按钮,可以新增所有合集内视频至同名收藏夹
|
||||
|
||||
### [评论区IP属地显示](../../registry/dist/components/utils/ip-show.js)
|
||||
`ipShow`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/utils/ip-show.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/utils/ip-show.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/utils/ip-show.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/utils/ip-show.js)
|
||||
|
||||
by [@Light_Quanta](https://github.com/LightQuanta)
|
||||
|
||||
在评论区显示评论的IP属地信息
|
||||
|
||||
### [快捷键扩展](../../registry/dist/components/utils/keymap.js)
|
||||
`keymap`
|
||||
|
||||
@ -658,6 +770,17 @@ by [@aiden](https://github.com/swhoro)
|
||||
- `保留活动横幅`: 保留视频页面的活动横幅.
|
||||
- `保留小喇叭`: 保留视频页面的活动横幅下方评论区上方的黄色小喇叭通知以及动态的黄色小喇叭.
|
||||
|
||||
### [关注时间显示](../../registry/dist/components/utils/subscribe-time-show.js)
|
||||
`subscribeTimeShow`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/utils/subscribe-time-show.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/utils/subscribe-time-show.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/utils/subscribe-time-show.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/utils/subscribe-time-show.js)
|
||||
|
||||
by [@Light_Quanta](https://github.com/LightQuanta)
|
||||
|
||||
在粉丝/关注列表显示关注的具体时间
|
||||
|
||||
### [网址参数清理](../../registry/dist/components/utils/url-params-clean.js)
|
||||
`urlParamsClean`
|
||||
|
||||
@ -665,7 +788,7 @@ by [@aiden](https://github.com/swhoro)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/utils/url-params-clean.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/utils/url-params-clean.js)
|
||||
|
||||
自动删除网址中的多余跟踪参数. 请注意这会导致浏览器历史记录出现重复的标题 (分别是转换前后的网址), 并可能导致后退要多退几次.
|
||||
自动删除网址中的多余跟踪参数。请注意这会导致浏览器历史记录出现重复的标题(分别是转换前后的网址),并可能导致后退要多退几次。
|
||||
|
||||
### [v1 设置迁移](../../registry/dist/components/utils/v1-migrate.js)
|
||||
`v1Migrate`
|
||||
@ -694,6 +817,18 @@ by [@aiden](https://github.com/swhoro)
|
||||
|
||||
将稍后再看的链接重定向为普通播放网址.
|
||||
|
||||
### [自动移出稍后再看](../../registry/dist/components/video/auto-remove-watchlater.js)
|
||||
`autoRemoveWatchlater`
|
||||
|
||||
**jsDelivr:** [`Stable`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@master/registry/dist/components/video/auto-remove-watchlater.js) / [`Preview`](https://cdn.jsdelivr.net/gh/the1812/Bilibili-Evolved@preview/registry/dist/components/video/auto-remove-watchlater.js)
|
||||
|
||||
**GitHub:** [`Stable`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/registry/dist/components/video/auto-remove-watchlater.js) / [`Preview`](https://raw.githubusercontent.com/the1812/Bilibili-Evolved/preview/registry/dist/components/video/auto-remove-watchlater.js)
|
||||
|
||||
在稍后再看页面播放结束时, 自动将当前视频移出稍后再看.
|
||||
注意:
|
||||
- 一定要播放结束, 快结束时手动切走不算
|
||||
- b 站的稍后再看列表不会实时刷新
|
||||
|
||||
### [网址AV号转换](../../registry/dist/components/video/av-url.js)
|
||||
`avUrl`
|
||||
|
||||
@ -1050,6 +1185,9 @@ by [@wisokey](https://github.com/wisokey)
|
||||
- s: 秒
|
||||
- q: 季度
|
||||
- up: up名
|
||||
- \r: 回车符
|
||||
- \n:换行符
|
||||
- \t:制表符
|
||||
|
||||
### [跳过充电鸣谢](../../registry/dist/components/video/player/skip-charge-list.js)
|
||||
`skipChargeList`
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
"type": "component",
|
||||
"name": "disableSpecialDanmaku",
|
||||
"displayName": "禁用特殊弹幕样式",
|
||||
"description": "移除高赞弹幕或 UP 主弹幕的特殊样式, 弹幕内容不会移除.",
|
||||
"description": "移除高赞弹幕或 UP 主弹幕的特殊样式, 弹幕内容不会移除.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/special-danmaku.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/special-danmaku.js"
|
||||
},
|
||||
@ -53,7 +53,7 @@
|
||||
"type": "component",
|
||||
"name": "simplifyComments",
|
||||
"displayName": "简化评论区",
|
||||
"description": "- 删除热评头像下方的关注按钮\r\n- 删除用户的等级标识\r\n- 删除发送源信息(\\`来自安卓客户端\\` 这种)\r\n- 删除用户名右边的勋章\r\n- 删除评论区顶部的横幅\r\n- 发送时间移动到右上角\r\n- 位图图标全部换用矢量图标, 高分屏不会模糊\r\n- 投票仅显示链接, 隐藏下面的大框.\r\n\r\n> 注: 关注和等级可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看.\r\n",
|
||||
"description": "去除或优化评论区内的元素, 可配置以下选项 (描述的是勾选时的效果):\r\n\r\n> 配置项仅对新版评论区有效\r\n\r\n- `用户等级`: 隐藏用户等级标识. (隐藏后, 可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看)\r\n- `装扮 & 时间`: 隐藏装扮图片, 并把发送时间移动到装扮的位置.\r\n- `头像框`: 隐藏头像框.\r\n- `回复换行`:\r\n - 楼中楼回复也另起一行显示, 和一级回复保持一致.\r\n - `热评`, `UP 主点赞` 等标记和点赞栏放在同一行.\r\n- `编辑框`: 将提示文本居上, 符合用户实际输入文字的位置, 并将发布按钮的字号略微调小.\r\n- `粉丝勋章`: 隐藏用户的粉丝勋章.\r\n- `活动横幅`: 隐藏评论区顶部的活动横幅\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/simplify/comments.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/simplify/comments.js"
|
||||
},
|
||||
@ -85,7 +85,7 @@
|
||||
"type": "component",
|
||||
"name": "hideRelatedVideos",
|
||||
"displayName": "隐藏视频推荐",
|
||||
"description": "隐藏番剧和视频页面右侧的推荐视频列表. 注意: 如果你想关闭 b 站的自动连播 (自动播放下一个推荐视频) 功能, 需要先取消隐藏视频推荐才能看到开关.",
|
||||
"description": "隐藏番剧和视频页面右侧的推荐视频列表. 注意: 如果你想关闭 b 站的自动连播 (自动播放下一个推荐视频) 功能, 需要先取消隐藏视频推荐才能看到开关.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/hide/video/related-videos.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/hide/video/related-videos.js"
|
||||
},
|
||||
@ -192,7 +192,7 @@
|
||||
"type": "component",
|
||||
"name": "customNavbar",
|
||||
"displayName": "自定义顶栏",
|
||||
"description": "使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换), 启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:\r\n- *://message.bilibili.com/pages/nav/index_new_pc_sync\r\n- *://message.bilibili.com/pages/nav/index_new_sync\r\n",
|
||||
"description": "使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换).\r\n\r\n启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:\r\n- *://message.bilibili.com/pages/nav/index_new_pc_sync\r\n- *://message.bilibili.com/pages/nav/index_new_sync\r\n\r\n选项说明:\r\n- `全局固定`: 将顶栏固定显示, 即使页面向下滚动也仍然可见. (除了一部分有 b 站定制顶栏的页面会忽略此选项)\r\n- `主题色填充`: 使用通用设置中的主题颜色填充顶栏的背景.\r\n- `透明填充`: 在首页和主站的部分页面中, 当存在顶部横幅图片时, 顶栏背景将变为透明 + 阴影的样式.\r\n- `背景模糊`: 启用背景模糊效果, 会使背景颜色变得半透明, 注意这个效果非常非常消耗图形性能, 慎用.\r\n- `投影`: 在顶栏下方增加一小段投影效果.\r\n- `使用季节 Logo`: 使用 b 站首页的特殊 Logo 图片替换顶栏的 Logo 图, 通常会对应当前季节, 特殊活动期间可能会有变化导致看不清. (比如搞了个全白的 Logo, 而顶栏正好也是白色的)\r\n- `触摸模式`: 启用后顶栏的一级入口点击时不进行跳转, 方便触屏使用.\r\n- `新标签页打开`: 控制顶栏内链接是否在新标签页打开.\r\n- `自动刷新数据`: 启用后, 部分有弹窗的顶栏入口在鼠标经过时会自动刷新数据.\r\n- `顶栏高度`: 自定义顶栏的高度 (b 站原版的为 64px)\r\n- `消息提醒样式`: 自定义顶栏入口的消息提醒样式.\r\n- `搜索栏宽度`: 自定义搜索栏占顶栏的比例.\r\n- `链接对齐样式`: 自定义纯链接弹窗内链接文字的对齐样式.\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/style/custom-navbar.js",
|
||||
"fullAbsolutePath": "registry/dist/components/style/custom-navbar.js"
|
||||
},
|
||||
@ -216,7 +216,7 @@
|
||||
"type": "component",
|
||||
"name": "urlParamsClean",
|
||||
"displayName": "网址参数清理",
|
||||
"description": "自动删除网址中的多余跟踪参数. 请注意这会导致浏览器历史记录出现重复的标题 (分别是转换前后的网址), 并可能导致后退要多退几次.\r\n",
|
||||
"description": "自动删除网址中的多余跟踪参数。请注意这会导致浏览器历史记录出现重复的标题(分别是转换前后的网址),并可能导致后退要多退几次。\r\n",
|
||||
"fullRelativePath": "../../registry/dist/components/utils/url-params-clean.js",
|
||||
"fullAbsolutePath": "registry/dist/components/utils/url-params-clean.js"
|
||||
},
|
||||
|
||||
@ -11,6 +11,5 @@
|
||||
## 预览版
|
||||
- 更新版本号, 打上 tag
|
||||
- 生成文档
|
||||
- 编译预览版
|
||||
- 带 tag 推送
|
||||
- 测下 Edge + Tampermonkey
|
||||
|
||||
28
package.json
28
package.json
@ -14,14 +14,14 @@
|
||||
"type": "tsc -p tsconfig.type-check.json --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.20.12",
|
||||
"@babel/parser": "^7.20.15",
|
||||
"@babel/core": "7.22.10",
|
||||
"@babel/parser": "^7.22.10",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@babel/types": "^7.20.7",
|
||||
"@babel/preset-env": "^7.22.10",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@babel/types": "^7.22.10",
|
||||
"@types/async-exit-hook": "^2.0.0",
|
||||
"@types/babel__core": "^7.20.0",
|
||||
"@types/babel__core": "^7.20.1",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/lodash": "^4.14.172",
|
||||
@ -36,7 +36,7 @@
|
||||
"@typescript-eslint/parser": "^5.50.0",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"autoprefixer": "^10.0.1",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"browserslist": "^4.21.4",
|
||||
"css-loader": "^5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
@ -44,7 +44,7 @@
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "7.1.0",
|
||||
"eslint-plugin-vue": "^9.15.1",
|
||||
"fast-sass-loader": "^2.0.0",
|
||||
"glob": "^10.2.6",
|
||||
"postcss": "^8.1.0",
|
||||
@ -60,13 +60,13 @@
|
||||
"to-string-loader": "^1.2.0",
|
||||
"ts-node": "^10.7.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vue-loader": "^15.8.3",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vue-loader": "^15.10.1",
|
||||
"webpack": "^5.31.2",
|
||||
"webpack-bundle-analyzer": "^4.5.0",
|
||||
"webpack-cli": "^4.6.0",
|
||||
"ws": "^8.5.0",
|
||||
"yaml": "^2.3.1"
|
||||
"yaml": "^2.3.1",
|
||||
"zx": "^7.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.6.0",
|
||||
@ -78,12 +78,12 @@
|
||||
"protobufjs": "^6.11.2",
|
||||
"streamsaver": "^2.0.6",
|
||||
"tippy.js": "^6.3.1",
|
||||
"vue": "^2.6.11",
|
||||
"vue-fragment": "^1.5.2"
|
||||
"vue": "^2.7.14"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"caniuse-lite": "^1.0.30001481"
|
||||
"caniuse-lite": "^1.0.30001481",
|
||||
"consolidate": "1.0.1"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
11400
pnpm-lock.yaml
generated
11400
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1
registry/dist/components/style/clear-home.js
vendored
Normal file
1
registry/dist/components/style/clear-home.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(e,a){"object"==typeof exports&&"object"==typeof module?module.exports=a():"function"==typeof define&&define.amd?define([],a):"object"==typeof exports?exports["style/clear-home"]=a():e["style/clear-home"]=a()}(globalThis,(()=>(()=>{"use strict";var e={d:(a,l)=>{for(var t in l)e.o(l,t)&&!e.o(a,t)&&Object.defineProperty(a,t,{enumerable:!0,get:l[t]})},o:(e,a)=>Object.prototype.hasOwnProperty.call(e,a)},a={};e.d(a,{component:()=>c});const l=coreApis.componentApis.define,t=coreApis.settings,s=[];let d=!1,o=!0;const i=(0,l.defineOptionsMetadata)({广告:{displayName:"广告",defaultValue:!0},番剧:{displayName:"番剧",defaultValue:!0},电影:{displayName:"电影",defaultValue:!0},国创:{displayName:"国创",defaultValue:!0},电视剧:{displayName:"电视剧",defaultValue:!0},综艺:{displayName:"综艺",defaultValue:!0},纪录片:{displayName:"纪录片",defaultValue:!0},动画:{displayName:"动画",defaultValue:!0},游戏:{displayName:"游戏",defaultValue:!0},鬼畜:{displayName:"鬼畜",defaultValue:!0},音乐:{displayName:"音乐",defaultValue:!0},舞蹈:{displayName:"舞蹈",defaultValue:!0},影视:{displayName:"影视",defaultValue:!0},娱乐:{displayName:"娱乐",defaultValue:!0},知识:{displayName:"知识",defaultValue:!0},科技:{displayName:"科技",defaultValue:!0},资讯:{displayName:"资讯",defaultValue:!0},美食:{displayName:"美食",defaultValue:!0},生活:{displayName:"生活",defaultValue:!0},汽车:{displayName:"汽车",defaultValue:!0},时尚:{displayName:"时尚",defaultValue:!0},运动:{displayName:"运动",defaultValue:!0},动物圈:{displayName:"动物圈",defaultValue:!0},VLOG:{displayName:"VLOG",defaultValue:!0},搞笑:{displayName:"搞笑",defaultValue:!0},单机游戏:{displayName:"单机游戏",defaultValue:!0},虚拟UP主:{displayName:"虚拟UP主",defaultValue:!0},公益:{displayName:"公益",defaultValue:!0},公开课:{displayName:"公开课",defaultValue:!0},专栏:{displayName:"专栏",defaultValue:!0},直播:{displayName:"直播",defaultValue:!0},赛事:{displayName:"赛事",defaultValue:!0},活动:{displayName:"活动",defaultValue:!0},课堂:{displayName:"课堂",defaultValue:!0},社区中心:{displayName:"社区中心",defaultValue:!0},新歌热榜:{displayName:"新歌热榜",defaultValue:!0},漫画:{displayName:"漫画",defaultValue:!0}});function u(e){console.log(e);for(const a of e)if(a.classList)if(a.classList.contains("floor-single-card")){const e=a.querySelector(".badge").textContent;e&&s.includes(e)&&(a.remove(),console.log("remove",e))}else a.classList.contains("bili-live-card")?d&&(a.remove(),console.log("remove live")):(a.classList.contains("bili-video-card")||a.classList.contains("feed-card"))&&o&&(a.querySelector(".bili-video-card__info--ad")||a.querySelector(".bili-video-card__info--creative-ad"))&&(a.remove(),console.log("remove ads"))}function n(e){const a=[];for(const l of e){const e=l.target;if(!(e.classList&&e.classList.contains("carousel-transform")||"SPAN"===e.tagName)&&l.addedNodes.length>0)for(const e of l.addedNodes){const l=e;"#text"===l.nodeName||l.classList&&l.classList.contains("bili-watch-later")||a.push(l)}}a.length>0&&u(a)}const c=(0,l.defineComponentMetadata)({name:"clear-home",author:{name:"RieN7",link:"https://github.com/rien7"},tags:[componentsTags.style],displayName:"首页净化",description:"删除首页特定类型的卡片",entry:async e=>{let{metadata:a,settings:l}=e;const i=document.querySelector("main > .feed2 > .recommended-container_floor-aside > .container");if(!i)return;new MutationObserver(n).observe(i,{childList:!0}),Object.keys(l.options).forEach((e=>{(0,t.addComponentListener)(`${a.name}.${e}`,(a=>{if(a){if("广告"===e)return void(o=!0);s.push(e),"直播"===e&&(d=!0)}}),!0)}));u(i.children)},options:i,commitHash:"1167f721dc805a6bfd3d4bcba753607eff4bacd5",coreVersion:"2.7.0"});return a=a.component})()));
|
||||
@ -69,7 +69,10 @@ const entry = async () => {
|
||||
return
|
||||
}
|
||||
if (postContent.classList.contains('repost') || card.type === feedsCardTypes.repost) {
|
||||
const contents = dq(postContent, '.content, .bili-dyn-content__orig__desc') as HTMLElement
|
||||
const contents = dq(
|
||||
postContent,
|
||||
'.content, .bili-dyn-content__orig__desc, .dyn-card-opus__summary',
|
||||
) as HTMLElement
|
||||
if (!contents) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -293,7 +293,7 @@ body.disable-feeds-filter {
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
@include semi-bold();
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="multiple-widgets">
|
||||
<div ref="element" class="multiple-widgets">
|
||||
<VPopup
|
||||
ref="medalPopup"
|
||||
v-model="medalOpen"
|
||||
@ -50,85 +50,76 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { addComponentListener, getComponentSettings } from '@/core/settings'
|
||||
import { descendingSort } from '@/core/utils/sort'
|
||||
import { DefaultWidget, VPopup } from '@/ui'
|
||||
import { Medal, Title, Badge, getMedalList, getTitleList } from './badge'
|
||||
import { BadgeHelperOptions } from './options'
|
||||
|
||||
const { options } = getComponentSettings('badgeHelper')
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
DefaultWidget,
|
||||
VPopup,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
medalList: [],
|
||||
titleList: [],
|
||||
medalOpen: false,
|
||||
titleOpen: false,
|
||||
grayEffect: true,
|
||||
const { options } = getComponentSettings<BadgeHelperOptions>('badgeHelper')
|
||||
const element = ref<HTMLElement>()
|
||||
const medalList = ref<Medal[]>([])
|
||||
const titleList = ref<Title[]>([])
|
||||
const medalOpen = ref(false)
|
||||
const titleOpen = ref(false)
|
||||
const grayEffect = ref(true)
|
||||
|
||||
const updateColumnsCount = () => {
|
||||
const maxColumnCount = 15
|
||||
const columnLength = 18
|
||||
const medalColumns = Math.min(Math.ceil(medalList.value.length / columnLength), maxColumnCount)
|
||||
element.value.style.setProperty('--medal-columns', medalColumns.toString())
|
||||
const titleColumns = Math.min(Math.ceil(titleList.value.length / columnLength), maxColumnCount)
|
||||
element.value.style.setProperty('--title-columns', titleColumns.toString())
|
||||
}
|
||||
const loadMedalList = async () => {
|
||||
medalList.value = (await getMedalList())
|
||||
.sort(descendingSort(it => it.level))
|
||||
.slice(0, options.maxBadgeCount)
|
||||
}
|
||||
const loadTitleList = async () => {
|
||||
titleList.value = (await getTitleList()).slice(0, options.maxBadgeCount)
|
||||
}
|
||||
const toggleBadge = async (badge: Badge, list: Badge[]) => {
|
||||
console.log(badge)
|
||||
if (badge.isActive) {
|
||||
badge.isActive = false
|
||||
await badge.deactivate()
|
||||
} else {
|
||||
const activeBadge = list.find(b => b.isActive)
|
||||
if (activeBadge) {
|
||||
activeBadge.isActive = false
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
addComponentListener(
|
||||
'badgeHelper.grayEffect',
|
||||
(enable: boolean) => {
|
||||
this.grayEffect = enable
|
||||
},
|
||||
true,
|
||||
)
|
||||
const init = async () => {
|
||||
const medal = this.loadMedalList()
|
||||
await Title.getImageMap()
|
||||
const title = this.loadTitleList()
|
||||
return Promise.all([medal, title])
|
||||
badge.isActive = true
|
||||
await badge.activate()
|
||||
if (badge instanceof Medal) {
|
||||
options.defaultMedalID = badge.id
|
||||
}
|
||||
await init()
|
||||
this.updateColumnsCount()
|
||||
},
|
||||
methods: {
|
||||
updateColumnsCount() {
|
||||
const maxColumnCount = 15
|
||||
const columnLength = 18
|
||||
const element = this.$el as HTMLElement
|
||||
const medalColumns = Math.min(Math.ceil(this.medalList.length / columnLength), maxColumnCount)
|
||||
element.style.setProperty('--medal-columns', medalColumns.toString())
|
||||
const titleColumns = Math.min(Math.ceil(this.titleList.length / columnLength), maxColumnCount)
|
||||
element.style.setProperty('--title-columns', titleColumns.toString())
|
||||
}
|
||||
if (badge instanceof Medal) {
|
||||
await loadMedalList()
|
||||
} else if (badge instanceof Title) {
|
||||
await loadTitleList()
|
||||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
addComponentListener(
|
||||
'badgeHelper.grayEffect',
|
||||
(enable: boolean) => {
|
||||
grayEffect.value = enable
|
||||
},
|
||||
async loadMedalList() {
|
||||
this.medalList = (await getMedalList())
|
||||
.sort(descendingSort(it => it.level))
|
||||
.slice(0, options.maxBadgeCount)
|
||||
},
|
||||
async loadTitleList() {
|
||||
this.titleList = (await getTitleList()).slice(0, options.maxBadgeCount)
|
||||
},
|
||||
async toggleBadge(badge: Badge, list: Badge[]) {
|
||||
console.log(badge)
|
||||
if (badge.isActive) {
|
||||
badge.isActive = false
|
||||
await badge.deactivate()
|
||||
} else {
|
||||
const activeBadge = list.find(b => b.isActive)
|
||||
if (activeBadge) {
|
||||
activeBadge.isActive = false
|
||||
}
|
||||
badge.isActive = true
|
||||
await badge.activate()
|
||||
if (badge instanceof Medal) {
|
||||
options.defaultMedalID = badge.id
|
||||
}
|
||||
}
|
||||
if (badge instanceof Medal) {
|
||||
await this.loadMedalList()
|
||||
} else if (badge instanceof Title) {
|
||||
await this.loadTitleList()
|
||||
}
|
||||
},
|
||||
},
|
||||
true,
|
||||
)
|
||||
const init = async () => {
|
||||
const medal = loadMedalList()
|
||||
await Title.getImageMap()
|
||||
const title = loadTitleList()
|
||||
return Promise.all([medal, title])
|
||||
}
|
||||
await init()
|
||||
updateColumnsCount()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
getTextWithCredentials,
|
||||
postTextWithCredentials,
|
||||
} from '@/core/ajax'
|
||||
import { formData, getCsrf } from '@/core/utils'
|
||||
import { getCsrf } from '@/core/utils'
|
||||
import { logError } from '@/core/utils/log'
|
||||
|
||||
const validateJson = (json: any, errorMessage: string) => {
|
||||
@ -18,22 +18,6 @@ const validateJson = (json: any, errorMessage: string) => {
|
||||
|
||||
export abstract class Badge {
|
||||
constructor(public isActive: boolean = false, public id: number = 0) {}
|
||||
/** @deprecated */
|
||||
static parseJson<T>(
|
||||
text: string,
|
||||
actions: {
|
||||
successAction: (json: any) => T
|
||||
errorMessage: string
|
||||
errorAction: (json: any) => T
|
||||
},
|
||||
) {
|
||||
const json = JSON.parse(text)
|
||||
if (json.code !== 0) {
|
||||
logError(`${actions.errorMessage} 错误码:${json.code} ${json.message || ''}`)
|
||||
return actions.errorAction(json)
|
||||
}
|
||||
return actions.successAction(json)
|
||||
}
|
||||
abstract activate(): Promise<boolean>
|
||||
abstract deactivate(): Promise<boolean>
|
||||
}
|
||||
@ -59,8 +43,8 @@ export class Medal extends Badge {
|
||||
async activate() {
|
||||
const text = await postTextWithCredentials(
|
||||
'https://api.live.bilibili.com/xlive/web-room/v1/fansMedal/wear',
|
||||
formData({
|
||||
medal_id: this.id,
|
||||
new URLSearchParams({
|
||||
medal_id: this.id.toString(),
|
||||
csrf_token: getCsrf(),
|
||||
csrf: getCsrf(),
|
||||
}),
|
||||
@ -72,7 +56,7 @@ export class Medal extends Badge {
|
||||
async deactivate() {
|
||||
const text = await postTextWithCredentials(
|
||||
'https://api.live.bilibili.com/xlive/web-room/v1/fansMedal/take_off',
|
||||
formData({
|
||||
new URLSearchParams({
|
||||
csrf_token: getCsrf(),
|
||||
csrf: getCsrf(),
|
||||
}),
|
||||
@ -104,9 +88,9 @@ export class Title extends Badge {
|
||||
source: string
|
||||
imageUrl: string
|
||||
constructor(json: any) {
|
||||
const { id, cid, wear, css, name, source } = json
|
||||
super(wear, css)
|
||||
this.tid = id
|
||||
const { identification, wear, tid, cid, name, source } = json
|
||||
super(wear, identification)
|
||||
this.tid = tid
|
||||
this.cid = cid
|
||||
this.name = name
|
||||
this.source = source
|
||||
@ -116,66 +100,56 @@ export class Title extends Badge {
|
||||
}
|
||||
static async getImageMap() {
|
||||
if (Title.imageMap === undefined) {
|
||||
return Badge.parseJson(
|
||||
const json = JSON.parse(
|
||||
await getTextWithCredentials('https://api.live.bilibili.com/rc/v1/Title/webTitles'),
|
||||
{
|
||||
successAction(json) {
|
||||
Title.imageMap = {}
|
||||
json.data.forEach((it: any) => {
|
||||
Title.imageMap[it.identification] = it.web_pic_url
|
||||
})
|
||||
return Title.imageMap
|
||||
},
|
||||
errorAction: () => ({}),
|
||||
errorMessage: '获取头衔图片失败.',
|
||||
},
|
||||
)
|
||||
if (validateJson(json, '获取头衔图片失败.')) {
|
||||
Title.imageMap = {}
|
||||
json.data.forEach((it: any) => {
|
||||
Title.imageMap[it.identification] = it.web_pic_url
|
||||
})
|
||||
return Title.imageMap
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
return Title.imageMap
|
||||
}
|
||||
async activate() {
|
||||
return Badge.parseJson(
|
||||
const json = JSON.parse(
|
||||
await postTextWithCredentials(
|
||||
'https://api.live.bilibili.com/i/ajaxWearTitle',
|
||||
`id=${this.tid}&cid=${this.cid}&csrf=${getCsrf()}&csrf_token=${getCsrf()}`,
|
||||
),
|
||||
{
|
||||
successAction: () => {
|
||||
this.isActive = true
|
||||
return true
|
||||
},
|
||||
errorAction: () => false,
|
||||
errorMessage: '佩戴头衔失败.',
|
||||
},
|
||||
)
|
||||
if (validateJson(json, '佩戴头衔失败.')) {
|
||||
this.isActive = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
async deactivate() {
|
||||
return Badge.parseJson(
|
||||
const json = JSON.parse(
|
||||
await postTextWithCredentials(
|
||||
'https://api.live.bilibili.com/i/ajaxCancelWearTitle',
|
||||
`csrf=${getCsrf()}&csrf_token=${getCsrf()}`,
|
||||
),
|
||||
{
|
||||
successAction: () => {
|
||||
this.isActive = false
|
||||
return true
|
||||
},
|
||||
errorAction: () => false,
|
||||
errorMessage: '卸下头衔失败.',
|
||||
},
|
||||
)
|
||||
if (validateJson(json, '卸下头衔失败.')) {
|
||||
this.isActive = false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
export const getTitleList = async () =>
|
||||
Badge.parseJson(
|
||||
export const getTitleList = async (): Promise<Title[]> => {
|
||||
const json = JSON.parse(
|
||||
await getTextWithCredentials(
|
||||
'https://api.live.bilibili.com/i/api/ajaxTitleInfo?page=1&pageSize=256&had=1',
|
||||
'https://api.live.bilibili.com/xlive/web-ucenter/v1/user_title/TitleList?normal=0&special=0&had=1&page=1&page_size=256',
|
||||
),
|
||||
{
|
||||
successAction: json =>
|
||||
lodash.get(json, 'data.list', []).map((it: any) => new Title(it)) as Title[],
|
||||
errorAction: () => [] as Title[],
|
||||
errorMessage: '无法获取头衔列表.',
|
||||
},
|
||||
)
|
||||
if (validateJson(json, '无法获取头衔列表.')) {
|
||||
return lodash.get(json, 'data.list', []).map((it: any) => new Title(it))
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
1
registry/lib/components/live/badge-helper/index.md
Normal file
1
registry/lib/components/live/badge-helper/index.md
Normal file
@ -0,0 +1 @@
|
||||
在直播区中, 可从功能面板中直接切换勋章和头衔. 默认显示 256 个 (同时也是上限), 可在选项中修改.
|
||||
@ -1,14 +1,11 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { getNumberValidator, getUID, none } from '@/core/utils'
|
||||
import { getUID, none } from '@/core/utils'
|
||||
import { autoMatchMedal } from './auto-match'
|
||||
import { badgeHelperOptions } from './options'
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'badgeHelper',
|
||||
displayName: '直播勋章快速更换',
|
||||
description: {
|
||||
'zh-CN':
|
||||
'在直播区中, 可从功能面板中直接切换勋章和头衔. 默认显示 256 个 (同时也是上限), 可在选项中修改.',
|
||||
},
|
||||
entry: () => autoMatchMedal(),
|
||||
reload: none,
|
||||
unload: none,
|
||||
@ -17,25 +14,6 @@ export const component = defineComponentMetadata({
|
||||
component: () => import('./BadgeHelper.vue').then(m => m.default),
|
||||
condition: () => Boolean(getUID()),
|
||||
},
|
||||
options: {
|
||||
autoMatchMedal: {
|
||||
defaultValue: true,
|
||||
displayName: '自动佩戴当前直播间勋章',
|
||||
},
|
||||
maxBadgeCount: {
|
||||
defaultValue: 256,
|
||||
displayName: '最大显示数量',
|
||||
validator: getNumberValidator(1, 256),
|
||||
},
|
||||
defaultMedalID: {
|
||||
displayName: '默认勋章ID',
|
||||
hidden: true,
|
||||
defaultValue: 0,
|
||||
},
|
||||
grayEffect: {
|
||||
displayName: '显示勋章的未点亮状态',
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
options: badgeHelperOptions,
|
||||
urlInclude: ['//live.bilibili.com'],
|
||||
})
|
||||
|
||||
24
registry/lib/components/live/badge-helper/options.ts
Normal file
24
registry/lib/components/live/badge-helper/options.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { OptionsOfMetadata, defineOptionsMetadata } from '@/components/define'
|
||||
import { getNumberValidator } from '@/core/utils'
|
||||
|
||||
export const badgeHelperOptions = defineOptionsMetadata({
|
||||
autoMatchMedal: {
|
||||
defaultValue: true,
|
||||
displayName: '自动佩戴当前直播间勋章',
|
||||
},
|
||||
maxBadgeCount: {
|
||||
defaultValue: 256,
|
||||
displayName: '最大显示数量',
|
||||
validator: getNumberValidator(1, 256),
|
||||
},
|
||||
defaultMedalID: {
|
||||
displayName: '默认勋章ID',
|
||||
hidden: true,
|
||||
defaultValue: 0,
|
||||
},
|
||||
grayEffect: {
|
||||
displayName: '显示勋章的未点亮状态',
|
||||
defaultValue: true,
|
||||
},
|
||||
})
|
||||
export type BadgeHelperOptions = OptionsOfMetadata<typeof badgeHelperOptions>
|
||||
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div
|
||||
ref="draggerElement"
|
||||
class="chat-panel-fit-dragger"
|
||||
:class="{ dragging: isDragging }"
|
||||
:style="{ transform: `translateX(${-movement}px)` }"
|
||||
@pointerdown="startDragging"
|
||||
@pointermove="handlePointerMove"
|
||||
@dblclick="resetCustomWidth"
|
||||
>
|
||||
<div class="chat-panel-fit-dragger-bar"></div>
|
||||
<div class="chat-panel-fit-dragger-preview-area" :style="{ width: `${previewWidth}px` }">
|
||||
<div class="chat-panel-fit-dragger-preview-area-background">
|
||||
{{ previewWidth.toFixed(1) }}px
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, onBeforeMount } from 'vue'
|
||||
import { getComponentSettings } from '@/core/settings'
|
||||
import { ChatPanelFitOptions, ChatPanelFitOptionsMinWidth } from './options'
|
||||
|
||||
const options = reactive(getComponentSettings<ChatPanelFitOptions>('liveChatPanelFit').options)
|
||||
onBeforeMount(() => {
|
||||
if (options.customWidth !== 0) {
|
||||
document.documentElement.style.setProperty(
|
||||
'--live-chat-panel-width',
|
||||
`${options.customWidth}px`,
|
||||
)
|
||||
}
|
||||
})
|
||||
const getAutoWidth = () =>
|
||||
parseFloat(document.documentElement.style.getPropertyValue('--live-chat-panel-width'))
|
||||
const draggerElement = ref<HTMLElement>()
|
||||
const startPoint = ref(0)
|
||||
const movement = ref(0)
|
||||
const previewWidth = computed(() => {
|
||||
if (options.customWidth) {
|
||||
return options.customWidth + movement.value
|
||||
}
|
||||
return getAutoWidth()
|
||||
})
|
||||
const isDragging = ref(false)
|
||||
|
||||
const resetCustomWidth = () => {
|
||||
options.customWidth = 0
|
||||
window.dispatchEvent(new CustomEvent('customWidthReset'))
|
||||
}
|
||||
const handlePointerMove = (e: PointerEvent) => {
|
||||
if (!isDragging.value) {
|
||||
return
|
||||
}
|
||||
const currentMovement = startPoint.value - e.screenX
|
||||
movement.value =
|
||||
lodash.clamp(
|
||||
options.customWidth + currentMovement,
|
||||
ChatPanelFitOptionsMinWidth,
|
||||
options.maxWidth,
|
||||
) - options.customWidth
|
||||
}
|
||||
const startDragging = (e: PointerEvent) => {
|
||||
isDragging.value = true
|
||||
startPoint.value = e.screenX
|
||||
if (options.customWidth === 0) {
|
||||
options.customWidth = getAutoWidth()
|
||||
}
|
||||
draggerElement.value.setPointerCapture(e.pointerId)
|
||||
document.documentElement.style.cursor = 'ew-resize'
|
||||
document.documentElement.classList.add('custom-width-dragging')
|
||||
document.documentElement.addEventListener(
|
||||
'pointerup',
|
||||
() => {
|
||||
document.documentElement.style.cursor = ''
|
||||
document.documentElement.classList.remove('custom-width-dragging')
|
||||
isDragging.value = false
|
||||
startPoint.value = 0
|
||||
if (!movement.value) {
|
||||
return
|
||||
}
|
||||
const newWidth = previewWidth.value
|
||||
options.customWidth = newWidth
|
||||
movement.value = 0
|
||||
document.documentElement.style.setProperty('--live-chat-panel-width', `${newWidth}px`)
|
||||
},
|
||||
{ once: true },
|
||||
)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import 'common';
|
||||
|
||||
// 拖动时暂时提高 z-index, 否则预览区域会被挡住
|
||||
html.custom-width-dragging .player-full-win:not(.hide-aside-area) .player-ctnr {
|
||||
z-index: 1001 !important;
|
||||
.head-info-section {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.aside-area-toggle-btn {
|
||||
height: 90px;
|
||||
@include absolute-v-center();
|
||||
}
|
||||
.chat-panel-fit-dragger {
|
||||
$dragger-width: 8px;
|
||||
pointer-events: none;
|
||||
touch-action: none;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
@include v-stretch();
|
||||
.player-full-win:not(.hide-aside-area) & {
|
||||
pointer-events: initial;
|
||||
}
|
||||
&-bar {
|
||||
opacity: 0;
|
||||
cursor: ew-resize;
|
||||
transition: opacity 0.2s ease-out;
|
||||
flex-grow: 1;
|
||||
width: $dragger-width;
|
||||
height: 100%;
|
||||
background-color: var(--theme-color);
|
||||
transform: translateX(#{-$dragger-width});
|
||||
@include v-center();
|
||||
html.custom-width-dragging &,
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&-preview-area {
|
||||
opacity: 0;
|
||||
@include v-stretch();
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
font-size: 16px;
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
body.dark & {
|
||||
background-color: #222;
|
||||
color: #eee;
|
||||
}
|
||||
&-background {
|
||||
flex-grow: 1;
|
||||
background-color: var(--theme-color-10);
|
||||
@include h-center();
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
&.dragging &-preview-area {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,10 @@
|
||||
.player-full-win:not(.hide-aside-area) {
|
||||
.live-room-app {
|
||||
.aside-area {
|
||||
width: var(--live-chat-panel-width, 302px) !important;
|
||||
}
|
||||
.player-section {
|
||||
width: calc(100% - var(--live-chat-panel-width, 302px)) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
registry/lib/components/live/chat-panel-fit/index.md
Normal file
6
registry/lib/components/live/chat-panel-fit/index.md
Normal file
@ -0,0 +1,6 @@
|
||||
在直播网页全屏时, 自动调整侧边栏的宽度, 使得视频区域的比例和视频源相匹配, 达到无黑边的效果.
|
||||
如果在侧边栏的边缘拖动, 可以自定义侧边栏的固定宽度, 双击边缘可以还原到自动宽度.
|
||||
|
||||
- `侧边栏最大宽度 (px)`: 限制侧边栏可被拉伸到的最大宽度. (最小宽度固定为 190px, 再小的话布局就要出问题了)
|
||||
|
||||
> 注意, 由于有最大宽度和最小宽度的限制, 部分窗口尺寸下仍然无法做到无黑边.
|
||||
94
registry/lib/components/live/chat-panel-fit/index.ts
Normal file
94
registry/lib/components/live/chat-panel-fit/index.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import {
|
||||
addComponentListener,
|
||||
getComponentSettings,
|
||||
removeComponentListener,
|
||||
} from '@/core/settings'
|
||||
import { select, sq } from '@/core/spin-query'
|
||||
import { mountVueComponent } from '@/core/utils'
|
||||
import { useScopedConsole } from '@/core/utils/log'
|
||||
import { liveUrls } from '@/core/utils/urls'
|
||||
import { ChatPanelFitOptions, ChatPanelFitOptionsMinWidth, chatPanelFitOptions } from './options'
|
||||
|
||||
const name = 'liveChatPanelFit'
|
||||
const console = useScopedConsole(name)
|
||||
|
||||
const calcPanelWidth = () => {
|
||||
const { maxWidth, customWidth } = getComponentSettings<ChatPanelFitOptions>(name).options
|
||||
if (customWidth !== 0) {
|
||||
return
|
||||
}
|
||||
const video = dq('.live-player-ctnr video') as HTMLVideoElement
|
||||
if (!video) {
|
||||
return
|
||||
}
|
||||
const widthRatio = video.videoWidth
|
||||
const heightRatio = video.videoHeight
|
||||
if (widthRatio === 0 || heightRatio === 0) {
|
||||
return
|
||||
}
|
||||
const { innerWidth, innerHeight } = window
|
||||
const liveChatPanelWidth = innerWidth - (widthRatio * innerHeight) / heightRatio
|
||||
console.log({ liveChatPanelWidth })
|
||||
document.documentElement.style.setProperty(
|
||||
'--live-chat-panel-width',
|
||||
`${lodash.clamp(liveChatPanelWidth, ChatPanelFitOptionsMinWidth, maxWidth)}px`,
|
||||
)
|
||||
}
|
||||
const debounceCalcPanelWidth = lodash.debounce(calcPanelWidth, 200)
|
||||
|
||||
let draggerInstance: Vue
|
||||
const load = async () => {
|
||||
addComponentListener(`${name}.targetRatio`, calcPanelWidth)
|
||||
addComponentListener(`${name}.maxWidth`, calcPanelWidth)
|
||||
window.addEventListener('customWidthReset', calcPanelWidth)
|
||||
window.addEventListener('resize', debounceCalcPanelWidth)
|
||||
const video = await sq(
|
||||
() => dq('.live-player-ctnr video') as HTMLVideoElement,
|
||||
v => v !== null && v.readyState !== HTMLMediaElement.HAVE_NOTHING,
|
||||
)
|
||||
if (!video) {
|
||||
console.log('未找到 video 元素')
|
||||
return
|
||||
}
|
||||
calcPanelWidth()
|
||||
|
||||
const asideToggleButton = (await select('.aside-area-toggle-btn')) as HTMLElement
|
||||
if (!asideToggleButton) {
|
||||
console.log('未找到侧边栏按钮')
|
||||
return
|
||||
}
|
||||
const { default: ChatPanelFitDragger } = await import('./ChatPanelFitDragger.vue')
|
||||
draggerInstance = mountVueComponent(ChatPanelFitDragger)
|
||||
asideToggleButton.insertAdjacentElement('afterend', draggerInstance.$el)
|
||||
}
|
||||
const unload = () => {
|
||||
removeComponentListener(`${name}.targetRatio`, calcPanelWidth)
|
||||
removeComponentListener(`${name}.maxWidth`, calcPanelWidth)
|
||||
window.removeEventListener('customWidthReset', calcPanelWidth)
|
||||
window.removeEventListener('resize', debounceCalcPanelWidth)
|
||||
document.documentElement.style.removeProperty('--live-chat-panel-width')
|
||||
if (draggerInstance) {
|
||||
draggerInstance.$el.remove()
|
||||
draggerInstance.$destroy()
|
||||
draggerInstance = undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name,
|
||||
displayName: '直播间网页全屏自适应',
|
||||
tags: [componentsTags.live, componentsTags.style],
|
||||
urlInclude: [...liveUrls],
|
||||
entry: load,
|
||||
reload: load,
|
||||
unload,
|
||||
instantStyles: [
|
||||
{
|
||||
name,
|
||||
style: () => import('./chat-panel-fit.scss'),
|
||||
important: true,
|
||||
},
|
||||
],
|
||||
options: chatPanelFitOptions,
|
||||
})
|
||||
16
registry/lib/components/live/chat-panel-fit/options.ts
Normal file
16
registry/lib/components/live/chat-panel-fit/options.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { OptionsOfMetadata, defineOptionsMetadata } from '@/components/define'
|
||||
import { getNumberValidator } from '@/core/utils'
|
||||
|
||||
export const ChatPanelFitOptionsMinWidth = 190
|
||||
export const chatPanelFitOptions = defineOptionsMetadata({
|
||||
customWidth: {
|
||||
hidden: true,
|
||||
defaultValue: 0,
|
||||
},
|
||||
maxWidth: {
|
||||
defaultValue: 1000,
|
||||
displayName: '侧边栏最大宽度 (px)',
|
||||
validator: getNumberValidator(ChatPanelFitOptionsMinWidth),
|
||||
},
|
||||
})
|
||||
export type ChatPanelFitOptions = OptionsOfMetadata<typeof chatPanelFitOptions>
|
||||
@ -0,0 +1,7 @@
|
||||
#full-screen-interactive-wrap {
|
||||
display: none !important
|
||||
}
|
||||
|
||||
#fullscreen-danmaku-vm .fullscreen-danmaku {
|
||||
bottom: 5px !important
|
||||
}
|
||||
21
registry/lib/components/live/hide-gift-fullscreen/index.ts
Normal file
21
registry/lib/components/live/hide-gift-fullscreen/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { liveUrls } from '@/core/utils/urls'
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'hide-fullscreen-gift-bar',
|
||||
displayName: '全屏直播礼物简化',
|
||||
description: '移除全屏观看直播时的底部礼物栏',
|
||||
author: {
|
||||
name: 'TimmyOVO',
|
||||
link: 'https://github.com/TimmyOVO',
|
||||
},
|
||||
instantStyles: [
|
||||
{
|
||||
name: 'hide-fullscreen-gift',
|
||||
style: () => import('./hide-full-screen-gift.css'),
|
||||
},
|
||||
],
|
||||
entry: none,
|
||||
tags: [componentsTags.live, componentsTags.style],
|
||||
urlInclude: liveUrls,
|
||||
})
|
||||
@ -5,20 +5,15 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { DefaultWidget } from '@/ui'
|
||||
import { getOriginalLiveroomUrl } from './get-original-liveroom-url'
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
DefaultWidget,
|
||||
},
|
||||
data() {
|
||||
const match = document.URL.match(/^https:\/\/live\.bilibili\.com\/([\d]+)/)
|
||||
if (!match) {
|
||||
return {
|
||||
href: document.URL,
|
||||
}
|
||||
}
|
||||
return {
|
||||
href: `https://live.bilibili.com/blanc/${match[1]}`,
|
||||
href: getOriginalLiveroomUrl(document.URL),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 如果成功从参数 url 中捕获到直播间号,那么返回原版直播间页面链接,否则返回参数 url
|
||||
* @param url 直播间页面链接
|
||||
*/
|
||||
export const getOriginalLiveroomUrl = (url: string): string => {
|
||||
const match = url.match(/^https:\/\/live\.bilibili\.com\/([\d]+)/)
|
||||
return match ? `https://live.bilibili.com/blanc/${match[1]}` : url
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { matchUrlPattern } from '@/core/utils'
|
||||
import { isIframe, isNotHtml, matchUrlPattern } from '@/core/utils'
|
||||
import { getOriginalLiveroomUrl } from './get-original-liveroom-url'
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'originalLiveroom',
|
||||
@ -7,7 +8,20 @@ export const component = defineComponentMetadata({
|
||||
description:
|
||||
'在直播间中提供返回原版直播间的按钮, 原版直播间将无视活动皮肤, 强制使用标准的直播页面.',
|
||||
tags: [componentsTags.live],
|
||||
entry: none,
|
||||
entry: ({ settings }) => {
|
||||
if (isNotHtml() || isIframe()) {
|
||||
return
|
||||
}
|
||||
if (settings.options.defaultBack) {
|
||||
window.location.assign(getOriginalLiveroomUrl(document.URL))
|
||||
}
|
||||
},
|
||||
options: {
|
||||
defaultBack: {
|
||||
displayName: '默认返回原版直播间',
|
||||
defaultValue: false,
|
||||
},
|
||||
},
|
||||
urlInclude: [
|
||||
// 不能直接用 liveUrls, 那个是带 blanc 检测的
|
||||
/^https:\/\/live\.bilibili\.com\/[\d]+/,
|
||||
|
||||
270
registry/lib/components/style/clear-home/index.ts
Normal file
270
registry/lib/components/style/clear-home/index.ts
Normal file
@ -0,0 +1,270 @@
|
||||
import {
|
||||
OptionsOfMetadata,
|
||||
defineComponentMetadata,
|
||||
defineOptionsMetadata,
|
||||
} from '@/components/define'
|
||||
import { ComponentEntry } from '@/components/types'
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
|
||||
type Options = OptionsOfMetadata<typeof options>
|
||||
|
||||
const removeBadgeList: string[] = []
|
||||
let removeLive = false
|
||||
let removeAds = true
|
||||
|
||||
const options = defineOptionsMetadata({
|
||||
广告: {
|
||||
displayName: '广告',
|
||||
defaultValue: true,
|
||||
},
|
||||
番剧: {
|
||||
displayName: '番剧',
|
||||
defaultValue: true,
|
||||
},
|
||||
电影: {
|
||||
displayName: '电影',
|
||||
defaultValue: true,
|
||||
},
|
||||
国创: {
|
||||
displayName: '国创',
|
||||
defaultValue: true,
|
||||
},
|
||||
电视剧: {
|
||||
displayName: '电视剧',
|
||||
defaultValue: true,
|
||||
},
|
||||
综艺: {
|
||||
displayName: '综艺',
|
||||
defaultValue: true,
|
||||
},
|
||||
纪录片: {
|
||||
displayName: '纪录片',
|
||||
defaultValue: true,
|
||||
},
|
||||
动画: {
|
||||
displayName: '动画',
|
||||
defaultValue: true,
|
||||
},
|
||||
游戏: {
|
||||
displayName: '游戏',
|
||||
defaultValue: true,
|
||||
},
|
||||
鬼畜: {
|
||||
displayName: '鬼畜',
|
||||
defaultValue: true,
|
||||
},
|
||||
音乐: {
|
||||
displayName: '音乐',
|
||||
defaultValue: true,
|
||||
},
|
||||
舞蹈: {
|
||||
displayName: '舞蹈',
|
||||
defaultValue: true,
|
||||
},
|
||||
影视: {
|
||||
displayName: '影视',
|
||||
defaultValue: true,
|
||||
},
|
||||
娱乐: {
|
||||
displayName: '娱乐',
|
||||
defaultValue: true,
|
||||
},
|
||||
知识: {
|
||||
displayName: '知识',
|
||||
defaultValue: true,
|
||||
},
|
||||
科技: {
|
||||
displayName: '科技',
|
||||
defaultValue: true,
|
||||
},
|
||||
资讯: {
|
||||
displayName: '资讯',
|
||||
defaultValue: true,
|
||||
},
|
||||
美食: {
|
||||
displayName: '美食',
|
||||
defaultValue: true,
|
||||
},
|
||||
生活: {
|
||||
displayName: '生活',
|
||||
defaultValue: true,
|
||||
},
|
||||
汽车: {
|
||||
displayName: '汽车',
|
||||
defaultValue: true,
|
||||
},
|
||||
时尚: {
|
||||
displayName: '时尚',
|
||||
defaultValue: true,
|
||||
},
|
||||
运动: {
|
||||
displayName: '运动',
|
||||
defaultValue: true,
|
||||
},
|
||||
动物圈: {
|
||||
displayName: '动物圈',
|
||||
defaultValue: true,
|
||||
},
|
||||
VLOG: {
|
||||
displayName: 'VLOG',
|
||||
defaultValue: true,
|
||||
},
|
||||
搞笑: {
|
||||
displayName: '搞笑',
|
||||
defaultValue: true,
|
||||
},
|
||||
单机游戏: {
|
||||
displayName: '单机游戏',
|
||||
defaultValue: true,
|
||||
},
|
||||
虚拟UP主: {
|
||||
displayName: '虚拟UP主',
|
||||
defaultValue: true,
|
||||
},
|
||||
公益: {
|
||||
displayName: '公益',
|
||||
defaultValue: true,
|
||||
},
|
||||
公开课: {
|
||||
displayName: '公开课',
|
||||
defaultValue: true,
|
||||
},
|
||||
专栏: {
|
||||
displayName: '专栏',
|
||||
defaultValue: true,
|
||||
},
|
||||
直播: {
|
||||
displayName: '直播',
|
||||
defaultValue: true,
|
||||
},
|
||||
赛事: {
|
||||
displayName: '赛事',
|
||||
defaultValue: true,
|
||||
},
|
||||
活动: {
|
||||
displayName: '活动',
|
||||
defaultValue: true,
|
||||
},
|
||||
课堂: {
|
||||
displayName: '课堂',
|
||||
defaultValue: true,
|
||||
},
|
||||
社区中心: {
|
||||
displayName: '社区中心',
|
||||
defaultValue: true,
|
||||
},
|
||||
新歌热榜: {
|
||||
displayName: '新歌热榜',
|
||||
defaultValue: true,
|
||||
},
|
||||
漫画: {
|
||||
displayName: '漫画',
|
||||
defaultValue: true,
|
||||
},
|
||||
})
|
||||
|
||||
function cardClear(mutationsList: HTMLElement[]) {
|
||||
for (const mutation of mutationsList) {
|
||||
if (!mutation.classList) {
|
||||
continue
|
||||
}
|
||||
if (mutation.classList.contains('floor-single-card')) {
|
||||
const badge = mutation.querySelector('.badge').textContent
|
||||
if (badge && removeBadgeList.includes(badge)) {
|
||||
mutation.remove()
|
||||
}
|
||||
} else if (mutation.classList.contains('bili-live-card')) {
|
||||
if (removeLive) {
|
||||
mutation.remove()
|
||||
}
|
||||
} else if (
|
||||
mutation.classList.contains('bili-video-card') ||
|
||||
mutation.classList.contains('feed-card')
|
||||
) {
|
||||
if (
|
||||
removeAds &&
|
||||
(mutation.querySelector('.bili-video-card__info--ad') ||
|
||||
mutation.querySelector('.bili-video-card__info--creative-ad'))
|
||||
) {
|
||||
mutation.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cardFilter(mutationsList: MutationRecord[]) {
|
||||
const filteredList: HTMLElement[] = []
|
||||
for (const mutation of mutationsList) {
|
||||
const mutationTarget = mutation.target as HTMLElement
|
||||
if (
|
||||
(mutationTarget.classList && mutationTarget.classList.contains('carousel-transform')) ||
|
||||
mutationTarget.tagName === 'SPAN'
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (mutation.addedNodes.length > 0) {
|
||||
for (const node of mutation.addedNodes) {
|
||||
const nodeTarget = node as HTMLElement
|
||||
if (
|
||||
nodeTarget.nodeName === '#text' ||
|
||||
(nodeTarget.classList && nodeTarget.classList.contains('bili-watch-later'))
|
||||
) {
|
||||
continue
|
||||
}
|
||||
filteredList.push(nodeTarget)
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (filteredList.length > 0) {
|
||||
cardClear(filteredList)
|
||||
}
|
||||
}
|
||||
|
||||
const clearHome: ComponentEntry<Options> = async ({ metadata, settings }) => {
|
||||
const container = document.querySelector(
|
||||
'main > .feed2 > .recommended-container_floor-aside > .container',
|
||||
)
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
const config = { childList: true }
|
||||
const observer = new MutationObserver(cardFilter)
|
||||
observer.observe(container, config)
|
||||
|
||||
Object.keys(settings.options).forEach(disableType => {
|
||||
addComponentListener(
|
||||
`${metadata.name}.${disableType}`,
|
||||
(value: boolean) => {
|
||||
if (value) {
|
||||
if (disableType === '广告') {
|
||||
removeAds = true
|
||||
return
|
||||
}
|
||||
removeBadgeList.push(disableType)
|
||||
if (disableType === '直播') {
|
||||
removeLive = true
|
||||
}
|
||||
}
|
||||
},
|
||||
true,
|
||||
)
|
||||
})
|
||||
const containerList = container.children as unknown as HTMLElement[]
|
||||
cardClear(containerList)
|
||||
}
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'clear-home',
|
||||
author: {
|
||||
name: 'RieN7',
|
||||
link: 'https://github.com/rien7',
|
||||
},
|
||||
tags: [componentsTags.style],
|
||||
displayName: '首页净化',
|
||||
description: '删除首页特定类型的卡片',
|
||||
entry: clearHome,
|
||||
options,
|
||||
})
|
||||
@ -0,0 +1,16 @@
|
||||
$name: 'custom-font-family';
|
||||
|
||||
html[#{$name}--options--disable-quotation-mark-text-indent='true']
|
||||
:is(
|
||||
p[title^='「'],
|
||||
p[title^='『'],
|
||||
p[title^='【'],
|
||||
h1[title^='「'],
|
||||
h1[title^='『'],
|
||||
h1[title^='【'],
|
||||
h3[title^='「'],
|
||||
h3[title^='『'],
|
||||
h3[title^='【']
|
||||
) {
|
||||
text-indent: initial !important;
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="custom-font-family-extra-options-entry">
|
||||
<VButton ref="button" @mouseover="loadExtraOptions()" @click="toggleExtraOptionsDisplay()">
|
||||
字体设置<VIcon icon="right-arrow" :size="16"></VIcon>
|
||||
</VButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { VIcon, VButton } from '@/ui'
|
||||
import {
|
||||
setTriggerElement,
|
||||
getExtraOptionsLoadState,
|
||||
loadExtraOptions,
|
||||
toggleExtraOptionsDisplay,
|
||||
} from './vm'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
VIcon,
|
||||
VButton,
|
||||
},
|
||||
|
||||
methods: {
|
||||
async loadExtraOptions() {
|
||||
const isLoaded = await getExtraOptionsLoadState()
|
||||
if (!isLoaded) {
|
||||
await loadExtraOptions()
|
||||
const triggerButton = this.$refs.button.$el as HTMLElement
|
||||
setTriggerElement(triggerButton)
|
||||
}
|
||||
},
|
||||
toggleExtraOptionsDisplay,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.custom-font-family-extra-options-entry {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,109 @@
|
||||
@import 'common';
|
||||
|
||||
$pref: 'eop';
|
||||
$prefH: 'eop-h';
|
||||
$prefHL: 'eop-h-l';
|
||||
$prefHR: 'eop-h-r';
|
||||
$prefC: 'eop-c';
|
||||
$prefCS: 'eop-c-s';
|
||||
|
||||
$panelPadding: 22px;
|
||||
|
||||
@mixin extra-options-panel {
|
||||
&.extra-options-panel {
|
||||
@include popup();
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 500px;
|
||||
max-height: 80vh;
|
||||
padding: $panelPadding 0;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%) scale(0.95);
|
||||
transition: all 0.2s ease-out;
|
||||
font-size: 14px;
|
||||
z-index: 100002;
|
||||
|
||||
&.open {
|
||||
transform: translateX(-50%) translateY(-50%) scale(1);
|
||||
}
|
||||
|
||||
&.peek {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
> .#{$pref}-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 $panelPadding 18px $panelPadding;
|
||||
flex-shrink: 0;
|
||||
|
||||
> .#{$prefH}-left {
|
||||
display: flex;
|
||||
column-gap: 4px;
|
||||
|
||||
> .#{$prefHL}-title {
|
||||
@include semi-bold();
|
||||
font-size: 19px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
> .#{$prefH}-right {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
|
||||
> [class*='#{$prefHR}'] {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .#{$pref}-separator {
|
||||
height: 1px;
|
||||
margin: 0 $panelPadding;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
flex-shrink: 0;
|
||||
|
||||
body.dark & {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
> .#{$pref}-content {
|
||||
padding: 0 $panelPadding;
|
||||
overflow: auto;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
> .#{$prefC}-section {
|
||||
margin: 18px 0;
|
||||
|
||||
&:nth-child(1):nth-last-child(1) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
> .#{$prefCS}-title {
|
||||
margin-bottom: 2px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
> .#{$prefCS}-description {
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.6;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
> .#{$prefCS}-input {
|
||||
@include h-center();
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<VPopup
|
||||
ref="popup"
|
||||
v-model="popupOpen"
|
||||
class="custom-font-family-extra-options-panel extra-options-panel"
|
||||
fixed
|
||||
:lazy="false"
|
||||
:trigger-element="triggerElement"
|
||||
:class="{ peek: isPeeking }"
|
||||
>
|
||||
<div class="eop-header">
|
||||
<div class="eop-h-left">
|
||||
<VIcon class="eop-h-l-symbol" icon="mdi-format-font" :size="24"></VIcon>
|
||||
<div class="eop-h-l-title">字体设置</div>
|
||||
</div>
|
||||
|
||||
<div class="eop-h-right">
|
||||
<VIcon
|
||||
class="eop-h-r-reset"
|
||||
title="重置面板中的所有选项为默认值"
|
||||
icon="mdi-cog-sync-outline"
|
||||
:size="24"
|
||||
@click="confirmResetOptions()"
|
||||
></VIcon>
|
||||
<VIcon
|
||||
class="eop-h-r-peek"
|
||||
title="透视"
|
||||
icon="mdi-eye-outline"
|
||||
:size="24"
|
||||
@mouseover="mouseOnPeekIcon = true"
|
||||
@mouseout="mouseOnPeekIcon = false"
|
||||
></VIcon>
|
||||
<VIcon
|
||||
class="eop-h-r-close"
|
||||
title="关闭"
|
||||
icon="mdi-close"
|
||||
:size="24"
|
||||
@click="popupOpen = false"
|
||||
></VIcon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="eop-separator"></div>
|
||||
|
||||
<div class="eop-content">
|
||||
<div class="eop-c-section">
|
||||
<div class="eop-c-s-title">自定义字体</div>
|
||||
<div class="eop-c-s-description">输入字体,不同字体之间必须以英文逗号分隔</div>
|
||||
<div class="eop-c-s-input input-font-family">
|
||||
<TextArea v-model="inputFontFamily" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</VPopup>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { VPopup, VIcon, TextArea } from '@/ui'
|
||||
import { getComponentSettings } from '@/core/settings'
|
||||
import { delay } from '@/core/utils'
|
||||
import { useScopedConsole } from '@/core/utils/log'
|
||||
import { fontFamilyDefaultValue } from '../font-family-default-value'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
VPopup,
|
||||
VIcon,
|
||||
TextArea,
|
||||
},
|
||||
|
||||
props: {
|
||||
triggerElement: {
|
||||
type: HTMLElement,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
popupOpen: false,
|
||||
isPeeking: false,
|
||||
mouseOnPeekIcon: false,
|
||||
// 设置 inputFontFamily 初始值为组件 fontFamily 选项的值
|
||||
inputFontFamily: getComponentSettings('customFontFamily').options.fontFamily,
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 监听 inputFontFamily 修改动作,实时修改组件 fontFamily 选项为 inputFontFamily 修改后的新值,并拥有 500ms 防抖
|
||||
inputFontFamily: lodash.debounce(value => {
|
||||
getComponentSettings('customFontFamily').options.fontFamily = value
|
||||
}, 500),
|
||||
|
||||
async mouseOnPeekIcon(value: boolean) {
|
||||
if (!value) {
|
||||
this.isPeeking = false
|
||||
return
|
||||
}
|
||||
if (value) {
|
||||
await delay(200)
|
||||
}
|
||||
if (this.mouseOnPeekIcon) {
|
||||
this.isPeeking = true
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
confirmResetOptions() {
|
||||
if (confirm('确定将面板中的所有选项重置为默认值吗?')) {
|
||||
this.resetOptions()
|
||||
}
|
||||
},
|
||||
|
||||
resetOptions() {
|
||||
getComponentSettings('customFontFamily').options.fontFamily = fontFamilyDefaultValue
|
||||
this.inputFontFamily = fontFamilyDefaultValue
|
||||
const console = useScopedConsole('自定义字体')
|
||||
console.log('已将字体设置面板中的所有选项重置为默认值')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './extra-options-panel';
|
||||
|
||||
.custom-font-family-extra-options-panel {
|
||||
@include extra-options-panel;
|
||||
|
||||
> .eop-content > .eop-c-section > .eop-c-s-input.input-font-family .be-text-area {
|
||||
min-height: 160px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,24 @@
|
||||
import { mountVueComponent } from '@/core/utils'
|
||||
|
||||
let extraOptionsVm: Vue & {
|
||||
popupOpen: boolean
|
||||
triggerElement: HTMLElement
|
||||
}
|
||||
|
||||
export const setTriggerElement = (element: HTMLElement) => {
|
||||
extraOptionsVm.triggerElement = element
|
||||
}
|
||||
|
||||
export const getExtraOptionsLoadState = async () => {
|
||||
return Boolean(extraOptionsVm)
|
||||
}
|
||||
|
||||
export const loadExtraOptions = async () => {
|
||||
const extraOptions = await import('./panel.vue').then(m => m.default)
|
||||
extraOptionsVm = mountVueComponent(extraOptions)
|
||||
document.body.insertAdjacentElement('beforeend', extraOptionsVm.$el)
|
||||
}
|
||||
|
||||
export const toggleExtraOptionsDisplay = async () => {
|
||||
extraOptionsVm.popupOpen = !extraOptionsVm.popupOpen
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
export const fontFamilyDefaultValue =
|
||||
'Microsoft Yahei, Helvetica Neue, Helvetica, Arial, Hiragino Sans GB, Heiti SC, WenQuanYi Micro Hei'
|
||||
133
registry/lib/components/style/custom-font-family/index.ts
Normal file
133
registry/lib/components/style/custom-font-family/index.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
import { fontFamilyDefaultValue } from './font-family-default-value'
|
||||
|
||||
const docElement = document.documentElement
|
||||
|
||||
const kebabName = 'custom-font-family'
|
||||
|
||||
const camelName = 'customFontFamily'
|
||||
|
||||
const name = camelName
|
||||
|
||||
const displayName = '自定义字体'
|
||||
|
||||
const description = {
|
||||
'zh-CN':
|
||||
'使用组件提供的字体设置覆盖原版的主站字体,并使主站字体可被自定义。字体设置写法请参考 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family) 、默认设置与设置说明',
|
||||
}
|
||||
|
||||
const tags = [componentsTags.style, componentsTags.general]
|
||||
|
||||
const entry = () => {
|
||||
addComponentListener(
|
||||
`${name}.fontFamily`,
|
||||
(value: string) => {
|
||||
if (!value) {
|
||||
docElement.setAttribute(`${kebabName}--detect--is-input-empty`, 'true')
|
||||
return
|
||||
}
|
||||
docElement.style.setProperty(`--${kebabName}--options--font-family`, `${value}`)
|
||||
docElement.setAttribute(`${kebabName}--detect--is-input-empty`, 'false')
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
addComponentListener(
|
||||
`${name}.disableQuotationMarkTextIndent`,
|
||||
(value: boolean) => {
|
||||
docElement.setAttribute(
|
||||
`${kebabName}--options--disable-quotation-mark-text-indent`,
|
||||
`${value}`,
|
||||
)
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
const onOptionNames = [
|
||||
{ camel: 'onOrnament', kebab: 'on-ornament' },
|
||||
{ camel: 'onFansMedal', kebab: 'on-fans-medal' },
|
||||
{ camel: 'onDanmaku', kebab: 'on-danmaku' },
|
||||
{ camel: 'onIconFont', kebab: 'on-icon-font' },
|
||||
{ camel: 'onColumn', kebab: 'on-column' },
|
||||
{ camel: 'onScore', kebab: 'on-score' },
|
||||
]
|
||||
|
||||
onOptionNames.forEach(onOptionName => {
|
||||
addComponentListener(
|
||||
`${name}.${onOptionName.camel}`,
|
||||
(value: boolean) => {
|
||||
docElement.setAttribute(`${kebabName}--options--${onOptionName.kebab}`, `${value}`)
|
||||
},
|
||||
true,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const options = {
|
||||
fontFamily: {
|
||||
displayName: '字体',
|
||||
defaultValue: fontFamilyDefaultValue,
|
||||
hidden: true,
|
||||
},
|
||||
disableQuotationMarkTextIndent: {
|
||||
displayName: '禁用引号缩进',
|
||||
defaultValue: true,
|
||||
},
|
||||
onOrnament: {
|
||||
displayName: '覆盖装扮字体',
|
||||
defaultValue: false,
|
||||
},
|
||||
onFansMedal: {
|
||||
displayName: '覆盖粉丝勋章字体',
|
||||
defaultValue: false,
|
||||
},
|
||||
onDanmaku: {
|
||||
displayName: '覆盖弹幕字体',
|
||||
defaultValue: false,
|
||||
},
|
||||
onIconFont: {
|
||||
displayName: '覆盖图标字体',
|
||||
defaultValue: false,
|
||||
},
|
||||
onColumn: {
|
||||
displayName: '覆盖专栏自定义字体',
|
||||
defaultValue: false,
|
||||
},
|
||||
onScore: {
|
||||
displayName: '覆盖评分字体',
|
||||
defaultValue: false,
|
||||
},
|
||||
}
|
||||
|
||||
const extraOptions = () => import('./extra-options/entry.vue').then(m => m.default)
|
||||
|
||||
const instantStyles = [
|
||||
{
|
||||
name: `${name}--style--setFontFamily`,
|
||||
style: () => import('./set-font-family.scss'),
|
||||
important: true,
|
||||
},
|
||||
{
|
||||
name: `${name}--style--disableQuotationMarkTextIndent`,
|
||||
style: () => import('./disable-quotation-mark-text-indent.scss'),
|
||||
important: true,
|
||||
},
|
||||
]
|
||||
|
||||
const author = {
|
||||
name: 'Tinhone',
|
||||
link: 'https://github.com/Tinhone',
|
||||
}
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name,
|
||||
displayName,
|
||||
description,
|
||||
tags,
|
||||
entry,
|
||||
options,
|
||||
extraOptions,
|
||||
instantStyles,
|
||||
author,
|
||||
})
|
||||
@ -0,0 +1,115 @@
|
||||
$name: 'custom-font-family';
|
||||
$fontFamilyCss: var(--#{$name}--options--font-family) !important;
|
||||
|
||||
// 收集 CSS 路径为单一字符串
|
||||
@function collectPaths($pathList) {
|
||||
$stringPath: '';
|
||||
@each $path in $pathList {
|
||||
$i: index($pathList, $path);
|
||||
@if $i!=1 {
|
||||
$stringPath: '#{$stringPath}, ';
|
||||
}
|
||||
$stringPath: '#{$stringPath}#{$path}';
|
||||
}
|
||||
@return $stringPath;
|
||||
}
|
||||
|
||||
@function getOrnamentPath() {
|
||||
$newVideo: '.reply-item .root-reply-container .content-warp .reply-decorate .user-sailing .user-sailing-text .sailing-text';
|
||||
$dynamic: '.bili-dyn-ornament__type--3 span';
|
||||
$dynamicCommentArea: '.bb-comment .sailing .sailing-info, .comment-bilibili-fold .sailing .sailing-info';
|
||||
@return collectPaths(($newVideo, $dynamic, $dynamicCommentArea));
|
||||
}
|
||||
|
||||
@function getFansMedalPath() {
|
||||
$newVideo: '.badge-level';
|
||||
@return collectPaths(($newVideo));
|
||||
}
|
||||
|
||||
@function getDanmakuPath() {
|
||||
$main: '.bili-dm';
|
||||
@return collectPaths(($main));
|
||||
}
|
||||
|
||||
@function getIconFontPath() {
|
||||
$mainA: '.iconfont';
|
||||
$mainB: '.icon-font';
|
||||
$note: '.read-icon';
|
||||
$noteEdit: '.bili-note-iconfont';
|
||||
$footer: '.bili-footer-font';
|
||||
$bangumiInfo: '[class^="icon-"]';
|
||||
$history: '.bilifont';
|
||||
$message: '.bp-icon-font';
|
||||
$creativeCenterA: '.bcc-iconfont';
|
||||
$creativeCenterB: '.fontvt';
|
||||
@return collectPaths(
|
||||
(
|
||||
$mainA,
|
||||
$mainB,
|
||||
$note,
|
||||
$noteEdit,
|
||||
$footer,
|
||||
$bangumiInfo,
|
||||
$history,
|
||||
$message,
|
||||
$creativeCenterA,
|
||||
$creativeCenterB
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@function getColumnPath() {
|
||||
$mainA: '.article-detail .article-container .article-container__content .article-content .read-article-holder';
|
||||
$mainB: '.article-detail .article-container .article-container__content .article-content .read-article-holder *';
|
||||
@return collectPaths(($mainA, $mainB));
|
||||
}
|
||||
|
||||
@function getScorePath() {
|
||||
$documentary: '.season-cover .score';
|
||||
$big: '.season-cover .season-cover-score';
|
||||
@return collectPaths(($documentary, $big));
|
||||
}
|
||||
|
||||
// 装扮,在评论区、动态等界面显示
|
||||
$ornament: getOrnamentPath();
|
||||
|
||||
// 粉丝勋章,在评论区界面显示
|
||||
$fansMedal: getFansMedalPath();
|
||||
|
||||
// 弹幕,在播放器里显示
|
||||
$danmaku: getDanmakuPath();
|
||||
|
||||
// 图标字体
|
||||
$iconFont: getIconFontPath();
|
||||
|
||||
// 专栏阅读页可能出现的部分自定义字体
|
||||
$column: getColumnPath();
|
||||
|
||||
// 分数,在部分剧集的右下角显示
|
||||
$score: getScorePath();
|
||||
|
||||
// 收集所有路径
|
||||
$discardPaths: collectPaths(($ornament, $fansMedal, $danmaku, $iconFont, $column, $score));
|
||||
|
||||
$onOptionNames: (
|
||||
'on-ornament',
|
||||
'on-fans-medal',
|
||||
'on-danmaku',
|
||||
'on-icon-font',
|
||||
'on-column',
|
||||
'on-score'
|
||||
);
|
||||
$onOptionPathLists: ($ornament, $fansMedal, $danmaku, $iconFont, $column, $score);
|
||||
$onOptionGroups: zip($onOptionNames, $onOptionPathLists);
|
||||
|
||||
html[#{$name}--detect--is-input-empty='false'] {
|
||||
:not(#{$discardPaths}) {
|
||||
font-family: $fontFamilyCss;
|
||||
}
|
||||
|
||||
@each $onOptionName, $onOptionPathList in $onOptionGroups {
|
||||
&[#{$name}--options--#{$onOptionName}='true'] :is(#{$onOptionPathList}) {
|
||||
font-family: $fontFamilyCss;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,9 +314,10 @@ export default Vue.extend({
|
||||
color: var(--foreground-color);
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
&:not(:empty):not(.hidden) {
|
||||
html:not([data-navbar-notify-style='hidden']) &:not(:empty):not(.hidden) {
|
||||
opacity: 1;
|
||||
}
|
||||
html[data-navbar-notify-style='dot'] &,
|
||||
&.dot {
|
||||
color: transparent;
|
||||
border-radius: 50%;
|
||||
|
||||
@ -6,8 +6,9 @@
|
||||
border-bottom: 2px solid transparent;
|
||||
font-size: 15px;
|
||||
line-height: normal;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc(100% - 4px);
|
||||
left: 8px;
|
||||
@ -18,7 +19,23 @@
|
||||
transition: 0.16s 0.1s ease-out;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
html[data-navbar-link-popup-content-align-style='left'] &,
|
||||
&.left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
html[data-navbar-link-popup-content-align-style='center'] &,
|
||||
&.center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
html[data-navbar-link-popup-content-align-style='right'] &,
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { logo } from './logo/logo'
|
||||
import { home } from './home/home'
|
||||
import { gamesIframe, livesIframe, mangaIframe } from './iframe/iframe'
|
||||
import { blanks } from './flexible-blank/flexible-blank'
|
||||
import { bangumi, music, drawing, shop, match } from './simple-links/simple-links'
|
||||
import { bangumi, music, drawing, shop, match, creations } from './simple-links/simple-links'
|
||||
import { upload } from './upload/upload'
|
||||
import { search } from './search/search'
|
||||
import { feeds } from './feeds/feeds'
|
||||
@ -39,6 +39,7 @@ export const getBuiltInItems = (): CustomNavbarItemInit[] => [
|
||||
watchlater,
|
||||
favorites,
|
||||
history,
|
||||
creations,
|
||||
upload,
|
||||
blank4,
|
||||
]
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { ComponentEntry } from '@/components/types'
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
import { isIframe, isNotHtml, matchUrlPattern, mountVueComponent } from '@/core/utils'
|
||||
import { setupNotifyStyle } from './notify-style'
|
||||
import { setupLinkPopupContentAlignStyle } from './link-popup-content-align-style'
|
||||
|
||||
export const entry: ComponentEntry = async ({ metadata: { name } }) => {
|
||||
// const url = document.URL.replace(location.search, '')
|
||||
@ -20,6 +22,13 @@ export const entry: ComponentEntry = async ({ metadata: { name } }) => {
|
||||
},
|
||||
true,
|
||||
)
|
||||
addComponentListener(
|
||||
`${name}.searchBarWidth`,
|
||||
value => {
|
||||
document.documentElement.style.setProperty('--navbar-search-width', `${value}vw`)
|
||||
},
|
||||
true,
|
||||
)
|
||||
const globalFixedExclude = [
|
||||
'https://space.bilibili.com',
|
||||
'https://www.bilibili.com/read',
|
||||
@ -43,4 +52,6 @@ export const entry: ComponentEntry = async ({ metadata: { name } }) => {
|
||||
;['fill', 'shadow', 'blur'].forEach(style => {
|
||||
addComponentListener(`${name}.${style}`, value => customNavbar.toggleStyle(value, style), true)
|
||||
})
|
||||
setupNotifyStyle()
|
||||
setupLinkPopupContentAlignStyle()
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { VideoCard } from '@/components/feeds/video-card'
|
||||
import { formatDuration, formatCount, parseDuration } from '@/core/utils/formatters'
|
||||
import { formatDuration, formatCount, parseDuration, parseCount } from '@/core/utils/formatters'
|
||||
import { isNewID } from '@/components/feeds/notify'
|
||||
import { feedsCardTypes, groupVideoFeeds } from '@/components/feeds/api'
|
||||
import VideoCardComponent from '@/components/feeds/VideoCard.vue'
|
||||
@ -98,7 +98,7 @@ export default Vue.extend({
|
||||
upName: author.name,
|
||||
upID: author.mid,
|
||||
watchlater: true,
|
||||
playCount: formatCount(archive.stat.play),
|
||||
playCount: formatCount(parseCount(archive.stat.play)),
|
||||
get new() {
|
||||
return isNewID(this.id)
|
||||
},
|
||||
|
||||
@ -1,3 +1,20 @@
|
||||
使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换), 启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:
|
||||
使用脚本提供的顶栏替换原版的主站顶栏 (非主站不替换).
|
||||
|
||||
启用后还可以在反广告插件 (例如 ABP) 中配置禁止原版顶栏的消息 iframe 以提升性能:
|
||||
- *://message.bilibili.com/pages/nav/index_new_pc_sync
|
||||
- *://message.bilibili.com/pages/nav/index_new_sync
|
||||
|
||||
选项说明:
|
||||
- `全局固定`: 将顶栏固定显示, 即使页面向下滚动也仍然可见. (除了一部分有 b 站定制顶栏的页面会忽略此选项)
|
||||
- `主题色填充`: 使用通用设置中的主题颜色填充顶栏的背景.
|
||||
- `透明填充`: 在首页和主站的部分页面中, 当存在顶部横幅图片时, 顶栏背景将变为透明 + 阴影的样式.
|
||||
- `背景模糊`: 启用背景模糊效果, 会使背景颜色变得半透明, 注意这个效果非常非常消耗图形性能, 慎用.
|
||||
- `投影`: 在顶栏下方增加一小段投影效果.
|
||||
- `使用季节 Logo`: 使用 b 站首页的特殊 Logo 图片替换顶栏的 Logo 图, 通常会对应当前季节, 特殊活动期间可能会有变化导致看不清. (比如搞了个全白的 Logo, 而顶栏正好也是白色的)
|
||||
- `触摸模式`: 启用后顶栏的一级入口点击时不进行跳转, 方便触屏使用.
|
||||
- `新标签页打开`: 控制顶栏内链接是否在新标签页打开.
|
||||
- `自动刷新数据`: 启用后, 部分有弹窗的顶栏入口在鼠标经过时会自动刷新数据.
|
||||
- `顶栏高度`: 自定义顶栏的高度 (b 站原版的为 64px)
|
||||
- `消息提醒样式`: 自定义顶栏入口的消息提醒样式.
|
||||
- `搜索栏宽度`: 自定义搜索栏占顶栏的比例.
|
||||
- `链接对齐样式`: 自定义纯链接弹窗内链接文字的对齐样式.
|
||||
|
||||
@ -7,12 +7,23 @@ import { LaunchBarActionProvider } from '@/components/launch-bar/launch-bar-acti
|
||||
import { urlInclude, urlExclude } from './urls'
|
||||
import { entry } from './entry'
|
||||
import { getNumberValidator } from '@/core/utils'
|
||||
import { NavbarNotifyStyle } from './notify-style'
|
||||
import { NavbarLinkPopupContentAlignStyle } from './link-popup-content-align-style'
|
||||
|
||||
const styleID = 'custom-navbar-style'
|
||||
const options = defineOptionsMetadata({
|
||||
hidden: {
|
||||
hidden: true,
|
||||
defaultValue: ['blank1', 'blank4', 'drawing', 'music', 'gamesIframe', 'bangumi', 'match'],
|
||||
defaultValue: [
|
||||
'blank1',
|
||||
'blank4',
|
||||
'drawing',
|
||||
'music',
|
||||
'gamesIframe',
|
||||
'bangumi',
|
||||
'match',
|
||||
'creations',
|
||||
],
|
||||
displayName: '隐藏的元素',
|
||||
},
|
||||
order: {
|
||||
@ -76,6 +87,24 @@ const options = defineOptionsMetadata({
|
||||
displayName: '显示已失效视频',
|
||||
hidden: true,
|
||||
},
|
||||
notifyStyle: {
|
||||
defaultValue: NavbarNotifyStyle.Number,
|
||||
dropdownEnum: NavbarNotifyStyle,
|
||||
displayName: '消息提醒样式',
|
||||
},
|
||||
linkPopupContentAlignStyle: {
|
||||
defaultValue: NavbarLinkPopupContentAlignStyle.Left,
|
||||
dropdownEnum: NavbarLinkPopupContentAlignStyle,
|
||||
displayName: '链接对齐样式',
|
||||
},
|
||||
searchBarWidth: {
|
||||
defaultValue: 15,
|
||||
slider: {
|
||||
min: 8,
|
||||
max: 64,
|
||||
},
|
||||
displayName: '搜索栏宽度 (%)',
|
||||
},
|
||||
})
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'customNavbar',
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
|
||||
export enum NavbarLinkPopupContentAlignStyle {
|
||||
Left = '左侧对齐',
|
||||
Center = '居中对齐',
|
||||
Right = '右侧对齐',
|
||||
}
|
||||
|
||||
export const setupLinkPopupContentAlignStyle = () => {
|
||||
const map = {
|
||||
[NavbarLinkPopupContentAlignStyle.Left]: 'left',
|
||||
[NavbarLinkPopupContentAlignStyle.Center]: 'center',
|
||||
[NavbarLinkPopupContentAlignStyle.Right]: 'right',
|
||||
}
|
||||
addComponentListener(
|
||||
'customNavbar.linkPopupContentAlignStyle',
|
||||
(value: NavbarLinkPopupContentAlignStyle) => {
|
||||
document.documentElement.setAttribute(
|
||||
'data-navbar-link-popup-content-align-style',
|
||||
map[value],
|
||||
)
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
22
registry/lib/components/style/custom-navbar/notify-style.ts
Normal file
22
registry/lib/components/style/custom-navbar/notify-style.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
|
||||
export enum NavbarNotifyStyle {
|
||||
Number = '数字',
|
||||
Hidden = '隐藏',
|
||||
Dot = '点状',
|
||||
}
|
||||
|
||||
export const setupNotifyStyle = () => {
|
||||
const map = {
|
||||
[NavbarNotifyStyle.Number]: 'number',
|
||||
[NavbarNotifyStyle.Hidden]: 'hidden',
|
||||
[NavbarNotifyStyle.Dot]: 'dot',
|
||||
}
|
||||
addComponentListener(
|
||||
'customNavbar.notifyStyle',
|
||||
(value: NavbarNotifyStyle) => {
|
||||
document.documentElement.setAttribute('data-navbar-notify-style', map[value])
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
@ -30,6 +30,14 @@ const entries = [
|
||||
href: 'https://www.bilibili.com/v/popular/rank/all',
|
||||
name: '排行榜',
|
||||
},
|
||||
{
|
||||
href: 'https://www.bilibili.com/v/popular/music',
|
||||
name: '全站音乐榜',
|
||||
},
|
||||
{
|
||||
href: 'https://www.bilibili.com/v/popular/drama',
|
||||
name: '短剧榜',
|
||||
},
|
||||
] as RankingEntry[]
|
||||
export default Vue.extend({
|
||||
name: 'RankingPopup',
|
||||
|
||||
@ -29,6 +29,7 @@ export default Vue.extend({
|
||||
}
|
||||
.input-area .launch-bar-form .input {
|
||||
padding: 8px 6px;
|
||||
width: var(--navbar-search-width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -41,3 +41,8 @@ export const match = getSimpleLinkItem({
|
||||
displayName: '赛事',
|
||||
href: 'https://www.bilibili.com/v/game/match/',
|
||||
})
|
||||
export const creations = getSimpleLinkItem({
|
||||
name: 'creations',
|
||||
displayName: '创作中心',
|
||||
href: 'https://member.bilibili.com/platform/home',
|
||||
})
|
||||
|
||||
@ -111,10 +111,16 @@ export default Vue.extend({
|
||||
async nextPage() {
|
||||
try {
|
||||
const filter = this.filter as SubscriptionStatusFilter
|
||||
const followStatus = filter.viewAll ? 0 : (filter.status as number)
|
||||
const params = new URLSearchParams({
|
||||
type: this.type !== SubscriptionTypes.Bangumi ? '2' : '1',
|
||||
pn: this.page,
|
||||
ps: '16',
|
||||
vmid: getUID(),
|
||||
follow_status: followStatus.toString(),
|
||||
})
|
||||
const json = await getJsonWithCredentials(
|
||||
`https://api.bilibili.com/x/space/bangumi/follow/list?type=${
|
||||
this.type !== SubscriptionTypes.Bangumi ? '2' : '1'
|
||||
}&pn=${this.page}&ps=16&vmid=${getUID()}`,
|
||||
`https://api.bilibili.com/x/space/bangumi/follow/list?${params}`,
|
||||
)
|
||||
if (json.code !== 0) {
|
||||
logError(`加载订阅信息失败: ${json.message}`)
|
||||
@ -137,16 +143,10 @@ export default Vue.extend({
|
||||
),
|
||||
card => card.id,
|
||||
)
|
||||
.filter(card => {
|
||||
if (filter.viewAll) {
|
||||
return true
|
||||
}
|
||||
return card.status === filter.status
|
||||
})
|
||||
.sort(subscriptionSorter)
|
||||
this.page++
|
||||
this.cards = this.cards.concat(newCards)
|
||||
this.hasMorePage = newCards.length > 0
|
||||
this.hasMorePage = this.cards.length < json.data.total
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
@ -436,7 +436,7 @@ export default Vue.extend({
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
@include semi-bold();
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.video-toolbar-container .video-note .video-note-inner {
|
||||
display: none !important;
|
||||
}
|
||||
1
registry/lib/components/style/hide/video/notes/index.md
Normal file
1
registry/lib/components/style/hide/video/notes/index.md
Normal file
@ -0,0 +1 @@
|
||||
隐藏视频页面中的 "记笔记" 按钮.
|
||||
15
registry/lib/components/style/hide/video/notes/index.ts
Normal file
15
registry/lib/components/style/hide/video/notes/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
|
||||
const name = 'hideVideoNotes'
|
||||
export const component = defineComponentMetadata({
|
||||
name,
|
||||
displayName: '隐藏记笔记',
|
||||
tags: [componentsTags.video, componentsTags.style],
|
||||
entry: none,
|
||||
instantStyles: [
|
||||
{
|
||||
name,
|
||||
style: () => import('./hide-video-notes.scss'),
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -0,0 +1 @@
|
||||
隐藏番剧和视频页面右侧的推荐视频列表. 注意: 如果你想关闭 b 站的自动连播 (自动播放下一个推荐视频) 功能, 需要先取消隐藏视频推荐才能看到开关.
|
||||
@ -12,9 +12,5 @@ export const component = defineComponentMetadata({
|
||||
},
|
||||
],
|
||||
tags: [componentsTags.style, componentsTags.video],
|
||||
description: {
|
||||
'zh-CN':
|
||||
'隐藏番剧和视频页面右侧的推荐视频列表. 注意: 如果你想关闭 b 站的自动连播 (自动播放下一个推荐视频) 功能, 需要先取消隐藏视频推荐才能看到开关.',
|
||||
},
|
||||
urlInclude: videoAndBangumiUrls,
|
||||
})
|
||||
|
||||
@ -2,13 +2,16 @@
|
||||
#reco_list,
|
||||
.bilibili-player-ending-panel-box-videos,
|
||||
.r-con .rcmd-list,
|
||||
.playlist-container .recommend-list-container,
|
||||
.bpx-player-ending-related,
|
||||
.plp-r [class*="recommend_wrap"] {
|
||||
display: none !important;
|
||||
}
|
||||
.bilibili-player-ending-panel-box-functions .bilibili-player-upinfo-spans {
|
||||
position: static !important;
|
||||
}
|
||||
.bilibili-player-ending-panel-box {
|
||||
.bilibili-player-ending-panel-box,
|
||||
.bpx-player-ending-content {
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
flex-direction: column !important;
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.video-toolbar-container .video-complaint {
|
||||
display: none !important;
|
||||
}
|
||||
1
registry/lib/components/style/hide/video/report/index.md
Normal file
1
registry/lib/components/style/hide/video/report/index.md
Normal file
@ -0,0 +1 @@
|
||||
隐藏视频页面中的 "稿件投诉" 按钮.
|
||||
15
registry/lib/components/style/hide/video/report/index.ts
Normal file
15
registry/lib/components/style/hide/video/report/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
|
||||
const name = 'hideVideoReport'
|
||||
export const component = defineComponentMetadata({
|
||||
name,
|
||||
displayName: '隐藏稿件投诉',
|
||||
tags: [componentsTags.video, componentsTags.style],
|
||||
entry: none,
|
||||
instantStyles: [
|
||||
{
|
||||
name,
|
||||
style: () => import('./hide-video-report.scss'),
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -2,37 +2,67 @@
|
||||
<div class="fresh-home-extra-options">
|
||||
<div class="fresh-home-extra-options-title">布局配置</div>
|
||||
<VLoading v-if="!loaded" />
|
||||
<div v-if="loaded" class="fresh-home-extra-options-tips">
|
||||
<div class="fresh-home-extra-options-tip">
|
||||
按住并拖动可以调整顺序, 打开换行后下一个元素将会另起一行.
|
||||
</div>
|
||||
<div class="fresh-home-extra-options-legends">
|
||||
<div class="fresh-home-extra-options-legend">
|
||||
<VIcon icon="mdi-arrow-expand-horizontal" :size="16" />
|
||||
<div class="fresh-home-extra-options-legend-text">可变宽度</div>
|
||||
</div>
|
||||
<div class="fresh-home-extra-options-legend">
|
||||
<VIcon icon="mdi-arrow-collapse-horizontal" :size="16" />
|
||||
<div class="fresh-home-extra-options-legend-text">固定宽度</div>
|
||||
</div>
|
||||
<div class="fresh-home-extra-options-legend">
|
||||
<VIcon icon="mdi-wrap" :size="16" />
|
||||
<div class="fresh-home-extra-options-legend-text">切换换行</div>
|
||||
</div>
|
||||
<div class="fresh-home-extra-options-legend">
|
||||
<VIcon icon="mdi-eye-outline" :size="16" />
|
||||
<div class="fresh-home-extra-options-legend-text">切换显示</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="loaded" ref="sortList" class="fresh-home-extra-options-sort-list">
|
||||
<div
|
||||
v-for="item of sortItems"
|
||||
v-for="item of sortedItems"
|
||||
:key="item.layoutItem.name"
|
||||
class="fresh-home-extra-options-sort-item-container"
|
||||
:data-name="item.layoutItem.name"
|
||||
class="fresh-home-extra-options-sort-item"
|
||||
:class="{ 'fresh-home-extra-options-sort-item-dimmed': item.layoutSettings.hidden }"
|
||||
>
|
||||
<div
|
||||
:data-name="item.layoutItem.name"
|
||||
:style="{ 'flex-grow': item.layoutItem.grow ? '1' : '0' }"
|
||||
class="fresh-home-extra-options-sort-item"
|
||||
>
|
||||
<div class="fresh-home-extra-options-sort-item-name">
|
||||
<VIcon v-if="item.layoutItem.grow" icon="mdi-arrow-expand-horizontal" :size="16" />
|
||||
<VIcon v-else icon="mdi-arrow-collapse-horizontal" :size="16" />
|
||||
{{ item.layoutItem.displayName }}
|
||||
</div>
|
||||
<div class="fresh-home-extra-options-sort-item-config">
|
||||
<VButton
|
||||
icon
|
||||
round
|
||||
@click="item.layoutSettings.linebreak = !item.layoutSettings.linebreak"
|
||||
>
|
||||
<VIcon v-if="item.layoutSettings.linebreak" icon="mdi-wrap" :size="16" />
|
||||
<VIcon v-else icon="mdi-wrap-disabled" :size="16" />
|
||||
</VButton>
|
||||
</div>
|
||||
<VIcon
|
||||
v-if="item.layoutItem.grow"
|
||||
title="可变宽度"
|
||||
icon="mdi-arrow-expand-horizontal"
|
||||
class="fresh-home-extra-options-sort-item-type"
|
||||
:size="16"
|
||||
/>
|
||||
<VIcon
|
||||
v-else
|
||||
icon="mdi-arrow-collapse-horizontal"
|
||||
class="fresh-home-extra-options-sort-item-type"
|
||||
title="固定宽度"
|
||||
:size="16"
|
||||
/>
|
||||
<div class="fresh-home-extra-options-sort-item-name">{{ item.layoutItem.displayName }}</div>
|
||||
<div class="fresh-home-extra-options-sort-item-actions">
|
||||
<VButton
|
||||
type="transparent"
|
||||
title="切换换行"
|
||||
icon
|
||||
@click="toggleLinebreak(item.layoutItem)"
|
||||
>
|
||||
<VIcon v-if="item.layoutSettings.linebreak" icon="mdi-wrap" :size="16" />
|
||||
<VIcon v-else icon="mdi-wrap-disabled" :size="16" />
|
||||
</VButton>
|
||||
<VButton type="transparent" title="切换显示" icon @click="toggleVisible(item.layoutItem)">
|
||||
<VIcon v-if="item.layoutSettings.hidden" icon="mdi-eye-off-outline" :size="16" />
|
||||
<VIcon v-else icon="mdi-eye-outline" :size="16" />
|
||||
</VButton>
|
||||
</div>
|
||||
<div
|
||||
v-if="item.layoutSettings.linebreak"
|
||||
class="fresh-home-extra-options-sort-item-linebreak"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,15 +87,40 @@ export default Vue.extend({
|
||||
return {
|
||||
loaded: false,
|
||||
layouts,
|
||||
sortedItems: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
async mounted() {
|
||||
const list: HTMLElement = this.$refs.sortList
|
||||
const Sortable = await SortableJSLibrary
|
||||
console.log({ list })
|
||||
Sortable.create(list, {
|
||||
delay: 100,
|
||||
forceFallback: true,
|
||||
onEnd: (e: SortableEvent) => {
|
||||
const sortedItems = [...this.sortedItems] as SortItem[]
|
||||
const [targetItem] = sortedItems.splice(e.oldIndex, 1)
|
||||
console.log(targetItem.layoutItem.name, `${e.oldIndex} -> ${e.newIndex}`)
|
||||
sortedItems.splice(e.newIndex, 0, targetItem)
|
||||
const { layoutOptions } = freshHomeOptions
|
||||
sortedItems.forEach((it, index) => {
|
||||
;(layoutOptions[it.layoutItem.name] as FreshLayoutItemSettings).order = index + 1
|
||||
it.layoutSettings.order = index + 1
|
||||
})
|
||||
this.sortedItems = sortedItems
|
||||
},
|
||||
})
|
||||
this.sortItems()
|
||||
this.loaded = true
|
||||
},
|
||||
methods: {
|
||||
sortItems() {
|
||||
return (this.layouts as FreshLayoutItem[])
|
||||
this.sortedItems = (this.layouts as FreshLayoutItem[])
|
||||
.map((layoutItem, index): SortItem => {
|
||||
const layoutSettings: FreshLayoutItemSettings = {
|
||||
linebreak: false,
|
||||
order: index + 1,
|
||||
hidden: false,
|
||||
...freshHomeOptions.layoutOptions[layoutItem.name],
|
||||
}
|
||||
return {
|
||||
@ -76,19 +131,16 @@ export default Vue.extend({
|
||||
.filter(it => it !== null)
|
||||
.sort(ascendingSort(it => it.layoutSettings.order))
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
const list: HTMLElement = this.$refs.sortList
|
||||
const Sortable = await SortableJSLibrary
|
||||
console.log({ list })
|
||||
Sortable.create(list, {
|
||||
delay: 100,
|
||||
forceFallback: true,
|
||||
onEnd: (e: SortableEvent) => {
|
||||
console.log(e)
|
||||
},
|
||||
})
|
||||
this.loaded = true
|
||||
toggleLinebreak(item: FreshLayoutItem) {
|
||||
const options = freshHomeOptions.layoutOptions[item.name]
|
||||
options.linebreak = !options.linebreak
|
||||
this.sortItems()
|
||||
},
|
||||
toggleVisible(item: FreshLayoutItem) {
|
||||
const options = freshHomeOptions.layoutOptions[item.name]
|
||||
options.hidden = !options.hidden
|
||||
this.sortItems()
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@ -98,30 +150,57 @@ export default Vue.extend({
|
||||
.fresh-home-extra-options {
|
||||
@include v-stretch(10px);
|
||||
padding: 4px 0;
|
||||
&-sort-list {
|
||||
@include h-stretch();
|
||||
column-gap: 8px;
|
||||
row-gap: 4px;
|
||||
|
||||
&-tips {
|
||||
@include v-stretch(4px);
|
||||
}
|
||||
&-tip {
|
||||
opacity: 0.6;
|
||||
}
|
||||
&-legends {
|
||||
@include h-center();
|
||||
column-gap: 6px;
|
||||
row-gap: 2px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
&-sort-item-container {
|
||||
display: contents;
|
||||
&-legend {
|
||||
@include h-center(4px);
|
||||
}
|
||||
.mdi-arrow-expand-horizontal {
|
||||
&,
|
||||
& ~ .fresh-home-extra-options-legend-text {
|
||||
color: #a875ff;
|
||||
}
|
||||
}
|
||||
.mdi-arrow-collapse-horizontal {
|
||||
&,
|
||||
& ~ .fresh-home-extra-options-legend-text {
|
||||
color: #009fd9;
|
||||
}
|
||||
}
|
||||
&-sort-list {
|
||||
@include h-stretch();
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
&-sort-item {
|
||||
@include v-center(6px);
|
||||
@include h-center(4px);
|
||||
@include border-card();
|
||||
padding: 8px;
|
||||
@include shadow();
|
||||
padding: 2px 4px 2px 8px;
|
||||
transition: border-color 0.2s ease-out;
|
||||
body.dark &:hover,
|
||||
&:hover {
|
||||
border-color: var(--theme-color);
|
||||
cursor: move;
|
||||
}
|
||||
&-name {
|
||||
@include h-center(4px);
|
||||
&-dimmed {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&-linebreak {
|
||||
flex: 1 0 100%;
|
||||
&-actions {
|
||||
@include h-center();
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ export default Vue.extend({
|
||||
}
|
||||
&-title {
|
||||
color: var(--home-color);
|
||||
font-weight: bold;
|
||||
@include semi-bold();
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
<template>
|
||||
<fragment>
|
||||
<div
|
||||
class="fresh-home-content-layout-item"
|
||||
:class="{
|
||||
grow: item.grow,
|
||||
}"
|
||||
>
|
||||
<component :is="item.component" />
|
||||
</div>
|
||||
<div v-if="options.linebreak" class="fresh-home-content-layout-item linebreak"></div>
|
||||
</fragment>
|
||||
<div
|
||||
class="fresh-home-content-layout-item-container"
|
||||
:class="{ 'fresh-home-content-layout-item-hidden': options.hidden }"
|
||||
>
|
||||
<template v-if="!options.hidden">
|
||||
<div
|
||||
class="fresh-home-content-layout-item"
|
||||
:class="{
|
||||
grow: item.grow,
|
||||
}"
|
||||
:style="{ order: options.order }"
|
||||
>
|
||||
<component :is="item.component" />
|
||||
</div>
|
||||
<div
|
||||
v-if="options.linebreak"
|
||||
class="fresh-home-content-layout-item linebreak"
|
||||
:style="{ order: options.order }"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { freshHomeOptions } from './options'
|
||||
@ -33,6 +43,12 @@ export default Vue.extend({
|
||||
&-content-layout-item {
|
||||
flex: 0 0 auto;
|
||||
padding: 12px;
|
||||
&-container {
|
||||
display: contents;
|
||||
&.fresh-home-content-layout-item-hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.linebreak {
|
||||
padding: 0;
|
||||
flex: 1 0 100%;
|
||||
|
||||
@ -25,7 +25,7 @@ export const component = defineComponentMetadata({
|
||||
})
|
||||
},
|
||||
options: freshHomeOptionsMetadata,
|
||||
// extraOptions: () => import('./ExtraOptions.vue'),
|
||||
extraOptions: () => import('./ExtraOptions.vue'),
|
||||
unload: () => document.body.classList.add('home-redesign-off'),
|
||||
reload: () => document.body.classList.remove('home-redesign-off'),
|
||||
instantStyles: [
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
|
||||
export enum areaPrimaryTitleColorEnum {
|
||||
Dark = '深色',
|
||||
Light = '浅色',
|
||||
}
|
||||
|
||||
const map = {
|
||||
[areaPrimaryTitleColorEnum.Dark]: 'dark',
|
||||
[areaPrimaryTitleColorEnum.Light]: 'light',
|
||||
}
|
||||
|
||||
addComponentListener(
|
||||
'freshHome.areaPrimaryTitleColor',
|
||||
(value: areaPrimaryTitleColorEnum) => {
|
||||
document.documentElement.setAttribute(
|
||||
'fresh-home--options--area-primary-title-color',
|
||||
map[value],
|
||||
)
|
||||
},
|
||||
true,
|
||||
)
|
||||
@ -97,7 +97,16 @@ export default Vue.extend({
|
||||
bottom: 8px;
|
||||
font-size: 18px;
|
||||
@include semi-bold();
|
||||
color: black;
|
||||
transition: color 0.2s ease-out, text-shadow 0.2s ease-out;
|
||||
|
||||
html[fresh-home--options--area-primary-title-color='dark'] & {
|
||||
color: black;
|
||||
}
|
||||
|
||||
html[fresh-home--options--area-primary-title-color='light'] & {
|
||||
color: #fafafa;
|
||||
text-shadow: rgba(0, 0, 0, 0.2) 0 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-other {
|
||||
|
||||
@ -123,14 +123,13 @@ export default Vue.extend({
|
||||
}
|
||||
&-header {
|
||||
@include h-center();
|
||||
height: 26px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
&-rank-list {
|
||||
@include v-stretch(var(--fresh-home-categories-header-gap));
|
||||
&-header {
|
||||
// Timeline 的 header 中, 图标为 20px, 上下 padding 共 8px, 这里的 line-height 需要保持一致来对齐
|
||||
// line-height: calc(20px + 8px);
|
||||
@include h-center();
|
||||
justify-content: space-between;
|
||||
.be-icon {
|
||||
|
||||
@ -11,6 +11,10 @@
|
||||
class="fresh-home-categories-bangumi-timeline-item animation"
|
||||
:class="{ today: index === todayIndex }"
|
||||
>
|
||||
<div
|
||||
v-if="item.episodes.length === 0"
|
||||
class="fresh-home-categories-bangumi-timeline-empty-background"
|
||||
></div>
|
||||
<div class="fresh-home-categories-bangumi-timeline-date">
|
||||
<div
|
||||
class="fresh-home-categories-bangumi-timeline-date-icon"
|
||||
@ -29,8 +33,18 @@
|
||||
TODAY
|
||||
</div>
|
||||
</div>
|
||||
<VButton
|
||||
v-if="item.episodes.length > 0"
|
||||
icon
|
||||
type="transparent"
|
||||
title="上一页"
|
||||
@click="offsetPage(item, -1)"
|
||||
>
|
||||
<VIcon icon="left-arrow" :size="16" />
|
||||
</VButton>
|
||||
<div
|
||||
ref="seasonsList"
|
||||
:data-date="item.date"
|
||||
class="fresh-home-categories-bangumi-timeline-seasons-container scroll-top scroll-bottom"
|
||||
:class="{ 'not-empty': item.episodes.length > 0 }"
|
||||
>
|
||||
@ -55,7 +69,7 @@
|
||||
<div
|
||||
class="fresh-home-categories-bangumi-timeline-season-cover"
|
||||
:class="{
|
||||
published: index === todayIndex && publishedToday(season),
|
||||
published: index === todayIndex && isPublished(season),
|
||||
today: index === todayIndex,
|
||||
follow: season.follow,
|
||||
}"
|
||||
@ -78,15 +92,18 @@
|
||||
<div
|
||||
class="fresh-home-categories-bangumi-timeline-season-time"
|
||||
:class="{
|
||||
published: index === todayIndex && publishedToday(season),
|
||||
published: index === todayIndex && isPublished(season),
|
||||
follow: season.follow,
|
||||
today: index === todayIndex,
|
||||
}"
|
||||
>
|
||||
<div class="fresh-home-categories-bangumi-timeline-season-time-icon">
|
||||
<VIcon
|
||||
:icon="season.follow ? 'mdi-heart-outline' : 'mdi-progress-clock'"
|
||||
v-if="!season.follow"
|
||||
:icon="isPublished(season) ? 'mdi-clock-check-outline' : 'mdi-progress-clock'"
|
||||
:size="14"
|
||||
/>
|
||||
<VIcon v-else icon="mdi-heart-outline" :size="14" />
|
||||
</div>
|
||||
<div class="fresh-home-categories-bangumi-timeline-season-time-text">
|
||||
{{ season.pub_time }}
|
||||
@ -95,11 +112,20 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<VButton
|
||||
v-if="item.episodes.length > 0"
|
||||
icon
|
||||
type="transparent"
|
||||
title="下一页"
|
||||
@click="offsetPage(item, 1)"
|
||||
>
|
||||
<VIcon icon="right-arrow" :size="16" />
|
||||
</VButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { DpiImage, VIcon, VEmpty, VLoading } from '@/ui'
|
||||
import { DpiImage, VIcon, VEmpty, VButton, VLoading } from '@/ui'
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
import { enableHorizontalScroll } from '@/core/horizontal-scroll'
|
||||
import { cssVariableMixin, requestMixin } from '../../../../mixin'
|
||||
@ -138,11 +164,11 @@ const rankListHeight = rankListCssVars.panelHeight - 2 * rankListCssVars.padding
|
||||
const timelineCssVars = (() => {
|
||||
const seasonItemWidth = 250
|
||||
const seasonTodayWidth = 250
|
||||
const timelineItemHeight = 66
|
||||
const timelineTodayHeight = 96
|
||||
const timelineViewportItemsHeight = 6 * timelineItemHeight + timelineTodayHeight
|
||||
const timelineItemGap = (rankListHeight - timelineViewportItemsHeight) / 6
|
||||
const timelineViewportHeight = 6 * timelineItemGap + timelineViewportItemsHeight
|
||||
const timelineItemHeight = 82
|
||||
const timelineTodayHeight = 114
|
||||
const timelineViewportItemsHeight = 5 * timelineItemHeight + timelineTodayHeight
|
||||
const timelineItemGap = (rankListHeight - timelineViewportItemsHeight) / 5
|
||||
const timelineViewportHeight = 5 * timelineItemGap + timelineViewportItemsHeight
|
||||
return {
|
||||
seasonItemWidth,
|
||||
seasonTodayWidth,
|
||||
@ -159,6 +185,7 @@ export default Vue.extend({
|
||||
VIcon,
|
||||
VEmpty,
|
||||
VLoading,
|
||||
VButton,
|
||||
},
|
||||
mixins: [
|
||||
requestMixin({ requestMethod: getJsonWithCredentials }),
|
||||
@ -252,7 +279,7 @@ export default Vue.extend({
|
||||
if (seasonsData.length === 0) {
|
||||
return
|
||||
}
|
||||
const lastPublishedItem = [...seasonsData].reverse().find(it => this.publishedToday(it))
|
||||
const lastPublishedItem = [...seasonsData].reverse().find(it => this.isPublished(it))
|
||||
if (!lastPublishedItem) {
|
||||
this.scrolled = true
|
||||
return
|
||||
@ -273,7 +300,7 @@ export default Vue.extend({
|
||||
}
|
||||
return season.pub_index
|
||||
},
|
||||
publishedToday(season: TimelineSeason) {
|
||||
isPublished(season: TimelineSeason) {
|
||||
if (season.delay) {
|
||||
return false
|
||||
}
|
||||
@ -282,6 +309,16 @@ export default Vue.extend({
|
||||
dayOfWeekText(item: TimelineDay) {
|
||||
return `周${['日', '一', '二', '三', '四', '五', '六', '日'][item.day_of_week]}`
|
||||
},
|
||||
offsetPage(item: TimelineDay, offset: number) {
|
||||
const list = this.$refs.seasonsList as HTMLElement[]
|
||||
const container = list.find(it => it.dataset.date === item.date)
|
||||
const containerWidth = container.clientWidth
|
||||
const pageWidth =
|
||||
Math.trunc(containerWidth / timelineCssVars.seasonItemWidth) *
|
||||
timelineCssVars.seasonItemWidth
|
||||
const scrollArea = container.children[0]
|
||||
scrollArea?.scrollBy(offset * pageWidth, 0)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@ -326,8 +363,12 @@ export default Vue.extend({
|
||||
}
|
||||
}
|
||||
&-item {
|
||||
@include h-center(24px);
|
||||
@include h-center(4px);
|
||||
@include border-card();
|
||||
scroll-snap-align: start;
|
||||
overflow: hidden;
|
||||
padding: 0 4px 0 16px;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
height: var(--timeline-item-height);
|
||||
&.today {
|
||||
@ -337,6 +378,7 @@ export default Vue.extend({
|
||||
&-date {
|
||||
display: grid;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
grid-template: 'icon number' 18px 'icon text' 22px / 50px auto;
|
||||
gap: 8px;
|
||||
&-icon {
|
||||
@ -400,7 +442,12 @@ export default Vue.extend({
|
||||
}
|
||||
&-seasons-container {
|
||||
@include h-stretch();
|
||||
@include scroll-mask-x(18px, var(--home-base-color));
|
||||
body:not(.dark) & {
|
||||
@include scroll-mask-x(18px, #fdfdfd);
|
||||
}
|
||||
body.dark & {
|
||||
@include scroll-mask-x(18px, #222);
|
||||
}
|
||||
width: 0;
|
||||
flex: 1 0 0;
|
||||
margin: 0 2px;
|
||||
@ -415,7 +462,7 @@ export default Vue.extend({
|
||||
scroll-snap-type: x mandatory;
|
||||
}
|
||||
&-season {
|
||||
--cover-size: 50px;
|
||||
--cover-size: 56px;
|
||||
scroll-snap-align: start;
|
||||
flex-shrink: 0;
|
||||
padding: 7px;
|
||||
@ -429,11 +476,11 @@ export default Vue.extend({
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
width: var(--season-item-width);
|
||||
height: var(--timeline-item-height);
|
||||
// height: var(--timeline-item-height);
|
||||
transition: 0.2s ease-out;
|
||||
&:not(:last-child) {
|
||||
padding-right: calc(var(--timeline-item-gap) / 2 + 6px);
|
||||
border-right: 1px solid #8884;
|
||||
// border-right: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
&-cover {
|
||||
@ -464,7 +511,7 @@ export default Vue.extend({
|
||||
@include semi-bold();
|
||||
@include single-line();
|
||||
&.today {
|
||||
@include max-line(2, 1.25);
|
||||
@include max-line(2, 1.3);
|
||||
}
|
||||
}
|
||||
&-episode {
|
||||
@ -475,10 +522,11 @@ export default Vue.extend({
|
||||
}
|
||||
&-time {
|
||||
grid-area: time;
|
||||
@include card(6px);
|
||||
@include card();
|
||||
@include round-bar(20);
|
||||
@include h-center(4px);
|
||||
box-shadow: none;
|
||||
padding: 2px 4px;
|
||||
padding: 2px 6px 2px 4px;
|
||||
&.published {
|
||||
border-color: var(--theme-color);
|
||||
&.follow {
|
||||
@ -493,6 +541,9 @@ export default Vue.extend({
|
||||
&.follow:not(.published) &-icon {
|
||||
color: var(--theme-color);
|
||||
}
|
||||
&.today {
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover &-title {
|
||||
@ -504,8 +555,8 @@ export default Vue.extend({
|
||||
|
||||
&.today {
|
||||
width: var(--season-today-width);
|
||||
height: var(--timeline-today-height);
|
||||
--cover-size: 80px;
|
||||
// height: var(--timeline-today-height);
|
||||
--cover-size: 84px;
|
||||
grid-template:
|
||||
'cover title title' 2fr
|
||||
'cover episode episode' 1fr
|
||||
@ -519,6 +570,24 @@ export default Vue.extend({
|
||||
}
|
||||
}
|
||||
}
|
||||
&-empty-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
transparent 25%,
|
||||
#8882 0,
|
||||
#8882 50%,
|
||||
transparent 0,
|
||||
transparent 75%,
|
||||
#8882 0,
|
||||
#8882 100%
|
||||
);
|
||||
background-size: 60px 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -171,7 +171,7 @@ export default Vue.extend({
|
||||
border-radius: 50%;
|
||||
background-color: #333;
|
||||
color: #eee;
|
||||
font-weight: bold;
|
||||
@include semi-bold();
|
||||
&.top-three {
|
||||
background-color: var(--theme-color);
|
||||
color: var(--foreground-color);
|
||||
|
||||
@ -365,9 +365,9 @@ export default Vue.extend({
|
||||
content: attr(data-number);
|
||||
@include absolute-center();
|
||||
@include h-center();
|
||||
@include semi-bold();
|
||||
justify-content: center;
|
||||
top: 0;
|
||||
font-weight: bold;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
|
||||
@ -7,4 +7,5 @@ export interface FreshLayoutItem extends WithName {
|
||||
export interface FreshLayoutItemSettings {
|
||||
linebreak: boolean
|
||||
order: number
|
||||
hidden: boolean
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { defineOptionsMetadata } from '@/components/define'
|
||||
import { getNumberValidator } from '@/core/utils'
|
||||
import type { FreshLayoutItemSettings } from './layouts/fresh-layout-item'
|
||||
import { areaPrimaryTitleColorEnum } from './init-dropdown-options/area-primary-title-color'
|
||||
|
||||
export enum RankListMode {
|
||||
Default = 'default',
|
||||
@ -14,22 +15,28 @@ export const freshHomeOptionsMetadata = defineOptionsMetadata({
|
||||
blackboard: {
|
||||
linebreak: false,
|
||||
order: 1,
|
||||
// as boolean 一下, 不然 TS 会推断为只能 false
|
||||
hidden: false as boolean,
|
||||
},
|
||||
trending: {
|
||||
linebreak: true,
|
||||
order: 2,
|
||||
hidden: false,
|
||||
},
|
||||
feeds: {
|
||||
linebreak: false,
|
||||
order: 3,
|
||||
hidden: false,
|
||||
},
|
||||
areas: {
|
||||
linebreak: true,
|
||||
order: 4,
|
||||
hidden: false,
|
||||
},
|
||||
categories: {
|
||||
linebreak: false,
|
||||
order: 5,
|
||||
hidden: false,
|
||||
},
|
||||
} satisfies Record<string, FreshLayoutItemSettings>,
|
||||
hidden: true,
|
||||
@ -42,6 +49,11 @@ export const freshHomeOptionsMetadata = defineOptionsMetadata({
|
||||
displayName: '启用横向滚动',
|
||||
defaultValue: false,
|
||||
},
|
||||
areaPrimaryTitleColor: {
|
||||
displayName: '栏目主标题颜色',
|
||||
dropdownEnum: areaPrimaryTitleColorEnum,
|
||||
defaultValue: areaPrimaryTitleColorEnum.Dark,
|
||||
},
|
||||
maxWidth: {
|
||||
displayName: '最大宽度 (px)',
|
||||
defaultValue: 1440,
|
||||
|
||||
@ -1,6 +1,14 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { playerReady } from '@/core/utils'
|
||||
import { createPlayerModeChangeEvent } from '@/components/video/player-adaptor'
|
||||
import { getComponentSettings, addComponentListener } from '@/core/settings'
|
||||
import { playerReady, getNumberValidator } from '@/core/utils'
|
||||
import { videoUrls } from '@/core/utils/urls'
|
||||
|
||||
/**
|
||||
* 将 element 移动到 after 后面
|
||||
* @param element 要移动的元素
|
||||
* @param after 目标元素
|
||||
*/
|
||||
function moveElementAfter(element: Element, after: Element) {
|
||||
const parent = element.parentElement
|
||||
if (!parent) {
|
||||
@ -10,27 +18,110 @@ function moveElementAfter(element: Element, after: Element) {
|
||||
parent.insertBefore(element, after.nextSibling)
|
||||
}
|
||||
|
||||
const playerOnTop = async () => {
|
||||
/**
|
||||
* 处理屏幕尺寸变化
|
||||
* 实时获取到`#danmukuBox`的`marginTop`值
|
||||
* 修改自B站内联的JS代码,请确保此函数在播放器宽屏模式下被调用
|
||||
*/
|
||||
function calcMarginTop() {
|
||||
const e = window.innerHeight
|
||||
const t = Math.max((document.body && document.body.clientWidth) || window.innerWidth, 1100)
|
||||
const n = innerWidth > 1680 ? 411 : 350
|
||||
const o = parseInt(String((16 * (e - (window.innerWidth > 1690 ? 318 : 308))) / 9))
|
||||
const r = t - 112 - n
|
||||
const d = r < o ? r : o
|
||||
const a = Math.round((d + n) * (9 / 16)) + (innerWidth > 1680 ? 56 : 46)
|
||||
|
||||
return `${a}px`
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一添加副作用处理
|
||||
* 处理播放器宽屏切换后的UP信息与弹幕列表样式
|
||||
*/
|
||||
function sideEffect() {
|
||||
const author = document.querySelector('.up-panel-container') as HTMLDivElement
|
||||
const danmuku = document.querySelector('#danmukuBox') as HTMLDivElement
|
||||
|
||||
// 缓存当前播放器模式 用于判断是否为宽屏模式
|
||||
let currentMode = 'normal'
|
||||
|
||||
// 监听播放器宽屏模式 调整UP信息与弹幕列表样式
|
||||
window.addEventListener(
|
||||
'playerModeChange',
|
||||
(ev: ReturnType<typeof createPlayerModeChangeEvent>) => {
|
||||
const { mode } = ev.detail
|
||||
|
||||
currentMode = mode
|
||||
|
||||
if (mode === 'wide') {
|
||||
danmuku.style.marginTop = '0px'
|
||||
author.style.marginTop = calcMarginTop()
|
||||
} else {
|
||||
// 恢复原始样式
|
||||
author.style.marginTop = '0px'
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// 监听屏幕尺寸变化
|
||||
window.addEventListener('resize', () => {
|
||||
// 播放器宽屏模式
|
||||
if (currentMode === 'wide') {
|
||||
author.style.marginTop = calcMarginTop()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放器置顶
|
||||
* 从设置中获取顶部留白的高度
|
||||
*/
|
||||
async function playerOnTop({ settings: { options: opt }, metadata }) {
|
||||
await playerReady()
|
||||
const title = document.querySelector('#viewbox_report')
|
||||
const toolbar = document.querySelector('#arc_toolbar_report')
|
||||
moveElementAfter(title, toolbar)
|
||||
const player = document.querySelector('#playerWrap') as HTMLDivElement
|
||||
// 将标题放到播放器和点赞中间位置
|
||||
moveElementAfter(title, player)
|
||||
player.style.marginTop = `${opt.marginTop}px`
|
||||
|
||||
const author = document.querySelector('#v_upinfo').parentElement
|
||||
const danmuku = document.querySelector('#danmukuBox')
|
||||
moveElementAfter(author, danmuku)
|
||||
// 监听设置变化 实时更新到页面
|
||||
addComponentListener(`${metadata.name}.marginTop`, (value: number) => {
|
||||
player.style.marginTop = `${value}px`
|
||||
})
|
||||
|
||||
sideEffect()
|
||||
}
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'playerOnTopNew',
|
||||
author: {
|
||||
name: 'RieN7',
|
||||
link: 'https://github.com/rien7',
|
||||
},
|
||||
author: [
|
||||
{
|
||||
name: 'RieN7',
|
||||
link: 'https://github.com/rien7',
|
||||
},
|
||||
{
|
||||
name: 'ZiuChen',
|
||||
link: 'https://github.com/ZiuChen',
|
||||
},
|
||||
{
|
||||
name: 'LockRim',
|
||||
link: 'https://github.com/LockRim',
|
||||
},
|
||||
],
|
||||
tags: [componentsTags.style, componentsTags.video],
|
||||
options: {
|
||||
marginTop: {
|
||||
displayName: '顶部留白 (px)',
|
||||
defaultValue: 20,
|
||||
validator: getNumberValidator(0, Infinity),
|
||||
},
|
||||
},
|
||||
urlInclude: videoUrls,
|
||||
displayName: '播放器置顶(新)',
|
||||
description:
|
||||
'原来的播放器置顶插件,现在已经不可用了,这是一个新的版本,可以在视频页面中将播放器放在页面最上方.',
|
||||
entry: playerOnTop,
|
||||
reload: playerOnTop,
|
||||
reload: () =>
|
||||
playerOnTop({ settings: getComponentSettings('playerOnTopNew'), metadata: component }),
|
||||
})
|
||||
|
||||
@ -4,21 +4,8 @@ $prefix: 'simplifyComments-switch';
|
||||
|
||||
.bili-comment {
|
||||
.reply-item {
|
||||
// body.#{$prefix}-level & {
|
||||
.user-level,
|
||||
.sub-user-level {
|
||||
display: none !important;
|
||||
}
|
||||
// }
|
||||
// body.#{$prefix}-subReplyNewline & {
|
||||
.sub-user-info {
|
||||
display: flex !important;
|
||||
}
|
||||
// }
|
||||
// body.#{$prefix}-layout & {
|
||||
.reply-decorate {
|
||||
display: none !important;
|
||||
}
|
||||
// [布局微调]
|
||||
// 回复项
|
||||
.sub-user-info,
|
||||
.sub-user-name {
|
||||
line-height: 1.5 !important;
|
||||
@ -27,65 +14,105 @@ $prefix: 'simplifyComments-switch';
|
||||
margin-bottom: 4px !important;
|
||||
max-height: 19.5px !important;
|
||||
}
|
||||
.reply-content-container {
|
||||
display: block !important;
|
||||
flex-basis: 100% !important;
|
||||
}
|
||||
.root-reply:not(.reply-content-container) {
|
||||
padding: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
flex-wrap: wrap !important;
|
||||
}
|
||||
// 笔记
|
||||
.note-content {
|
||||
flex-basis: 100% !important;
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
// 笔记图片
|
||||
.image-exhibition {
|
||||
flex-basis: 100% !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
.root-reply,
|
||||
.reply-info,
|
||||
.sub-reply-info {
|
||||
position: static !important;
|
||||
}
|
||||
.reply-info,
|
||||
.sub-reply-info {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
.reply-time,
|
||||
.sub-reply-time {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
right: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
.sub-reply-time {
|
||||
top: 8px !important;
|
||||
}
|
||||
// 右侧操作菜单
|
||||
.reply-operation-warp,
|
||||
.sub-reply-operation-warp {
|
||||
right: -5px !important;
|
||||
}
|
||||
.reply-tag-list {
|
||||
display: inline-flex !important;
|
||||
margin: 2px 0 0 18px !important;
|
||||
.reply-tag-item {
|
||||
padding: 4px 6px !important;
|
||||
|
||||
body.#{$prefix}-userLevel & {
|
||||
.user-level,
|
||||
.sub-user-level {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
// body.#{$prefix}-replyEditor & {
|
||||
.reply-box-send {
|
||||
.send-text {
|
||||
font-size: 14px !important;
|
||||
|
||||
body.#{$prefix}-userPendent & {
|
||||
.bili-avatar-pendent-dom {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
body.#{$prefix}-decorateAndTime & {
|
||||
.reply-decorate {
|
||||
display: none !important;
|
||||
}
|
||||
.reply-time,
|
||||
.sub-reply-time {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
right: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
.sub-reply-time {
|
||||
top: 8px !important;
|
||||
}
|
||||
// .sub-user-info {
|
||||
// display: flex !important;
|
||||
// }
|
||||
}
|
||||
|
||||
body.#{$prefix}-fansMedal & {
|
||||
.fan-badge {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
body.#{$prefix}-subReplyNewLine & {
|
||||
.root-reply:not(.reply-content-container) {
|
||||
padding: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
flex-wrap: wrap !important;
|
||||
}
|
||||
.reply-content-container {
|
||||
display: block !important;
|
||||
flex-basis: 100% !important;
|
||||
}
|
||||
.root-reply,
|
||||
.reply-info,
|
||||
.sub-reply-info {
|
||||
position: static !important;
|
||||
}
|
||||
.reply-info,
|
||||
.sub-reply-info {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
.reply-tag-list {
|
||||
display: inline-flex !important;
|
||||
margin: 2px 0 0 18px !important;
|
||||
.reply-tag-item {
|
||||
padding: 4px 6px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.reply-box-textarea {
|
||||
line-height: normal !important;
|
||||
|
||||
body.#{$prefix}-eventBanner & {
|
||||
.reply-notice {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
body.#{$prefix}-replyEditor & {
|
||||
.reply-box-send {
|
||||
.send-text {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
}
|
||||
.reply-box-textarea {
|
||||
line-height: normal !important;
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
- 删除热评头像下方的关注按钮
|
||||
- 删除用户的等级标识
|
||||
- 删除发送源信息(\`来自安卓客户端\` 这种)
|
||||
- 删除用户名右边的勋章
|
||||
- 删除评论区顶部的横幅
|
||||
- 发送时间移动到右上角
|
||||
- 位图图标全部换用矢量图标, 高分屏不会模糊
|
||||
- 投票仅显示链接, 隐藏下面的大框.
|
||||
去除或优化评论区内的元素, 可配置以下选项 (描述的是勾选时的效果):
|
||||
|
||||
> 注: 关注和等级可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看.
|
||||
> 配置项仅对新版评论区有效
|
||||
|
||||
- `用户等级`: 隐藏用户等级标识. (隐藏后, 可以通过鼠标停留在头像上, 在弹出的资料卡小窗中查看)
|
||||
- `装扮 & 时间`: 隐藏装扮图片, 并把发送时间移动到装扮的位置.
|
||||
- `头像框`: 隐藏头像框.
|
||||
- `回复换行`:
|
||||
- 楼中楼回复也另起一行显示, 和一级回复保持一致.
|
||||
- `热评`, `UP 主点赞` 等标记和点赞栏放在同一行.
|
||||
- `编辑框`: 将提示文本居上, 符合用户实际输入文字的位置, 并将发布按钮的字号略微调小.
|
||||
- `粉丝勋章`: 隐藏用户的粉丝勋章.
|
||||
- `活动横幅`: 隐藏评论区顶部的活动横幅
|
||||
|
||||
@ -1,8 +1,46 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { wrapSwitchOptions } from '@/components/switch-options'
|
||||
|
||||
const name = 'simplifyComments'
|
||||
export const component = defineComponentMetadata({
|
||||
export const component = wrapSwitchOptions({
|
||||
name: 'simplifyOptions',
|
||||
switchProps: {
|
||||
checkedIcon: 'mdi-checkbox-marked-circle',
|
||||
notCheckedIcon: 'mdi-checkbox-blank-circle-outline',
|
||||
},
|
||||
dimAt: false,
|
||||
switches: {
|
||||
userLevel: {
|
||||
defaultValue: true,
|
||||
displayName: '用户等级',
|
||||
},
|
||||
decorateAndTime: {
|
||||
defaultValue: true,
|
||||
displayName: '装扮 & 时间',
|
||||
},
|
||||
userPendent: {
|
||||
defaultValue: false,
|
||||
displayName: '头像框',
|
||||
},
|
||||
subReplyNewLine: {
|
||||
defaultValue: true,
|
||||
displayName: '回复换行',
|
||||
},
|
||||
replyEditor: {
|
||||
defaultValue: true,
|
||||
displayName: '编辑框',
|
||||
},
|
||||
fansMedal: {
|
||||
defaultValue: false,
|
||||
displayName: '粉丝勋章',
|
||||
},
|
||||
eventBanner: {
|
||||
defaultValue: true,
|
||||
displayName: '活动横幅',
|
||||
},
|
||||
},
|
||||
})({
|
||||
name,
|
||||
displayName: '简化评论区',
|
||||
entry: async ({ metadata }) => {
|
||||
const { addComponentListener } = await import('@/core/settings')
|
||||
addComponentListener(
|
||||
@ -23,6 +61,5 @@ export const component = defineComponentMetadata({
|
||||
style: () => import('./comments-v2.scss'),
|
||||
},
|
||||
],
|
||||
displayName: '简化评论区',
|
||||
tags: [componentsTags.style],
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { newSwitchComponentWrapper, defineSwitchMetadata } from '@/components/switch-options'
|
||||
import { wrapSwitchOptions, defineSwitchMetadata } from '@/components/switch-options'
|
||||
|
||||
import { addComponentListener, getComponentSettings } from '@/core/settings'
|
||||
import { sq } from '@/core/spin-query'
|
||||
@ -9,11 +9,6 @@ import { mainSiteUrls } from '@/core/utils/urls'
|
||||
|
||||
const switchMetadata = defineSwitchMetadata({
|
||||
name: 'simplifyOptions',
|
||||
dimAt: 'checked',
|
||||
switchProps: {
|
||||
checkedIcon: 'mdi-eye-off-outline',
|
||||
notCheckedIcon: 'mdi-eye-outline',
|
||||
},
|
||||
switches: {
|
||||
categories: {
|
||||
defaultValue: false,
|
||||
@ -47,7 +42,7 @@ const switchMetadata = defineSwitchMetadata({
|
||||
})
|
||||
const console = useScopedConsole('简化首页')
|
||||
|
||||
export const component = newSwitchComponentWrapper(switchMetadata)({
|
||||
export const component = wrapSwitchOptions(switchMetadata)({
|
||||
name: 'simplifyHome',
|
||||
displayName: '简化首页',
|
||||
description: '隐藏原版首页不需要的元素 / 分区.',
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
import { newSwitchComponentWrapper } from '@/components/switch-options'
|
||||
import { wrapSwitchOptions } from '@/components/switch-options'
|
||||
import { styledComponentEntry } from '@/components/styled-component'
|
||||
import { liveUrls } from '@/core/utils/urls'
|
||||
|
||||
export const component = newSwitchComponentWrapper({
|
||||
export const component = wrapSwitchOptions({
|
||||
name: 'simplifyOptions',
|
||||
dimAt: 'checked',
|
||||
switchProps: {
|
||||
checkedIcon: 'mdi-eye-off-outline',
|
||||
notCheckedIcon: 'mdi-eye-outline',
|
||||
},
|
||||
switches: {
|
||||
vip: {
|
||||
defaultValue: true,
|
||||
@ -18,6 +13,10 @@ export const component = newSwitchComponentWrapper({
|
||||
defaultValue: true,
|
||||
displayName: '入场通知',
|
||||
},
|
||||
wealthMedal: {
|
||||
defaultValue: true,
|
||||
displayName: '荣耀等级勋章',
|
||||
},
|
||||
fansMedal: {
|
||||
defaultValue: true,
|
||||
displayName: '粉丝勋章',
|
||||
|
||||
@ -3,6 +3,7 @@ $prefix: 'simplifyLiveroom-switch';
|
||||
|
||||
.#{$prefix} {
|
||||
&-vip .vip-icon,
|
||||
&-wealthMedal .wealth-medal-ctnr,
|
||||
&-fansMedal .fans-medal-item-ctnr,
|
||||
&-title .title-label,
|
||||
&-userLevel .user-level-icon,
|
||||
@ -24,6 +25,7 @@ $prefix: 'simplifyLiveroom-switch';
|
||||
&-enterPrompt .chat-history-panel .brush-prompt,
|
||||
&-enterPrompt .chat-item.convention-msg,
|
||||
&-pk .chaos-pk,
|
||||
&-pk .awesome-pk-box,
|
||||
&-topRank .chat-item.top3-notice,
|
||||
&-topRank .chat-item .rank-icon {
|
||||
display: none !important;
|
||||
@ -250,6 +252,9 @@ $prefix: 'simplifyLiveroom-switch';
|
||||
border-radius: 0 !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.wealth-medal-ctnr.medal-guard {
|
||||
display: none !important;
|
||||
}
|
||||
.fans-medal-item.medal-guard {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
1
registry/lib/components/style/special-danmaku/index.md
Normal file
1
registry/lib/components/style/special-danmaku/index.md
Normal file
@ -0,0 +1 @@
|
||||
移除高赞弹幕或 UP 主弹幕的特殊样式, 弹幕内容不会移除.
|
||||
@ -6,6 +6,38 @@ import {
|
||||
import { ComponentEntry } from '@/components/types'
|
||||
import { playerUrls } from '@/core/utils/urls'
|
||||
import { addComponentListener } from '@/core/settings'
|
||||
import { watchLocalStorage } from '@/core/local-storage'
|
||||
|
||||
enum DanmakuBorderStyle {
|
||||
Heavy,
|
||||
Stroke,
|
||||
Shadow,
|
||||
}
|
||||
const danmakuTextShadowMap = {
|
||||
[DanmakuBorderStyle.Heavy]:
|
||||
'1px 0 1px #000000,0 1px 1px #000000,0 -1px 1px #000000,-1px 0 1px #000000',
|
||||
[DanmakuBorderStyle.Stroke]: '0px 0px 1px #000000,0 0 1px #000000,0 0 1px #000000',
|
||||
[DanmakuBorderStyle.Shadow]: '1px 1px 2px #000000,0 0 1px #000000',
|
||||
}
|
||||
const watchDanmakuBorderSettings = () => {
|
||||
const localStorageKey = 'bpx_player_profile'
|
||||
const updateDanmakuTextShadow = (playerSettings: any) => {
|
||||
const danmakuBorderStyle: DanmakuBorderStyle = lodash.get(
|
||||
playerSettings,
|
||||
'dmSetting.fontborder',
|
||||
)
|
||||
document.documentElement.style.setProperty(
|
||||
'--danmaku-text-shadow',
|
||||
danmakuTextShadowMap[danmakuBorderStyle],
|
||||
)
|
||||
}
|
||||
updateDanmakuTextShadow(JSON.parse(localStorage.getItem(localStorageKey)))
|
||||
watchLocalStorage((key, value) => {
|
||||
if (key === localStorageKey) {
|
||||
updateDanmakuTextShadow(JSON.parse(value))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const options = defineOptionsMetadata({
|
||||
highlight: {
|
||||
@ -16,6 +48,10 @@ const options = defineOptionsMetadata({
|
||||
displayName: '禁用UP主弹幕',
|
||||
defaultValue: true,
|
||||
},
|
||||
vip: {
|
||||
displayName: '禁用大会员弹幕',
|
||||
defaultValue: true,
|
||||
},
|
||||
})
|
||||
|
||||
type Options = OptionsOfMetadata<typeof options>
|
||||
@ -30,6 +66,7 @@ const entry: ComponentEntry<Options> = ({ metadata, settings }) => {
|
||||
true,
|
||||
)
|
||||
})
|
||||
watchDanmakuBorderSettings()
|
||||
}
|
||||
|
||||
const name = 'disableSpecialDanmaku'
|
||||
@ -45,8 +82,5 @@ export const component = defineComponentMetadata({
|
||||
},
|
||||
],
|
||||
urlInclude: playerUrls,
|
||||
description: {
|
||||
'zh-CN': '移除高赞弹幕或 UP 主弹幕的特殊样式, 弹幕内容不会移除.',
|
||||
},
|
||||
options,
|
||||
})
|
||||
|
||||
@ -28,3 +28,12 @@ body.disable-up-danmaku-style {
|
||||
}
|
||||
}
|
||||
}
|
||||
body.disable-vip-danmaku-style {
|
||||
.bili-dm-vip {
|
||||
display: contents !important;
|
||||
text-shadow: inherit !important;
|
||||
}
|
||||
.bili-dm:not([style*='--textShadow']) {
|
||||
--textShadow: var(--danmaku-text-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
/* 保持相簿元素高度 */
|
||||
.album-card {
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
|
||||
/* 上移原互动数据 */
|
||||
.album-card .album-card__info {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
/* 相簿发布时间样式 */
|
||||
.album-pub-time {
|
||||
color: #99a2aa;
|
||||
|
||||
body.dark & {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
margin-top: -5px;
|
||||
}
|
||||
63
registry/lib/components/utils/album-time-show/index.ts
Normal file
63
registry/lib/components/utils/album-time-show/index.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { attributesSubtree, childList } from '@/core/observer'
|
||||
import { select } from '@/core/spin-query'
|
||||
|
||||
let albumList: Element
|
||||
let oldObserver: MutationObserver
|
||||
const observeAlbum = async (node: Element) => {
|
||||
// 相簿更新时会复用原有元素,必须得监听子元素属性变化
|
||||
const [observer] = attributesSubtree(node, () => {
|
||||
// 为所有相簿添加发布时间显示
|
||||
albumList.querySelectorAll('.album-card').forEach(e => {
|
||||
const pubTimeElement = e.querySelector('.album-pub-time')
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const time = (e as any).__vue__.item.ctime
|
||||
|
||||
if (time !== undefined) {
|
||||
if (pubTimeElement !== null) {
|
||||
pubTimeElement.innerHTML = new Date(time * 1000).toLocaleString()
|
||||
} else {
|
||||
e.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
`<div class="album-pub-time">${new Date(time * 1000).toLocaleString()}</div>`,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 移除旧的MutationObserver
|
||||
oldObserver?.disconnect()
|
||||
oldObserver = observer
|
||||
|
||||
const { addImportantStyle } = await import('@/core/style')
|
||||
const { default: style } = await import('./album-time.scss')
|
||||
addImportantStyle(style, 'album-pub-time-style')
|
||||
}
|
||||
const entry = async () => {
|
||||
const spaceContainer = await select('.s-space')
|
||||
childList(spaceContainer, async () => {
|
||||
if (!document.URL.match(/^https:\/\/space\.bilibili\.com\/\d+\/album/)) {
|
||||
return
|
||||
}
|
||||
|
||||
albumList = await select('.album-list__content')
|
||||
observeAlbum(albumList)
|
||||
})
|
||||
}
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'albumPubTimeShow',
|
||||
author: {
|
||||
name: 'Light_Quanta',
|
||||
link: 'https://github.com/LightQuanta',
|
||||
},
|
||||
displayName: '相簿发布时间显示',
|
||||
tags: [componentsTags.utils],
|
||||
urlInclude: [/^https:\/\/space\.bilibili\.com/],
|
||||
entry,
|
||||
description: {
|
||||
'zh-CN': '在相簿界面显示相簿的发布时间',
|
||||
},
|
||||
})
|
||||
207
registry/lib/components/utils/auto-like/blackList.vue
Normal file
207
registry/lib/components/utils/auto-like/blackList.vue
Normal file
@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<VPopup
|
||||
ref="popup"
|
||||
v-model="isOpen"
|
||||
class="like-black-list"
|
||||
fixed
|
||||
:lazy="false"
|
||||
:trigger-element="triggerElement"
|
||||
>
|
||||
<div class="black-list-header">
|
||||
<VIcon class="title-icon" icon="mdi-sort" :size="24"></VIcon>
|
||||
<div class="title">添加黑名单</div>
|
||||
<div class="close" @click="isOpen = false">
|
||||
<VIcon icon="close" :size="18"></VIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="black-list-content">
|
||||
<div class="black-list-section">
|
||||
<div class="black-list-section-title">添加到黑名单</div>
|
||||
<div class="black-list-section-input">
|
||||
<TextBox :text="name" @change="changeName" />
|
||||
<VButton @click="add">添加</VButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="black-list-section">
|
||||
<div class="black-list-section-title">黑名单列表</div>
|
||||
<div class="black-list-section-description">点击×图标可以删除名单.</div>
|
||||
<VLoading v-if="!isLoaded" />
|
||||
<div
|
||||
v-show="isLoaded"
|
||||
ref="black-listSortList"
|
||||
class="black-list-section-content black-list-sort-list"
|
||||
>
|
||||
<div v-for="item of list" :key="item" class="black-list-sort-item" :data-name="item">
|
||||
<div class="item-name">
|
||||
{{ item }}
|
||||
</div>
|
||||
<div class="toggle-visible">
|
||||
<VIcon :size="18" icon="close" @click="toggleVisible(item)"></VIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</VPopup>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { VPopup, TextBox, VIcon, VButton } from '@/ui'
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
VPopup,
|
||||
TextBox,
|
||||
VIcon,
|
||||
VButton,
|
||||
},
|
||||
props: {
|
||||
triggerElement: {
|
||||
type: HTMLElement,
|
||||
default: null,
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
titleName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
isLoaded: false,
|
||||
name: '',
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.isLoaded = true
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$refs.popup.toggle()
|
||||
},
|
||||
changeName(val: string) {
|
||||
this.name = val
|
||||
},
|
||||
add() {
|
||||
if (this.name.length && !this.list.includes(this.name)) {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
this.list.push(this.name)
|
||||
}
|
||||
this.name = ''
|
||||
},
|
||||
toggleVisible(item: string) {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
this.list.splice(this.list.indexOf(item), 1)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'common';
|
||||
.like-black-list {
|
||||
@include popup();
|
||||
width: 400px;
|
||||
font-size: 14px;
|
||||
padding: 12px 12px 12px 18px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%) scale(0.95);
|
||||
transition: 0.2s ease-out;
|
||||
z-index: 100002;
|
||||
&.open {
|
||||
transform: translateX(-50%) translateY(-50%) scale(1);
|
||||
}
|
||||
.black-list-header {
|
||||
@include h-center();
|
||||
justify-content: space-between;
|
||||
.title {
|
||||
margin-left: 6px;
|
||||
font-size: 18px;
|
||||
@include semi-bold();
|
||||
}
|
||||
.grow {
|
||||
flex: 1;
|
||||
}
|
||||
.close {
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease-out;
|
||||
&:hover {
|
||||
color: var(--theme-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.black-list-content {
|
||||
.black-list-section {
|
||||
margin-top: 12px;
|
||||
> :not(:last-child) {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
&-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
&-input {
|
||||
display: flex;
|
||||
div {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
&-description {
|
||||
font-size: 12px;
|
||||
opacity: 0.6;
|
||||
line-height: 1.5;
|
||||
}
|
||||
&-content {
|
||||
@include h-center();
|
||||
flex-wrap: wrap;
|
||||
.be-slider {
|
||||
margin: 0 4px;
|
||||
flex: 1;
|
||||
}
|
||||
.padding-value {
|
||||
margin-left: 12px;
|
||||
width: 50px;
|
||||
text-align: end;
|
||||
}
|
||||
.black-list-sort-item {
|
||||
@include card();
|
||||
@include h-center();
|
||||
transition: none;
|
||||
white-space: nowrap;
|
||||
padding: 6px;
|
||||
padding-left: 8px;
|
||||
margin: 0 4px 4px 0;
|
||||
cursor: move;
|
||||
&:hover {
|
||||
border-color: var(--theme-color);
|
||||
}
|
||||
&.black-list-hidden {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&.sortable-ghost {
|
||||
opacity: 0;
|
||||
}
|
||||
&.sortable-chosen {
|
||||
box-shadow: 0 4px 16px 0 rgb(0 0 0 / 16%);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
&.sortable-drag {
|
||||
opacity: 1;
|
||||
}
|
||||
&.sortable-drag.black-list-hidden {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.toggle-visible {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1 +1,4 @@
|
||||
进入视频 / 查看动态时, 自动点赞.
|
||||
进入视频 / 查看动态时, 自动点赞\
|
||||
无法触发未加载动态的点赞,当启用手动对动态点赞后可手动触发,启用后不会触发动态自动点赞\
|
||||
安装快捷键扩展后可以点击【l】键或【L】键触发\
|
||||
还可以添加动态点赞的黑名单\
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { forEachFeedsCard } from '@/components/feeds/api'
|
||||
import { select } from '@/core/spin-query'
|
||||
import { matchUrlPattern, playerReady } from '@/core/utils'
|
||||
import { feedsUrls, videoAndBangumiUrls } from '@/core/utils/urls'
|
||||
import { KeyBindingAction } from '../keymap/bindings'
|
||||
import { forEachFeedsCard } from '@/components/feeds/api'
|
||||
import { BlackListDataKey, loadlikeButton } from './vm'
|
||||
import { getData, registerData } from '@/plugins/data'
|
||||
|
||||
const feedsLikeQueue = [] as HTMLElement[]
|
||||
export const component = defineComponentMetadata({
|
||||
@ -10,6 +13,10 @@ export const component = defineComponentMetadata({
|
||||
displayName: '自动点赞',
|
||||
tags: [componentsTags.utils, componentsTags.feeds, componentsTags.video],
|
||||
urlInclude: [...videoAndBangumiUrls, ...feedsUrls],
|
||||
author: {
|
||||
name: 'CrazyboyQCD',
|
||||
link: 'https://github.com/CrazyboyQCD',
|
||||
},
|
||||
options: {
|
||||
video: {
|
||||
defaultValue: true,
|
||||
@ -19,35 +26,72 @@ export const component = defineComponentMetadata({
|
||||
defaultValue: true,
|
||||
displayName: '对动态点赞',
|
||||
},
|
||||
manualFeed: {
|
||||
defaultValue: true,
|
||||
displayName: '手动对动态点赞',
|
||||
},
|
||||
users: {
|
||||
displayName: '黑名单',
|
||||
defaultValue: [] as string[],
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
extraOptions: () => import('./settings.vue').then(m => m.default),
|
||||
entry: async ({ settings: { options } }) => {
|
||||
if (options.video && videoAndBangumiUrls.some(url => matchUrlPattern(url))) {
|
||||
await playerReady()
|
||||
const likeButton = (await select(
|
||||
'.video-toolbar .like, .tool-bar .like-info, .video-toolbar-v1 .like',
|
||||
)) as HTMLSpanElement
|
||||
const likeButton = (await select('.video-like')) as HTMLSpanElement
|
||||
if (!likeButton || likeButton.classList.contains('on')) {
|
||||
return
|
||||
}
|
||||
likeButton.click()
|
||||
}
|
||||
if (options.feed && feedsUrls.some(url => matchUrlPattern(url))) {
|
||||
window.setInterval(() => {
|
||||
if (feedsLikeQueue.length === 0) {
|
||||
return
|
||||
}
|
||||
const button = feedsLikeQueue.shift()
|
||||
button?.click()
|
||||
}, 1000)
|
||||
forEachFeedsCard({
|
||||
added: card => {
|
||||
const likeButton = dq(card.element, '.bili-dyn-action.like') as HTMLElement
|
||||
if (!likeButton || likeButton.classList.contains('active')) {
|
||||
const blackListData = {
|
||||
users: options.users,
|
||||
}
|
||||
registerData(BlackListDataKey, blackListData)
|
||||
if (options.manualFeed) {
|
||||
loadlikeButton()
|
||||
} else {
|
||||
window.setInterval(() => {
|
||||
if (feedsLikeQueue.length === 0) {
|
||||
return
|
||||
}
|
||||
feedsLikeQueue.push(likeButton)
|
||||
},
|
||||
})
|
||||
const button = feedsLikeQueue.shift()
|
||||
button?.click()
|
||||
}, 1000)
|
||||
const blackList = getData(BlackListDataKey)
|
||||
forEachFeedsCard({
|
||||
added: card => {
|
||||
if (blackList.includes(card.username)) {
|
||||
return
|
||||
}
|
||||
const likeButtons = dq(card.element, '.bili-dyn-action.like') as HTMLElement
|
||||
if (!likeButtons || likeButtons.classList.contains('active')) {
|
||||
return
|
||||
}
|
||||
feedsLikeQueue.push(likeButtons)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
plugin: {
|
||||
displayName: '点赞 - 手动',
|
||||
setup: ({ addData }) => {
|
||||
addData('keymap.actions', (actions: Record<string, KeyBindingAction>) => {
|
||||
actions.manuallike = {
|
||||
displayName: '手动触发对动态点赞',
|
||||
run: context => {
|
||||
const { clickElement } = context
|
||||
return clickElement('.manual-like', context)
|
||||
},
|
||||
}
|
||||
})
|
||||
addData('keymap.presets', (presetBase: Record<string, string>) => {
|
||||
presetBase.manuallike = 'l L'
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
89
registry/lib/components/utils/auto-like/like.vue
Normal file
89
registry/lib/components/utils/auto-like/like.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<VButton round class="manual-like" @click="like">
|
||||
<div id="text" :class="{ like: isLike }">正在点赞: {{ curLikeCnt }} / {{ totalLikeCnt }}</div>
|
||||
<VIcon v-show="!isLike" colored icon="like" :size="20" />
|
||||
</VButton>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { VButton, VIcon } from '@/ui'
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
VButton,
|
||||
VIcon,
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isClick: false,
|
||||
isLike: false,
|
||||
totalLikeCnt: 0,
|
||||
curLikeCnt: 0,
|
||||
feedsLikeQueue: [] as HTMLElement[],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
like() {
|
||||
if (this.isClick) {
|
||||
return
|
||||
}
|
||||
this.isClick = true
|
||||
// forEachFeedsCard异步执行顺序有问题,不能及时同步,用dqa代替
|
||||
const likeButtons = (
|
||||
Array.from(document.getElementsByClassName('bili-dyn-title__text')) as HTMLElement[]
|
||||
).reduce((buttons, e) => {
|
||||
if (this.list.includes(e.textContent.trim())) {
|
||||
return buttons
|
||||
}
|
||||
const likeButton = e.closest('.bili-dyn-item__main').querySelector('.bili-dyn-action.like')
|
||||
if (likeButton && !likeButton.classList.contains('active')) {
|
||||
buttons.push(likeButton as HTMLElement)
|
||||
}
|
||||
return buttons
|
||||
}, [] as HTMLElement[])
|
||||
this.feedsLikeQueue.push(...likeButtons)
|
||||
this.totalLikeCnt = this.feedsLikeQueue.length
|
||||
this.curLikeCnt = 0
|
||||
this.isLike = true
|
||||
const t = window.setInterval(() => {
|
||||
if (this.feedsLikeQueue.length === 0) {
|
||||
this.isLike = false
|
||||
this.isClick = false
|
||||
clearInterval(t)
|
||||
return
|
||||
}
|
||||
const button = this.feedsLikeQueue.shift()
|
||||
button?.click()
|
||||
this.curLikeCnt++
|
||||
}, 1200)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.manual-like {
|
||||
left: 5px;
|
||||
z-index: 1001;
|
||||
height: 30px;
|
||||
position: fixed;
|
||||
* {
|
||||
color: rgb(251, 114, 153);
|
||||
}
|
||||
#text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 0px;
|
||||
transition: max-width 0.4s linear !important;
|
||||
&.like {
|
||||
max-width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
50
registry/lib/components/utils/auto-like/settings.vue
Normal file
50
registry/lib/components/utils/auto-like/settings.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="custom-black-list-extra-options">
|
||||
<VButton ref="button" @mouseover="setBlackListProps" @click="toggleBlackList">
|
||||
黑名单
|
||||
<VIcon icon="right-arrow" :size="16" />
|
||||
</VButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { VIcon, VButton } from '@/ui'
|
||||
import { loadBlackList, setBlackListProps, toggleBlackList } from './vm'
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
VIcon,
|
||||
VButton,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isFirstLoad: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$refs.popup.toggle()
|
||||
},
|
||||
async setBlackListProps() {
|
||||
if (this.isFirstLoad) {
|
||||
return
|
||||
}
|
||||
this.isFirstLoad = await loadBlackList()
|
||||
if (this.isFirstLoad) {
|
||||
const triggerButton = this.$refs.button.$el as HTMLElement
|
||||
setBlackListProps(triggerButton)
|
||||
}
|
||||
},
|
||||
toggleBlackList,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.custom-black-list-extra-options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
48
registry/lib/components/utils/auto-like/vm.ts
Normal file
48
registry/lib/components/utils/auto-like/vm.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { select } from '@/core/spin-query'
|
||||
import { mountVueComponent } from '@/core/utils'
|
||||
import { getData } from '@/plugins/data'
|
||||
|
||||
export const BlackListDataKey = 'like-black-List.data'
|
||||
|
||||
type SettingsVmType = Vue & {
|
||||
toggle: () => void
|
||||
triggerElement: HTMLElement
|
||||
list: string[]
|
||||
titleName: string
|
||||
}
|
||||
let blacklistVM: SettingsVmType = null
|
||||
|
||||
export const setBlackListProps = (element: HTMLElement) => {
|
||||
if (!blacklistVM) {
|
||||
return
|
||||
}
|
||||
blacklistVM.triggerElement = element
|
||||
const blackList = getData(BlackListDataKey)
|
||||
blacklistVM.list = blackList[0].users
|
||||
blacklistVM.titleName = '黑名单'
|
||||
}
|
||||
export const loadlikeButton = async () => {
|
||||
const LikeButton = await import('./like.vue').then(m => m.default)
|
||||
const blackList = getData(BlackListDataKey)
|
||||
const likebuttonVM: Vue & { list: string[] } = mountVueComponent(LikeButton)
|
||||
likebuttonVM.list = blackList[0].users
|
||||
const bg = (await select('#app')) as HTMLElement
|
||||
bg.insertAdjacentElement('afterbegin', likebuttonVM.$el)
|
||||
}
|
||||
|
||||
export const loadBlackList = async () => {
|
||||
if (blacklistVM) {
|
||||
return false
|
||||
}
|
||||
const blackList = await import('./blackList.vue').then(m => m.default)
|
||||
blacklistVM = mountVueComponent(blackList)
|
||||
document.body.insertAdjacentElement('beforeend', blacklistVM.$el)
|
||||
return true
|
||||
}
|
||||
|
||||
export const toggleBlackList = async () => {
|
||||
if (!blacklistVM) {
|
||||
await loadBlackList()
|
||||
}
|
||||
blacklistVM?.toggle()
|
||||
}
|
||||
@ -132,7 +132,7 @@ export default Vue.extend({
|
||||
.title {
|
||||
margin-left: 6px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
@include semi-bold();
|
||||
}
|
||||
.grow {
|
||||
flex: 1;
|
||||
|
||||
171
registry/lib/components/utils/ip-show/index.ts
Normal file
171
registry/lib/components/utils/ip-show/index.ts
Normal file
@ -0,0 +1,171 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { CommentItem, CommentReplyItem } from '@/components/utils/comment-apis'
|
||||
|
||||
// 新版评论区IP属地获取
|
||||
const getIpLocation = (element: HTMLElement) => {
|
||||
const props = (element as any).__vueParentComponent?.props
|
||||
const reply = props?.reply ?? props?.subReply
|
||||
return reply?.reply_control?.location ?? undefined
|
||||
}
|
||||
|
||||
let version = 2
|
||||
let bbComment: any
|
||||
|
||||
// 带IP属地显示的评论创建
|
||||
const createListCon = function listCon(item, i, pos) {
|
||||
const blCon = this._parentBlacklistDom(item, i, pos)
|
||||
const con = [
|
||||
`<div class="con ${pos === i ? 'no-border' : ''}">`,
|
||||
`<div class="user">${this._createNickNameDom(item)}`,
|
||||
this._createLevelLink(item),
|
||||
this._identity(item.mid, item.assist, item.member.fans_detail),
|
||||
`${this._createNameplate(item.member.nameplate) + this._createUserSailing(item)}</div>`,
|
||||
this._createMsgContent(item),
|
||||
this._createPerfectReply(item),
|
||||
'<div class="info">',
|
||||
this._createPlatformDom(item.content.plat),
|
||||
'<span class="time-location">',
|
||||
'<span class="reply-time">'.concat(this._formateTime(item.ctime), '</span>'),
|
||||
item?.reply_control?.location
|
||||
? `<span class="reply-location">${item?.reply_control?.location || ''}</span>`
|
||||
: '',
|
||||
'</span>',
|
||||
item.lottery_id
|
||||
? ''
|
||||
: `<span class="like ${item.action === 1 ? 'liked' : ''}"><i></i><span>${
|
||||
item.like ? item.like : ''
|
||||
}</span></span>`,
|
||||
item.lottery_id ? '' : `<span class="hate ${item.action === 2 ? 'hated' : ''}"><i></i></span>`,
|
||||
item.lottery_id ? '' : this._createReplyBtn(item.rcount),
|
||||
item.lottery_id && item.mid !== bbComment.userStatus.mid
|
||||
? ''
|
||||
: `<div class="operation more-operation"><div class="spot"></div><div class="opera-list"><ul>${
|
||||
this._canSetTop(item)
|
||||
? `<li class="set-top">${item.isUpTop ? '取消置顶' : '设为置顶'}</li>`
|
||||
: ''
|
||||
}${this._canBlackList(item.mid) ? '<li class="blacklist">加入黑名单</li>' : ''}${
|
||||
this._canReport(item.mid) ? '<li class="report">举报</li>' : ''
|
||||
}${
|
||||
this._canDel(item.mid) && !item.isTop
|
||||
? `<li class="del" data-mid="${item.mid}">删除</li>`
|
||||
: ''
|
||||
}</ul></div></div>`,
|
||||
this._createLotteryContent(item.content),
|
||||
this._createVoteContent(item.content),
|
||||
this._createTags(item),
|
||||
'</div>',
|
||||
'<div class="reply-box">',
|
||||
this._createSubReplyList(
|
||||
item.replies,
|
||||
item.rcount,
|
||||
false,
|
||||
item.rpid,
|
||||
item.folder && item.folder.has_folded,
|
||||
item.reply_control,
|
||||
),
|
||||
'</div>',
|
||||
'<div class="paging-box">',
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join('')
|
||||
return item.state === bbComment.blacklistCode ? blCon : con
|
||||
}
|
||||
|
||||
// 带IP属地显示的回复创建
|
||||
const createSubReplyItem = function subReply(item, i) {
|
||||
const dom = [
|
||||
`<div class="reply-item reply-wrap" data-id="${item.rpid}" data-index="${i}">`,
|
||||
this._createSubReplyUserFace(item),
|
||||
'<div class="reply-con">',
|
||||
'<div class="user">',
|
||||
this._createNickNameDom(item),
|
||||
this._createLevelLink(item),
|
||||
this._identity(item.mid),
|
||||
this._createSubMsgContent(item),
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="info">',
|
||||
'<span class="time-location">',
|
||||
'<span class="reply-time">'.concat(this._formateTime(item.ctime), '</span>'),
|
||||
item?.reply_control?.location
|
||||
? `<span class="reply-location">${item?.reply_control?.location || ''}</span>`
|
||||
: '',
|
||||
'</span>',
|
||||
`<span class="like ${item.action === 1 ? 'liked' : ''}"><i></i><span>${
|
||||
item.like ? item.like : ''
|
||||
}</span></span>`,
|
||||
`<span class="hate ${item.action === 2 ? 'hated' : ''}"><i></i></span>`,
|
||||
'<span class="reply btn-hover">回复</span>',
|
||||
`<div class="operation btn-hover btn-hide-re"><div class="spot"></div><div class="opera-list"><ul>${
|
||||
this._canBlackList(item.mid) ? '<li class="blacklist">加入黑名单</li>' : ''
|
||||
}${this._canReport(item.mid) ? '<li class="report">举报</li>' : ''}${
|
||||
this._canDel(item.mid) ? `<li class="del" data-mid="${item.mid}">删除</li>` : ''
|
||||
}</ul></div></div>`,
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join('')
|
||||
|
||||
return dom
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(mutations => {
|
||||
mutations.forEach(() => {
|
||||
// 监听旧版评论区全局变量
|
||||
if (typeof unsafeWindow.bbComment !== 'undefined') {
|
||||
// 评论创建函数替换
|
||||
bbComment = unsafeWindow.bbComment
|
||||
bbComment.prototype._createListCon = createListCon
|
||||
bbComment.prototype._createSubReplyItem = createSubReplyItem
|
||||
version = 1
|
||||
observer.disconnect()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
observer.observe(document.head, { childList: true })
|
||||
|
||||
const processItems = (items: CommentReplyItem[]) => {
|
||||
items.forEach(item => {
|
||||
const location = getIpLocation(item.element)
|
||||
if (location !== undefined) {
|
||||
const replyTime =
|
||||
item.element.querySelector('.reply-info>.reply-time') ??
|
||||
item.element.querySelector('.sub-reply-info>.sub-reply-time')
|
||||
if (replyTime.childElementCount === 0) {
|
||||
// 避免在评论更新的情况下重复添加
|
||||
const replyLocation = document.createElement('span')
|
||||
replyLocation.style.marginLeft = '5px'
|
||||
replyLocation.innerText = location
|
||||
replyTime.appendChild(replyLocation)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const entry = async () => {
|
||||
const { forEachCommentItem } = await import('@/components/utils/comment-apis')
|
||||
const addIpLocation = (comment: CommentItem) => {
|
||||
if (version === 2) {
|
||||
processItems([comment, ...comment.replies])
|
||||
comment.addEventListener('repliesUpdate', replies => processItems(replies.detail))
|
||||
}
|
||||
}
|
||||
forEachCommentItem({
|
||||
added: addIpLocation,
|
||||
})
|
||||
}
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'ipShow',
|
||||
author: {
|
||||
name: 'Light_Quanta',
|
||||
link: 'https://github.com/LightQuanta',
|
||||
},
|
||||
displayName: '评论区IP属地显示',
|
||||
tags: [componentsTags.utils],
|
||||
entry,
|
||||
description: {
|
||||
'zh-CN': '在评论区显示评论的IP属地信息',
|
||||
},
|
||||
})
|
||||
@ -129,8 +129,8 @@ export default Vue.extend({
|
||||
&-title {
|
||||
flex: 1 0 auto;
|
||||
@include h-center();
|
||||
@include semi-bold();
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
.be-icon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
.rank-container .cm-module,
|
||||
body:not(.preserve-event-banner) .activity-m,
|
||||
body:not(.preserve-event-banner) .activity-m-v1,
|
||||
body:not(.preserve-reply-notice) .reply-notice,
|
||||
body.remove-game-match-module .bili-wrapper > .home-match,
|
||||
body.remove-game-match-module #reportFirst3,
|
||||
.home-content .ad-panel,
|
||||
@ -64,7 +63,7 @@ body:not(.preserve-feed-goods) .dyn-goods {
|
||||
color: #888;
|
||||
background-color: #8882;
|
||||
font-size: 24pt;
|
||||
font-weight: bold;
|
||||
@include semi-bold();
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
@ -81,11 +80,11 @@ body.dark .blocked-ads.new {
|
||||
pointer-events: none !important;
|
||||
opacity: 0 !important;
|
||||
}
|
||||
body:not(.preserve-feed-goods) .bili-dyn-content__orig__additional:has(.dyn-goods) {
|
||||
body:not(.preserve-feed-goods) .bili-dyn-content__orig__additional:has(.dyn-goods) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.bili-video-card__wrap {
|
||||
a[href*="cm.bilibili.com"] {
|
||||
a[href*='cm.bilibili.com'] {
|
||||
pointer-events: none !important;
|
||||
cursor: default !important;
|
||||
|
||||
@ -96,7 +95,7 @@ body:not(.preserve-feed-goods) .bili-dyn-content__orig__additional:has(.dyn-good
|
||||
display: none !important;
|
||||
}
|
||||
body.promotion-show-placeholder &::before {
|
||||
content: "🚫已屏蔽广告";
|
||||
content: '🚫已屏蔽广告';
|
||||
border-radius: 6px;
|
||||
@include h-center();
|
||||
justify-content: center;
|
||||
|
||||
66
registry/lib/components/utils/subscribe-time-show/index.ts
Normal file
66
registry/lib/components/utils/subscribe-time-show/index.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { defineComponentMetadata } from '@/components/define'
|
||||
import { childList } from '@/core/observer'
|
||||
import { select } from '@/core/spin-query'
|
||||
|
||||
let relationList: Element
|
||||
let oldObserver: MutationObserver
|
||||
|
||||
const observeFans = async (node: Element) => {
|
||||
// 监听关注列表元素变化
|
||||
const [observer] = childList(node, () => {
|
||||
// 读取Vue属性里的关注列表
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const subscribeTime = (
|
||||
relationList.parentElement.parentElement.parentElement.parentElement as any
|
||||
).__vue__.relationList.map(l => l.mtime)
|
||||
|
||||
// 为所有子元素添加关注时间显示
|
||||
relationList.querySelectorAll('.list-item>.content').forEach((e, index) => {
|
||||
// 防止重复添加元素
|
||||
if (e.querySelector('.subscribe-time-fix') === null) {
|
||||
const time = subscribeTime[index]
|
||||
if (time !== undefined) {
|
||||
e.querySelector('p').insertAdjacentHTML(
|
||||
'afterend',
|
||||
`<div class="desc subscribe-time-fix">关注时间:${new Date(
|
||||
time * 1000,
|
||||
).toLocaleString()}</div>`,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 移除旧的MutationObserver
|
||||
oldObserver?.disconnect()
|
||||
oldObserver = observer
|
||||
|
||||
const { addImportantStyle } = await import('@/core/style')
|
||||
const { default: style } = await import('./subscribe-time.scss')
|
||||
addImportantStyle(style, 'subscribe-time-style')
|
||||
}
|
||||
const entry = async () => {
|
||||
const spaceContainer = await select('.s-space')
|
||||
childList(spaceContainer, async () => {
|
||||
if (!document.URL.match(/^https:\/\/space\.bilibili\.com\/\d+\/fans/)) {
|
||||
return
|
||||
}
|
||||
relationList = await select('.relation-list')
|
||||
observeFans(relationList)
|
||||
})
|
||||
}
|
||||
|
||||
export const component = defineComponentMetadata({
|
||||
name: 'subscribeTimeShow',
|
||||
author: {
|
||||
name: 'Light_Quanta',
|
||||
link: 'https://github.com/LightQuanta',
|
||||
},
|
||||
displayName: '关注时间显示',
|
||||
tags: [componentsTags.utils],
|
||||
urlInclude: [/^https:\/\/space\.bilibili\.com/],
|
||||
entry,
|
||||
description: {
|
||||
'zh-CN': '在粉丝/关注列表显示关注的具体时间',
|
||||
},
|
||||
})
|
||||
@ -0,0 +1,14 @@
|
||||
/* 上移原名称 */
|
||||
#page-follows .list-item .content .title {
|
||||
margin-top: -9px;
|
||||
}
|
||||
|
||||
/* 上移原简介/官方认证 */
|
||||
#page-follows .list-item .content p {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* 修复关注时间元素的高度带来的布局影响 */
|
||||
.subscribe-time-fix {
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
@ -1 +1 @@
|
||||
自动删除网址中的多余跟踪参数. 请注意这会导致浏览器历史记录出现重复的标题 (分别是转换前后的网址), 并可能导致后退要多退几次.
|
||||
自动删除网址中的多余跟踪参数。请注意这会导致浏览器历史记录出现重复的标题(分别是转换前后的网址),并可能导致后退要多退几次。
|
||||
|
||||
@ -49,6 +49,7 @@ const entry = async () => {
|
||||
'vd_source',
|
||||
'is_story_h5',
|
||||
'buvid',
|
||||
'plat_id',
|
||||
'goFrom',
|
||||
'jumpLinkType',
|
||||
'hasBack',
|
||||
@ -56,6 +57,10 @@ const entry = async () => {
|
||||
'msource',
|
||||
'live_from',
|
||||
'plat_id',
|
||||
'extra_jump_from',
|
||||
'subarea_rank',
|
||||
'popular_rank',
|
||||
'launch_id',
|
||||
]
|
||||
const [blockParams] = registerAndGetData('urlParamsClean.params', builtInBlockParams)
|
||||
const builtInSiteSpecifiedParams = [
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user