├── images
├── menu.png
├── pure-mode.png
├── main-window.png
└── import-notification.png
├── 一键导入
└── 一键导入.cpq
├── 命令
├── 虚拟分隔线.ini
├── 合并所选项.ini
├── 用浏览器打开.ini
├── 切换标签页.ini
├── 粘贴当前日期和时间.ini
├── 纯文本颜色标记.ini
├── 图片标签页.ini
├── 显示Hex颜色.ini
├── 储存复制时间.ini
├── 复制文件.ini
├── 储存软件名.ini
├── 切换单行模式.ini
├── 重命名MIME.ini
├── 强制复制.ini
├── 切换置顶状态.ini
├── 清理当前标签页.ini
├── 切换固定.ini
├── 标记相关.ini
├── 大文件标签页.ini
├── 切换大小写.ini
├── 获取屏幕分辨率.ini
├── 居中显示或隐藏主窗口.ini
├── 更新条目修改时间.ini
├── 预览图片文件.ini
├── 全局搜索.ini
├── 切换简洁模式.ini
├── 自动预览图片或长文本.ini
├── 复制命令时通知导入.ini
├── 筛选.ini
├── 清理格式并粘贴.ini
├── 自动清理过期条目.ini
├── 另存为.ini
├── 排序.ini
├── 可撤销的删除.ini
└── 全部命令.ini
├── README.md
├── 主题
├── default.ini
├── dracula.ini
├── dracula_mod.ini
├── pure.ini
└── mint.ini
└── LICENSE
/images/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GFDGIT/CopyQ_lazy/HEAD/images/menu.png
--------------------------------------------------------------------------------
/一键导入/一键导入.cpq:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GFDGIT/CopyQ_lazy/HEAD/一键导入/一键导入.cpq
--------------------------------------------------------------------------------
/images/pure-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GFDGIT/CopyQ_lazy/HEAD/images/pure-mode.png
--------------------------------------------------------------------------------
/images/main-window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GFDGIT/CopyQ_lazy/HEAD/images/main-window.png
--------------------------------------------------------------------------------
/images/import-notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GFDGIT/CopyQ_lazy/HEAD/images/import-notification.png
--------------------------------------------------------------------------------
/命令/虚拟分隔线.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=----------------------------------
3 | Command="
4 | // \x865a\x62df\x5206\x5272\x7ebf
5 | "
6 | InMenu=true
--------------------------------------------------------------------------------
/命令/合并所选项.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=合并所选项
3 | MatchCommand="
4 | copyq:
5 | if (selectedItems().length < 2) fail()
6 | "
7 | Command="
8 | copyq add -- %1
9 | "
10 | InMenu=true
--------------------------------------------------------------------------------
/命令/用浏览器打开.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x7528\x6d4f\x89c8\x5668\x6253\x5f00
3 | Match="^(?=(https?|ftps?|smb|mailto)://)(?=\\S{12,})"
4 | Command="
5 | copyq open %1
"
6 | InMenu=true
7 | HideWindow=true
8 | Icon=\xf0ac
9 | Shortcut=ctrl+b
--------------------------------------------------------------------------------
/命令/切换标签页.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x663e\x793a/\x9690\x85cf\x6807\x7b7e\x9875
3 | Command="
4 | copyq:
5 | var on = config(\"hide_tabs\") === \"true\"
6 | config(\"hide_tabs\", !on)"
7 | InMenu=true
8 | Enable=false
9 | Icon=\xf141
10 | Shortcut=f11
--------------------------------------------------------------------------------
/命令/粘贴当前日期和时间.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x7c98\x8d34\x5f53\x524d\x65e5\x671f\x548c\x65f6\x95f4
3 | Command="
4 | copyq:
5 | var time = dateString('yyyy'+'\x5e74'+'MM'+'\x6708'+'dd'+'\x65e5 '+'hh:mm')
6 | copy(time)
7 | paste()"
8 | IsGlobalShortcut=true
9 | Icon=\xf017
10 | GlobalShortcut=alt+shift+t
--------------------------------------------------------------------------------
/命令/纯文本颜色标记.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x7eaf\x6587\x672c\x989c\x8272\x6807\x8bb0
3 | Command="
4 | copyq:
5 | if ( str(data(mimeText)) && !str(data(mimeHtml)) ) {
6 | html = escapeHtml(data(mimeText))
7 | setData(mimeHtml, '' + html + '')
8 | }"
9 | Display=true
10 | Icon=\xf15b
--------------------------------------------------------------------------------
/命令/图片标签页.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x56fe\x7247\x6807\x7b7e\x9875
3 | Command="
4 | copyq:
5 | var imageTab = '\x56fe\x7247'
6 | var formats = dataFormats()
7 | for (var i in formats) {
8 | if (formats[i].match(/^image\\//)) {
9 | setData(mimeOutputTab, imageTab)
10 | break
11 | }
12 | }"
13 | Automatic=true
14 | Enable=false
15 | Icon=\xf03e
--------------------------------------------------------------------------------
/命令/显示Hex颜色.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=显示Hex颜色
3 | Match=^#[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]
4 | Command="
5 | copyq:
6 | var color = str(input())
7 | setData(mimeHtml,
8 | '
'
9 | + ' | '
10 | + ' ' + color + ' | '
11 | + '
'
12 | )"
13 | Input=text/plain
14 | Display=true
15 | Icon=
--------------------------------------------------------------------------------
/命令/储存复制时间.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=储存复制时间
3 | Command="
4 | copyq:
5 | const time_mime = 'application/x-copyq-user-time'
6 | const tags_mime = 'application/x-copyq-tags'
7 | const time = dateString('yyyy-MM-dd hh:mm:ss')
8 | const oldTags = data(tags_mime)
9 | let tags = ''
10 | oldTags=='' ? tags = time : tags = `${oldTags}, ${time}`
11 | setData(tags_mime, tags)
12 | setData(time_mime, time)"
13 | Automatic=true
14 | Icon=
--------------------------------------------------------------------------------
/命令/复制文件.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x590d\x5236/\x7c98\x8d34\x6587\x4ef6\x652f\x6301
3 | Command="
4 | // \x4e0d\x652f\x6301\x526a\x5207\x64cd\x4f5c
5 | var originalFunction = global.clipboardFormatsToSave
6 | global.clipboardFormatsToSave = function() {
7 | return originalFunction().concat([
8 | mimeUriList,
9 | 'x-special/gnome-copied-files',
10 | 'application/x-kde-cutselection',
11 | ])
12 | }"
13 | IsScript=true
14 | Icon=\xf0c1
--------------------------------------------------------------------------------
/命令/储存软件名.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=储存窗口名
3 | Command="
4 | copyq:
5 | const tags_mime = 'application/x-copyq-tags'
6 | const window = str(data(mimeWindowTitle))
7 | const oldTags = data(tags_mime)
8 | const reg = /([^-–—]{1,30})$/
9 | let title = window.match(reg)
10 | title ? title = title[0].trim() : title = 'unknow'
11 | if (oldTags) {
12 | title = `${oldTags}, ${title}`
13 | }
14 | setData(tags_mime, title )"
15 | Automatic=true
16 |
17 | Icon=
18 |
--------------------------------------------------------------------------------
/命令/切换单行模式.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x5207\x6362\x5355\x884c\x6a21\x5f0f
3 | MatchCommand="
4 | copyq:
5 | var on = config(\"show_simple_items\") === \"true\"
6 | if (on) {
7 | menuItem['text'] = '\x591a\x884c\x6a21\x5f0f'
8 | menuItem['icon'] = '\xf065'
9 | } else {
10 | menuItem['text'] = '\x5355\x884c\x6a21\x5f0f'
11 | menuItem['icon'] = '\xf066'
12 | }"
13 | Command="
14 | copyq:
15 | var row = selectedItems()
16 | var on = config(\"show_simple_items\") === \"true\"
17 | config(\"show_simple_items\", !on)
18 | selectItems(row)"
19 | InMenu=true
20 | Icon=\xf066
21 | Shortcut=f8
--------------------------------------------------------------------------------
/命令/重命名MIME.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x91cd\x547d\x540dMIME
3 | Command="
4 | copyq:
5 | var old_Mime = 'application/x-copyq-user-copy-time'
6 | var new_Mime = 'application/x-copyq-user-item-time'
7 | var tab_names = tab()
8 | for (var i in tab_names) {
9 | var tab_name = tab_names[i]
10 | tab(tab_name)
11 | var item_count = count()
12 |
13 | for (var j = 0; j < item_count; ++j) {
14 | var time = str(read(old_Mime, j))
15 | if (time != '') {
16 | change(j, new_Mime, time)
17 | change(j, old_Mime, undefined)
18 | }
19 | }
20 | }"
21 | InMenu=true
22 | Enable=false
23 | Icon=\xf15b
--------------------------------------------------------------------------------
/命令/强制复制.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x5f3a\x5236\x590d\x5236\xff08\x5f53\x5173\x95ed\x6355\x83b7\x65f6\xff09
3 | Command="
4 | copyq:
5 | copy()
6 |
7 | if (monitoring())
8 | abort()
9 |
10 | var item = {}
11 | for (const format of clipboardFormatsToSave()) {
12 | var data = clipboard(format)
13 | if (data.length)
14 | item[format] = data
15 | }
16 |
17 | var time = dateString('yyyy-MM-dd hh:mm:ss')
18 | var tagsMime = 'application/x-copyq-tags'
19 | var timeMime = 'application/x-copyq-user-item-time'
20 |
21 | item[timeMime] = time
22 | item[tagsMime] = time
23 | add(item)"
24 | IsGlobalShortcut=true
25 | Icon=\xf0c5
26 | GlobalShortcut=alt+c
--------------------------------------------------------------------------------
/命令/切换置顶状态.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x5207\x6362\x7f6e\x9876\x72b6\x6001
3 | MatchCommand="
4 | copyq:
5 | var on = config(\"always_on_top\") === \"true\"
6 | if (on) {
7 | menuItem['text'] = '\x53d6\x6d88\x7f6e\x9876'
8 | menuItem['icon'] = '\xf023'
9 | } else {
10 | menuItem['text'] = '\x7f6e\x9876\x7a97\x53e3'
11 | menuItem['icon'] = '\xf3c1'
12 | }"
13 | Command="
14 | copyq:
15 | var rows = selectedItems()
16 | var on = config(\"always_on_top\") === \"true\"
17 |
18 | config(\"always_on_top\", !on)
19 | config(\"activate_closes\",on)
20 | config(\"close_on_unfocus\",on)
21 |
22 | selectItems.apply(this, rows)"
23 | InMenu=true
24 | Icon=\xf023
25 | Shortcut=ctrl+`
--------------------------------------------------------------------------------
/命令/清理当前标签页.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=清理当前标签页
3 | MatchCommand="
4 | copyq: tab(selectedTab()); if (size() == 0) fail()"
5 | Command="
6 | copyq:
7 | var source_tab = selectedtab()
8 | var sel = ItemSelection(selectedTab()).selectRemovable()
9 |
10 | if (sel.length === 0) {
11 | abort()
12 | }
13 |
14 | var label = {
15 | [settings('trash_tab')]:'⚠︎ 该操作无法撤销!',
16 | other: '该操作可撤销。',
17 | }
18 |
19 | var remove = dialog(
20 | '.title', '确认清理?',
21 | '.label', label[source_tab] || label['other'],
22 | )
23 | if (remove) {
24 | sel.removeAll()
25 | }"
26 | InMenu=true
27 | Icon=
--------------------------------------------------------------------------------
/命令/切换固定.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=切换固定
3 | MatchCommand="
4 | copyq:
5 | var row = index()
6 | if (plugins.itempinned.isPinned(row)) {
7 | menuItem['text'] = '取消固定'
8 | menuItem['tag'] = 'x'
9 | } else {
10 | menuItem['text'] = '固定'
11 | menuItem['tag'] = ''
12 | }"
13 | Command="
14 | copyq:
15 | // 是否移动至标签页顶部
16 | var top = true
17 |
18 | var len = selectedItems().length
19 | if (plugins.itempinned.isPinned(currentItem())) {
20 | plugins.itempinned.unpin()
21 | removeData(mimeColor)
22 | } else {
23 | var color = 'rgba(255,0,0,0.1)'
24 | setData(mimeColor, color)
25 | plugins.itempinned.pin()
26 | if (top) {
27 | move(0)
28 | selectItems.apply(this, Array.from({length: len}, (_, i) => i))
29 | }
30 | }"
31 | InMenu=true
32 | Icon=
33 | Shortcut=ctrl+shift+p
--------------------------------------------------------------------------------
/命令/标记相关.ini:
--------------------------------------------------------------------------------
1 | [Commands]
2 | 1\Name=\x6807\x8bb0|\x5207\x6362\x201c\x91cd\x8981\x201d\x6807\x8bb0
3 | 1\Command="
4 | copyq:
5 | var tag = decodeURIComponent('\x91cd\x8981')
6 | if (plugins.itemtags.hasTag(tag))
7 | plugins.itemtags.untag(tag)
8 | else
9 | plugins.itemtags.tag(tag)"
10 | 1\InMenu=true
11 | 1\Icon=\xf02e
12 | 1\Shortcut=shift+i
13 | 2\Name=\x6807\x8bb0|\x6dfb\x52a0\x4e00\x4e2a\x6807\x8bb0
14 | 2\Command="
15 | copyq: plugins.itemtags.tag()
16 | "
17 | 2\InMenu=true
18 | 2\Icon=\xf02b
19 | 2\Shortcut=shift+t
20 | 3\Name=\x6807\x8bb0|\x79fb\x9664\x4e00\x4e2a\x6807\x8bb0
21 | 3\Command="
22 | copyq: plugins.itemtags.untag()
23 | "
24 | 3\InMenu=true
25 | 3\Icon=\xf00d
26 | 3\Shortcut=ctrl+shift+t
27 | 4\Name=\x6807\x8bb0|\x6e05\x7406\x6240\x6709\x6807\x8bb0
28 | 4\Command="
29 | copyq: plugins.itemtags.clearTags()
30 | "
31 | 4\InMenu=true
32 | 4\Icon=\xe59a
33 | size=4
--------------------------------------------------------------------------------
/命令/大文件标签页.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=大文件标签页
3 | Command="
4 | copyq:
5 | const tabName = '大文件'
6 | // 文件大小,默认值1000kb
7 | const minBytes = 1000 * 1000
8 | // 是否识别复制的本地文件大小
9 | // 主要用于避免视频等大文件同步
10 | const readFileSize = true
11 |
12 | function hasBigData() {
13 | let itemSize = 0
14 | let formats = dataFormats()
15 | for (let i in formats) {
16 | if (readFileSize && formats[i] == 'text/uri-list') {
17 | let fpath = str(data(formats[i])).split('///')[1]
18 | let f = new File(fpath)
19 | itemSize += f.size()
20 | } else
21 | itemSize += data(formats[i]).size()
22 | }
23 |
24 | if (itemSize >= minBytes) {
25 | return true
26 | }
27 | return false
28 | }
29 |
30 | if (hasBigData()) {
31 | setData(mimeOutputTab, tabName)
32 | }"
33 | Automatic=true
34 | Icon=
--------------------------------------------------------------------------------
/命令/切换大小写.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x5207\x6362 \x5927\x5199/\x5c0f\x5199/\x9996\x5b57\x6bcd\x5927\x5199
3 | Command="
4 | copyq:
5 | try {
6 | copy()
7 | } catch (e) {
8 | popup('\x590d\x5236\x5931\x8d25\xff0c\x8bf7\x91cd\x65b0\x9009\x62e9\x6587\x672c\xff01', e)
9 | abort()
10 | }
11 |
12 | var text = str(clipboard())
13 |
14 | var UpperCase = text.toUpperCase()
15 | var LowerCase = text.toLowerCase()
16 | var TitleCase = toTitleCase(text)
17 |
18 | if (text == UpperCase) {
19 | text = LowerCase
20 | }
21 | else {
22 | if (text == LowerCase) {
23 | text = TitleCase
24 | }
25 | else
26 | text=UpperCase
27 | }
28 |
29 | copy(text)
30 | paste()
31 |
32 | function toTitleCase(str) {
33 | return str.replace(
34 | /\\w\\S*/g,
35 | function(txt) {
36 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
37 | }
38 | );
39 | }"
40 | IsGlobalShortcut=true
41 | Icon=\xf034
42 | GlobalShortcut=meta+ctrl+u
--------------------------------------------------------------------------------
/命令/获取屏幕分辨率.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=获取屏幕分辨率
3 | Command="
4 | global.getScreenResolution = function () {
5 | var x = 0
6 | var y = 0
7 | popup('正在获取屏幕分辨率... ', '鼠标指针自动移动是正常现象。\\n预计耗时大于30s,请耐心等待!', 15000)
8 | var i = 3
9 | while (i > -1) {
10 | try {
11 | x += 10 ** i
12 | setPointerPosition(x, y)
13 | } catch (error) {
14 | x -= 10 ** i
15 | i--
16 | }
17 | }
18 | i = 3
19 | while (i > -1) {
20 | try {
21 | y += 10 ** i
22 | setPointerPosition(x, y)
23 | } catch (error) {
24 | y -= 10 ** i
25 | i--
26 | }
27 | }
28 | setPointerPosition(++x / 2, ++y / 2)
29 | popup('屏幕分辨率为:' + [x, y])
30 | return [x, y]
31 | }
32 |
33 | const onStart_ = global.onStart
34 | global.onStart = function () {
35 | onStart_()
36 | if (!settings('ScreenResolution')) {
37 | settings('ScreenResolution', getScreenResolution())
38 | }
39 | }"
40 | IsScript=true
41 | Icon=
--------------------------------------------------------------------------------
/命令/居中显示或隐藏主窗口.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=显示/隐藏主窗口
3 | Command="
4 | copyq:
5 | // 要显示的标签页,留空则显示上次打开的标签页
6 | const tabName = ''
7 | // 居中显示窗口
8 | const center = true
9 | // 在鼠标指针下显示窗口,关闭居中时生效
10 | const showAtCursor = true
11 | // 窗口大小,居中窗口时生效(单位:像素)
12 | const windowSize = { w: 1000, h: 600 }
13 | // 系统缩放系数,100%=1.0,125%=1.25
14 | const sys_Scale_Factor = 1.0
15 |
16 | if (center) {
17 | var resolution = settings_res = settings('ScreenResolution') || [1920, 1080]
18 | }
19 |
20 | // Qt缩放系数[x, y](估算值)
21 | const QT_SCALE_FACTOR = [1.2608, 1.32]
22 |
23 | const w = windowSize.w / QT_SCALE_FACTOR[0] / sys_Scale_Factor ** 2
24 | const h = windowSize.h / QT_SCALE_FACTOR[1] / sys_Scale_Factor ** 2
25 |
26 | let windowPotion = { x: 0, y: 0 }
27 |
28 | if (visible()) {
29 | hide()
30 | } else {
31 | if (center) {
32 | windowPotion.x = Math.round((resolution[0] - windowSize.w) / 2)
33 | windowPotion.y = Math.round((resolution[1] - windowSize.h) / 2)
34 | showAt(windowPotion.x, windowPotion.y, Math.round(w), Math.round(h), tabName)
35 | }
36 | else {
37 | showAtCursor ? showAt() : show()
38 | }
39 | }"
40 | IsGlobalShortcut=true
41 | Icon=
42 | GlobalShortcut=alt+v
--------------------------------------------------------------------------------
/命令/更新条目修改时间.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=更新条目修改时间
3 | Command="
4 | const onItemsChanged_ = global.onItemsChanged
5 |
6 | global.onItemsChanged = function () {
7 | onItemsChanged_()
8 |
9 | const tags_mime = 'application/x-copyq-tags'
10 | const time_mime = 'application/x-copyq-user-time'
11 | const time = dateString('yyyy-MM-dd hh:mm:ss')
12 | const rows = selectedItems()
13 | tab(selectedTab())
14 | for (let i = 0; i < rows.length; i++) {
15 | const item = getItem(rows[i])
16 | const old_tag = str(item[tags_mime])
17 | // get old_time from tags or time_mime
18 | const old_time = old_tag.match(/\\d{4}-\\d{2}-\\d{2} \\d{2}\\:\\d{2}\\:\\d{2}/) || str(item[time_mime])
19 |
20 | if (old_time != 'undefined') {
21 | // Ignore repeated calls within 1 second
22 | if (new Date(time) - new Date(old_time) < 1000)
23 | return
24 | }
25 | let new_tag = ''
26 | if (old_tag != 'undefined') {
27 | if (old_tag.includes(old_time)) {
28 | new_tag = old_tag.replace(old_time, time)
29 | } else {
30 | new_tag = old_tag + ',' + time
31 | }
32 | } else {
33 | new_tag = time
34 | }
35 | change(rows[i], time_mime, time, tags_mime, new_tag)
36 | }
37 | }"
38 | IsScript=true
39 | Icon=
40 |
--------------------------------------------------------------------------------
/命令/预览图片文件.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=预览图片文件
3 | Command="
4 | copyq:
5 | var prefix_reg = /^file:\\/+(.*)/
6 | var suffixToMime = {
7 | 'png': 'image/png',
8 | 'jpg': 'image/jpeg',
9 | 'jpeg': 'image/jpeg',
10 | 'bmp': 'image/bmp',
11 | 'gif': 'image/gif',
12 | 'svg': 'image/svg+xml',
13 | 'ico': 'image/png',
14 | 'webp': 'image/png',
15 | }
16 |
17 | function tryShowImage(mime) {
18 | var path = str(data(mime))
19 | path = path.replace(prefix_reg, '$1')
20 | if (!path)
21 | return false
22 | var i = path.lastIndexOf('.')
23 | if (i == -1)
24 | return false
25 | var suffix = path.substring(i + 1)
26 | var imageMime = suffixToMime[suffix]
27 | if (imageMime === undefined)
28 | return false
29 | var f = new File(path)
30 | if (!f.openReadOnly())
31 | return false
32 | var imageData = f.readAll()
33 | f.close()
34 | if (imageData.size() === 0)
35 | return false
36 | setData(mimeItemNotes, path)
37 | setData(imageMime, imageData)
38 | return true
39 | }
40 |
41 | if (!hasImage()) {
42 | tryShowImage(mimeText) || tryShowImage(mimeUriList)
43 | }
44 |
45 | function hasImage() {
46 | var formats = dataFormats()
47 | for (var i in formats) {
48 | if (formats[i].match(/^image\\//)) {
49 | return true
50 | }
51 | }
52 | return false
53 | }"
54 | Display=true
55 | Icon=
--------------------------------------------------------------------------------
/命令/全局搜索.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=全局搜索
3 | Command="
4 | copyq:
5 | // 默认快捷键与Win10自带输入法的简繁切换快捷键冲突,请自行调整
6 | // Name for tab for storing matching items.
7 | var search_tab_name = \"全局搜索\"
8 | // Returns true if item at index matches regex.
9 | function item_matches(item_index, re)
10 | {
11 | var item = getitem(item_index)
12 | var note = str(item[mimeItemNotes])
13 | var text = str(item[mimeText])
14 | try {
15 | var tag = str(plugins.itemtags.tags(item_index))
16 | } catch (e) {
17 | var tag = ''
18 | }
19 | return text && (re.test(text) || re.test(note) || re.test(tag))
20 | }
21 | // Ask for search expression.
22 | var match = dialog(\"搜索\")
23 | if (!match)
24 | abort()
25 | var re = new RegExp(match,'i') // 'i' case-insensitive
26 | // Clear tab with results.
27 | try {
28 | removeTab(search_tab_name)
29 | } catch (e) {}
30 | // Search all tabs.
31 | var tab_names = tab()
32 | for (var i in tab_names) {
33 | var tab_name = tab_names[i]
34 | tab(tab_name)
35 | var item_count = count()
36 | // Search all items in tab.
37 | for (var j = 0; j < item_count; ++j) {
38 | // Add matching item to tab with results.
39 | if (item_matches(j, re)) {
40 | var item = getItem(j)
41 | tab(search_tab_name)
42 | setItem(j, item)
43 | tab(tab_name)
44 | }
45 | }
46 | }
47 | show(search_tab_name)"
48 | InMenu=true
49 | Icon=
50 | Shortcut=ctrl+shift+f
--------------------------------------------------------------------------------
/命令/切换简洁模式.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x5207\x6362\x7b80\x6d01\x6a21\x5f0f
3 | MatchCommand="
4 | copyq:
5 | var on = config(\"show_simple_items\") === \"true\"
6 | if (on) {
7 | menuItem['text'] = '\x666e\x901a\x6a21\x5f0f'
8 | menuItem['icon'] = '\xf065'
9 | } else {
10 | menuItem['text'] = '\x7b80\x6d01\x6a21\x5f0f'
11 | menuItem['icon'] = '\xf066'
12 | }"
13 | Command="
14 | copyq:
15 |
16 | popup('\x6309 F8 \x5207\x6362\x7b80\x6d01/\x666e\x901a\x6a21\x5f0f','',3000)
17 |
18 | var rows = selectedItems()
19 |
20 | var on = config(\"show_simple_items\") === \"true\"
21 |
22 | // \x4e0d\x8981\x968f\x610f\x8c03\x6574\x4e0b\x9762\x9009\x9879\x7684\x987a\x5e8f\xff0c\x53ef\x80fd\x5bfc\x81f4\x5207\x6362\x6a21\x5f0f\x65f6\x7a97\x53e3\x95ea\x70c1\x589e\x52a0
23 |
24 | // \x9690\x85cf\x6807\x7b7e\x680f
25 | config(\"hide_tabs\",!on)
26 |
27 | // \x9690\x85cf\x5de5\x5177\x680f
28 | config(\"hide_toolbar\",!on)
29 |
30 | // \x5355\x884c\x663e\x793a\xff0c\x4e0d\x80fd\x5173\x95ed\x6b64\x9879
31 | config(\"show_simple_items\", !on)
32 |
33 | // \x5207\x6362\x4e3b\x9898\xff08pure\x4e3b\x9898\x4f1a\x9690\x85cf\x83dc\x5355\x680f\xff0cmint\x5219\x4e0d\x4f1a\xff09
34 | !on? loadTheme(\"./config/copyq/themes/pure.ini\") : loadTheme(\"./config/copyq/themes/mint.ini\")
35 |
36 | // \x5931\x53bb\x7126\x70b9\x65f6\x4e0d\x5173\x95ed\x7a97\x53e3
37 | config(\"activate_closes\",on)
38 | config(\"close_on_unfocus\",on)
39 |
40 | // \x7f6e\x9876\x663e\x793a\xff0c\x5173\x95ed\x6b64\x9879\x53ef\x5927\x5e45\x51cf\x5c11\x7a97\x53e3\x95ea\x70c1
41 | // config(\"always_on_top\",!on)
42 |
43 | selectItems.apply(this, rows)"
44 | InMenu=true
45 | Icon=\xf066
46 | Shortcut=f8
--------------------------------------------------------------------------------
/命令/自动预览图片或长文本.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=自动预览图片/长文本
3 | MatchCommand="
4 | copyq:
5 | if (visible()) {
6 | if (settings('AutoPreview') == 1) {
7 |
8 | if (selectedItems().length > 1) {
9 | preview(false)
10 | abort()
11 | }
12 |
13 | var preview_image = true
14 | var preview_longText = true
15 |
16 | // The characters count of long-text
17 | var LongTextCharacters = 100
18 | // The lines count of long-text
19 | var LongTextLines = 2
20 |
21 | var format = str(dataFormats())
22 | var content = str(data(mimeUriList)) || str(data(mimeText))
23 |
24 | icon_on()
25 | preview(condition())
26 | } else {
27 | icon_off()
28 | }
29 | }
30 | function condition() {
31 | return (
32 | preview_image && (/image\\/.*/.test(format) || /^file:.*(png|jpe?g|bmp|svg|gif|ico|webp)$/.test(content))
33 | ||
34 | preview_longText && (content.length > LongTextCharacters || (content.split(/\\n/)).length > LongTextLines)
35 | )
36 | }
37 | function icon_on() {
38 | menuItem['text'] = '关闭自动预览'
39 | menuItem['icon'] = ''
40 | }
41 | function icon_off() {
42 | menuItem['text'] = '开启自动预览'
43 | menuItem['icon'] = ''
44 | }"
45 | Command="
46 | copyq:
47 | if (str(data(mimeShortcut))=='space') {
48 | preview(!preview())
49 | abort()
50 | }
51 | if (settings('AutoPreview') == 1) {
52 | settings('AutoPreview', 0)
53 | popup('自动预览❎','',1200)
54 | } else {
55 | settings('AutoPreview', 1)
56 | popup('自动预览✅','',1200)
57 | }
58 | "
59 | InMenu=true
60 | Icon=
61 | Shortcut=f7, space
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CopyQ_lazy
2 | 个人的[CopyQ](https://github.com/hluk/CopyQ)仓库,包括自定义的命令和配置。
3 |
4 | 所有命令基本上来自官方的命令示例或Issue区的评论,经过汉化和修改得到。
5 |
6 | **主窗口**
7 |
8 | 
9 |
10 | **简洁模式**
11 |
12 | 
13 |
14 |
15 | **右键菜单**
16 |
17 | 
18 |
19 |
20 | **导入通知**
21 |
22 | 
23 |
24 | ## 功能增强
25 |
26 | * 支持复制/粘贴文件
27 | * 完善的删除/撤销/确认删除功能
28 | * 按时清理标签页
29 | * 自动预览图片/长文本
30 | * 切换 大写/小写/首字母大写
31 | * 排序(按条目大小、字母、复制时间、置顶固定条目)
32 | * 简洁模式及相应主题
33 | * 官方命令的汉化及细节优化
34 |
35 | ## 命令的使用方法
36 |
37 | ### 一键导入
38 | 如果你是第一次安装CopyQ,可以选择[一键导入](一键导入/%E4%B8%80%E9%94%AE%E5%AF%BC%E5%85%A5.cpq)我的配置。但请注意,这会包含一些我个人的个性化设置,不过相比默认设置不会有太大的调整。
39 |
40 | 方法是:安装完CopyQ后,在菜单栏-文件-导入,选择下载的一键导入文件,确定即可。
41 |
42 | 如果只想导入全部命令,可以按下面的方法导入[全部命令](命令/%E5%85%A8%E9%83%A8%E5%91%BD%E4%BB%A4.ini)这一个文件。
43 |
44 | ### 逐个导入
45 | 首先安装[复制命令时通知导入](命令/%E5%A4%8D%E5%88%B6%E5%91%BD%E4%BB%A4%E6%97%B6%E9%80%9A%E7%9F%A5%E5%AF%BC%E5%85%A5.ini)。方法:全选并复制命令,在CopyQ主窗口按F6打开`命令/全局快捷键`,点击右下角的粘贴命令,点OK即可。
46 |
47 | 之后每次复制命令,都会弹出通知,点击导入并确定即可。
48 |
49 |
50 | ### 须知
51 | * 按时间[排序](命令/%E6%8E%92%E5%BA%8F.ini)命令依赖于[保存复制时间](命令/%E4%BF%9D%E5%AD%98%E5%A4%8D%E5%88%B6%E6%97%B6%E9%97%B4.ini)命令。
52 |
53 |
54 | * [居中显示/隐藏主窗口](命令/%E5%B1%85%E4%B8%AD%E6%98%BE%E7%A4%BA%E6%88%96%E9%9A%90%E8%97%8F%E4%B8%BB%E7%AA%97%E5%8F%A3.ini)需按实际屏幕分辨率和缩放系数调整参数,或者关闭居中显示,否则窗口尺寸和位置可能异常。
55 |
56 | ## 主题的使用方法
57 | 在主窗口-文件-首选项-外观-加载主题,选择下载好的主题即可。
58 |
59 | ## 参考
60 | [CopyQ官方文档](https://copyq.readthedocs.io/en/latest/scripting-api.html)
61 |
62 | [CopyQ官方命令仓库](https://github.com/hluk/copyq-commands)
63 |
64 | [暗黑主题](https://github.com/dracula/copyq) 来自 `Dracula Theme`
65 |
66 | ## Star History
67 |
68 | [](https://www.star-history.com/#GFDGIT/CopyQ_lazy&Date)
--------------------------------------------------------------------------------
/命令/复制命令时通知导入.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=复制命令时通知导入
3 | Match=^\\[Commands?\\]|https?://github\\.com/.*/copyq-commands/blob/.*\\.ini
4 | Command="
5 | copyq:
6 | // Imports raw commands code (starting with [Command] or [Commands])
7 | // or from a link ('https://github.com/**/copyq-commands/blob/**.ini').
8 | const timeOutSeconds = 10
9 | const rawDataUrlPrefix = 'https://raw.githubusercontent.com'
10 |
11 | // Don't run this when mouse selection changes.
12 | if ( dataFormats().indexOf(mimeClipboardMode) != -1 )
13 | abort()
14 |
15 | function importCommandsFromUrl(url)
16 | {
17 | let m = url.match(/^https?:\\/\\/github\\.com(\\/.*)\\/blob(\\/.*\\.ini)/)
18 | if (!m)
19 | return;
20 |
21 | let rawDataUrl = rawDataUrlPrefix + m[1] + m[2]
22 | let reply = networkGet(rawDataUrl)
23 | let commandsData = str(reply.data)
24 | if (reply.status != 200) {
25 | throw '获取命令失败!'
26 | + '\\nStatus code: ' + reply.status
27 | + '\\nError: ' + reply.error
28 | }
29 | if ( !commandsData.match(/^\\[Commands?\\]/) )
30 | return;
31 |
32 | return importCommands(commandsData);
33 | }
34 |
35 | const ini = dataFormats().includes(mimeTextUtf8) ? data(mimeTextUtf8) : input();
36 | const cmds = importCommandsFromUrl(str(ini)) || importCommands(ini)
37 |
38 | let list = ''
39 | for (const cmd of cmds) {
40 | let cmdType =
41 | cmd.automatic ? 'automatic' :
42 | cmd.inMenu ? 'menu/shortcut' :
43 | cmd.globalShortcuts ? 'global shortcut' : '???';
44 | list += cmd.name + ' (' + cmdType + ')\\n'
45 | }
46 |
47 | notification(
48 | '.title', '从剪贴板导入 CopyQ 命令?',
49 | '.message', list,
50 | '.time', timeOutSeconds * 1000,
51 | '.icon', '',
52 | '.id', 'CopyQ_commands_in_clipboard',
53 | '.button', '取消', '', '',
54 | '.button', '导入', 'copyq: add(input()); addCommands( importCommands(input()) )', exportCommands(cmds)
55 | )"
56 | Input=text/plain
57 | Automatic=true
58 | Icon=
--------------------------------------------------------------------------------
/命令/筛选.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=筛选
3 | Command="
4 | copyq:
5 | var image = {
6 | [mimeText]: '图片 ---------------- I',
7 | [mimeIcon]: '',
8 | filter: '(^image/.*)|(?=^file\\:.*\\.(png|jpe?g|bmp|svg|gif)$)',
9 | shortcut: 'i'
10 | }
11 |
12 | var file = {
13 | [mimeText]: '文件 ---------------- F',
14 | [mimeIcon]: '',
15 | filter: '(?=^file://)',
16 | shortcut: 'f'
17 | }
18 |
19 | var url = {
20 | [mimeText]: 'URL ---------------- U',
21 | [mimeIcon]: '',
22 | filter: '^(?=(https?|ftps?|smb|mailto)://)',
23 | shortcut: 'u'
24 | }
25 |
26 | var html = {
27 | [mimeText]: 'HTML',
28 | [mimeIcon]: '',
29 | filter: '^text/html$',
30 | shortcut: 'h'
31 | }
32 |
33 | var PhoneMail = {
34 | [mimeText]: '手机号/邮箱',
35 | [mimeIcon]: '',
36 | filter: '(^0{0,1}(13[0-9]|15[7-9]|153|156|18[7-9])[0-9]{8}$)|(^([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$)',
37 | shortcut: 'p'
38 | }
39 |
40 | var filters = [image, file, url, html, PhoneMail]
41 | var selectedFilter = ''
42 | var shortcut = str(data(mimeShortcut))
43 |
44 | if (shortcut) {
45 | for (let f in filters) {
46 | if (filters[f].shortcut === shortcut) {
47 | selectedFilter = filters[f].filter
48 | filter_toggle(selectedFilter)
49 | abort()
50 | }
51 | }
52 | }
53 |
54 | var selectedIndex = menuItems(filters)
55 |
56 | if (selectedIndex != -1) {
57 | selectedFilter = filters[selectedIndex].filter
58 | filter_toggle(selectedFilter)
59 | } else {
60 | filter('')
61 | }
62 |
63 | function filter_toggle(filter_) {
64 | if (filter() == filter_) {
65 | filter('')
66 | }
67 | else {
68 | filter('')
69 | filter(filter_)
70 | }
71 | }"
72 | InMenu=true
73 | Icon=
74 | Shortcut=shift+f, f, i, u
--------------------------------------------------------------------------------
/命令/清理格式并粘贴.ini:
--------------------------------------------------------------------------------
1 | [Commands]
2 | 1\Name=\x6e05\x7406\x683c\x5f0f\x5e76\x7c98\x8d34
3 | 1\Command="
4 | copyq:
5 |
6 | var text = str(input())
7 |
8 | if (!text) {
9 | try {
10 | copy()
11 | } catch (e) {
12 | popup('\x590d\x5236\x5931\x8d25\xff0c\x8bf7\x91cd\x65b0\x9009\x62e9\x6587\x672c\xff01', e, time=2000)
13 | abort()
14 | }
15 | text = str(clipboard())
16 | }
17 |
18 | copy(format(text))
19 | paste()
20 |
21 | function format(text) {
22 | //\x5168\x89d2\x8f6c\x534a\x89d2
23 | text = ToCDB(text).trim()
24 | // \x5220\x9664\x6240\x6709\x56de\x8f66\x6362\x884c\x7b26
25 | text = text.replace(new RegExp(/[\\r\\n]+/g), ' ')
26 | // \x4e24\x4e2a\x4ee5\x4e0a\x7684\x7a7a\x683c\x66ff\x6362\x6210\x4e00\x4e2a
27 | text = text.replace(new RegExp(/\\s+/g), ' ')
28 | // \x5220\x9664\x5355\x4e2a\x5b57\x6bcd\xff08\x41/a\x9664\x5916\xff09\x4e4b\x95f4\x7684\x7a7a\x683c
29 | text = text.replace(/([^Aa])\\s(?!\\S{2,}|A|a)/g,'$1')
30 | return text
31 | }
32 |
33 | function ToCDB(str) {
34 | var tmp = \"\"
35 | for(var i=0;i 65280 && str.charCodeAt(i) < 65375){
41 | tmp += String.fromCharCode(str.charCodeAt(i)-65248)
42 | }
43 | else{
44 | tmp += String.fromCharCode(str.charCodeAt(i))
45 | }
46 | }
47 | return tmp
48 | }"
49 | 1\Input=text/plain
50 | 1\InMenu=true
51 | 1\IsGlobalShortcut=true
52 | 1\HideWindow=true
53 | 1\Icon=\xf12d
54 | 1\GlobalShortcut=alt+shift+f
55 | 2\Name=\x7c98\x8d34\x4e3a\x7eaf\x6587\x672c
56 | 2\Command="
57 | copyq:
58 | var text = input()
59 | copy(text)
60 | copySelection(text)
61 | paste()"
62 | 2\Input=text/plain
63 | 2\InMenu=true
64 | 2\HideWindow=true
65 | 2\Icon=\xf0ea
66 | 2\Shortcut=shift+v
67 | size=2
--------------------------------------------------------------------------------
/命令/自动清理过期条目.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=自动清除过期条目
3 | Command="
4 | // Require commands:Store Copy Time、Move to Trash (Undoable)
5 |
6 | // Get clipboard, trash_tab names
7 | const clip_tab = config(\"clipboard_tab\");
8 | const trash_tab = settings(\"trash_tab\");
9 |
10 | let tabs_times = {};
11 | // Set the tabs to clean and the expiration time (in hours)
12 | // The default is 15 days for the clipboard and 30 days for the trash_tab
13 | tabs_times[trash_tab] = 30 * 24; // Clean up the trash_tab first to make space
14 | tabs_times[clip_tab] = 15 * 24;
15 |
16 | // check items expiration on copyq start
17 | const onStart_ = global.onStart
18 | global.onStart = function () {
19 | onStart_()
20 | check_items_expire()
21 | }
22 |
23 | // check_items_expire can be called manually
24 | // and will ignore the interval
25 | global.check_items_expire = function (manual = false) {
26 | const time_mime = \"application/x-copyq-user-time\";
27 | const now = new Date();
28 | if (!manual) {
29 | const interval = Math.min(...Object.values(tabs_times)) / 10;
30 | const last_check_time = new Date(settings(\"last_check_time\"));
31 | if (hoursBetween(now, last_check_time) < interval) {
32 | return;
33 | }
34 | }
35 | settings(\"last_check_time\", str(now));
36 | for (let t in tabs_times) {
37 | const hours = tabs_times[t];
38 | let de_sel = [];
39 | let count = {};
40 | tab(t);
41 | let sel = ItemSelection().selectRemovable();
42 | let items = sel.items();
43 | for (let i=0; i < items.length; i++) {
44 | let item_time = items[i][time_mime];
45 | if (hoursBetween(now, new Date(item_time)) < hours) {
46 | de_sel.push(i);
47 | }
48 | }
49 | sel = sel.deselectIndexes(de_sel);
50 | count[t] = sel.length;
51 | if (count[t]> 0) sel.removeAll();
52 | }
53 | return count;
54 | };
55 | // Calculate the time difference in hours
56 | function hoursBetween(now, then) {
57 | let hours = (now - then) / (1000 * 60 * 60);
58 | return Math.floor(hours);
59 | }"
60 | IsScript=true
61 | Icon=
--------------------------------------------------------------------------------
/主题/default.ini:
--------------------------------------------------------------------------------
1 | [General]
2 | alt_bg=default_alt_bg
3 | alt_item_css=
4 | bg=default_bg
5 | css=
6 | css_template_items=items
7 | css_template_main_window=main_window
8 | css_template_menu=menu
9 | css_template_notification=notification
10 | cur_item_css="\n ;border: 0.1em solid ${sel_bg}"
11 | edit_bg=default_bg
12 | edit_fg=default_text
13 | edit_font=
14 | fg=default_text
15 | find_bg=#ff0
16 | find_fg=#000
17 | find_font=
18 | font=
19 | font_antialiasing=true
20 | hover_item_css=
21 | icon_size=16
22 | item_css=
23 | item_spacing=
24 | menu_bar_css="\n ;background: ${bg}\n ;color: ${fg}"
25 | menu_bar_disabled_css="\n ;color: ${bg - #666}"
26 | menu_bar_selected_css="\n ;background: ${sel_bg}\n ;color: ${sel_fg}"
27 | menu_css="\n ;border: 1px solid ${sel_bg}\n ;background: ${bg}\n ;color: ${fg}"
28 | notes_bg=default_tooltip_bg
29 | notes_css=
30 | notes_fg=default_tooltip_text
31 | notes_font=
32 | notification_bg=#333
33 | notification_fg=#ddd
34 | notification_font=
35 | num_fg=default_placeholder_text
36 | num_font=
37 | num_margin=2
38 | search_bar="\n ;background: ${edit_bg}\n ;color: ${edit_fg}\n ;border: 1px solid ${alt_bg}\n ;margin: 2px"
39 | search_bar_focused="\n ;border: 1px solid ${sel_bg}"
40 | sel_bg=default_highlight_bg
41 | sel_fg=default_highlight_text
42 | sel_item_css=
43 | show_number=true
44 | show_scrollbars=true
45 | style_main_window=false
46 | tab_bar_css="\n ;background: ${bg - #222}"
47 | tab_bar_item_counter="\n ;color: ${fg - #044 + #400}\n ;font-size: 6pt"
48 | tab_bar_scroll_buttons_css="\n ;background: ${bg - #222}\n ;color: ${fg}\n ;border: 0"
49 | tab_bar_sel_item_counter="\n ;color: ${sel_bg - #044 + #400}"
50 | tab_bar_tab_selected_css="\n ;padding: 0.5em\n ;background: ${bg}\n ;border: 0.05em solid ${bg}\n ;color: ${fg}"
51 | tab_bar_tab_unselected_css="\n ;border: 0.05em solid ${bg}\n ;padding: 0.5em\n ;background: ${bg - #222}\n ;color: ${fg - #333}"
52 | tab_tree_css="\n ;color: ${fg}\n ;background-color: ${bg}"
53 | tab_tree_item_counter="\n ;color: ${fg - #044 + #400}\n ;font-size: 6pt"
54 | tab_tree_sel_item_counter="\n ;color: ${sel_fg - #044 + #400}"
55 | tab_tree_sel_item_css="\n ;color: ${sel_fg}\n ;background-color: ${sel_bg}\n ;border-radius: 2px"
56 | tool_bar_css="\n ;color: ${fg}\n ;background-color: ${bg}\n ;border: 0"
57 | tool_button_css="\n ;color: ${fg}\n ;background: ${bg}\n ;border: 0\n ;border-radius: 2px"
58 | tool_button_pressed_css="\n ;background: ${sel_bg}"
59 | tool_button_selected_css="\n ;background: ${sel_bg - #222}\n ;color: ${sel_fg}\n ;border: 1px solid ${sel_bg}"
60 | use_system_icons=false
61 |
--------------------------------------------------------------------------------
/命令/另存为.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=\x53e6\x5b58\x4e3a
3 | Command="
4 | copyq:
5 | var defaultPath = Dir().homePath()
6 | var customPath = '/Desktop'
7 | var suffices = {
8 | 'image/svg': 'svg',
9 | 'image/png': 'png',
10 | 'image/jpeg': 'jpg',
11 | 'image/jpg': 'jpg',
12 | 'image/bmp': 'bmp',
13 | 'text/html': 'html',
14 | 'text/plain' : 'txt',
15 | }
16 |
17 | if (customPath) {
18 | currentpath(customPath)
19 | }
20 | else {
21 | currentpath(defaultPath)
22 | }
23 |
24 | function hasSuffix(fileName)
25 | {
26 | return /\\.[0-9a-zA-z]+$/.test(fileName);
27 | }
28 | function addSuffix(fileName, format)
29 | {
30 | var suffix = suffices[format]
31 | return suffix ? fileName + \".\" + suffix : fileName
32 | }
33 | function filterFormats(format)
34 | {
35 | return /^[a-z]/.test(format) && !/^application\\/x/.test(format)
36 | }
37 | function itemFormats(row)
38 | {
39 | return str(read('?', row))
40 | .split('\\n')
41 | .filter(filterFormats)
42 | }
43 | function formatPriority(format)
44 | {
45 | var k = Object.keys(suffices);
46 | var i = k.indexOf(format);
47 | return i === -1 ? k.length : i
48 | }
49 | function reorderFormats(formats)
50 | {
51 | formats.sort(function(lhs, rhs){
52 | var i = formatPriority(lhs);
53 | var j = formatPriority(rhs);
54 | return i === j ? lhs.localeCompare(rhs) : i - j;
55 | })
56 | }
57 | if (selectedtab()) tab(selectedtab())
58 | var row = selectedtab() ? currentitem() : -1
59 | var formats = itemFormats(row)
60 | reorderFormats(formats)
61 | var defaultFileName = dateString('yyyy-MM-dd ')+'\x672a\x547d\x540d'
62 | var keyFormat = '\x683c\x5f0f'
63 | var keyFileName = '\x6587\x4ef6\x540d'
64 | var defaultFormat = formats[0]
65 | var result = dialog(
66 | '.title', '\x53e6\x5b58\x4e3a...',
67 | '.width', 300,
68 | keyFormat, [defaultFormat].concat(formats),
69 | keyFileName, File(defaultFileName)
70 | ) || abort()
71 | var fileName = result[keyFileName]
72 | var format = result[keyFormat]
73 | if (!format || !fileName)
74 | abort()
75 | if (!hasSuffix(fileName))
76 | fileName = addSuffix(fileName, format)
77 | var f = File(fileName)
78 | if (!f.open()) {
79 | popup('\x6253\x5f00\x5931\x8d25 \"' + f.fileName() + '\"', f.errorString())
80 | abort()
81 | }
82 | f.write(selectedtab() ? getitem(currentitem())[format] : clipboard(format))
83 | popup(\"\x4fdd\x5b58\x6210\x529f\", '\x6761\x76ee\x5df2\x4fdd\x5b58\x4e3a \"' + f.fileName() + '\".')"
84 | InMenu=true
85 | Shortcut=ctrl+s
--------------------------------------------------------------------------------
/主题/dracula.ini:
--------------------------------------------------------------------------------
1 | [General]
2 | alt_bg=#21222c
3 | alt_item_css=
4 | bg=#282a36
5 | css=
6 | css_template_items=items
7 | css_template_main_window=main_window
8 | css_template_notification=notification
9 | css_template_tooltip=tooltip
10 | cur_item_css="\n ;border: 0.1em solid ${sel_bg}"
11 | edit_bg=#282a36
12 | edit_fg=#a4ffff
13 | edit_font="Monospace,9,-1,5,50,0,0,0,0,0"
14 | fg=#f8f8f2
15 | find_bg="rgba(40,42,54,100)"
16 | find_fg=#ffffa5
17 | find_font="Segoe UI,9,-1,5,50,0,0,0,0,0,Regular"
18 | font="Segoe UI,9,-1,5,50,0,0,0,0,0,Regular"
19 | font_antialiasing=true
20 | hover_item_css=
21 | icon_size=20
22 | item_css=
23 | item_spacing=
24 | menu_bar_css="\n ;background: ${bg}\n ;color: ${fg}"
25 | menu_bar_disabled_css="\n ;color: ${bg - #666}"
26 | menu_bar_selected_css="\n ;background: ${sel_bg}\n ;color: ${sel_fg}"
27 | menu_css="\n ;border: 1px solid ${sel_bg}\n ;background: ${bg}\n ;color: ${fg}"
28 | notes_bg=#44475a
29 | notes_css=
30 | notes_fg=#d6acff
31 | notes_font="Serif,10,-1,5,50,0,0,0,0,0"
32 | notification_bg=#44475a
33 | notification_fg=#f8f8f2
34 | notification_font=
35 | num_fg=#a4ffff
36 | num_font="Monospace,7,-1,5,25,0,0,0,0,0"
37 | search_bar="\n ;background: ${edit_bg}\n ;color: ${edit_fg}\n ;border: 1px solid ${alt_bg}\n ;margin: 2px"
38 | search_bar_focused="\n ;border: 1px solid ${sel_bg}"
39 | sel_bg=#44475a
40 | sel_fg=#f8f8f2
41 | sel_item_css=
42 | show_number=true
43 | show_scrollbars=true
44 | style_main_window=true
45 | tab_bar_css="\n ;background: ${bg - #222}"
46 | tab_bar_item_counter="\n ;color: ${fg - #044 + #400}\n ;font-size: 6pt"
47 | tab_bar_scroll_buttons_css="\n ;background: ${bg - #222}\n ;color: ${fg}\n ;border: 0"
48 | tab_bar_sel_item_counter="\n ;color: ${sel_bg - #044 + #400}"
49 | tab_bar_tab_selected_css="\n ;padding: 0.5em\n ;background: ${bg}\n ;border: 0.05em solid ${bg}\n ;color: ${fg}"
50 | tab_bar_tab_unselected_css="\n ;border: 0.05em solid ${bg}\n ;padding: 0.5em\n ;background: ${bg - #222}\n ;color: ${fg - #333}"
51 | tab_tree_css="\n ;color: ${fg}\n ;background-color: ${bg}"
52 | tab_tree_item_counter="\n ;color: ${fg - #044 + #400}\n ;font-size: 6pt"
53 | tab_tree_sel_item_counter="\n ;color: ${sel_fg - #044 + #400}"
54 | tab_tree_sel_item_css="\n ;color: ${sel_fg}\n ;background-color: ${sel_bg}\n ;border-radius: 2px"
55 | tool_bar_css="\n ;color: ${fg}\n ;background-color: ${bg}\n ;border: 0"
56 | tool_button_css="\n ;color: ${fg}\n ;background: ${bg}\n ;border: 0\n ;border-radius: 2px"
57 | tool_button_pressed_css="\n ;background: ${sel_bg}"
58 | tool_button_selected_css="\n ;background: ${sel_bg - #222}\n ;color: ${sel_fg}\n ;border: 1px solid ${sel_bg}"
59 | use_system_icons=true
60 |
--------------------------------------------------------------------------------
/主题/dracula_mod.ini:
--------------------------------------------------------------------------------
1 | [General]
2 | alt_bg=#21222c
3 | alt_item_css=
4 | bg=#282a36
5 | css=
6 | css_template_items=items
7 | css_template_main_window=main_window
8 | css_template_menu=menu
9 | css_template_notification=notification
10 | cur_item_css="\r\n ;border: 0.1em solid ${sel_bg}"
11 | edit_bg=#282a36
12 | edit_fg=#a4ffff
13 | edit_font="Courier New,10,-1,5,50,0,0,0,0,0,Regular"
14 | fg=#f8f8f2
15 | find_bg="rgba(40,42,54,100)"
16 | find_fg=#ffffa5
17 | find_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
18 | font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
19 | font_antialiasing=true
20 | hover_item_css=
21 | icon_size=20
22 | item_css=
23 | item_spacing=
24 | menu_bar_css="\r\n ;background: ${bg}\r\n ;color: ${fg}"
25 | menu_bar_disabled_css="\r\n ;color: ${bg - #666}"
26 | menu_bar_selected_css="\r\n ;background: ${sel_bg}\r\n ;color: ${sel_fg}"
27 | menu_css="\r\n ;border: 1px solid ${sel_bg}\r\n ;background: ${bg}\r\n ;color: ${fg}"
28 | notes_bg=#44475a
29 | notes_css=
30 | notes_fg=#d6acff
31 | notes_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
32 | notification_bg=#44475a
33 | notification_fg=#f8f8f2
34 | notification_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
35 | num_fg=#a4ffff
36 | num_font="\x5fae\x8f6f\x96c5\x9ed1,8,-1,5,50,0,0,0,0,0,Regular"
37 | num_margin=2
38 | search_bar="\r\n ;background: ${edit_bg}\r\n ;color: ${edit_fg}\r\n ;border: 1px solid ${alt_bg}\r\n ;margin: 2px"
39 | search_bar_focused="\r\n ;border: 1px solid ${sel_bg}"
40 | sel_bg=#44475a
41 | sel_fg=#f8f8f2
42 | sel_item_css=
43 | show_number=true
44 | show_scrollbars=true
45 | style_main_window=true
46 | tab_bar_css="\r\n ;background: ${bg - #222}"
47 | tab_bar_item_counter="\r\n ;color: ${fg - #044 + #400}\r\n ;font-size: 8pt"
48 | tab_bar_scroll_buttons_css="\r\n ;background: ${bg - #222}\r\n ;color: ${fg}\r\n ;border: 0"
49 | tab_bar_sel_item_counter="\r\n ;color: ${sel_bg - #044 + #400}"
50 | tab_bar_tab_selected_css="\r\n ;padding: 0.5em\r\n ;background: ${bg}\r\n ;border: 0.05em solid ${bg}\r\n ;color: ${fg}"
51 | tab_bar_tab_unselected_css="\r\n ;border: 0.05em solid ${bg}\r\n ;padding: 0.5em\r\n ;background: ${bg - #222}\r\n ;color: ${fg - #333}"
52 | tab_tree_css="\r\n ;color: ${fg}\r\n ;background-color: ${bg}"
53 | tab_tree_item_counter="\r\n ;color: ${fg - #044 + #400}\r\n ;font-size: 8pt"
54 | tab_tree_sel_item_counter="\r\n ;color: ${sel_fg - #044 + #400}"
55 | tab_tree_sel_item_css="\r\n ;color: ${sel_fg}\r\n ;background-color: ${sel_bg}\r\n ;border-radius: 2px"
56 | tool_bar_css="\r\n ;color: ${fg}\r\n ;background-color: ${bg}\r\n ;border: 0"
57 | tool_button_css="\r\n ;color: ${fg}\r\n ;background: ${bg}\r\n ;border: 0\r\n ;border-radius: 2px"
58 | tool_button_pressed_css="\r\n ;background: ${sel_bg}"
59 | tool_button_selected_css="\r\n ;background: ${sel_bg - #222}\r\n ;color: ${sel_fg}\r\n ;border: 1px solid ${sel_bg}"
60 | use_system_icons=true
61 |
--------------------------------------------------------------------------------
/主题/pure.ini:
--------------------------------------------------------------------------------
1 | [General]
2 | alt_bg=#f7f7f7
3 | alt_item_css="\r\n ;border-radius: 2px\r\n ;background: qlineargradient(\r\n x1: 0, y1: 0,\r\n x2: 1.5, y2: 0,\r\n stop: 0 ${alt_bg - #100c0a},\r\n stop: 1 ${alt_bg}\r\n )\r\n "
4 | bg=#ffffff
5 | css=ClipboardBrowser::selected:item{outline:0px}
6 | css_template_items=items
7 | css_template_main_window=main_window
8 | css_template_menu=menu
9 | css_template_notification=notification
10 | cur_item_css="\r\n ;border: 0.1em solid ${sel_bg}"
11 | edit_bg=bg
12 | edit_fg=fg - #111
13 | edit_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
14 | fg=#284350
15 | find_bg="rgba(0,0,0,0)"
16 | find_fg=#08a
17 | find_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,1,0,0,0,Regular"
18 | font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
19 | font_antialiasing=true
20 | hover_item_css=
21 | icon_size=16
22 | item_css=padding:1em
23 | item_spacing=
24 | menu_bar_css="\r\n ;height: 0\r\n ;background: ${bg}\r\n ;color: ${fg}"
25 | menu_bar_disabled_css="\r\n ;color: ${bg - #666}"
26 | menu_bar_selected_css="\r\n ;background: ${sel_bg}\r\n ;color: ${sel_fg}"
27 | menu_css="\r\n ;border: 1px solid ${sel_bg}\r\n ;background: ${bg}\r\n ;color: ${fg}"
28 | notes_bg=bg
29 | notes_css=
30 | notes_fg=#222
31 | notes_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
32 | notification_bg=#333
33 | notification_fg=#ddd
34 | notification_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
35 | num_fg=#586e75
36 | num_font="\x5fae\x8f6f\x96c5\x9ed1,8,-1,5,50,0,0,0,0,0,Regular"
37 | num_margin=2
38 | search_bar="\r\n ;background: ${edit_bg}\r\n ;color: ${edit_fg}\r\n ;border: 1px solid ${alt_bg}\r\n ;margin: 2px"
39 | search_bar_focused="\r\n ;border: 1px solid ${sel_bg}"
40 | sel_bg=bg + #000409 - #100
41 | sel_fg=#234
42 | sel_item_css="\r\n ;border: 1px solid ${bg - #940}\r\n ;border-radius: 2px\r\n ;background: qlineargradient(\r\n x1: 0, y1: 0,\r\n x2: 1.5, y2: 0,\r\n stop: 0 ${sel_bg},\r\n stop: 1 ${bg}\r\n )\r\n "
43 | show_number=true
44 | show_scrollbars=false
45 | style_main_window=true
46 | tab_bar_css="\r\n ;background: ${bg - #222}"
47 | tab_bar_item_counter="\r\n ;color: ${fg - #044 + #400}\r\n ;font-size: 8pt"
48 | tab_bar_scroll_buttons_css="\r\n ;background: ${bg - #222}\r\n ;color: ${fg}\r\n ;border: 0"
49 | tab_bar_sel_item_counter="\r\n ;color: ${sel_bg - #044 + #400}"
50 | tab_bar_tab_selected_css="\r\n ;padding: 0.5em\r\n ;background: ${bg}\r\n ;border: 0.05em solid ${bg}\r\n ;color: ${fg}"
51 | tab_bar_tab_unselected_css="\r\n ;border: 0.05em solid ${bg}\r\n ;padding: 0.5em\r\n ;background: ${bg - #222}\r\n ;color: ${fg - #333}"
52 | tab_tree_css="\r\n ;color: ${fg}\r\n ;background-color: ${bg}"
53 | tab_tree_item_counter="\r\n ;color: ${fg - #044 + #400}\r\n ;font-size: 8pt"
54 | tab_tree_sel_item_counter="\r\n ;color: ${sel_fg - #044 + #400}"
55 | tab_tree_sel_item_css="\r\n ;color: ${sel_fg}\r\n ;background-color: ${sel_bg}\r\n ;border-radius: 2px"
56 | tool_bar_css="\r\n ;color: ${fg}\r\n ;background-color: ${bg}\r\n ;border: 0"
57 | tool_button_css="\r\n ;color: ${fg}\r\n ;background: ${bg}"
58 | tool_button_pressed_css="\r\n ;background: ${sel_bg}"
59 | tool_button_selected_css="\r\n ;background: ${sel_bg - #222}\r\n ;color: ${sel_fg}\r\n ;border: 1px solid ${sel_bg}"
60 | use_system_icons=true
61 |
--------------------------------------------------------------------------------
/主题/mint.ini:
--------------------------------------------------------------------------------
1 | [General]
2 | alt_bg=#e3e9e8
3 | alt_item_css=
4 | bg=#eeeeee
5 | css=
6 | css_template_items=items
7 | css_template_main_window=main_window
8 | css_template_menu=menu
9 | css_template_notification=notification
10 | cur_item_css="\r\r\r\r\r\r\r\n ;border: 0.1em solid ${sel_bg}"
11 | edit_bg=#cccccc
12 | edit_fg=fg - #111
13 | edit_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
14 | fg=#3c3c3c
15 | find_bg="rgba(0,0,0,0)"
16 | find_fg=#f30f0c
17 | find_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
18 | font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
19 | font_antialiasing=true
20 | hover_item_css=
21 | icon_size=16
22 | item_css=padding:0.5em
23 | item_spacing=
24 | menu_bar_css="\r\r\r\r\r\r\r\n ;background: ${bg}\r\r\r\r\r\r\r\n ;color: ${fg + #444}"
25 | menu_bar_disabled_css="\r\r\r\r\r\r\r\n ;color: ${bg - #666}"
26 | menu_bar_selected_css="\r\r\r\r\r\r\r\n ;background: ${sel_bg}\r\r\r\r\r\r\r\n ;color: ${sel_fg}"
27 | menu_css="\r\r\r\r\r\r\r\n ;border-top: 0.08em solid ${bg + #333}\r\r\r\r\r\r\r\n ;border-left: 0.08em solid ${bg + #333}\r\r\r\r\r\r\r\n ;border-bottom: 0.08em solid ${bg - #333}\r\r\r\r\r\r\r\n ;border-right: 0.08em solid ${bg - #333}"
28 | notes_bg=bg
29 | notes_css=
30 | notes_fg=#666666
31 | notes_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
32 | notification_bg=#444444
33 | notification_fg=#ffffff
34 | notification_font="\x5fae\x8f6f\x96c5\x9ed1,10,-1,5,50,0,0,0,0,0,Regular"
35 | num_fg=#009588
36 | num_font="sans-serif,8,-1,5,25,0,0,0,0,0"
37 | num_margin=2
38 | search_bar="\r\r\r\r\r\r\r\n ;background: ${edit_bg}\r\r\r\r\r\r\r\n ;color: ${edit_fg}\r\r\r\r\r\r\r\n ;border: 1px solid ${alt_bg}\r\r\r\r\r\r\r\n ;margin: 2px"
39 | search_bar_focused="\r\r\r\r\r\r\r\n ;border: 1px solid ${sel_bg}"
40 | sel_bg=#7fc6d1
41 | sel_fg=#444444
42 | sel_item_css=
43 | show_number=true
44 | show_scrollbars=true
45 | style_main_window=true
46 | tab_bar_css="\r\r\r\r\r\r\r\n ;background: ${bg - #222}"
47 | tab_bar_item_counter="\r\r\r\r\r\r\r\n ;color: ${fg + #222}\r\n ;font-size: 8pt"
48 | tab_bar_scroll_buttons_css="\r\r\r\r\r\r\r\n ;background: ${bg - #222}\r\r\r\r\r\r\r\n ;color: ${fg}\r\r\r\r\r\r\r\n ;border: 0"
49 | tab_bar_sel_item_counter="\r\r\r\r\r\r\r\n ;color: ${num_fg}"
50 | tab_bar_tab_selected_css="\r\r\r\r\r\r\r\n ;padding: 0.5em\r\r\r\r\r\r\r\n ;background: ${bg}\r\r\r\r\r\r\r\n ;border: 0.05em solid ${bg}\r\r\r\r\r\r\r\n ;color: ${fg}"
51 | tab_bar_tab_unselected_css="\r\r\r\r\r\r\r\n ;border: 0.05em solid ${bg}\r\r\r\r\r\r\r\n ;padding: 0.5em\r\r\r\r\r\r\r\n ;background: ${bg - #222}\r\r\r\r\r\r\r\n ;color: ${fg - #333}"
52 | tab_tree_css="\r\r\r\r\r\r\r\n ;font-family: x5fae\x8f6f\x96c5\x9ed1\r\r\r\r\r\r\r\n ;font-size: 10pt\r\r\r\r\r\r\r\n ;padding: .20em\r\r\r\r\r\r\r\n ;color: ${fg + #222}\r\r\r\r\r\r\r\n ;background-color: ${bg}"
53 | tab_tree_item_counter="\r\r\r\r\r\r\r\n ;padding:.33em\r\r\r\r\r\r\r\n ;color: ${num_fg + #222}\r\r\r\r\r\r\r\n ;font-size: 8pt"
54 | tab_tree_sel_item_counter="\r\r\r\r\r\r\r\n ;color: ${num_fg}"
55 | tab_tree_sel_item_css="\r\r\r\r\r\r\r\n ;color: ${fg + #222}\r\r\r\r\r\r\r\n ;background: ${bg - #211}"
56 | tool_bar_css="\r\r\r\r\r\r\r\n ;color: ${fg}\r\r\r\r\r\r\r\n ;background-color: ${bg}\r\r\r\r\r\r\r\n ;border: 0"
57 | tool_button_css="\r\r\r\r\r\r\r\n ;background-color: transparent"
58 | tool_button_pressed_css="\r\r\r\r\r\r\r\n ;background: ${sel_bg}"
59 | tool_button_selected_css="\r\r\r\r\r\r\r\n ;background: ${sel_bg - #222}\r\r\r\r\r\r\r\n ;color: ${sel_fg}\r\r\r\r\r\r\r\n ;border: 1px solid ${sel_bg}"
60 | use_system_icons=true
61 |
--------------------------------------------------------------------------------
/命令/排序.ini:
--------------------------------------------------------------------------------
1 | [Command]
2 | Name=排序
3 | Command="
4 | copyq:
5 | // 单选条目时对整个标签页进行排序
6 | // 多选条目时对选中的条目进行排序
7 |
8 | var sort_top_pinned = true
9 |
10 | var date_reverse = {
11 | [mimeText]: '最新在前',
12 | [mimeIcon]: '',
13 | format: \"application/x-copyq-user-item-time\",
14 | reverse: true
15 | }
16 |
17 | var date = {
18 | [mimeText]: '最旧在前',
19 | [mimeIcon]: '',
20 | format: \"application/x-copyq-user-item-time\",
21 | reverse: false
22 | }
23 |
24 | var pinned = {
25 | [mimeText]: '置顶固定',
26 | [mimeIcon]: '',
27 | format: \"application/x-copyq-item-pinned\",
28 | reverse: false
29 | }
30 |
31 | var alphabet = {
32 | [mimeText]: '字母顺序',
33 | [mimeIcon]: '',
34 | format: mimeText,
35 | reverse: false
36 | }
37 | var alphabet_reverse = {
38 | [mimeText]: '字母倒序',
39 | [mimeIcon]: '',
40 | format: mimeText,
41 | reverse: true
42 | }
43 |
44 | var size_reverse = {
45 | [mimeText]: '从大到小',
46 | [mimeIcon]: '',
47 | format: \"ItemSize\",
48 | reverse: true
49 | }
50 |
51 | var size = {
52 | [mimeText]: '从小到大',
53 | [mimeIcon]: '',
54 | format: \"ItemSize\",
55 | reverse: false
56 | }
57 |
58 | // 排序菜单项目
59 | var sorts = [
60 | date_reverse,
61 | date,
62 | pinned,
63 | alphabet,
64 | alphabet_reverse,
65 | size_reverse,
66 | size
67 | ]
68 | // 显示排序菜单
69 | var selectedIndex = menuItems(sorts)
70 |
71 | var sel = ItemSelection().current()
72 | if (sel.length <= 1) {
73 | // 当前标签页只一个条目,则退出
74 | length() <= 1 ? abort() : ''
75 | // 单选,将选区设置为整个标签页
76 | tab(selectedTab())
77 | sel = ItemSelection().selectAll()
78 | if (sort_top_pinned) handle_pinned(sel.length)
79 | } else {
80 | // 多选,判断是否从0开始选择。
81 | // 是:处理固定行;否:无操作
82 | if (sel.rows()[0] == 0 && sort_top_pinned) {
83 | handle_pinned(sel.length)
84 | }
85 | }
86 |
87 | const rows = sel.rows()
88 | var order = ''
89 | if (selectedIndex != -1) {
90 | // 获取选择的排序条件
91 | const selectedFormat = sorts[selectedIndex].format
92 | switch (selectedFormat) {
93 | case 'ItemSize':
94 | popup('排序中⏳', '', rows.length * 3)
95 | order = sizeList()
96 | break;
97 | case \"application/x-copyq-item-pinned\":
98 | order = sel.itemsFormat(selectedFormat).map((item) => item === undefined);
99 | break;
100 | default:
101 | order = sel.itemsFormat(selectedFormat)
102 | }
103 | } else {
104 | abort()
105 | }
106 |
107 | // 排序
108 | if (sorts[selectedIndex].reverse) {
109 | sel.sort((i, j) => order[i] > order[j]);
110 | } else {
111 | sel.sort((i, j) => order[i] < order[j]);
112 | }
113 | popup('排序完成✅', '', 1000)
114 |
115 | // 获取sel选区中每个条目的字节数
116 | function sizeList() {
117 | var items = sel.items()
118 | var sizes = []
119 | for (let row in rows) {
120 | var itemSize = 0
121 | var item = items[row]
122 | for (var format in item) {
123 | itemSize += read(format, row).size()
124 | }
125 | sizes.push(itemSize)
126 | }
127 | return sizes
128 | }
129 |
130 | // 处理固定行
131 | function handle_pinned(sel_length) {
132 | var pinned = []
133 | // 获取从0行开始连续的固定行
134 | for (var i = 0; i < sel_length; i++) {
135 | if (plugins.itempinned.isPinned(i)) {
136 | pinned.push(i)
137 | } else {
138 | break
139 | }
140 | }
141 | // 固定行向下移动1行
142 | if (0 < pinned.length < sel_length) {
143 | var selAll = ItemSelection().selectAll()
144 | selAll.deselectIndexes(pinned)
145 | selAll.invert().move(pinned.length + 1)
146 | }
147 | }"
148 | InMenu=true
149 | Icon=
150 | Shortcut=shift+s
--------------------------------------------------------------------------------
/命令/可撤销的删除.ini:
--------------------------------------------------------------------------------
1 | [Commands]
2 | 1\Name=撤销删除
3 | 1\Command="
4 | copyq:
5 | // true: update time_mime and tags_mime
6 | // false: keep tags, and use time from tags
7 | const update_item_time = true;
8 |
9 | const trash_tab = settings(\"trash_tab\");
10 | const tab_mime = \"application/x-copyq-user-tab\";
11 | const index_mime = \"application/x-copyq-user-index\";
12 | const time_mime = \"application/x-copyq-user-time\";
13 | const tags_mime = \"application/x-copyq-tags\";
14 | const now = dateString(\"yyyy-MM-dd hh:mm:ss\");
15 | const time_reg = /\\d{4}-\\d{2}-\\d{2} \\d{2}\\:\\d{2}\\:\\d{2}/;
16 |
17 | tab(trash_tab);
18 | if (length() == 0) {
19 | popup('Nothing to undo.');
20 | abort();
21 | }
22 |
23 | const trigger_tab = selectedTab();
24 | let remove_rows = []; // list to be remove, type: int array
25 | let target_index = [];
26 | let items = []; // list to be restore, type: item array
27 | let target_tab = [];
28 |
29 | // Triggered from the trash tab, only selected items are restored
30 | if (trigger_tab == trash_tab) {
31 | items = ItemSelection().current().items();
32 | remove_rows = selectedItems();
33 | target_tab = items.map(item => str(item[tab_mime]) || selectedTab());
34 | target_index = items.map((_, i) => i); // items are always restored at the top of the tab
35 | } else {
36 | let item = getitem(0);
37 | let time = str(item[time_mime]);
38 | let i = 0;
39 | while (true) {
40 | target_tab.push(str(item[tab_mime]) || selectedTab());
41 | target_index.push(item[index_mime] || 0)
42 | remove_rows.push(i);
43 | items.push(item);
44 |
45 | item = getItem(++i)
46 | if (!time || time !== str(item[time_mime])) {
47 | break;
48 | }
49 | }
50 | }
51 |
52 | for (let i = items.length-1; i >=0; i--) {
53 | let item = items[i];
54 |
55 | delete item[tab_mime];
56 | delete item[index_mime];
57 |
58 | let old_tag = str(item[tags_mime]);
59 | let old_time = old_tag.match(time_reg);
60 | if (update_item_time && old_time) {
61 | item[time_mime] = now;
62 | item[tags_mime] = old_tag.replace(old_time, now);
63 | } else {
64 | item[time_mime] = old_time;
65 | }
66 | tab(target_tab[i]);
67 | insert(target_index[i], item);
68 | }
69 |
70 | show(target_tab[0]);
71 | tab(target_tab[0]);
72 |
73 | selectItems.apply(this, target_index);
74 |
75 | tab(trash_tab);
76 | remove.apply(this, remove_rows);"
77 | 1\InMenu=true
78 | 1\Icon=
79 | 1\Shortcut=ctrl+z
80 | 2\Name=删除至回收站
81 | 2\Command="
82 | const onItemsRemoved_ = global.onItemsRemoved
83 | global.onItemsRemoved = function () {
84 | const trash_tab = '回收站'
85 | const tab_mime = 'application/x-copyq-user-tab'
86 | const index_mime = 'application/x-copyq-user-index'
87 | const time_mime = 'application/x-copyq-user-time'
88 |
89 | const source_tab = selectedtab()
90 | const sel = ItemSelection().current()
91 |
92 | if (!settings('trash_tab'))
93 | settings('trash_tab', trash_tab)
94 |
95 | tab(trash_tab)
96 | var available = config('maxitems') - count()
97 | if (sel.length > available) {
98 | popup('待删除条目过多,无法移至回收站。\\n请先清理回收站,或调大条目历史数量上限。', 3000)
99 | }
100 | if (source_tab == trash_tab) {
101 | serverLog(`Removing ${sel.length} items from ${source_tab}`)
102 | } else {
103 | serverLog(`Removing ${sel.length} items from ${source_tab} to ${trash_tab}`)
104 | const time = dateString('yyyy-MM-dd hh:mm:ss.zzz')
105 |
106 | const rows = sel.rows()
107 | let trashed = sel.items().map((item, i) => {
108 | item[tab_mime] = source_tab
109 | item[index_mime] = rows[i]
110 | item[time_mime] = time
111 | return item
112 | })
113 |
114 | tab(trash_tab)
115 | write(0, trashed)
116 | tab(source_tab)
117 | }
118 | onItemsRemoved_()
119 | }"
120 | 2\IsScript=true
121 | 2\Icon=
122 | size=2
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/命令/全部命令.ini:
--------------------------------------------------------------------------------
1 | [Commands]
2 | 1\Name=全局搜索
3 | 1\Command="
4 | copyq:
5 | // 默认快捷键与Win10自带输入法的简繁切换快捷键冲突,请自行调整
6 | // Name for tab for storing matching items.
7 | var search_tab_name = \"全局搜索\"
8 | // Returns true if item at index matches regex.
9 | function item_matches(item_index, re)
10 | {
11 | var item = getitem(item_index)
12 | var note = str(item[mimeItemNotes])
13 | var text = str(item[mimeText])
14 | try {
15 | var tag = str(plugins.itemtags.tags(item_index))
16 | } catch (e) {
17 | var tag = ''
18 | }
19 | return text && (re.test(text) || re.test(note) || re.test(tag))
20 | }
21 | // Ask for search expression.
22 | var match = dialog(\"搜索\")
23 | if (!match)
24 | abort()
25 | var re = new RegExp(match,'i') // 'i' case-insensitive
26 | // Clear tab with results.
27 | try {
28 | removeTab(search_tab_name)
29 | } catch (e) {}
30 | // Search all tabs.
31 | var tab_names = tab()
32 | for (var i in tab_names) {
33 | var tab_name = tab_names[i]
34 | tab(tab_name)
35 | var item_count = count()
36 | // Search all items in tab.
37 | for (var j = 0; j < item_count; ++j) {
38 | // Add matching item to tab with results.
39 | if (item_matches(j, re)) {
40 | var item = getItem(j)
41 | tab(search_tab_name)
42 | setItem(j, item)
43 | tab(tab_name)
44 | }
45 | }
46 | }
47 | show(search_tab_name)"
48 | 1\InMenu=true
49 | 1\Icon=
50 | 1\Shortcut=ctrl+shift+f
51 | 2\Name=筛选
52 | 2\Command="
53 | copyq:
54 | var image = {
55 | [mimeText]: '图片 ---------------- I',
56 | [mimeIcon]: '',
57 | filter: '(^image/.*)|(?=^file\\:.*\\.(png|jpe?g|bmp|svg|gif)$)',
58 | shortcut: 'i'
59 | }
60 |
61 | var file = {
62 | [mimeText]: '文件 ---------------- F',
63 | [mimeIcon]: '',
64 | filter: '(?=^file://)',
65 | shortcut: 'f'
66 | }
67 |
68 | var url = {
69 | [mimeText]: 'URL ---------------- U',
70 | [mimeIcon]: '',
71 | filter: '^(?=(https?|ftps?|smb|mailto)://)',
72 | shortcut: 'u'
73 | }
74 |
75 | var html = {
76 | [mimeText]: 'HTML',
77 | [mimeIcon]: '',
78 | filter: '^text/html$',
79 | shortcut: 'h'
80 | }
81 |
82 | var PhoneMail = {
83 | [mimeText]: '手机号/邮箱',
84 | [mimeIcon]: '',
85 | filter: '(^0{0,1}(13[0-9]|15[7-9]|153|156|18[7-9])[0-9]{8}$)|(^([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\_|\\.]?)*[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$)',
86 | shortcut: 'p'
87 | }
88 |
89 | var filters = [image, file, url, html, PhoneMail]
90 | var selectedFilter = ''
91 | var shortcut = str(data(mimeShortcut))
92 |
93 | if (shortcut) {
94 | for (let f in filters) {
95 | if (filters[f].shortcut === shortcut) {
96 | selectedFilter = filters[f].filter
97 | filter_toggle(selectedFilter)
98 | abort()
99 | }
100 | }
101 | }
102 |
103 | var selectedIndex = menuItems(filters)
104 |
105 | if (selectedIndex != -1) {
106 | selectedFilter = filters[selectedIndex].filter
107 | filter_toggle(selectedFilter)
108 | } else {
109 | filter('')
110 | }
111 |
112 | function filter_toggle(filter_) {
113 | if (filter() == filter_) {
114 | filter('')
115 | }
116 | else {
117 | filter('')
118 | filter(filter_)
119 | }
120 | }"
121 | 2\InMenu=true
122 | 2\Icon=
123 | 2\Shortcut=shift+f, f, i, u
124 | 3\Name=排序
125 | 3\Command="
126 | copyq:
127 | // 单选条目时对整个标签页进行排序
128 | // 多选条目时对选中的条目进行排序
129 |
130 | var sort_top_pinned = true
131 |
132 | var date_reverse = {
133 | [mimeText]: '最新在前',
134 | [mimeIcon]: '',
135 | format: \"application/x-copyq-user-item-time\",
136 | reverse: true
137 | }
138 |
139 | var date = {
140 | [mimeText]: '最旧在前',
141 | [mimeIcon]: '',
142 | format: \"application/x-copyq-user-item-time\",
143 | reverse: false
144 | }
145 |
146 | var pinned = {
147 | [mimeText]: '置顶固定',
148 | [mimeIcon]: '',
149 | format: \"application/x-copyq-item-pinned\",
150 | reverse: false
151 | }
152 |
153 | var alphabet = {
154 | [mimeText]: '字母顺序',
155 | [mimeIcon]: '',
156 | format: mimeText,
157 | reverse: false
158 | }
159 | var alphabet_reverse = {
160 | [mimeText]: '字母倒序',
161 | [mimeIcon]: '',
162 | format: mimeText,
163 | reverse: true
164 | }
165 |
166 | var size_reverse = {
167 | [mimeText]: '从大到小',
168 | [mimeIcon]: '',
169 | format: \"ItemSize\",
170 | reverse: true
171 | }
172 |
173 | var size = {
174 | [mimeText]: '从小到大',
175 | [mimeIcon]: '',
176 | format: \"ItemSize\",
177 | reverse: false
178 | }
179 |
180 | // 排序菜单项目
181 | var sorts = [
182 | date_reverse,
183 | date,
184 | pinned,
185 | alphabet,
186 | alphabet_reverse,
187 | size_reverse,
188 | size
189 | ]
190 | // 显示排序菜单
191 | var selectedIndex = menuItems(sorts)
192 |
193 | var sel = ItemSelection().current()
194 | if (sel.length <= 1) {
195 | // 当前标签页只一个条目,则退出
196 | length() <= 1 ? abort() : ''
197 | // 单选,将选区设置为整个标签页
198 | tab(selectedTab())
199 | sel = ItemSelection().selectAll()
200 | if (sort_top_pinned) handle_pinned(sel.length)
201 | } else {
202 | // 多选,判断是否从0开始选择。
203 | // 是:处理固定行;否:无操作
204 | if (sel.rows()[0] == 0 && sort_top_pinned) {
205 | handle_pinned(sel.length)
206 | }
207 | }
208 |
209 | const rows = sel.rows()
210 | var order = ''
211 | if (selectedIndex != -1) {
212 | // 获取选择的排序条件
213 | const selectedFormat = sorts[selectedIndex].format
214 | switch (selectedFormat) {
215 | case 'ItemSize':
216 | popup('排序中⏳', '', rows.length * 3)
217 | order = sizeList()
218 | break;
219 | case \"application/x-copyq-item-pinned\":
220 | order = sel.itemsFormat(selectedFormat).map((item) => item === undefined);
221 | break;
222 | default:
223 | order = sel.itemsFormat(selectedFormat)
224 | }
225 | } else {
226 | abort()
227 | }
228 |
229 | // 排序
230 | if (sorts[selectedIndex].reverse) {
231 | sel.sort((i, j) => order[i] > order[j]);
232 | } else {
233 | sel.sort((i, j) => order[i] < order[j]);
234 | }
235 | popup('排序完成✅', '', 1000)
236 |
237 | // 获取sel选区中每个条目的字节数
238 | function sizeList() {
239 | var items = sel.items()
240 | var sizes = []
241 | for (let row in rows) {
242 | var itemSize = 0
243 | var item = items[row]
244 | for (var format in item) {
245 | itemSize += read(format, row).size()
246 | }
247 | sizes.push(itemSize)
248 | }
249 | return sizes
250 | }
251 |
252 | // 处理固定行
253 | function handle_pinned(sel_length) {
254 | var pinned = []
255 | // 获取从0行开始连续的固定行
256 | for (var i = 0; i < sel_length; i++) {
257 | if (plugins.itempinned.isPinned(i)) {
258 | pinned.push(i)
259 | } else {
260 | break
261 | }
262 | }
263 | // 固定行向下移动1行
264 | if (0 < pinned.length < sel_length) {
265 | var selAll = ItemSelection().selectAll()
266 | selAll.deselectIndexes(pinned)
267 | selAll.invert().move(pinned.length + 1)
268 | }
269 | }"
270 | 3\InMenu=true
271 | 3\Icon=
272 | 3\Shortcut=shift+s
273 | 4\Name=----------------------------------
274 | 4\Command="
275 | // 虚拟分割线
276 |
277 |
278 |
"
279 | 4\InMenu=true
280 | 5\Name=另存为
281 | 5\Command="
282 | copyq:
283 | var defaultPath = Dir().homePath()
284 | var customPath = '/Desktop'
285 | var suffices = {
286 | 'image/svg': 'svg',
287 | 'image/png': 'png',
288 | 'image/jpeg': 'jpg',
289 | 'image/jpg': 'jpg',
290 | 'image/bmp': 'bmp',
291 | 'text/html': 'html',
292 | 'text/plain' : 'txt',
293 | }
294 |
295 | if (customPath) {
296 | currentpath(customPath)
297 | }
298 | else {
299 | currentpath(defaultPath)
300 | }
301 |
302 | function hasSuffix(fileName)
303 | {
304 | return /\\.[0-9a-zA-z]+$/.test(fileName);
305 | }
306 | function addSuffix(fileName, format)
307 | {
308 | var suffix = suffices[format]
309 | return suffix ? fileName + \".\" + suffix : fileName
310 | }
311 | function filterFormats(format)
312 | {
313 | return /^[a-z]/.test(format) && !/^application\\/x/.test(format)
314 | }
315 | function itemFormats(row)
316 | {
317 | return str(read('?', row))
318 | .split('\\n')
319 | .filter(filterFormats)
320 | }
321 | function formatPriority(format)
322 | {
323 | var k = Object.keys(suffices);
324 | var i = k.indexOf(format);
325 | return i === -1 ? k.length : i
326 | }
327 | function reorderFormats(formats)
328 | {
329 | formats.sort(function(lhs, rhs){
330 | var i = formatPriority(lhs);
331 | var j = formatPriority(rhs);
332 | return i === j ? lhs.localeCompare(rhs) : i - j;
333 | })
334 | }
335 | if (selectedtab()) tab(selectedtab())
336 | var row = selectedtab() ? currentitem() : -1
337 | var formats = itemFormats(row)
338 | reorderFormats(formats)
339 | var defaultFileName = dateString('yyyy-MM-dd ')+'未命名'
340 | var keyFormat = '格式'
341 | var keyFileName = '文件名'
342 | var defaultFormat = formats[0]
343 | var result = dialog(
344 | '.title', '另存为...',
345 | '.width', 300,
346 | keyFormat, [defaultFormat].concat(formats),
347 | keyFileName, File(defaultFileName)
348 | ) || abort()
349 | var fileName = result[keyFileName]
350 | var format = result[keyFormat]
351 | if (!format || !fileName)
352 | abort()
353 | if (!hasSuffix(fileName))
354 | fileName = addSuffix(fileName, format)
355 | var f = File(fileName)
356 | if (!f.open()) {
357 | popup('打开失败 \"' + f.fileName() + '\"', f.errorString())
358 | abort()
359 | }
360 | f.write(selectedtab() ? getitem(currentitem())[format] : clipboard(format))
361 | popup(\"保存成功\", '条目已保存为 \"' + f.fileName() + '\".')"
362 | 5\InMenu=true
363 | 5\Shortcut=ctrl+s
364 | 6\Name=清理当前标签页
365 | 6\MatchCommand="
366 | copyq: tab(selectedTab()); if (size() == 0) fail()"
367 | 6\Command="
368 | copyq:
369 | var source_tab = selectedtab()
370 | var sel = ItemSelection(selectedTab()).selectRemovable()
371 |
372 | if (sel.length === 0)
373 | abort()
374 |
375 | if (source_tab == settings('trash_tab')) {
376 | var text = dialog(
377 | '.title', '确认清理?',
378 | '.label', '⚠︎ 无法撤销!',
379 | '.combo:确认 ', ['是', '否']
380 | )
381 | if (text == '是') {
382 | sel.removeAll()
383 | }
384 | }"
385 | 6\InMenu=true
386 | 6\Icon=
387 | 7\Name=检查过期条目
388 | 7\Command="
389 | copyq:
390 | check_items_expire(1)
391 |
392 |
"
393 | 7\InMenu=true
394 | 7\Icon=
395 | 8\Name=撤销删除
396 | 8\Command="
397 | copyq:
398 | // true: update time_mime and tags_mime
399 | // false: keep tags, and use time from tags
400 | const update_item_time = true;
401 |
402 | const trash_tab = settings(\"trash_tab\");
403 | const tab_mime = \"application/x-copyq-user-tab\";
404 | const index_mime = \"application/x-copyq-user-index\";
405 | const time_mime = \"application/x-copyq-user-time\";
406 | const tags_mime = \"application/x-copyq-tags\";
407 | const now = dateString(\"yyyy-MM-dd hh:mm:ss\");
408 | const time_reg = /\\d{4}-\\d{2}-\\d{2} \\d{2}\\:\\d{2}\\:\\d{2}/;
409 |
410 | tab(trash_tab);
411 | if (length() == 0) {
412 | popup('Nothing to undo.');
413 | abort();
414 | }
415 |
416 | const trigger_tab = selectedTab();
417 | let remove_rows = []; // list to be remove, type: int array
418 | let target_index = [];
419 | let items = []; // list to be restore, type: item array
420 | let target_tab = [];
421 |
422 | // Triggered from the trash tab, only selected items are restored
423 | if (trigger_tab == trash_tab) {
424 | items = ItemSelection().current().items();
425 | remove_rows = selectedItems();
426 | target_tab = items.map(item => str(item[tab_mime]) || selectedTab());
427 | target_index = items.map((_, i) => i); // items are always restored at the top of the tab
428 | } else {
429 | let item = getitem(0);
430 | let time = str(item[time_mime]);
431 | let i = 0;
432 | while (true) {
433 | target_tab.push(str(item[tab_mime]) || selectedTab());
434 | target_index.push(item[index_mime] || 0)
435 | remove_rows.push(i);
436 | items.push(item);
437 |
438 | item = getItem(++i)
439 | if (!time || time !== str(item[time_mime])) {
440 | break;
441 | }
442 | }
443 | }
444 |
445 | for (let i = items.length-1; i >=0; i--) {
446 | let item = items[i];
447 |
448 | delete item[tab_mime];
449 | delete item[index_mime];
450 |
451 | let old_tag = str(item[tags_mime]);
452 | let old_time = old_tag.match(time_reg);
453 | if (update_item_time && old_time) {
454 | item[time_mime] = now;
455 | item[tags_mime] = old_tag.replace(old_time, now);
456 | } else {
457 | item[time_mime] = old_time;
458 | }
459 | tab(target_tab[i]);
460 | insert(target_index[i], item);
461 | }
462 |
463 | show(target_tab[0]);
464 | tab(target_tab[0]);
465 |
466 | selectItems.apply(this, target_index);
467 |
468 | tab(trash_tab);
469 | remove.apply(this, remove_rows);"
470 | 8\InMenu=true
471 | 8\Icon=
472 | 8\Shortcut=ctrl+z
473 | 9\Name=删除至回收站
474 | 9\Command="
475 | const onItemsRemoved_ = global.onItemsRemoved
476 | global.onItemsRemoved = function () {
477 | const trash_tab = '回收站'
478 | const tab_mime = 'application/x-copyq-user-tab'
479 | const index_mime = 'application/x-copyq-user-index'
480 | const time_mime = 'application/x-copyq-user-time'
481 |
482 | const source_tab = selectedtab()
483 | const sel = ItemSelection().current()
484 |
485 | if (!settings('trash_tab'))
486 | settings('trash_tab', trash_tab)
487 |
488 | tab(trash_tab)
489 | var available = config('maxitems') - count()
490 | if (sel.length > available) {
491 | popup('待删除条目过多,无法移至回收站。\\n请先清理回收站,或调大条目历史数量上限。', 3000)
492 | }
493 | if (source_tab == trash_tab) {
494 | serverLog(`Removing ${sel.length} items from ${source_tab}`)
495 | } else {
496 | serverLog(`Removing ${sel.length} items from ${source_tab} to ${trash_tab}`)
497 | const time = dateString('yyyy-MM-dd hh:mm:ss.zzz')
498 |
499 | const rows = sel.rows()
500 | let trashed = sel.items().map((item, i) => {
501 | item[tab_mime] = source_tab
502 | item[index_mime] = rows[i]
503 | item[time_mime] = time
504 | return item
505 | })
506 |
507 | tab(trash_tab)
508 | write(0, trashed)
509 | tab(source_tab)
510 | }
511 | onItemsRemoved_()
512 | }"
513 | 9\IsScript=true
514 | 9\Icon=
515 | 10\Name=----------------------------------
516 | 10\Command="
517 | // 虚拟分割线
518 |
519 |
"
520 | 10\InMenu=true
521 | 11\Name=切换固定
522 | 11\MatchCommand="
523 | copyq:
524 | var row = index()
525 | if (plugins.itempinned.isPinned(row)) {
526 | menuItem['text'] = '取消固定'
527 | menuItem['tag'] = 'x'
528 | } else {
529 | menuItem['text'] = '固定'
530 | menuItem['tag'] = ''
531 | }"
532 | 11\Command="
533 | copyq:
534 | // 是否移动至标签页顶部
535 | var top = true
536 |
537 | var len = selectedItems().length
538 | if (plugins.itempinned.isPinned(currentItem())) {
539 | plugins.itempinned.unpin()
540 | removeData(mimeColor)
541 | } else {
542 | var color = 'rgba(255,0,0,0.1)'
543 | setData(mimeColor, color)
544 | plugins.itempinned.pin()
545 | if (top) {
546 | move(0)
547 | selectItems.apply(this, Array.from({length: len}, (_, i) => i))
548 | }
549 | }"
550 | 11\InMenu=true
551 | 11\Icon=
552 | 11\Shortcut=ctrl+shift+p
553 | 12\Name=切换高亮
554 | 12\MatchCommand="
555 | copyq:
556 | var color = 'rgba(255, 255, 0, 0.5)'
557 | var currentColor = str(selectedItemData(0)[mimeColor])
558 | if (currentColor != color) {
559 | menuItem['text'] = '高亮'
560 | menuItem['tag'] = '__'
561 | menuItem['color'] = color.replace(/\\d+\\.\\d+/, 1)
562 | } else {
563 | menuItem['text'] = '取消高亮'
564 | menuItem['tag'] = 'x'
565 | menuItem['color'] = 'white'
566 | }"
567 | 12\Command="
568 | copyq:
569 | var color = 'rgba(255, 255, 0, 0.5)'
570 | var currentColor = str(selectedItemData(0)[mimeColor]);
571 | if (currentColor != color) {
572 | setData(mimeColor, color)
573 | } else {
574 | removeData(mimeColor)
575 | }"
576 | 12\InMenu=true
577 | 12\Enable=false
578 | 12\Icon=
579 | 12\Shortcut=ctrl+m
580 | 13\Name=标记|切换“重要”标记
581 | 13\Command="
582 | copyq: (plugins.itemtags.hasTag(decodeURIComponent('%E9%87%8D%E8%A6%81')) ? plugins.itemtags.untag : plugins.itemtags.tag)(decodeURIComponent('%E9%87%8D%E8%A6%81'))
583 |
"
584 | 13\InMenu=true
585 | 13\Icon=
586 | 13\Shortcut=shift+i
587 | 14\Name=标记|添加一个标记
588 | 14\Command="
589 | copyq: plugins.itemtags.tag()
590 |
591 |
"
592 | 14\InMenu=true
593 | 14\Icon=
594 | 14\Shortcut=shift+t
595 | 15\Name=标记|移除一个标记
596 | 15\Command="
597 | copyq: plugins.itemtags.untag()
598 |
599 |
"
600 | 15\InMenu=true
601 | 15\Icon=
602 | 15\Shortcut=ctrl+shift+t
603 | 16\Name=标记|清理所有标记
604 | 16\Command="
605 | copyq: plugins.itemtags.clearTags()
606 |
607 |
"
608 | 16\InMenu=true
609 | 16\Icon=
610 | 17\Name=----------------------------------
611 | 17\Command="
612 | // 虚拟分割线
613 |
614 |
"
615 | 17\InMenu=true
616 | 18\Name=合并所选项
617 | 18\MatchCommand="
618 | copyq:
619 | if (selectedItems().length < 2) fail()
620 |
"
621 | 18\Command="
622 | copyq add -- %1
623 |
"
624 | 18\InMenu=true
625 | 19\Name=粘贴为纯文本
626 | 19\Command="
627 | copyq:
628 | var text = input()
629 | copy(text)
630 | copySelection(text)
631 | paste()"
632 | 19\InMenu=true
633 | 19\HideWindow=true
634 | 19\Icon=
635 | 19\Shortcut=shift+v
636 | 20\Name=清理格式并粘贴
637 | 20\Command="
638 | copyq:
639 |
640 | var text = str(input())
641 |
642 | if (!text) {
643 | try {
644 | copy()
645 | } catch (e) {
646 | popup('复制失败,请重新选择文本!', e, time=2000)
647 | abort()
648 | }
649 | text = str(clipboard())
650 | }
651 |
652 | copy(format(text))
653 | paste()
654 |
655 | function format(text) {
656 | //全角转半角
657 | text = ToCDB(text).trim()
658 | // 删除所有回车换行符
659 | text = text.replace(new RegExp(/[\\r\\n]+/g), ' ')
660 | // 两个以上的空格替换成一个
661 | text = text.replace(new RegExp(/\\s+/g), ' ')
662 | // 删除单个字母(A/a除外)之间的空格
663 | text = text.replace(/([^Aa])\\s(?!\\S{2,}|A|a)/g,'$1')
664 | return text
665 | }
666 |
667 | function ToCDB(str) {
668 | var tmp = \"\"
669 | for(var i=0;i 65280 && str.charCodeAt(i) < 65375){
675 | tmp += String.fromCharCode(str.charCodeAt(i)-65248)
676 | }
677 | else{
678 | tmp += String.fromCharCode(str.charCodeAt(i))
679 | }
680 | }
681 | return tmp
682 | }"
683 | 20\InMenu=true
684 | 20\HideWindow=true
685 | 20\Icon=
686 | 20\Shortcut=alt+shift+f
687 | 21\Name=用浏览器打开
688 | 21\Match="^(?=(https?|ftps?|smb|mailto)://)(?=\\S{12,})"
689 | 21\Command="
690 | copyq open %1
691 |
692 |
693 |
694 |
695 |
696 |
697 |
"
698 | 21\InMenu=true
699 | 21\HideWindow=true
700 | 21\Icon=
701 | 21\Shortcut=ctrl+b
702 | 22\Name=----------------------------------
703 | 22\Command="
704 | // 虚拟分割线
705 |
706 |
"
707 | 22\InMenu=true
708 | 23\Name=显示/隐藏标签页
709 | 23\Command="
710 | copyq:
711 | var on = config(\"hide_tabs\") === \"true\"
712 | config(\"hide_tabs\", !on)"
713 | 23\InMenu=true
714 | 23\Enable=false
715 | 23\Icon=
716 | 23\Shortcut=f11
717 | 24\Name=切换置顶状态
718 | 24\MatchCommand="
719 | copyq:
720 | var on = config(\"always_on_top\") === \"true\"
721 | if (on) {
722 | menuItem['text'] = '取消置顶'
723 | menuItem['tag'] = 'x'
724 | } else {
725 | menuItem['text'] = '置顶窗口'
726 | menuItem['tag'] = ''
727 | }"
728 | 24\Command="
729 | copyq:
730 | var rows = selectedItems()
731 | var on = config(\"always_on_top\") === \"true\"
732 |
733 | config(\"always_on_top\", !on)
734 | config(\"activate_closes\",on)
735 | config(\"close_on_unfocus\",on)
736 |
737 | selectItems.apply(this, rows)"
738 | 24\InMenu=true
739 | 24\Icon=
740 | 24\Shortcut=ctrl+`
741 | 25\Name=切换简洁模式
742 | 25\MatchCommand="
743 | copyq:
744 | var on = config(\"show_simple_items\") === \"true\"
745 | if (on) {
746 | menuItem['text'] = '普通模式'
747 | menuItem['icon'] = ''
748 | } else {
749 | menuItem['text'] = '简洁模式'
750 | menuItem['icon'] = ''
751 | }"
752 | 25\Command="
753 | copyq:
754 |
755 | popup('按 F8 切换简洁/普通模式','',3000)
756 |
757 | var rows = selectedItems()
758 |
759 | var on = config(\"show_simple_items\") === \"true\"
760 |
761 | // 不要随意调整下面选项的顺序,可能导致切换模式时窗口闪烁增加
762 |
763 | // 隐藏标签栏
764 | config(\"hide_tabs\", !on)
765 |
766 | // 隐藏工具栏
767 | config(\"hide_toolbar\", !on)
768 |
769 | // 单行显示,不能关闭此项
770 | config(\"show_simple_items\", !on)
771 |
772 | // 切换主题(pure主题会隐藏菜单栏,mint则不会)
773 | !on ? loadTheme(\"./config/copyq/themes/pure.ini\") : loadTheme(\"./config/copyq/themes/mint.ini\")
774 |
775 | // 失去焦点时不关闭窗口
776 | config(\"activate_closes\", on)
777 | config(\"close_on_unfocus\", on)
778 |
779 | // 置顶显示,关闭此项可大幅减少窗口闪烁
780 | // config(\"always_on_top\",!on)
781 |
782 | selectItems.apply(this, rows)"
783 | 25\InMenu=true
784 | 25\Icon=
785 | 25\Shortcut=f8
786 | 26\Name=自动预览图片/长文本
787 | 26\MatchCommand="
788 | copyq:
789 | if (visible()) {
790 | if (settings('AutoPreview') == 1) {
791 |
792 | if (selectedItems().length > 1) {
793 | preview(false)
794 | abort()
795 | }
796 |
797 | var preview_image = true
798 | var preview_longText = true
799 |
800 | // The characters count of long-text
801 | var LongTextCharacters = 100
802 | // The lines count of long-text
803 | var LongTextLines = 2
804 |
805 | var format = str(dataFormats())
806 | var content = str(data(mimeUriList)) || str(data(mimeText))
807 |
808 | icon_on()
809 | preview(condition())
810 | } else {
811 | icon_off()
812 | }
813 | }
814 | function condition() {
815 | return (
816 | preview_image && (/image\\/.*/.test(format) || /^file:.*(png|jpe?g|bmp|svg|gif|ico|webp)$/.test(content))
817 | ||
818 | preview_longText && (content.length > LongTextCharacters || (content.split(/\\n/)).length > LongTextLines)
819 | )
820 | }
821 | function icon_on() {
822 | menuItem['text'] = '关闭自动预览'
823 | menuItem['icon'] = ''
824 | }
825 | function icon_off() {
826 | menuItem['text'] = '开启自动预览'
827 | menuItem['icon'] = ''
828 | }"
829 | 26\Command="
830 | copyq:
831 | if (str(data(mimeShortcut))=='space') {
832 | preview(!preview())
833 | abort()
834 | }
835 | if (settings('AutoPreview') == 1) {
836 | settings('AutoPreview', 0)
837 | popup('自动预览❎','',1200)
838 | } else {
839 | settings('AutoPreview', 1)
840 | popup('自动预览✅','',1200)
841 | }
842 | "
843 | 26\InMenu=true
844 | 26\Icon=
845 | 26\Shortcut=f7, space
846 | 27\Name=----------------------------------
847 | 27\Command="
848 | // 虚拟分割线
849 |
850 |
851 |
852 |
"
853 | 27\InMenu=true
854 | 27\Enable=false
855 | 28\Name=自动合并换行
856 | 28\Command="
857 | copyq:
858 | function isAlphaNumeric(s) {
859 | return /^[a-zA-Z0-9]+$/.test(s);
860 | }
861 |
862 | function mergeLinesIntoParagraph(text) {
863 | const paragraphEndFlags = ['.', '!', '?', '?', '!', '。'];
864 | const lines = text.split('\\n').map(str => str.trim()).filter(str => str !== '');
865 | const paragraphs = [];
866 | var currentParagraph = '';
867 | for (var i = 0; i < lines.length; i++) {
868 | const line = lines[i];
869 | const lastChar = line.charAt(line.length - 1);
870 | const isEndOfSentence = paragraphEndFlags.indexOf(lastChar) > -1;
871 | currentParagraph += line + (isAlphaNumeric(lastChar) ? ' ' : '');
872 | if (isEndOfSentence) {
873 | paragraphs.push(currentParagraph.trim());
874 | currentParagraph = '';
875 | }
876 | }
877 | paragraphs.push(currentParagraph.trim());
878 | const mergedText = paragraphs.join('\\n');
879 | return mergedText;
880 | }
881 | var mytext = str(clipboard('text/plain'));
882 | if (mytext) {
883 | tab(selectedtab());
884 | myindex = index() + 1;
885 | write(myindex, 'text/plain', mergeLinesIntoParagraph(mytext));
886 | select(myindex);
887 | paste();
888 | }
889 | "
890 | 28\IsGlobalShortcut=true
891 | 28\Enable=false
892 | 28\Icon=
893 | 29\Name=剪贴板粘贴为纯文本
894 | 29\Command="
895 | copyq:
896 | var text = clipboard()
897 | copy(text)
898 | copySelection(text)
899 | paste()"
900 | 29\IsGlobalShortcut=true
901 | 29\Icon=T
902 | 29\GlobalShortcut=ctrl+shift+v
903 | 29\InternalId=copyq_global_paste_clipboard_plain
904 | 30\Name=粘贴并复制上一项
905 | 30\Command="
906 | copyq: paste(); previous()"
907 | 30\IsGlobalShortcut=true
908 | 30\Icon=
909 | 30\GlobalShortcut=ctrl+f9
910 | 30\InternalId=copyq_global_paste_copy_previous
911 | 31\Name=粘贴并复制下一项
912 | 31\Command="
913 | copyq: paste(); next()"
914 | 31\IsGlobalShortcut=true
915 | 31\Icon=
916 | 31\GlobalShortcut=ctrl+f10
917 | 31\InternalId=copyq_global_paste_copy_next
918 | 32\Name=粘贴第N项
919 | 32\Command="
920 | copyq:
921 | var n = str(data(mimeShortcut)).slice(-1)
922 | select(n)
923 | paste()"
924 | 32\IsGlobalShortcut=true
925 | 32\Enable=false
926 | 32\Icon=
927 | 32\GlobalShortcut=ctrl+shift+0, ctrl+shift+1, ctrl+shift+2, ctrl+shift+3, ctrl+shift+4, ctrl+shift+5, ctrl+shift+6, ctrl+shift+7, ctrl+shift+8, ctrl+shift+9
928 | 33\Name=强制复制(当关闭捕获时)
929 | 33\Command="
930 | copyq:
931 | copy()
932 |
933 | if (monitoring())
934 | abort()
935 |
936 | var item = {}
937 | for (const format of clipboardFormatsToSave()) {
938 | var data = clipboard(format)
939 | if (data.length)
940 | item[format] = data
941 | }
942 | /*
943 | var time = dateString('yyyy-MM-dd hh:mm:ss')
944 | var tagsMime = 'application/x-copyq-tags'
945 | var timeMime = 'application/x-copyq-user-item-time'
946 |
947 | item[timeMime] = time
948 | item[tagsMime] = time
949 | */
950 | add(item)"
951 | 33\IsGlobalShortcut=true
952 | 33\Icon=
953 | 33\GlobalShortcut=alt+c
954 | 34\Name=粘贴当前日期和时间
955 | 34\Command="
956 | copyq:
957 | var time = dateString('yyyy'+'年'+'MM'+'月'+'dd'+'日 '+'hh:mm')
958 | copy(time)
959 | paste()"
960 | 34\IsGlobalShortcut=true
961 | 34\Icon=
962 | 34\GlobalShortcut=alt+shift+t
963 | 35\Name=切换 大写/小写/首字母大写
964 | 35\Command="
965 | copyq:
966 | try {
967 | copy()
968 | } catch (e) {
969 | popup('复制失败,请重新选择文本!', e)
970 | abort()
971 | }
972 |
973 | var text = str(clipboard())
974 |
975 | var UpperCase = text.toUpperCase()
976 | var LowerCase = text.toLowerCase()
977 | var TitleCase = toTitleCase(text)
978 |
979 | if (text == UpperCase) {
980 | text = LowerCase
981 | }
982 | else {
983 | if (text == LowerCase) {
984 | text = TitleCase
985 | }
986 | else
987 | text=UpperCase
988 | }
989 |
990 | copy(text)
991 | paste()
992 |
993 | function toTitleCase(str) {
994 | return str.replace(
995 | /\\w\\S*/g,
996 | function(txt) {
997 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
998 | }
999 | );
1000 | }"
1001 | 35\IsGlobalShortcut=true
1002 | 35\Icon=
1003 | 35\GlobalShortcut=meta+ctrl+u
1004 | 36\Name=显示/隐藏主窗口
1005 | 36\Command="
1006 | copyq:
1007 | // 要显示的标签页,留空则显示上次打开的标签页
1008 | const tabName = ''
1009 | // 居中显示窗口
1010 | const center = true
1011 | // 在鼠标指针下显示窗口,关闭居中时生效
1012 | const showAtCursor = true
1013 | // 窗口大小,居中窗口时生效(单位:像素)
1014 | const windowSize = { w: 1000, h: 600 }
1015 | // 系统缩放系数,100%=1.0,125%=1.25
1016 | const sys_Scale_Factor = 1.0
1017 |
1018 | // 屏幕分辨率
1019 | let resolution = settings('ScreenResolution')
1020 | if (!resolution) {
1021 | resolution = [1920, 1080]
1022 | getScreenResolution()
1023 | }
1024 |
1025 | // Qt缩放系数[x, y](估算值)
1026 | const QT_SCALE_FACTOR = [1.2608, 1.32]
1027 |
1028 | const w = windowSize.w / QT_SCALE_FACTOR[0] / sys_Scale_Factor ** 2
1029 | const h = windowSize.h / QT_SCALE_FACTOR[1] / sys_Scale_Factor ** 2
1030 |
1031 | let windowPotion = { x: 0, y: 0 }
1032 |
1033 | if (visible()) {
1034 | hide()
1035 | } else {
1036 | if (center) {
1037 | windowPotion.x = Math.round((resolution[0] - windowSize.w) / 2)
1038 | windowPotion.y = Math.round((resolution[1] - windowSize.h) / 2)
1039 | showAt(windowPotion.x, windowPotion.y, Math.round(w), Math.round(h), tabName)
1040 | }
1041 | else {
1042 | showAtCursor ? showAt() : show()
1043 | }
1044 | }"
1045 | 36\IsGlobalShortcut=true
1046 | 36\Icon=
1047 | 36\GlobalShortcut=alt+v
1048 | 37\Name=----------------------------------
1049 | 37\Command="
1050 | // 虚拟分割线
1051 |
1052 |
1053 |
"
1054 | 37\InMenu=true
1055 | 37\Enable=false
1056 | 38\Name=储存窗口名
1057 | 38\Command="
1058 | copyq:
1059 | const tags_mime = 'application/x-copyq-tags'
1060 | const window = str(data(mimeWindowTitle))
1061 | const oldTags = data(tags_mime)
1062 | const reg = /([^-–—]{1,30})$/
1063 | const title = window.match(reg)[0].trim()
1064 | let tags = ''
1065 | oldTags=='' ? tags = title : tags = `${oldTags}, ${title}`
1066 | setData(tags_mime, tags)"
1067 | 38\Automatic=true
1068 | 38\Icon=
1069 | 39\Name=储存条目时间
1070 | 39\Command="
1071 | copyq:
1072 | const time_mime = 'application/x-copyq-user-time'
1073 | const tags_mime = 'application/x-copyq-tags'
1074 | const time = dateString('yyyy-MM-dd hh:mm:ss')
1075 | const oldTags = data(tags_mime)
1076 | let tags = ''
1077 | oldTags=='' ? tags = time : tags = `${oldTags}, ${time}`
1078 | setData(tags_mime, tags)
1079 | setData(time_mime, time)"
1080 | 39\Automatic=true
1081 | 39\Icon=
1082 | 40\Name=复制命令时通知导入
1083 | 40\Match=^\\[Commands?\\]|https?://github\\.com/.*/copyq-commands/blob/.*\\.ini
1084 | 40\Command="
1085 | copyq:
1086 | // Imports raw commands code (starting with [Command] or [Commands])
1087 | // or from a link ('https://github.com/**/copyq-commands/blob/**.ini').
1088 | var timeOutSeconds = 10
1089 | var rawDataUrlPrefix = 'https://raw.githubusercontent.com'
1090 | // Don't run this when mouse selection changes.
1091 | if ( dataFormats().indexOf(mimeClipboardMode) != -1 )
1092 | abort()
1093 | function importCommandsFromUrl(url)
1094 | {
1095 | var m = url.match(/^https?:\\/\\/github\\.com(\\/.*)\\/blob(\\/.*\\.ini)/)
1096 | if (!m)
1097 | return;
1098 | var rawDataUrl = rawDataUrlPrefix + m[1] + m[2]
1099 | var reply = networkGet(rawDataUrl)
1100 | var commandsData = str(reply.data)
1101 | if (reply.status != 200) {
1102 | throw '命令获取失败!'
1103 | + '\\nStatus code: ' + reply.status
1104 | + '\\nError: ' + reply.error
1105 | }
1106 | if ( !commandsData.match(/^\\[Commands?\\]/) )
1107 | return;
1108 | return importCommands(commandsData);
1109 | }
1110 | var cmds = importCommandsFromUrl(str(input())) || importCommands(input())
1111 | var list = ''
1112 | for (var i in cmds) {
1113 | var cmd = cmds[i]
1114 | var cmdType =
1115 | cmd.automatic ? 'automatic' :
1116 | cmd.inMenu ? 'menu/shortcut' :
1117 | cmd.globalShortcuts ? 'global shortcut' : '???';
1118 | list += '- ' + escapeHtml(cmds[i].name) + ' (' + cmdType + ')
'
1119 | }
1120 | list += '
'
1121 | var message = '从剪贴板导入命令?
' + list
1122 | notification(
1123 | '.title', '有可用的CopyQ命令',
1124 | '.message', message,
1125 | '.time', timeOutSeconds*1000,
1126 | '.icon', '',
1127 | '.id', 'CopyQ_commands_in_clipboard',
1128 | '.button', '取消', '', '',
1129 | '.button', '导入', 'copyq: addCommands( importCommands(input()) )', exportCommands(cmds)
1130 | )"
1131 | 40\Input=text/plain
1132 | 40\Automatic=true
1133 | 40\Icon=
1134 | 41\Name=大文件标签页
1135 | 41\Command="
1136 | copyq:
1137 | const tabName = '大文件'
1138 | // 文件大小,默认值1000kb
1139 | const minBytes = 1000 * 1000
1140 | // 是否识别复制的本地文件大小
1141 | // 主要用于避免视频等大文件同步
1142 | const readFileSize = true
1143 |
1144 | function hasBigData() {
1145 | let itemSize = 0
1146 | let formats = dataFormats()
1147 | for (let i in formats) {
1148 | if (readFileSize && formats[i] == 'text/uri-list') {
1149 | let fpath = str(data(formats[i])).split('///')[1]
1150 | let f = new File(fpath)
1151 | itemSize += f.size()
1152 | } else
1153 | itemSize += data(formats[i]).size()
1154 | }
1155 |
1156 | if (itemSize >= minBytes) {
1157 | return true
1158 | }
1159 | return false
1160 | }
1161 |
1162 | if (hasBigData()) {
1163 | setData(mimeOutputTab, tabName)
1164 | }"
1165 | 41\Automatic=true
1166 | 41\Enable=false
1167 | 41\Icon=
1168 | 42\Name=图片标签页
1169 | 42\Command="
1170 | copyq:
1171 | var imageTab = '图片'
1172 | var formats = dataFormats()
1173 | for (var i in formats) {
1174 | if (formats[i].match(/^image\\//)) {
1175 | setData(mimeOutputTab, imageTab)
1176 | break
1177 | }
1178 | }"
1179 | 42\Automatic=true
1180 | 42\Enable=false
1181 | 42\Icon=
1182 | 43\Name=自动清理过期条目
1183 | 43\Command="
1184 | // Require commands:Store Copy Time、Move to Trash (Undoable)
1185 |
1186 | // Get clipboard, trash_tab names
1187 | const clip_tab = config(\"clipboard_tab\");
1188 | const trash_tab = settings(\"trash_tab\");
1189 |
1190 | let tabs_times = {};
1191 | // Set the tabs to clean and the expiration time (in hours)
1192 | // The default is 15 days for the clipboard and 30 days for the trash_tab
1193 | tabs_times[trash_tab] = 30 * 24; // Clean up the trash_tab first to make space
1194 | tabs_times[clip_tab] = 15 * 24;
1195 |
1196 | // check items expiration on copyq start
1197 | const onStart_ = global.onStart
1198 | global.onStart = function () {
1199 | onStart_()
1200 | check_items_expire()
1201 | }
1202 |
1203 | // check_items_expire can be called manually
1204 | // and will ignore the interval
1205 | global.check_items_expire = function (manual = false) {
1206 | const time_mime = \"application/x-copyq-user-time\";
1207 | const now = new Date();
1208 | if (!manual) {
1209 | const interval = Math.min(...Object.values(tabs_times)) / 10;
1210 | const last_check_time = new Date(settings(\"last_check_time\"));
1211 | if (hoursBetween(now, last_check_time) < interval) {
1212 | return;
1213 | }
1214 | }
1215 | settings(\"last_check_time\", str(now));
1216 | let de_sel = [];
1217 | let count = {};
1218 | for (let t in tabs_times) {
1219 | const hours = tabs_times[t];
1220 | tab(t);
1221 | let sel = ItemSelection().selectRemovable();
1222 | let items = sel.items();
1223 | for (let i=0; i < items.length; i++) {
1224 | let item_time = items[i][time_mime];
1225 | if (hoursBetween(now, new Date(item_time)) < hours) {
1226 | de_sel.push(i);
1227 | }
1228 | }
1229 | sel = sel.deselectIndexes(de_sel);
1230 | count[t] = sel.length;
1231 | if (count[t]> 0) sel.removeAll();
1232 | }
1233 | return count;
1234 | };
1235 | // Calculate the time difference in hours
1236 | function hoursBetween(now, then) {
1237 | let hours = (now - then) / (1000 * 60 * 60);
1238 | return Math.floor(hours);
1239 | }"
1240 | 43\IsScript=true
1241 | 43\Icon=
1242 | 44\Name=修改时更新条目时间
1243 | 44\Command="
1244 | const onItemsChanged_ = global.onItemsChanged
1245 |
1246 | global.onItemsChanged = function () {
1247 | onItemsChanged_()
1248 |
1249 | const tags_mime = 'application/x-copyq-tags'
1250 | const time_mime = 'application/x-copyq-user-time'
1251 | const time = dateString('yyyy-MM-dd hh:mm:ss')
1252 | const rows = selectedItems()
1253 | tab(selectedTab())
1254 | for (let i = 0; i < rows.length; i++) {
1255 | const item = getItem(rows[i])
1256 | const old_tag = str(item[tags_mime])
1257 | // get old_time from tags or time_mime
1258 | const old_time = old_tag.match(/\\d{4}-\\d{2}-\\d{2} \\d{2}\\:\\d{2}\\:\\d{2}/) || str(item[time_mime])
1259 |
1260 | if (old_time != 'undefined') {
1261 | // Ignore repeated calls within 1 second
1262 | if (new Date(time) - new Date(old_time) < 1000)
1263 | return
1264 | }
1265 | let new_tag = ''
1266 | if (old_tag != 'undefined') {
1267 | if (old_tag.includes(old_time)) {
1268 | new_tag = old_tag.replace(old_time, time)
1269 | } else {
1270 | new_tag = old_tag + ',' + time
1271 | }
1272 | } else {
1273 | new_tag = time
1274 | }
1275 | change(rows[i], time_mime, time, tags_mime, new_tag)
1276 | }
1277 | }"
1278 | 44\IsScript=true
1279 | 44\Icon=
1280 | 45\Name=按时清理过期条目
1281 | 45\Command="
1282 | // 依赖:保存复制时间、移至回收站
1283 | // 其他标签页的条目先删除至回收站
1284 | // 回收站中条目过期时直接删除
1285 |
1286 | // 获取剪贴板、回收站标签页名称
1287 | const clip_tab = config(\"clipboard_tab\");
1288 | const trash_tab = settings(\"trash_tab\");
1289 |
1290 | let tabs_times = {};
1291 | // 设置要清理的标签页和过期时间(单位:小时)
1292 | // 默认为剪贴板 15 天,回收站 30 天
1293 | tabs_times[`${trash_tab}`] = 30 * 24; // 先清理回收站以便腾出空间
1294 | tabs_times[`${clip_tab}`] = 15 * 24;
1295 |
1296 |
1297 | const onStart_ = global.onStart
1298 | global.onStart = function () {
1299 | onStart_()
1300 | check_items_expire()
1301 | }
1302 |
1303 | global.check_items_expire = function (enforce = false) {
1304 | const time_mime = \"application/x-copyq-user-time\";
1305 | const now = new Date();
1306 | if (!enforce) {
1307 | const interval = Math.min(...Object.values(tabs_times)) / 10;
1308 | const last_check_time = new Date(settings(\"last_check_time\"));
1309 | if (hoursBetween(now, last_check_time) < interval) {
1310 | return;
1311 | }
1312 | }
1313 | settings(\"last_check_time\", str(now));
1314 | let count = {};
1315 | for (let t in tabs_times) {
1316 | const hours = tabs_times[t];
1317 | tab(t);
1318 | let sel = ItemSelection().selectRemovable();
1319 | let de_sel = [];
1320 | const items = sel.items();
1321 | for (let i = 0; i < items.length; i++) {
1322 | const item_time = items[i][time_mime];
1323 | if (hoursBetween(now, new Date(item_time)) < hours) {
1324 | de_sel.push(i);
1325 | }
1326 | }
1327 | sel = sel.deselectIndexes(de_sel);
1328 | count[t] = sel.length;
1329 | if (count[t] > 0) sel.removeAll();
1330 | }
1331 | return count;
1332 | };
1333 | // 计算时间差,单位:小时
1334 | global.hoursBetween = function (now, then) {
1335 | let hours = (now - then) / (1000 * 60 * 60);
1336 | return Math.floor(hours);
1337 | }"
1338 | 45\IsScript=true
1339 | 45\Icon=
1340 | 46\Name=复制/粘贴文件支持
1341 | 46\Command="
1342 | // 不支持剪切操作
1343 | var originalFunction = global.clipboardFormatsToSave
1344 | global.clipboardFormatsToSave = function() {
1345 | return originalFunction().concat([
1346 | mimeUriList,
1347 | 'x-special/gnome-copied-files',
1348 | 'application/x-kde-cutselection',
1349 | ])
1350 | }"
1351 | 46\IsScript=true
1352 | 46\Icon=
1353 | 47\Name=复制时图标红点提示
1354 | 47\Command="
1355 | var timeMs = 10000;
1356 | var iconTags = [
1357 | '',
1358 | ' ',
1359 | ' ',
1360 | '',
1361 | ]
1362 | function clipboardNotification() {
1363 | var id = Number(settings('icon-activation-id') || 0) + 1;
1364 | settings('icon-activation-id', id);
1365 | iconTagColor('red');
1366 | for (const tag of iconTags.values()) {
1367 | if ( settings('icon-activation-id') != id )
1368 | break;
1369 | iconTag(tag);
1370 | sleep(timeMs);
1371 | }
1372 | }
1373 | onClipboardChanged_ = onClipboardChanged
1374 | onClipboardChanged = function() {
1375 | onClipboardChanged_()
1376 | clipboardNotification()
1377 | }
1378 | onOwnClipboardChanged_ = onOwnClipboardChanged
1379 | onOwnClipboardChanged = function() {
1380 | onOwnClipboardChanged_()
1381 | clipboardNotification()
1382 | }
1383 | onHiddenClipboardChanged_ = onHiddenClipboardChanged
1384 | onHiddenClipboardChanged = function() {
1385 | onHiddenClipboardChanged_()
1386 | clipboardNotification()
1387 | }"
1388 | 47\IsScript=true
1389 | 47\Icon=
1390 | 48\Name=忽略空字符或单个字符的条目
1391 | 48\Command="
1392 | function hasEmptyOrSingleCharText() {
1393 | if (dataFormats().includes(mimeText)) {
1394 | var text = str(data(mimeText));
1395 | if (text.match(/^\\s*.?\\s*$/)) {
1396 | serverLog('Ignoring text with single or no character');
1397 | return true;
1398 | }
1399 | }
1400 | return false;
1401 | }
1402 |
1403 | var onClipboardChanged_ = onClipboardChanged;
1404 | onClipboardChanged = function() {
1405 | if (!hasEmptyOrSingleCharText()) {
1406 | onClipboardChanged_();
1407 | }
1408 | }
1409 |
1410 | var synchronizeFromSelection_ = synchronizeFromSelection;
1411 | synchronizeFromSelection = function() {
1412 | if (!hasEmptyOrSingleCharText()) {
1413 | synchronizeFromSelection_();
1414 | }
1415 | }
1416 |
1417 | var synchronizeToSelection_ = synchronizeToSelection;
1418 | synchronizeToSelection = function() {
1419 | if (!hasEmptyOrSingleCharText()) {
1420 | synchronizeToSelection_();
1421 | }
1422 | }
1423 | "
1424 | 48\IsScript=true
1425 | 48\Icon=
1426 | 49\Name=启动时执行...
1427 | 49\Command="
1428 | var onStartPrevious = global.onStart
1429 | global.onStart = function() {
1430 | onStartPrevious()
1431 | // 检查条目是否过期
1432 | check_items_expire()
1433 | if ( !settings('ScreenResolution') )
1434 | settings('ScreenResolution',getScreenResolution())
1435 | }"
1436 | 49\IsScript=true
1437 | 49\Icon=
1438 | 50\Name=获取屏幕分辨率
1439 | 50\Command="
1440 | global.getScreenResolution = function () {
1441 | var x = 0
1442 | var y = 0
1443 | popup('正在获取屏幕分辨率... ', '鼠标指针自动移动是正常现象。\\n预计耗时大于30s,请耐心等待!', 30000)
1444 | var i = 3
1445 | while (i > -1) {
1446 | try {
1447 | x += 10 ** i
1448 | setPointerPosition(x, y)
1449 | } catch (error) {
1450 | x -= 10 ** i
1451 | i--
1452 | }
1453 | }
1454 | i = 3
1455 | while (i > -1) {
1456 | try {
1457 | y += 10 ** i
1458 | setPointerPosition(x, y)
1459 | } catch (error) {
1460 | y -= 10 ** i
1461 | i--
1462 | }
1463 | }
1464 | setPointerPosition(++x / 2, ++y / 2)
1465 | popup('屏幕分辨率为:'+[x, y])
1466 | return [x, y]
1467 | }"
1468 | 50\IsScript=true
1469 | 50\Icon=
1470 | 51\Name=启动时显示主窗口
1471 | 51\Command="
1472 | var onStartPrevious = global.onStart
1473 | global.onStart = function() {
1474 | onStartPrevious()
1475 | show()
1476 | }"
1477 | 51\IsScript=true
1478 | 51\Enable=false
1479 | 51\Icon=
1480 | 52\Name=间隔30秒后清空系统剪贴板
1481 | 52\Command="
1482 | var timeoutSeconds = 30;
1483 | function option() {
1484 | return isClipboard()
1485 | ? 'clear_clipboard/clipboard_change_counter'
1486 | : 'clear_clipboard/selection_change_counter'
1487 | }
1488 | function getCount() {
1489 | return Number(settings(option())) || 0
1490 | }
1491 | function bumpCounter() {
1492 | var counter = getCount() + 1
1493 | settings(option(), counter)
1494 | return counter
1495 | }
1496 | function resetLater(counter) {
1497 | for (var i = 0; i < timeoutSeconds && counter == getCount(); ++i) {
1498 | sleep(1000)
1499 | }
1500 | if (counter != getCount())
1501 | return
1502 | if (isClipboard())
1503 | copy('')
1504 | else
1505 | copySelection('')
1506 | }
1507 | var onClipboardChanged_ = onClipboardChanged
1508 | onClipboardChanged = function() {
1509 | var counter = bumpCounter()
1510 | onClipboardChanged_()
1511 | resetLater(counter)
1512 | }
1513 | var onOwnClipboardChanged_ = onOwnClipboardChanged
1514 | onOwnClipboardChanged = function() {
1515 | var counter = bumpCounter()
1516 | onOwnClipboardChanged_()
1517 | resetLater(counter)
1518 | }"
1519 | 52\IsScript=true
1520 | 52\Enable=false
1521 | 52\Icon=
1522 | 53\Name=----------------------------------
1523 | 53\Command="
1524 | // 虚拟分割线
1525 |
1526 |
1527 |
1528 |
1529 |
"
1530 | 53\InMenu=true
1531 | 53\Enable=false
1532 | 54\Name=显示为纯文本
1533 | 54\Command="
1534 | copyq:
1535 | const mime = 'application/x-copyq-show-plain';
1536 | const sel = ItemSelection().current();
1537 | if (sel.length) {
1538 | const enabled = str(sel.itemAtIndex(0)[mime]) == '1';
1539 | sel.setItemsFormat(mime, enabled ? undefined : '1');
1540 | } else {
1541 | const enabled = str(data(mime)) == '1';
1542 | if (enabled)
1543 | removeData(mimeHtml);
1544 | }"
1545 | 54\Display=true
1546 | 54\InMenu=true
1547 | 54\Enable=false
1548 | 54\Icon=A
1549 | 54\Shortcut=ctrl+shift+x
1550 | 55\Name=预览图片文件
1551 | 55\Command="
1552 | copyq:
1553 | var prefix_reg = /^file:\\/+(.*)/
1554 | var suffixToMime = {
1555 | 'png': 'image/png',
1556 | 'jpg': 'image/jpeg',
1557 | 'jpeg': 'image/jpeg',
1558 | 'bmp': 'image/bmp',
1559 | 'gif': 'image/gif',
1560 | 'svg': 'image/svg+xml',
1561 | 'ico': 'image/png',
1562 | 'webp': 'image/png',
1563 | }
1564 |
1565 | function tryShowImage(mime) {
1566 | var path = str(data(mime))
1567 | path = path.replace(prefix_reg, '$1')
1568 | if (!path)
1569 | return false
1570 | var i = path.lastIndexOf('.')
1571 | if (i == -1)
1572 | return false
1573 | var suffix = path.substring(i + 1)
1574 | var imageMime = suffixToMime[suffix]
1575 | if (imageMime === undefined)
1576 | return false
1577 | var f = new File(path)
1578 | if (!f.openReadOnly())
1579 | return false
1580 | var imageData = f.readAll()
1581 | f.close()
1582 | if (imageData.size() === 0)
1583 | return false
1584 | setData(mimeItemNotes, path)
1585 | setData(imageMime, imageData)
1586 | return true
1587 | }
1588 |
1589 | if (!hasImage()) {
1590 | tryShowImage(mimeText) || tryShowImage(mimeUriList)
1591 | }
1592 |
1593 | function hasImage() {
1594 | var formats = dataFormats()
1595 | for (var i in formats) {
1596 | if (formats[i].match(/^image\\//)) {
1597 | return true
1598 | }
1599 | }
1600 | return false
1601 | }"
1602 | 55\Display=true
1603 | 55\Icon=
1604 | 56\Name=纯文本颜色标记
1605 | 56\Command="
1606 | copyq:
1607 | if ( str(data(mimeText)) && !str(data(mimeHtml)) ) {
1608 | html = escapeHtml(data(mimeText))
1609 | setData(mimeHtml, '' + html + '')
1610 | }"
1611 | 56\Display=true
1612 | 56\Icon=
1613 | 57\Name=显示Hex颜色
1614 | 57\Match=^#[0-9a-zA-Z]{6}
1615 | 57\Command="
1616 | copyq:
1617 | setData(mimeColor, input());"
1618 | 57\Input=text/plain
1619 | 57\Display=true
1620 | 57\Icon=
1621 | 58\Name=----------------------------------
1622 | 58\Command="
1623 | // 虚拟分割线
1624 |
1625 |
1626 |
1627 |
"
1628 | 58\InMenu=true
1629 | 58\Enable=false
1630 | 59\Name=重命名MIME
1631 | 59\Command="
1632 | copyq:
1633 | var old_Mime = 'application/x-copyq-user-copy-time'
1634 | var new_Mime = 'application/x-copyq-user-item-time'
1635 | var tab_names = tab()
1636 | for (var i in tab_names) {
1637 | var tab_name = tab_names[i]
1638 | tab(tab_name)
1639 | var item_count = count()
1640 |
1641 | for (var j = 0; j < item_count; ++j) {
1642 | var time = str(read(old_Mime, j))
1643 | if (time != '') {
1644 | change(j, new_Mime, time)
1645 | change(j, old_Mime, undefined)
1646 | }
1647 | }
1648 | }"
1649 | 59\InMenu=true
1650 | 59\Enable=false
1651 | 59\Icon=
1652 | 60\Name=固定
1653 | 60\Command="
1654 | copyq: plugins.itempinned.pin()
"
1655 | 60\Input=!OUTPUT
1656 | 60\Output=application/x-copyq-item-pinned
1657 | 60\InMenu=true
1658 | 60\Enable=false
1659 | 60\Icon=
1660 | 60\InternalId=copyq_pinned_pin
1661 | 61\Name=解除固定
1662 | 61\Command="
1663 | copyq: plugins.itempinned.unpin()
"
1664 | 61\Input=application/x-copyq-item-pinned
1665 | 61\InMenu=true
1666 | 61\Enable=false
1667 | 61\Icon=
1668 | 61\InternalId=copyq_pinned_unpin
1669 | 62\Name=切换标记 “重要”
1670 | 62\Command="
1671 | copyq: (plugins.itemtags.hasTag(decodeURIComponent('%E9%87%8D%E8%A6%81')) ? plugins.itemtags.untag : plugins.itemtags.tag)(decodeURIComponent('%E9%87%8D%E8%A6%81'))
"
1672 | 62\InMenu=true
1673 | 62\Enable=false
1674 | 62\Icon=
1675 | 62\InternalId=copyq_tags_tag:重要
1676 | 63\Name=添加一个标记
1677 | 63\Command="
1678 | copyq: plugins.itemtags.tag()
"
1679 | 63\InMenu=true
1680 | 63\Enable=false
1681 | 63\Icon=
1682 | 63\InternalId=copyq_tags_tag
1683 | 64\Name=移除一个标记
1684 | 64\Command="
1685 | copyq: plugins.itemtags.untag()
"
1686 | 64\Input=application/x-copyq-tags
1687 | 64\InMenu=true
1688 | 64\Enable=false
1689 | 64\Icon=
1690 | 64\InternalId=copyq_tags_untag
1691 | 65\Name=清理所有标记
1692 | 65\Command="
1693 | copyq: plugins.itemtags.clearTags()
"
1694 | 65\Input=application/x-copyq-tags
1695 | 65\InMenu=true
1696 | 65\Enable=false
1697 | 65\Icon=
1698 | 65\InternalId=copyq_tags_clear
1699 | size=65
--------------------------------------------------------------------------------