Merge branch 'v1.4.3'

This commit is contained in:
ZiuChen 2022-09-27 00:13:55 +08:00
commit 33306aadac
13 changed files with 229 additions and 71 deletions

56
public/listener.js Normal file
View File

@ -0,0 +1,56 @@
const { chmodSync, existsSync } = require('fs')
const { EventEmitter } = require('events')
const path = require('path')
const { execFile } = require('child_process')
class ClipboardEventListener extends EventEmitter {
constructor() {
super()
this.child = null
this.listening = false
}
startListening(dbPath) {
const targetMap = {
win32: 'clipboard-event-handler-win32.exe',
linux: 'clipboard-event-handler-linux'
}
const { platform } = process
const target = path.resolve(
dbPath.split('_utools_clipboard_manager_storage')[0],
targetMap[platform]
)
if (!existsSync(target)) {
this.emit('error', '剪贴板监听程序不存在')
return
}
if (platform === 'win32') {
this.child = execFile(target)
} else if (platform === 'linux') {
chmodSync(target, 0o755)
this.child = execFile(target)
} else {
throw 'Not yet supported'
}
this.child.stdout.on('data', (data) => {
if (data.trim() === 'CLIPBOARD_CHANGE') {
this.emit('change')
}
})
this.child.stdout.on('close', () => {
this.emit('close')
this.listening = false
})
this.child.stdout.on('exit', () => {
this.emit('exit')
this.listening = false
})
this.listening = true
}
stopListening() {
const res = this.child.kill()
this.listening = false
return res
}
}
module.exports = new ClipboardEventListener()

View File

