style: set eol to lf, code formatting (#7923)
* chore(gitattributes): set eol to lf * chore: git renormalize * style: reformatting * chore: keep eslint prettier plugin consistent on eol
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|||||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,2 +1,3 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
/.yarn/** linguist-vendored
|
/.yarn/** linguist-vendored
|
||||||
/.yarn/releases/* binary
|
/.yarn/releases/* binary
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/#3_others.yml
vendored
2
.github/ISSUE_TEMPLATE/#3_others.yml
vendored
@@ -73,4 +73,4 @@ body:
|
|||||||
id: additional
|
id: additional
|
||||||
attributes:
|
attributes:
|
||||||
label: 附加信息
|
label: 附加信息
|
||||||
description: 任何能让我们对您的问题有更多了解的信息,包括截图或相关链接
|
description: 任何能让我们对您的问题有更多了解的信息,包括截图或相关链接
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/3_others.yml
vendored
2
.github/ISSUE_TEMPLATE/3_others.yml
vendored
@@ -73,4 +73,4 @@ body:
|
|||||||
id: additional
|
id: additional
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional Information
|
label: Additional Information
|
||||||
description: Any other information that could help us better understand your question, including screenshots or relevant links
|
description: Any other information that could help us better understand your question, including screenshots or relevant links
|
||||||
|
|||||||
90
.github/issue-checker.yml
vendored
90
.github/issue-checker.yml
vendored
@@ -9,115 +9,115 @@ labels:
|
|||||||
# skips and removes
|
# skips and removes
|
||||||
- name: skip all
|
- name: skip all
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Aa]ll |)[Ll]abels?"
|
regexes: '[Ss]kip (?:[Aa]ll |)[Ll]abels?'
|
||||||
- name: remove all
|
- name: remove all
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Aa]ll |)[Ll]abels?"
|
regexes: '[Rr]emove (?:[Aa]ll |)[Ll]abels?'
|
||||||
|
|
||||||
- name: skip kind/bug
|
- name: skip kind/bug
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)kind/bug(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)kind/bug(?:`|)'
|
||||||
- name: remove kind/bug
|
- name: remove kind/bug
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)kind/bug(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)kind/bug(?:`|)'
|
||||||
|
|
||||||
- name: skip kind/enhancement
|
- name: skip kind/enhancement
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)kind/enhancement(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)kind/enhancement(?:`|)'
|
||||||
- name: remove kind/enhancement
|
- name: remove kind/enhancement
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)kind/enhancement(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)kind/enhancement(?:`|)'
|
||||||
|
|
||||||
- name: skip kind/question
|
- name: skip kind/question
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)kind/question(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)kind/question(?:`|)'
|
||||||
- name: remove kind/question
|
- name: remove kind/question
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)kind/question(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)kind/question(?:`|)'
|
||||||
|
|
||||||
- name: skip area/Connectivity
|
- name: skip area/Connectivity
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)area/Connectivity(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)area/Connectivity(?:`|)'
|
||||||
- name: remove area/Connectivity
|
- name: remove area/Connectivity
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)area/Connectivity(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)area/Connectivity(?:`|)'
|
||||||
|
|
||||||
- name: skip area/UI/UX
|
- name: skip area/UI/UX
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)area/UI/UX(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)area/UI/UX(?:`|)'
|
||||||
- name: remove area/UI/UX
|
- name: remove area/UI/UX
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)area/UI/UX(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)area/UI/UX(?:`|)'
|
||||||
|
|
||||||
- name: skip kind/documentation
|
- name: skip kind/documentation
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)kind/documentation(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)kind/documentation(?:`|)'
|
||||||
- name: remove kind/documentation
|
- name: remove kind/documentation
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)kind/documentation(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)kind/documentation(?:`|)'
|
||||||
|
|
||||||
- name: skip client:linux
|
- name: skip client:linux
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)client:linux(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)client:linux(?:`|)'
|
||||||
- name: remove client:linux
|
- name: remove client:linux
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)client:linux(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)client:linux(?:`|)'
|
||||||
|
|
||||||
- name: skip client:mac
|
- name: skip client:mac
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)client:mac(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)client:mac(?:`|)'
|
||||||
- name: remove client:mac
|
- name: remove client:mac
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)client:mac(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)client:mac(?:`|)'
|
||||||
|
|
||||||
- name: skip client:win
|
- name: skip client:win
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)client:win(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)client:win(?:`|)'
|
||||||
- name: remove client:win
|
- name: remove client:win
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)client:win(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)client:win(?:`|)'
|
||||||
|
|
||||||
- name: skip sig/Assistant
|
- name: skip sig/Assistant
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)sig/Assistant(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)sig/Assistant(?:`|)'
|
||||||
- name: remove sig/Assistant
|
- name: remove sig/Assistant
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)sig/Assistant(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)sig/Assistant(?:`|)'
|
||||||
|
|
||||||
- name: skip sig/Data
|
- name: skip sig/Data
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)sig/Data(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)sig/Data(?:`|)'
|
||||||
- name: remove sig/Data
|
- name: remove sig/Data
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)sig/Data(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)sig/Data(?:`|)'
|
||||||
|
|
||||||
- name: skip sig/MCP
|
- name: skip sig/MCP
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)sig/MCP(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)sig/MCP(?:`|)'
|
||||||
- name: remove sig/MCP
|
- name: remove sig/MCP
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)sig/MCP(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)sig/MCP(?:`|)'
|
||||||
|
|
||||||
- name: skip sig/RAG
|
- name: skip sig/RAG
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)sig/RAG(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)sig/RAG(?:`|)'
|
||||||
- name: remove sig/RAG
|
- name: remove sig/RAG
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)sig/RAG(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)sig/RAG(?:`|)'
|
||||||
|
|
||||||
- name: skip lgtm
|
- name: skip lgtm
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)lgtm(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)lgtm(?:`|)'
|
||||||
- name: remove lgtm
|
- name: remove lgtm
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)lgtm(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)lgtm(?:`|)'
|
||||||
|
|
||||||
- name: skip License
|
- name: skip License
|
||||||
content:
|
content:
|
||||||
regexes: "[Ss]kip (?:[Ll]abels? |)(?:`|)License(?:`|)"
|
regexes: '[Ss]kip (?:[Ll]abels? |)(?:`|)License(?:`|)'
|
||||||
- name: remove License
|
- name: remove License
|
||||||
content:
|
content:
|
||||||
regexes: "[Rr]emove (?:[Ll]abels? |)(?:`|)License(?:`|)"
|
regexes: '[Rr]emove (?:[Ll]abels? |)(?:`|)License(?:`|)'
|
||||||
|
|
||||||
# `Dev Team`
|
# `Dev Team`
|
||||||
- name: Dev Team
|
- name: Dev Team
|
||||||
@@ -129,7 +129,7 @@ labels:
|
|||||||
# Area labels
|
# Area labels
|
||||||
- name: area/Connectivity
|
- name: area/Connectivity
|
||||||
content: area/Connectivity
|
content: area/Connectivity
|
||||||
regexes: "代理|[Pp]roxy"
|
regexes: '代理|[Pp]roxy'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip area/Connectivity
|
- skip area/Connectivity
|
||||||
@@ -139,7 +139,7 @@ labels:
|
|||||||
|
|
||||||
- name: area/UI/UX
|
- name: area/UI/UX
|
||||||
content: area/UI/UX
|
content: area/UI/UX
|
||||||
regexes: "界面|[Uu][Ii]|重叠|按钮|图标|组件|渲染|菜单|栏目|头像|主题|样式|[Cc][Ss][Ss]"
|
regexes: '界面|[Uu][Ii]|重叠|按钮|图标|组件|渲染|菜单|栏目|头像|主题|样式|[Cc][Ss][Ss]'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip area/UI/UX
|
- skip area/UI/UX
|
||||||
@@ -150,7 +150,7 @@ labels:
|
|||||||
# Kind labels
|
# Kind labels
|
||||||
- name: kind/documentation
|
- name: kind/documentation
|
||||||
content: kind/documentation
|
content: kind/documentation
|
||||||
regexes: "文档|教程|[Dd]oc(s|umentation)|[Rr]eadme"
|
regexes: '文档|教程|[Dd]oc(s|umentation)|[Rr]eadme'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip kind/documentation
|
- skip kind/documentation
|
||||||
@@ -161,7 +161,7 @@ labels:
|
|||||||
# Client labels
|
# Client labels
|
||||||
- name: client:linux
|
- name: client:linux
|
||||||
content: client:linux
|
content: client:linux
|
||||||
regexes: "(?:[Ll]inux|[Uu]buntu|[Dd]ebian)"
|
regexes: '(?:[Ll]inux|[Uu]buntu|[Dd]ebian)'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip client:linux
|
- skip client:linux
|
||||||
@@ -171,7 +171,7 @@ labels:
|
|||||||
|
|
||||||
- name: client:mac
|
- name: client:mac
|
||||||
content: client:mac
|
content: client:mac
|
||||||
regexes: "(?:[Mm]ac|[Mm]acOS|[Oo]SX)"
|
regexes: '(?:[Mm]ac|[Mm]acOS|[Oo]SX)'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip client:mac
|
- skip client:mac
|
||||||
@@ -181,7 +181,7 @@ labels:
|
|||||||
|
|
||||||
- name: client:win
|
- name: client:win
|
||||||
content: client:win
|
content: client:win
|
||||||
regexes: "(?:[Ww]in|[Ww]indows)"
|
regexes: '(?:[Ww]in|[Ww]indows)'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip client:win
|
- skip client:win
|
||||||
@@ -192,7 +192,7 @@ labels:
|
|||||||
# SIG labels
|
# SIG labels
|
||||||
- name: sig/Assistant
|
- name: sig/Assistant
|
||||||
content: sig/Assistant
|
content: sig/Assistant
|
||||||
regexes: "快捷助手|[Aa]ssistant"
|
regexes: '快捷助手|[Aa]ssistant'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip sig/Assistant
|
- skip sig/Assistant
|
||||||
@@ -202,7 +202,7 @@ labels:
|
|||||||
|
|
||||||
- name: sig/Data
|
- name: sig/Data
|
||||||
content: sig/Data
|
content: sig/Data
|
||||||
regexes: "[Ww]ebdav|坚果云|备份|同步|数据|Obsidian|Notion|Joplin|思源"
|
regexes: '[Ww]ebdav|坚果云|备份|同步|数据|Obsidian|Notion|Joplin|思源'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip sig/Data
|
- skip sig/Data
|
||||||
@@ -212,7 +212,7 @@ labels:
|
|||||||
|
|
||||||
- name: sig/MCP
|
- name: sig/MCP
|
||||||
content: sig/MCP
|
content: sig/MCP
|
||||||
regexes: "[Mm][Cc][Pp]"
|
regexes: '[Mm][Cc][Pp]'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip sig/MCP
|
- skip sig/MCP
|
||||||
@@ -222,7 +222,7 @@ labels:
|
|||||||
|
|
||||||
- name: sig/RAG
|
- name: sig/RAG
|
||||||
content: sig/RAG
|
content: sig/RAG
|
||||||
regexes: "知识库|[Rr][Aa][Gg]"
|
regexes: '知识库|[Rr][Aa][Gg]'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip sig/RAG
|
- skip sig/RAG
|
||||||
@@ -233,7 +233,7 @@ labels:
|
|||||||
# Other labels
|
# Other labels
|
||||||
- name: lgtm
|
- name: lgtm
|
||||||
content: lgtm
|
content: lgtm
|
||||||
regexes: "(?:[Ll][Gg][Tt][Mm]|[Ll]ooks [Gg]ood [Tt]o [Mm]e)"
|
regexes: '(?:[Ll][Gg][Tt][Mm]|[Ll]ooks [Gg]ood [Tt]o [Mm]e)'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip lgtm
|
- skip lgtm
|
||||||
@@ -243,7 +243,7 @@ labels:
|
|||||||
|
|
||||||
- name: License
|
- name: License
|
||||||
content: License
|
content: License
|
||||||
regexes: "(?:[Ll]icense|[Cc]opyright|[Mm][Ii][Tt]|[Aa]pache)"
|
regexes: '(?:[Ll]icense|[Cc]opyright|[Mm][Ii][Tt]|[Aa]pache)'
|
||||||
skip-if:
|
skip-if:
|
||||||
- skip all
|
- skip all
|
||||||
- skip License
|
- skip License
|
||||||
|
|||||||
6
.github/workflows/issue-checker.yml
vendored
6
.github/workflows/issue-checker.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: "Issue Checker"
|
name: 'Issue Checker'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: MaaAssistantArknights/issue-checker@v1.14
|
- uses: MaaAssistantArknights/issue-checker@v1.14
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
configuration-path: .github/issue-checker.yml
|
configuration-path: .github/issue-checker.yml
|
||||||
not-before: 2022-08-05T00:00:00Z
|
not-before: 2022-08-05T00:00:00Z
|
||||||
include-title: 1
|
include-title: 1
|
||||||
|
|||||||
20
.github/workflows/issue-management.yml
vendored
20
.github/workflows/issue-management.yml
vendored
@@ -1,8 +1,8 @@
|
|||||||
name: "Stale Issue Management"
|
name: 'Stale Issue Management'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: '0 0 * * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -24,18 +24,18 @@ jobs:
|
|||||||
uses: actions/stale@v9
|
uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
only-labels: "needs-more-info"
|
only-labels: 'needs-more-info'
|
||||||
days-before-stale: ${{ env.daysBeforeStale }}
|
days-before-stale: ${{ env.daysBeforeStale }}
|
||||||
days-before-close: 0 # Close immediately after stale
|
days-before-close: 0 # Close immediately after stale
|
||||||
stale-issue-label: "inactive"
|
stale-issue-label: 'inactive'
|
||||||
close-issue-label: "closed:no-response"
|
close-issue-label: 'closed:no-response'
|
||||||
stale-issue-message: |
|
stale-issue-message: |
|
||||||
This issue has been labeled as needing more information and has been inactive for ${{ env.daysBeforeStale }} days.
|
This issue has been labeled as needing more information and has been inactive for ${{ env.daysBeforeStale }} days.
|
||||||
It will be closed now due to lack of additional information.
|
It will be closed now due to lack of additional information.
|
||||||
|
|
||||||
该问题被标记为"需要更多信息"且已经 ${{ env.daysBeforeStale }} 天没有任何活动,将立即关闭。
|
该问题被标记为"需要更多信息"且已经 ${{ env.daysBeforeStale }} 天没有任何活动,将立即关闭。
|
||||||
operations-per-run: 50
|
operations-per-run: 50
|
||||||
exempt-issue-labels: "pending, Dev Team"
|
exempt-issue-labels: 'pending, Dev Team'
|
||||||
days-before-pr-stale: -1
|
days-before-pr-stale: -1
|
||||||
days-before-pr-close: -1
|
days-before-pr-close: -1
|
||||||
|
|
||||||
@@ -45,11 +45,11 @@ jobs:
|
|||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-stale: ${{ env.daysBeforeStale }}
|
days-before-stale: ${{ env.daysBeforeStale }}
|
||||||
days-before-close: ${{ env.daysBeforeClose }}
|
days-before-close: ${{ env.daysBeforeClose }}
|
||||||
stale-issue-label: "inactive"
|
stale-issue-label: 'inactive'
|
||||||
stale-issue-message: |
|
stale-issue-message: |
|
||||||
This issue has been inactive for a prolonged period and will be closed automatically in ${{ env.daysBeforeClose }} days.
|
This issue has been inactive for a prolonged period and will be closed automatically in ${{ env.daysBeforeClose }} days.
|
||||||
该问题已长时间处于闲置状态,${{ env.daysBeforeClose }} 天后将自动关闭。
|
该问题已长时间处于闲置状态,${{ env.daysBeforeClose }} 天后将自动关闭。
|
||||||
exempt-issue-labels: "pending, Dev Team, kind/enhancement"
|
exempt-issue-labels: 'pending, Dev Team, kind/enhancement'
|
||||||
days-before-pr-stale: -1 # Completely disable stalling for PRs
|
days-before-pr-stale: -1 # Completely disable stalling for PRs
|
||||||
days-before-pr-close: -1 # Completely disable closing for PRs
|
days-before-pr-close: -1 # Completely disable closing for PRs
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -117,4 +117,4 @@ jobs:
|
|||||||
makeLatest: false
|
makeLatest: false
|
||||||
tag: ${{ steps.get-tag.outputs.tag }}
|
tag: ${{ steps.get-tag.outputs.tag }}
|
||||||
artifacts: 'dist/*.exe,dist/*.zip,dist/*.dmg,dist/*.AppImage,dist/*.snap,dist/*.deb,dist/*.rpm,dist/*.tar.gz,dist/latest*.yml,dist/rc*.yml,dist/*.blockmap'
|
artifacts: 'dist/*.exe,dist/*.zip,dist/*.dmg,dist/*.AppImage,dist/*.snap,dist/*.deb,dist/*.rpm,dist/*.tar.gz,dist/latest*.yml,dist/rc*.yml,dist/*.blockmap'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
12788
.yarn/patches/@google-genai-npm-1.0.1-e26f0f9af7.patch
vendored
12788
.yarn/patches/@google-genai-npm-1.0.1-e26f0f9af7.patch
vendored
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ export default defineConfig([
|
|||||||
'simple-import-sort/exports': 'error',
|
'simple-import-sort/exports': 'error',
|
||||||
'unused-imports/no-unused-imports': 'error',
|
'unused-imports/no-unused-imports': 'error',
|
||||||
'@eslint-react/no-prop-types': 'error',
|
'@eslint-react/no-prop-types': 'error',
|
||||||
'prettier/prettier': ['error', { endOfLine: 'auto' }]
|
'prettier/prettier': ['error']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Configuration for ensuring compatibility with the original ESLint(8.x) rules
|
// Configuration for ensuring compatibility with the original ESLint(8.x) rules
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
import { IpcChannel } from '@shared/IpcChannel'
|
import { IpcChannel } from '@shared/IpcChannel'
|
||||||
import { ThemeMode } from '@types'
|
import { ThemeMode } from '@types'
|
||||||
import { BrowserWindow, nativeTheme } from 'electron'
|
import { BrowserWindow, nativeTheme } from 'electron'
|
||||||
|
|
||||||
import { titleBarOverlayDark, titleBarOverlayLight } from '../config'
|
import { titleBarOverlayDark, titleBarOverlayLight } from '../config'
|
||||||
import { configManager } from './ConfigManager'
|
import { configManager } from './ConfigManager'
|
||||||
|
|
||||||
class ThemeService {
|
class ThemeService {
|
||||||
private theme: ThemeMode = ThemeMode.system
|
private theme: ThemeMode = ThemeMode.system
|
||||||
constructor() {
|
constructor() {
|
||||||
this.theme = configManager.getTheme()
|
this.theme = configManager.getTheme()
|
||||||
|
|
||||||
if (this.theme === ThemeMode.dark || this.theme === ThemeMode.light || this.theme === ThemeMode.system) {
|
if (this.theme === ThemeMode.dark || this.theme === ThemeMode.light || this.theme === ThemeMode.system) {
|
||||||
nativeTheme.themeSource = this.theme
|
nativeTheme.themeSource = this.theme
|
||||||
} else {
|
} else {
|
||||||
// 兼容旧版本
|
// 兼容旧版本
|
||||||
configManager.setTheme(ThemeMode.system)
|
configManager.setTheme(ThemeMode.system)
|
||||||
nativeTheme.themeSource = ThemeMode.system
|
nativeTheme.themeSource = ThemeMode.system
|
||||||
}
|
}
|
||||||
nativeTheme.on('updated', this.themeUpdatadHandler.bind(this))
|
nativeTheme.on('updated', this.themeUpdatadHandler.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
themeUpdatadHandler() {
|
themeUpdatadHandler() {
|
||||||
BrowserWindow.getAllWindows().forEach((win) => {
|
BrowserWindow.getAllWindows().forEach((win) => {
|
||||||
if (win && !win.isDestroyed() && win.setTitleBarOverlay) {
|
if (win && !win.isDestroyed() && win.setTitleBarOverlay) {
|
||||||
try {
|
try {
|
||||||
win.setTitleBarOverlay(nativeTheme.shouldUseDarkColors ? titleBarOverlayDark : titleBarOverlayLight)
|
win.setTitleBarOverlay(nativeTheme.shouldUseDarkColors ? titleBarOverlayDark : titleBarOverlayLight)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// don't throw error if setTitleBarOverlay failed
|
// don't throw error if setTitleBarOverlay failed
|
||||||
// Because it may be called with some windows have some title bar
|
// Because it may be called with some windows have some title bar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
win.webContents.send(IpcChannel.ThemeUpdated, nativeTheme.shouldUseDarkColors ? ThemeMode.dark : ThemeMode.light)
|
win.webContents.send(IpcChannel.ThemeUpdated, nativeTheme.shouldUseDarkColors ? ThemeMode.dark : ThemeMode.light)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setTheme(theme: ThemeMode) {
|
setTheme(theme: ThemeMode) {
|
||||||
if (theme === this.theme) {
|
if (theme === this.theme) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.theme = theme
|
this.theme = theme
|
||||||
nativeTheme.themeSource = theme
|
nativeTheme.themeSource = theme
|
||||||
configManager.setTheme(theme)
|
configManager.setTheme(theme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const themeService = new ThemeService()
|
export const themeService = new ThemeService()
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import { BrowserWindow } from 'electron'
|
import { BrowserWindow } from 'electron'
|
||||||
|
|
||||||
import { configManager } from '../services/ConfigManager'
|
import { configManager } from '../services/ConfigManager'
|
||||||
|
|
||||||
export function handleZoomFactor(wins: BrowserWindow[], delta: number, reset: boolean = false) {
|
export function handleZoomFactor(wins: BrowserWindow[], delta: number, reset: boolean = false) {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
wins.forEach((win) => {
|
wins.forEach((win) => {
|
||||||
win.webContents.setZoomFactor(1)
|
win.webContents.setZoomFactor(1)
|
||||||
})
|
})
|
||||||
configManager.setZoomFactor(1)
|
configManager.setZoomFactor(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delta === 0) {
|
if (delta === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentZoom = configManager.getZoomFactor()
|
const currentZoom = configManager.getZoomFactor()
|
||||||
const newZoom = Number((currentZoom + delta).toFixed(1))
|
const newZoom = Number((currentZoom + delta).toFixed(1))
|
||||||
if (newZoom >= 0.5 && newZoom <= 2.0) {
|
if (newZoom >= 0.5 && newZoom <= 2.0) {
|
||||||
wins.forEach((win) => {
|
wins.forEach((win) => {
|
||||||
win.webContents.setZoomFactor(newZoom)
|
win.webContents.setZoomFactor(newZoom)
|
||||||
})
|
})
|
||||||
configManager.setZoomFactor(newZoom)
|
configManager.setZoomFactor(newZoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,45 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' 'unsafe-inline' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
||||||
|
<title>Cherry Studio</title>
|
||||||
|
|
||||||
<head>
|
<style>
|
||||||
<meta charset="UTF-8" />
|
html,
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
body {
|
||||||
<meta http-equiv="Content-Security-Policy"
|
margin: 0;
|
||||||
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' 'unsafe-inline' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
}
|
||||||
<title>Cherry Studio</title>
|
|
||||||
|
|
||||||
<style>
|
#spinner {
|
||||||
html,
|
position: fixed;
|
||||||
body {
|
width: 100vw;
|
||||||
margin: 0;
|
height: 100vh;
|
||||||
}
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
#spinner {
|
#spinner img {
|
||||||
position: fixed;
|
width: 100px;
|
||||||
width: 100vw;
|
border-radius: 50px;
|
||||||
height: 100vh;
|
}
|
||||||
flex-direction: row;
|
</style>
|
||||||
justify-content: center;
|
</head>
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
#spinner img {
|
<body>
|
||||||
width: 100px;
|
<div id="root"></div>
|
||||||
border-radius: 50px;
|
<div id="spinner">
|
||||||
}
|
<img src="/src/assets/images/logo.png" />
|
||||||
</style>
|
</div>
|
||||||
</head>
|
<script>
|
||||||
|
console.time('init')
|
||||||
<body>
|
</script>
|
||||||
<div id="root"></div>
|
<script type="module" src="/src/init.ts"></script>
|
||||||
<div id="spinner">
|
<script type="module" src="/src/entryPoint.tsx"></script>
|
||||||
<img src="/src/assets/images/logo.png" />
|
</body>
|
||||||
</div>
|
</html>
|
||||||
<script>
|
|
||||||
console.time('init')
|
|
||||||
</script>
|
|
||||||
<script type="module" src="/src/init.ts"></script>
|
|
||||||
<script type="module" src="/src/entryPoint.tsx"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
||||||
|
<title>Cherry Studio</title>
|
||||||
|
|
||||||
<head>
|
<style>
|
||||||
<meta charset="UTF-8" />
|
html,
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
body {
|
||||||
<meta http-equiv="Content-Security-Policy"
|
margin: 0;
|
||||||
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
}
|
||||||
<title>Cherry Studio</title>
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
<style>
|
<body>
|
||||||
html,
|
<div id="root"></div>
|
||||||
body {
|
<script type="module" src="/src/windows/mini/entryPoint.tsx"></script>
|
||||||
margin: 0;
|
</body>
|
||||||
}
|
</html>
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/windows/mini/entryPoint.tsx"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,41 +1,39 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
<meta http-equiv="Content-Security-Policy"
|
<meta
|
||||||
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
||||||
<title>Cherry Studio Selection Assistant</title>
|
<title>Cherry Studio Selection Assistant</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
</head>
|
<body>
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/windows/selection/action/entryPoint.tsx"></script>
|
<script type="module" src="/src/windows/selection/action/entryPoint.tsx"></script>
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</body>
|
</body>
|
||||||
|
</html>
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,46 +1,43 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
||||||
|
<title>Cherry Studio Selection Toolbar</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body>
|
||||||
<meta charset="UTF-8" />
|
<div id="root"></div>
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
<script type="module" src="/src/windows/selection/toolbar/entryPoint.tsx"></script>
|
||||||
<meta http-equiv="Content-Security-Policy"
|
<style>
|
||||||
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
|
html {
|
||||||
<title>Cherry Studio Selection Toolbar</title>
|
margin: 0 !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
background-image: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
</head>
|
body {
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
width: 100vw !important;
|
||||||
|
height: 100vh !important;
|
||||||
|
|
||||||
<body>
|
-webkit-user-select: none;
|
||||||
<div id="root"></div>
|
-moz-user-select: none;
|
||||||
<script type="module" src="/src/windows/selection/toolbar/entryPoint.tsx"></script>
|
-ms-user-select: none;
|
||||||
<style>
|
user-select: none;
|
||||||
html {
|
}
|
||||||
margin: 0 !important;
|
|
||||||
background-color: transparent !important;
|
|
||||||
background-image: none !important;
|
|
||||||
|
|
||||||
}
|
#root {
|
||||||
|
margin: 0 !important;
|
||||||
body {
|
padding: 0 !important;
|
||||||
margin: 0 !important;
|
width: max-content !important;
|
||||||
padding: 0 !important;
|
height: fit-content !important;
|
||||||
overflow: hidden !important;
|
}
|
||||||
width: 100vw !important;
|
</style>
|
||||||
height: 100vh !important;
|
</body>
|
||||||
|
</html>
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#root {
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
width: max-content !important;
|
|
||||||
height: fit-content !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Twemoji Country Flags';
|
font-family: 'Twemoji Country Flags';
|
||||||
unicode-range:
|
unicode-range:
|
||||||
U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
|
U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
|
||||||
/*https://github.com/beyondkmp/country-flag-emoji-polyfill/blob/master/font/TwemojiCountryFlags.woff2 */
|
/*https://github.com/beyondkmp/country-flag-emoji-polyfill/blob/master/font/TwemojiCountryFlags.woff2 */
|
||||||
src: url('TwemojiCountryFlags.woff2') format('woff2');
|
src: url('TwemojiCountryFlags.woff2') format('woff2');
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 国旗字体样式类 */
|
/* 国旗字体样式类 */
|
||||||
.country-flag-font {
|
.country-flag-font {
|
||||||
font-family: 'Twemoji Country Flags', 'Apple Color Emoji', 'Segoe UI Emoji', sans-serif;
|
font-family: 'Twemoji Country Flags', 'Apple Color Emoji', 'Segoe UI Emoji', sans-serif;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
:root {
|
:root {
|
||||||
--font-family:
|
--font-family:
|
||||||
Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Roboto, Oxygen, Cantarell, 'Open Sans',
|
Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Roboto, Oxygen, Cantarell, 'Open Sans',
|
||||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||||
'Noto Color Emoji';
|
'Noto Color Emoji';
|
||||||
|
|
||||||
--font-family-serif:
|
--font-family-serif:
|
||||||
serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Ubuntu, Roboto, Oxygen, Cantarell, 'Open Sans',
|
serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Ubuntu, Roboto, Oxygen, Cantarell, 'Open Sans',
|
||||||
'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
|
|
||||||
--code-font-family: 'Cascadia Code', 'Fira Code', 'Consolas', Menlo, Courier, monospace;
|
--code-font-family: 'Cascadia Code', 'Fira Code', 'Consolas', Menlo, Courier, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Windows系统专用字体配置
|
// Windows系统专用字体配置
|
||||||
body[os='windows'] {
|
body[os='windows'] {
|
||||||
--font-family:
|
--font-family:
|
||||||
'Twemoji Country Flags', Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Roboto, Oxygen,
|
'Twemoji Country Flags', Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Roboto, Oxygen,
|
||||||
Cantarell, 'Open Sans', 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
Cantarell, 'Open Sans', 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1,178 +1,178 @@
|
|||||||
import type { MCPServer } from '@renderer/types'
|
import type { MCPServer } from '@renderer/types'
|
||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
|
|
||||||
// Token storage constants and utilities
|
// Token storage constants and utilities
|
||||||
const TOKEN_STORAGE_KEY = 'tokenLanyunToken'
|
const TOKEN_STORAGE_KEY = 'tokenLanyunToken'
|
||||||
export const TOKENLANYUN_HOST = 'https://mcp.lanyun.net'
|
export const TOKENLANYUN_HOST = 'https://mcp.lanyun.net'
|
||||||
export const LANYUN_MCP_HOST = TOKENLANYUN_HOST + '/mcp/manager/selectListByApiKey'
|
export const LANYUN_MCP_HOST = TOKENLANYUN_HOST + '/mcp/manager/selectListByApiKey'
|
||||||
export const LANYUN_KEY_HOST = TOKENLANYUN_HOST + '/#/manage/apiKey'
|
export const LANYUN_KEY_HOST = TOKENLANYUN_HOST + '/#/manage/apiKey'
|
||||||
|
|
||||||
export const saveTokenLanYunToken = (token: string): void => {
|
export const saveTokenLanYunToken = (token: string): void => {
|
||||||
localStorage.setItem(TOKEN_STORAGE_KEY, token)
|
localStorage.setItem(TOKEN_STORAGE_KEY, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTokenLanYunToken = (): string | null => {
|
export const getTokenLanYunToken = (): string | null => {
|
||||||
return localStorage.getItem(TOKEN_STORAGE_KEY)
|
return localStorage.getItem(TOKEN_STORAGE_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const clearTokenLanYunToken = (): void => {
|
export const clearTokenLanYunToken = (): void => {
|
||||||
localStorage.removeItem(TOKEN_STORAGE_KEY)
|
localStorage.removeItem(TOKEN_STORAGE_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hasTokenLanYunToken = (): boolean => {
|
export const hasTokenLanYunToken = (): boolean => {
|
||||||
return !!getTokenLanYunToken()
|
return !!getTokenLanYunToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TokenLanYunServer {
|
interface TokenLanYunServer {
|
||||||
id: string
|
id: string
|
||||||
/**
|
/**
|
||||||
* locales 字段用于存储多语言信息。
|
* locales 字段用于存储多语言信息。
|
||||||
* 其中 key(lang)为语言代码(如 'zh', 'en'),
|
* 其中 key(lang)为语言代码(如 'zh', 'en'),
|
||||||
* value 为该语言下的 name 和 description。
|
* value 为该语言下的 name 和 description。
|
||||||
* 例如:
|
* 例如:
|
||||||
* {
|
* {
|
||||||
* "zh": { name: "文档处理工具", description: "..." },
|
* "zh": { name: "文档处理工具", description: "..." },
|
||||||
* "en": { name: "Document Processor", description: "..." }
|
* "en": { name: "Document Processor", description: "..." }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
locales?: {
|
locales?: {
|
||||||
[lang: string]: {
|
[lang: string]: {
|
||||||
description?: string
|
description?: string
|
||||||
name?: string
|
name?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chineseName?: string
|
chineseName?: string
|
||||||
description?: string
|
description?: string
|
||||||
operationalUrls?: { url: string }[]
|
operationalUrls?: { url: string }[]
|
||||||
tags?: string[]
|
tags?: string[]
|
||||||
logoUrl?: string
|
logoUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TokenLanYunSyncResult {
|
interface TokenLanYunSyncResult {
|
||||||
success: boolean
|
success: boolean
|
||||||
message: string
|
message: string
|
||||||
addedServers: MCPServer[]
|
addedServers: MCPServer[]
|
||||||
errorDetails?: string
|
errorDetails?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to fetch and process TokenLanYun servers
|
// Function to fetch and process TokenLanYun servers
|
||||||
export const syncTokenLanYunServers = async (
|
export const syncTokenLanYunServers = async (
|
||||||
token: string,
|
token: string,
|
||||||
existingServers: MCPServer[]
|
existingServers: MCPServer[]
|
||||||
): Promise<TokenLanYunSyncResult> => {
|
): Promise<TokenLanYunSyncResult> => {
|
||||||
const t = i18next.t
|
const t = i18next.t
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(LANYUN_MCP_HOST, {
|
const response = await fetch(LANYUN_MCP_HOST, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Authorization: `Bearer ${token}`
|
Authorization: `Bearer ${token}`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handle authentication errors
|
// Handle authentication errors
|
||||||
if (response.status === 401 || response.status === 403) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
clearTokenLanYunToken()
|
clearTokenLanYunToken()
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'),
|
message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'),
|
||||||
addedServers: []
|
addedServers: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle server errors
|
// Handle server errors
|
||||||
if (response.status === 500 || !response.ok) {
|
if (response.status === 500 || !response.ok) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: t('settings.mcp.sync.error'),
|
message: t('settings.mcp.sync.error'),
|
||||||
addedServers: [],
|
addedServers: [],
|
||||||
errorDetails: `Status: ${response.status}`
|
errorDetails: `Status: ${response.status}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process successful response
|
// Process successful response
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
if (data.code === 401) {
|
if (data.code === 401) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'),
|
message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'),
|
||||||
addedServers: [],
|
addedServers: [],
|
||||||
errorDetails: `Status: ${response.status}`
|
errorDetails: `Status: ${response.status}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.code === 500) {
|
if (data.code === 500) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: t('settings.mcp.sync.error'),
|
message: t('settings.mcp.sync.error'),
|
||||||
addedServers: [],
|
addedServers: [],
|
||||||
errorDetails: `Status: ${response.status}`
|
errorDetails: `Status: ${response.status}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const servers: TokenLanYunServer[] = data.data || []
|
const servers: TokenLanYunServer[] = data.data || []
|
||||||
|
|
||||||
if (servers.length === 0) {
|
if (servers.length === 0) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: t('settings.mcp.sync.noServersAvailable', 'No MCP servers available'),
|
message: t('settings.mcp.sync.noServersAvailable', 'No MCP servers available'),
|
||||||
addedServers: []
|
addedServers: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform Token servers to MCP servers format
|
// Transform Token servers to MCP servers format
|
||||||
const addedServers: MCPServer[] = []
|
const addedServers: MCPServer[] = []
|
||||||
console.log('TokenLanYun servers:', servers)
|
console.log('TokenLanYun servers:', servers)
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
try {
|
try {
|
||||||
if (!server.operationalUrls?.[0]?.url) continue
|
if (!server.operationalUrls?.[0]?.url) continue
|
||||||
|
|
||||||
// If any existing server id contains '@lanyun', clear them before adding new ones
|
// If any existing server id contains '@lanyun', clear them before adding new ones
|
||||||
// if (existingServers.some((s) => s.id.startsWith('@lanyun'))) {
|
// if (existingServers.some((s) => s.id.startsWith('@lanyun'))) {
|
||||||
// for (let i = existingServers.length - 1; i >= 0; i--) {
|
// for (let i = existingServers.length - 1; i >= 0; i--) {
|
||||||
// if (existingServers[i].id.startsWith('@lanyun')) {
|
// if (existingServers[i].id.startsWith('@lanyun')) {
|
||||||
// existingServers.splice(i, 1)
|
// existingServers.splice(i, 1)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// Skip if server already exists after clearing
|
// Skip if server already exists after clearing
|
||||||
if (existingServers.some((s) => s.id === `@lanyun/${server.id}`)) continue
|
if (existingServers.some((s) => s.id === `@lanyun/${server.id}`)) continue
|
||||||
|
|
||||||
const mcpServer: MCPServer = {
|
const mcpServer: MCPServer = {
|
||||||
id: `@lanyun/${server.id}`,
|
id: `@lanyun/${server.id}`,
|
||||||
name:
|
name:
|
||||||
server.chineseName || server.locales?.zh?.name || server.locales?.en?.name || `LanYun Server ${server.id}`,
|
server.chineseName || server.locales?.zh?.name || server.locales?.en?.name || `LanYun Server ${server.id}`,
|
||||||
description: server.description || '',
|
description: server.description || '',
|
||||||
type: 'sse',
|
type: 'sse',
|
||||||
baseUrl: server.operationalUrls[0].url,
|
baseUrl: server.operationalUrls[0].url,
|
||||||
command: '',
|
command: '',
|
||||||
args: [],
|
args: [],
|
||||||
env: {},
|
env: {},
|
||||||
isActive: true,
|
isActive: true,
|
||||||
provider: '蓝耘科技',
|
provider: '蓝耘科技',
|
||||||
providerUrl: server.operationalUrls[0].url,
|
providerUrl: server.operationalUrls[0].url,
|
||||||
logoUrl: server.logoUrl || '',
|
logoUrl: server.logoUrl || '',
|
||||||
tags: server.tags ?? (server.chineseName ? [server.chineseName] : [])
|
tags: server.tags ?? (server.chineseName ? [server.chineseName] : [])
|
||||||
}
|
}
|
||||||
|
|
||||||
addedServers.push(mcpServer)
|
addedServers.push(mcpServer)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error processing LanYun server:', err)
|
console.error('Error processing LanYun server:', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: t('settings.mcp.sync.success', { count: addedServers.length }),
|
message: t('settings.mcp.sync.success', { count: addedServers.length }),
|
||||||
addedServers
|
addedServers
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('TokenLanyun sync error:', error)
|
console.error('TokenLanyun sync error:', error)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: t('settings.mcp.sync.error'),
|
message: t('settings.mcp.sync.error'),
|
||||||
addedServers: [],
|
addedServers: [],
|
||||||
errorDetails: String(error)
|
errorDetails: String(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,216 +1,216 @@
|
|||||||
import Logger from '@renderer/config/logger'
|
import Logger from '@renderer/config/logger'
|
||||||
import { FileMetadata } from '@renderer/types'
|
import { FileMetadata } from '@renderer/types'
|
||||||
import { getFileExtension } from '@renderer/utils'
|
import { getFileExtension } from '@renderer/utils'
|
||||||
|
|
||||||
// Track last focused component
|
// Track last focused component
|
||||||
type ComponentType = 'inputbar' | 'messageEditor' | null
|
type ComponentType = 'inputbar' | 'messageEditor' | null
|
||||||
let lastFocusedComponent: ComponentType = 'inputbar' // Default to inputbar
|
let lastFocusedComponent: ComponentType = 'inputbar' // Default to inputbar
|
||||||
|
|
||||||
// 处理函数类型
|
// 处理函数类型
|
||||||
type PasteHandler = (event: ClipboardEvent) => Promise<boolean>
|
type PasteHandler = (event: ClipboardEvent) => Promise<boolean>
|
||||||
|
|
||||||
// 处理函数存储
|
// 处理函数存储
|
||||||
const handlers: {
|
const handlers: {
|
||||||
inputbar?: PasteHandler
|
inputbar?: PasteHandler
|
||||||
messageEditor?: PasteHandler
|
messageEditor?: PasteHandler
|
||||||
} = {}
|
} = {}
|
||||||
|
|
||||||
// 初始化标志
|
// 初始化标志
|
||||||
let isInitialized = false
|
let isInitialized = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理粘贴事件的通用服务
|
* 处理粘贴事件的通用服务
|
||||||
* 处理各种粘贴场景,包括文本和文件
|
* 处理各种粘贴场景,包括文本和文件
|
||||||
*/
|
*/
|
||||||
export const handlePaste = async (
|
export const handlePaste = async (
|
||||||
event: ClipboardEvent,
|
event: ClipboardEvent,
|
||||||
isVisionModel: boolean,
|
isVisionModel: boolean,
|
||||||
isGenerateImageModel: boolean,
|
isGenerateImageModel: boolean,
|
||||||
supportExts: string[],
|
supportExts: string[],
|
||||||
setFiles: (updater: (prevFiles: FileMetadata[]) => FileMetadata[]) => void,
|
setFiles: (updater: (prevFiles: FileMetadata[]) => FileMetadata[]) => void,
|
||||||
setText?: (text: string) => void,
|
setText?: (text: string) => void,
|
||||||
pasteLongTextAsFile?: boolean,
|
pasteLongTextAsFile?: boolean,
|
||||||
pasteLongTextThreshold?: number,
|
pasteLongTextThreshold?: number,
|
||||||
text?: string,
|
text?: string,
|
||||||
resizeTextArea?: () => void,
|
resizeTextArea?: () => void,
|
||||||
t?: (key: string) => string
|
t?: (key: string) => string
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
// 优先处理文本粘贴
|
// 优先处理文本粘贴
|
||||||
const clipboardText = event.clipboardData?.getData('text')
|
const clipboardText = event.clipboardData?.getData('text')
|
||||||
if (clipboardText) {
|
if (clipboardText) {
|
||||||
// 1. 文本粘贴
|
// 1. 文本粘贴
|
||||||
if (pasteLongTextAsFile && pasteLongTextThreshold && clipboardText.length > pasteLongTextThreshold) {
|
if (pasteLongTextAsFile && pasteLongTextThreshold && clipboardText.length > pasteLongTextThreshold) {
|
||||||
// 长文本直接转文件,阻止默认粘贴
|
// 长文本直接转文件,阻止默认粘贴
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
const tempFilePath = await window.api.file.createTempFile('pasted_text.txt')
|
const tempFilePath = await window.api.file.createTempFile('pasted_text.txt')
|
||||||
await window.api.file.write(tempFilePath, clipboardText)
|
await window.api.file.write(tempFilePath, clipboardText)
|
||||||
const selectedFile = await window.api.file.get(tempFilePath)
|
const selectedFile = await window.api.file.get(tempFilePath)
|
||||||
if (selectedFile) {
|
if (selectedFile) {
|
||||||
setFiles((prevFiles) => [...prevFiles, selectedFile])
|
setFiles((prevFiles) => [...prevFiles, selectedFile])
|
||||||
if (setText && text) setText(text) // 保持输入框内容不变
|
if (setText && text) setText(text) // 保持输入框内容不变
|
||||||
if (resizeTextArea) setTimeout(() => resizeTextArea(), 50)
|
if (resizeTextArea) setTimeout(() => resizeTextArea(), 50)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// 短文本走默认粘贴行为,直接返回
|
// 短文本走默认粘贴行为,直接返回
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 文件/图片粘贴(仅在无文本时处理)
|
// 2. 文件/图片粘贴(仅在无文本时处理)
|
||||||
if (event.clipboardData?.files && event.clipboardData.files.length > 0) {
|
if (event.clipboardData?.files && event.clipboardData.files.length > 0) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
try {
|
try {
|
||||||
for (const file of event.clipboardData.files) {
|
for (const file of event.clipboardData.files) {
|
||||||
// 使用新的API获取文件路径
|
// 使用新的API获取文件路径
|
||||||
const filePath = window.api.file.getPathForFile(file)
|
const filePath = window.api.file.getPathForFile(file)
|
||||||
|
|
||||||
// 如果没有路径,可能是剪贴板中的图像数据
|
// 如果没有路径,可能是剪贴板中的图像数据
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
// 图像生成也支持图像编辑
|
// 图像生成也支持图像编辑
|
||||||
if (file.type.startsWith('image/') && (isVisionModel || isGenerateImageModel)) {
|
if (file.type.startsWith('image/') && (isVisionModel || isGenerateImageModel)) {
|
||||||
const tempFilePath = await window.api.file.createTempFile(file.name)
|
const tempFilePath = await window.api.file.createTempFile(file.name)
|
||||||
const arrayBuffer = await file.arrayBuffer()
|
const arrayBuffer = await file.arrayBuffer()
|
||||||
const uint8Array = new Uint8Array(arrayBuffer)
|
const uint8Array = new Uint8Array(arrayBuffer)
|
||||||
await window.api.file.write(tempFilePath, uint8Array)
|
await window.api.file.write(tempFilePath, uint8Array)
|
||||||
const selectedFile = await window.api.file.get(tempFilePath)
|
const selectedFile = await window.api.file.get(tempFilePath)
|
||||||
if (selectedFile) {
|
if (selectedFile) {
|
||||||
setFiles((prevFiles) => [...prevFiles, selectedFile])
|
setFiles((prevFiles) => [...prevFiles, selectedFile])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (t) {
|
if (t) {
|
||||||
window.message.info({
|
window.message.info({
|
||||||
key: 'file_not_supported',
|
key: 'file_not_supported',
|
||||||
content: t('chat.input.file_not_supported')
|
content: t('chat.input.file_not_supported')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 有路径的情况
|
// 有路径的情况
|
||||||
if (supportExts.includes(getFileExtension(filePath))) {
|
if (supportExts.includes(getFileExtension(filePath))) {
|
||||||
const selectedFile = await window.api.file.get(filePath)
|
const selectedFile = await window.api.file.get(filePath)
|
||||||
if (selectedFile) {
|
if (selectedFile) {
|
||||||
setFiles((prevFiles) => [...prevFiles, selectedFile])
|
setFiles((prevFiles) => [...prevFiles, selectedFile])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (t) {
|
if (t) {
|
||||||
window.message.info({
|
window.message.info({
|
||||||
key: 'file_not_supported',
|
key: 'file_not_supported',
|
||||||
content: t('chat.input.file_not_supported')
|
content: t('chat.input.file_not_supported')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error('[PasteService] onPaste:', error)
|
Logger.error('[PasteService] onPaste:', error)
|
||||||
if (t) {
|
if (t) {
|
||||||
window.message.error(t('chat.input.file_error'))
|
window.message.error(t('chat.input.file_error'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// 其他情况默认粘贴
|
// 其他情况默认粘贴
|
||||||
return false
|
return false
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error('[PasteService] handlePaste error:', error)
|
Logger.error('[PasteService] handlePaste error:', error)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置最后聚焦的组件
|
* 设置最后聚焦的组件
|
||||||
*/
|
*/
|
||||||
export const setLastFocusedComponent = (component: ComponentType) => {
|
export const setLastFocusedComponent = (component: ComponentType) => {
|
||||||
lastFocusedComponent = component
|
lastFocusedComponent = component
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取最后聚焦的组件
|
* 获取最后聚焦的组件
|
||||||
*/
|
*/
|
||||||
export const getLastFocusedComponent = (): ComponentType => {
|
export const getLastFocusedComponent = (): ComponentType => {
|
||||||
return lastFocusedComponent
|
return lastFocusedComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化全局粘贴事件监听
|
* 初始化全局粘贴事件监听
|
||||||
* 应用启动时只调用一次
|
* 应用启动时只调用一次
|
||||||
*/
|
*/
|
||||||
export const init = () => {
|
export const init = () => {
|
||||||
if (isInitialized) return
|
if (isInitialized) return
|
||||||
|
|
||||||
// 添加全局粘贴事件监听
|
// 添加全局粘贴事件监听
|
||||||
document.addEventListener('paste', async (event) => {
|
document.addEventListener('paste', async (event) => {
|
||||||
await handleGlobalPaste(event)
|
await handleGlobalPaste(event)
|
||||||
})
|
})
|
||||||
|
|
||||||
isInitialized = true
|
isInitialized = true
|
||||||
Logger.info('[PasteService] Global paste handler initialized')
|
Logger.info('[PasteService] Global paste handler initialized')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册组件的粘贴处理函数
|
* 注册组件的粘贴处理函数
|
||||||
*/
|
*/
|
||||||
export const registerHandler = (component: ComponentType, handler: PasteHandler) => {
|
export const registerHandler = (component: ComponentType, handler: PasteHandler) => {
|
||||||
if (!component) return
|
if (!component) return
|
||||||
|
|
||||||
// Only log and update if the handler actually changes
|
// Only log and update if the handler actually changes
|
||||||
if (!handlers[component] || handlers[component] !== handler) {
|
if (!handlers[component] || handlers[component] !== handler) {
|
||||||
handlers[component] = handler
|
handlers[component] = handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除组件的粘贴处理函数
|
* 移除组件的粘贴处理函数
|
||||||
*/
|
*/
|
||||||
export const unregisterHandler = (component: ComponentType) => {
|
export const unregisterHandler = (component: ComponentType) => {
|
||||||
if (!component || !handlers[component]) return
|
if (!component || !handlers[component]) return
|
||||||
|
|
||||||
delete handlers[component]
|
delete handlers[component]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局粘贴处理函数,根据最后聚焦的组件路由粘贴事件
|
* 全局粘贴处理函数,根据最后聚焦的组件路由粘贴事件
|
||||||
*/
|
*/
|
||||||
const handleGlobalPaste = async (event: ClipboardEvent): Promise<boolean> => {
|
const handleGlobalPaste = async (event: ClipboardEvent): Promise<boolean> => {
|
||||||
// 如果当前有活动元素且是输入区域,不执行全局处理
|
// 如果当前有活动元素且是输入区域,不执行全局处理
|
||||||
const activeElement = document.activeElement
|
const activeElement = document.activeElement
|
||||||
if (
|
if (
|
||||||
activeElement &&
|
activeElement &&
|
||||||
(activeElement.tagName === 'INPUT' ||
|
(activeElement.tagName === 'INPUT' ||
|
||||||
activeElement.tagName === 'TEXTAREA' ||
|
activeElement.tagName === 'TEXTAREA' ||
|
||||||
activeElement.getAttribute('contenteditable') === 'true')
|
activeElement.getAttribute('contenteditable') === 'true')
|
||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据最后聚焦的组件调用相应处理程序
|
// 根据最后聚焦的组件调用相应处理程序
|
||||||
if (lastFocusedComponent && handlers[lastFocusedComponent]) {
|
if (lastFocusedComponent && handlers[lastFocusedComponent]) {
|
||||||
const handler = handlers[lastFocusedComponent]
|
const handler = handlers[lastFocusedComponent]
|
||||||
if (handler) {
|
if (handler) {
|
||||||
return await handler(event)
|
return await handler(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有匹配的处理程序,默认使用inputbar处理
|
// 如果没有匹配的处理程序,默认使用inputbar处理
|
||||||
if (handlers.inputbar) {
|
if (handlers.inputbar) {
|
||||||
const handler = handlers.inputbar
|
const handler = handlers.inputbar
|
||||||
if (handler) {
|
if (handler) {
|
||||||
return await handler(event)
|
return await handler(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
handlePaste,
|
handlePaste,
|
||||||
setLastFocusedComponent,
|
setLastFocusedComponent,
|
||||||
getLastFocusedComponent,
|
getLastFocusedComponent,
|
||||||
init,
|
init,
|
||||||
registerHandler,
|
registerHandler,
|
||||||
unregisterHandler
|
unregisterHandler
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user