@ -1,5 +1,5 @@
{
"version": "1.4.2",
"version": "1.4.3",
"pluginName": "超级剪贴板",
"description": "强大的剪贴板管理工具",
"author": "ZiuChen",

View File

@ -1,5 +1,6 @@
const { existsSync, readFileSync, writeFileSync, mkdirSync } = require('fs')
const crypto = require('crypto')
const listener = require('./listener')
const { clipboard } = require('electron')
const time = require('./time')
@ -11,5 +12,6 @@ window.exports = {
mkdirSync,
crypto,
clipboard,
time
time,
Buffer
}

View File

@ -1,8 +1,8 @@
<template>
<div class="app">
<Main @show-setting="settingShown = true" v-show="!settingShown"></Main>
<Main @show-setting="settingShown = true" v-if="!settingShown"></Main>
<transition name="el-fade-in-linear">
<Setting v-show="settingShown" @back="settingShown = false"></Setting>
<Setting v-if="settingShown" @back="settingShown = false"></Setting>
</transition>
</div>
</template>

View File

@ -80,9 +80,18 @@ const props = defineProps({
currentActiveTab: {
type: String,
required: true
},
isSearchPanelExpand: {
type: Boolean,
required: true
}
})
const emit = defineEmits(['onDataChange', 'onDataRemove', 'onSelectItemAdd', 'onMultiCopyExecute'])
const emit = defineEmits([
'onDataChange',
'onDataRemove',
'onMultiCopyExecute',
'toggleMultiSelect'
])
const isOverSizedContent = (item) => {
const { type, data } = item
if (type === 'text') {
@ -111,6 +120,7 @@ const handleItemClick = (ev, item) => {
if (props.isMultiple === true) {
const i = selectItemList.value.indexOf(item) //
const index = props.showList.indexOf(item) //
activeIndex.value = index
if (selectItemList.value.length !== 0 && isShiftDown.value) {
// Shift
// selectList
@ -159,8 +169,6 @@ const handleItemClick = (ev, item) => {
selectItemList.value.push(item) //
}
}
emit('onSelectItemAdd')
} else {
const { button } = ev
if (button === 0) {
@ -178,7 +186,11 @@ const handleItemClick = (ev, item) => {
}
}
const activeIndex = ref(0)
const handleMouseOver = (index) => (activeIndex.value = index)
const handleMouseOver = (index) => {
if (!props.isMultiple) {
activeIndex.value = index
}
}
// getter
watch(
() => props.showList,
@ -194,25 +206,23 @@ onMounted(() => {
const isCopy = (ctrlKey || metaKey) && (key === 'C' || key === 'c')
const isNumber = parseInt(key) <= 9 && parseInt(key) >= 0
const isShift = key === 'Shift'
const isSpace = key === ' '
const activeNode = !props.isMultiple
? document.querySelector('.clip-item.active' + (isArrowDown ? '+.clip-item' : ''))
: document.querySelector('.clip-item.multi-active' + (isArrowDown ? '+.clip-item' : ''))
if (isArrowUp) {
if (activeIndex.value === 1) window.toTop()
if (activeIndex.value > 0) {
activeIndex.value--
const activeNode = document.querySelector('.clip-item.active')
if (activeIndex.value === 1) {
window.toTop()
} else {
activeNode?.previousElementSibling?.previousElementSibling?.scrollIntoView({
block: 'nearest',
inline: 'nearest'
})
}
activeNode.previousElementSibling.previousElementSibling.scrollIntoView({
block: 'nearest',
inline: 'nearest'
})
}
} else if (isArrowDown) {
if (activeIndex.value < props.showList.length - 1) {
activeIndex.value++
document
.querySelector('.clip-item.active+.clip-item')
?.scrollIntoView({ block: 'nearest', inline: 'nearest' })
activeNode.scrollIntoView({ block: 'nearest', inline: 'nearest' })
}
} else if (isCopy) {
if (!props.fullData.data) {
@ -246,6 +256,25 @@ onMounted(() => {
if (props.isMultiple) {
isShiftDown.value = true
}
} else if (isSpace) {
if (props.isSearchPanelExpand) {
//
return
}
if (!props.isMultiple) {
emit('toggleMultiSelect') //
}
e.preventDefault()
const i = selectItemList.value.findIndex((item) => item === props.showList[activeIndex.value])
if (i !== -1) {
selectItemList.value.splice(i, 1) //
} else {
selectItemList.value.push(props.showList[activeIndex.value]) //
activeIndex.value++
document
.querySelector('.clip-item.multi-active+.clip-item')
.scrollIntoView({ block: 'nearest', inline: 'nearest' })
}
}
})
document.addEventListener('keyup', (e) => {

View File

@ -4,7 +4,7 @@
<template v-for="tab of tabs">
<div
:class="{ 'clip-switch-item': true, active: activeTab === tab.type }"
@click="onNavClick(tab.type)"
@click="toggleNav(tab.type)"
>
<component :is="tab.icon"></component>
{{ tab.name }}
@ -27,12 +27,7 @@ const tabs = [
{ name: '收藏', type: 'collect', icon: Collection }
]
const activeTab = ref('all')
const emit = defineEmits(['onNavClick'])
const toggleNav = (type) => (activeTab.value = type)
const onNavClick = (type) => {
toggleNav(type)
emit('onNavClick', type)
}
defineExpose({
tabs,
activeTab,

View File

@ -1,5 +1,5 @@
{
"title": "重要版本更新提示",
"content": "1. 如果你是第一次使用此插件, 请务必设置跟随主程序启动选项, 否则可能导致剪贴板记录丢失</br>2. 插件使用过程中遇到任何问题, 请到论坛发布页回帖或加入QQ群反馈",
"version": 2
"content": "1. 如果你是第一次使用此插件, 请务必设置跟随主程序启动选项, 否则可能导致剪贴板记录丢失</br>2. 请手动安装`clipboard-event-handler`以获得最佳剪贴板监听性能</br>3. 插件使用过程中遇到任何问题, 请到论坛发布页回帖或加入QQ群反馈",
"version": 3
}

View File

@ -49,7 +49,7 @@
"regex": "^(?:#?[a-f0-9]{6}|(?:(?:25[0-5]|2[0-4]\\d|1?\\d{1,2}), ?){2}(?:25[0-5]|2[0-4]\\d|1?\\d{1,2})|rgba?\\((?:(?:25[0-5]|2[0-4]\\d|1?\\d{1,2}), ?){2}(?:25[0-5]|2[0-4]\\d|1?\\d{1,2})(?:, ?(?:1|0|0\\.\\d{1,2}))?\\)|hs[liv]a?\\((?:360|(?:(?:3[0-5]\\d|[1-2]\\d{2}|\\d{1,2})(?:\\.\\d{1,15})?))(?:deg)?(?:, |,| )(?:100|\\d{1,2}(?:\\.\\d{1,15})?)%?(?:, |,| )(?:100|\\d{1,2}(?:\\.\\d{1,15})?)%?(?:, ?(?:1|0|0\\.\\d{1,2}))?\\))$"
}
],
"command": "redirect:统计文本次数"
"command": "redirect:颜色信息"
},
{
"id": "custom.1663490862",

View File

@ -1,5 +1,15 @@
const { utools, existsSync, readFileSync, writeFileSync, mkdirSync, crypto, clipboard, time } =
window.exports
const {
utools,
existsSync,
readFileSync,
writeFileSync,
mkdirSync,
crypto,
listener,
clipboard,
time,
Buffer
} = window.exports
import setting from './readSetting'
export default function initPlugin() {
@ -205,23 +215,56 @@ export default function initPlugin() {
db.addItem(item)
}
let prev = db.dataBase.data[0] || {}
function loop() {
time.sleep(300).then(loop)
const item = pbpaste()
if (!item) return
item.id = crypto.createHash('md5').update(item.data).digest('hex')
if (item && prev.id != item.id) {
// 剪切板元素 与最近一次复制内容不同
prev = item
handleClipboardChange(item)
} else {
// 剪切板元素 与上次复制内容相同
const addCommonListener = () => {
let prev = db.dataBase.data[0] || {}
function loop() {
time.sleep(300).then(loop)
const item = pbpaste()
if (!item) return
item.id = crypto.createHash('md5').update(item.data).digest('hex')
if (item && prev.id != item.id) {
// 剪切板元素 与最近一次复制内容不同
prev = item
handleClipboardChange(item)
} else {
// 剪切板元素 与上次复制内容相同
}
}
loop()
}
const registerClipEvent = (listener) => {
const exitHandler = () => {
utools.showNotification('剪贴板监听异常退出 请重启插件以开启监听')
utools.outPlugin()
}
const errorHandler = (error) => {
const info = '请到设置页手动安装 clipboard-event-handler 剪贴板监听程序'
utools.showNotification('启动剪贴板监听程序启动出错: ' + error + info)
addCommonListener()
}
listener
.on('change', handleClipboardChange)
.on('close', exitHandler)
.on('exit', exitHandler)
.on('error', (error) => errorHandler(error))
}
if (!utools.isMacOs()) {
// 首次启动插件 即开启监听
registerClipEvent(listener)
listener.startListening(setting.database.path)
} else {
// macos 由于无法执行 clipboard-event-handler-mac 所以使用旧方法
addCommonListener()
}
loop()
utools.onPluginEnter(() => {
if (!listener.listening && !utools.isMacOs()) {
// 进入插件后 如果监听已关闭 则重新开启监听
registerClipEvent(listener)
listener.startListening(setting.database.path)
}
toTop()
resetNav()
})

View File

@ -9,6 +9,7 @@ import 'element-plus/theme-chalk/el-input.css'
import 'element-plus/theme-chalk/el-select.css'
import 'element-plus/theme-chalk/el-option.css'
import 'element-plus/theme-chalk/el-scrollbar.css'
import 'element-plus/theme-chalk/el-tag.css'
import {
ElButton,
ElMessageBox,
@ -17,7 +18,8 @@ import {
ElInput,
ElSelect,
ElOption,
ElScrollbar
ElScrollbar,
ElTag
} from 'element-plus'
const components = [
@ -28,7 +30,8 @@ const components = [
ElInput,
ElSelect,
ElOption,
ElScrollbar
ElScrollbar,
ElTag
]
document.querySelector('html').className = utools.isDarkColors() ? 'dark' : ''

View File

@ -12,12 +12,16 @@
width: 85px;
}
&.operation-select {
width: 450px;
width: 520px;
}
}
.el-textarea {
width: 70%;
}
.el-tag {
margin: 0 10px;
cursor: pointer;
}
.path {
width: 65%;
}

View File

@ -7,7 +7,7 @@
@onDataRemove="handleDataRemove"
@onOverlayClick="toggleFullData({ type: 'text', data: '' })"
></ClipFullData>
<ClipSwitch ref="ClipSwitchRef" @onNavClick="handleNavClick">
<ClipSwitch ref="ClipSwitchRef">
<template #SidePanel>
<div class="clip-switch-btn-list" v-show="!isSearchPanelExpand">
<span class="clip-switch-btn clip-select-count" v-show="isMultiple">
@ -47,7 +47,9 @@
:fullData="fullData"
:isMultiple="isMultiple"
:currentActiveTab="activeTab"
:isSearchPanelExpand="isSearchPanelExpand"
@onMultiCopyExecute="handleMultiCopyBtnClick"
@toggleMultiSelect="() => (isMultiple = true)"
@onDataChange="toggleFullData"
@onDataRemove="handleDataRemove"
>
@ -153,7 +155,7 @@ const textFilterCallBack = (item) => {
}
}
const updateShowList = (type) => {
const updateShowList = (type, toTop = true) => {
//
showList.value = list.value
.filter((item) =>
@ -162,7 +164,7 @@ const updateShowList = (type) => {
.filter((item) => (filterText.value ? item.type !== 'image' : item)) // DataURL
.filter((item) => textFilterCallBack(item))
.slice(0, GAP) //
window.toTop()
toTop && window.toTop()
}
const restoreDataBase = () => {
@ -179,11 +181,6 @@ const restoreDataBase = () => {
.catch(() => {})
}
const handleNavClick = (type) => {
updateShowList(type)
offset.value = 0 //
}
const fullData = ref({ type: 'text', data: '' })
const fullDataShow = ref(false)
const toggleFullData = (item) => {
@ -197,7 +194,7 @@ const ClipSwitchRef = ref()
const handleDataRemove = () => {
//
list.value = window.db.dataBase.data
updateShowList(ClipSwitchRef.value.activeTab)
updateShowList(ClipSwitchRef.value.activeTab, false)
}
const emit = defineEmits(['showSetting'])
@ -226,17 +223,26 @@ onMounted(() => {
updateShowList(activeTab.value)
//
let prev = {}
setInterval(() => {
const now = window.db.dataBase.data[0]
if (prev?.id === now?.id) {
} else {
//
if (!utools.isMacOs() && window.listener.listening) {
// macOS
window.listener.on('change', () => {
list.value = window.db.dataBase.data
updateShowList(activeTab.value)
prev = now
}
}, 800)
})
} else {
// macOS
let prev = {}
setInterval(() => {
const now = window.db.dataBase.data[0]
if (prev?.id === now?.id) {
} else {
//
list.value = window.db.dataBase.data
updateShowList(activeTab.value)
prev = now
}
}, 800)
}
//
watch(filterText, (val) => updateShowList(activeTab.value))
@ -287,6 +293,7 @@ onMounted(() => {
const isEnter = key === 'Enter'
const isShift = key === 'Shift'
const isAlt = key === 'Alt'
const isSpace = key === ' '
if (isTab) {
const tabTypes = tabs.map((item) => item.type)
const index = tabTypes.indexOf(activeTab.value)
@ -308,6 +315,7 @@ onMounted(() => {
} else if (isMultiple.value) {
// 退
isMultiple.value = !isMultiple.value
e.stopPropagation()
} else {
// : uTools
}
@ -323,6 +331,8 @@ onMounted(() => {
} else if (ctrlKey || metaKey || isAlt) {
// Ctrl: utools
// Alt:
} else if (isSpace) {
//
} else {
window.focus() //
}

View File

@ -2,13 +2,23 @@
<div class="setting">
<el-card class="setting-card">
<template #header>
<el-button type="primary" @click="handleLinkClick(0)">🚀 主页</el-button>
<el-button @click="handleLinkClick(1)"> 云同步教程</el-button>
<el-button @click="handleLinkClick(1)">💡 创造自己的功能按钮</el-button>
<el-button type="primary" @click="handleLinkClick(4)">💖 赞赏我</el-button>
<el-button @click="handleLinkClick(0)">🚀 主页</el-button>
<el-button @click="handleLinkClick(1)">迁移数据 | 云同步 | 自定义功能</el-button>
<el-button @click="handleLinkClick(2)"> 开源代码</el-button>
<el-button @click="handleLinkClick(3)">🎈 论坛发布页</el-button>
</template>
<div class="setting-card-content">
<div class="setting-card-content-item">
<span>剪贴板监听程序状态</span>
<el-tag
:type="listenStatus ? 'success' : 'warning'"
@click="handleLinkClick(1)"
title="手动安装剪贴板监听程序"
>
{{ listenStatus ? '已安装' : '未安装' }}
</el-tag>
</div>
<div class="setting-card-content-item">
<span>数据库路径</span>
<el-input class="path" v-model="path" :title="path" disabled></el-input>
@ -38,7 +48,6 @@
multiple
:multiple-limit="5"
placeholder="请选择"
:teleported="false"
>
<el-option
v-for="{ id, title, icon } in [
@ -66,7 +75,7 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import setting from '../global/readSetting'
import restoreSetting from '../global/restoreSetting'
@ -83,12 +92,15 @@ const shown = ref(operation.shown)
const custom = ref(operation.custom)
const stringCustom = ref(JSON.stringify(operation.custom))
const listenStatus = ref(false)
const handleLinkClick = (index) => {
const links = [
'https://ziuchen.gitee.io/project/ClipboardManager/',
'https://ziuchen.gitee.io/project/ClipboardManager/guide/',
'https://github.com/ZiuChen/ClipboardManager',
'https://yuanliao.info/d/5722'
'https://yuanliao.info/d/5722',
'https://ziuchen.gitee.io/project/ClipboardManager/#%F0%9F%92%9D-%E6%84%9F%E8%B0%A2%E8%B5%9E%E8%B5%8F'
]
utools.shellOpenExternal(links[index])
}
@ -169,6 +181,10 @@ const handleRestoreBtnClick = () => {
})
.catch(() => {})
}
onMounted(() => {
listenStatus.value = window.listener.listening
})
</script>
<style lang="less" scoped>