diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc6660..6f9d992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.8.0 + +- `Alt+X` / `Option+X` now supports quick double presses, mirroring the `Alt+C` double-press behavior: a double press appends the current selection's disk paths to the previously copied paths in the system clipboard instead of overwriting them. +- Appended disk paths are deduplicated line by line, and the press window shares the existing 连按判断间隔 setting. + ## 1.7.0 - Added a "转成磁盘路径" toggle button in the clipboard panel that converts mentions to full disk paths and back, mirroring the Obsidian URL toggle behavior. diff --git a/README.md b/README.md index 6774c1b..12b0055 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Copy Selected Name 是一个 Obsidian 桌面端插件,用于在文件列表中 - 在 Obsidian 里使用 Claudian、Claude、Codex 或其他 AI 对话插件时,快速把当前文件作为上下文引用插入输入框。 - 同时选择多个笔记文件,把它们整理成一串可直接粘贴给 AI 的文件名引用。 -- 按 `Alt+X`(macOS 为 `Option+X`)直接复制选中文件或文件夹的真实磁盘路径到系统剪贴板。 +- 按 `Alt+X`(macOS 为 `Option+X`)直接复制选中文件或文件夹的真实磁盘路径到系统剪贴板;双按 `Alt+X` 会把这次的路径追加到上一次复制的路径后面。 - 在做短视频拆解、选题策划、项目复盘、客户资料整理时,快速收集多个相关文件名。 - 需要在普通 `@文件名.md` 格式和 `obsidian://open?...` Obsidian URL 格式之间来回转换。 @@ -134,6 +134,19 @@ git clone https://github.com/mikeddy/obsidian-copy-selected-name.git copy-select 多选时会一行一个路径。这个功能不使用插件内部剪贴板,可以直接粘贴到浏览器、记事本、终端或外部文件中。 +和 `Alt+C` 一样,`Alt+X` 也支持双按追加:在连按判断间隔内双按 `Alt+X`(macOS 为 `Option+X`),会把这次选中的磁盘路径追加到上一次复制的路径后面,而不是覆盖。例如: + +1. 在 A 文件上按一次 `Alt+X`,系统剪贴板是 A 的路径。 +2. 切换到 B 文件,双按 `Alt+X`。 +3. 系统剪贴板变成: + +```text +/Users/you/Obsidian/项目/A文件.md +/Users/you/Obsidian/项目/B文件.md +``` + +追加时会自动去重,同一个路径不会出现两行。单按 `Alt+X` 仍然是覆盖,会重新开始攒路径。 + ### 单按、双按、三连按 - 单按 `Alt+C`(macOS 为 `Option+C`):覆盖插件内部剪贴板。 diff --git a/main.js b/main.js index ae5278d..674291e 100644 --- a/main.js +++ b/main.js @@ -104,6 +104,12 @@ module.exports = class CopySelectedNamePlugin extends Plugin { this.chainText = ""; this.chainLastKey = ""; this.pressCount = 0; + this.diskPressCount = 0; + this.lastDiskPressAt = 0; + this.lastDiskPressKey = ""; + this.diskChainText = ""; + this.diskSingleSnapshotText = ""; + this.diskSingleSnapshotForKey = ""; this.currentClipboardText = ""; this.clipboardArmedAt = 0; this.clipboardPanelEl = null; @@ -167,7 +173,7 @@ module.exports = class CopySelectedNamePlugin extends Plugin { } if (!checking) { - void this.copyDiskPathsFromItems(selectedItems); + void this.handleDiskPathHotkey(selectedItems); } return true; @@ -331,7 +337,7 @@ module.exports = class CopySelectedNamePlugin extends Plugin { } this.stopHotkeyEvent(event); - void this.copyDiskPathsFromItems(selectedItems); + void this.handleDiskPathHotkey(selectedItems); } stopHotkeyEvent(event) { @@ -845,7 +851,7 @@ module.exports = class CopySelectedNamePlugin extends Plugin { .join("\u001f"); } - async copyDiskPathsFromItems(items) { + async handleDiskPathHotkey(items) { if (!this.getVaultBasePath()) { new Notice("Cannot resolve vault disk path"); return; @@ -857,11 +863,57 @@ module.exports = class CopySelectedNamePlugin extends Plugin { return; } + // 与 Alt+C 相同的连按判定;状态必须在任何 await 之前同步写入,否则快速的 + // 第二次按键会读到旧值而被误判成单击。 + const pressKey = this.buildSelectionKey(items); + const now = Date.now(); + const isQuickPress = pressKey === this.lastDiskPressKey && + this.lastDiskPressAt > 0 && + now - this.lastDiskPressAt < this.getPressWindowMs(); + this.diskPressCount = isQuickPress ? this.diskPressCount + 1 : 1; + const isMultiPress = this.diskPressCount >= 2; + this.lastDiskPressAt = now; + this.lastDiskPressKey = pressKey; + + if (isMultiPress) { + // 双击追加:以单击覆盖之前的快照为基础,把这次的路径接到后面, + // 效果上等于撤销刚才那次单击覆盖再追加(对齐 Alt+C 的双击行为)。 + const hasSnapshotForPress = this.diskSingleSnapshotForKey === pressKey; + const baseText = hasSnapshotForPress ? this.diskSingleSnapshotText : this.diskChainText; + this.diskChainText = this.appendDiskPathText(baseText, text); + await this.writeDiskPathsToClipboard(this.diskChainText, { appended: true }); + return; + } + + this.diskSingleSnapshotText = this.diskChainText; + this.diskSingleSnapshotForKey = pressKey; + this.diskChainText = text; + await this.writeDiskPathsToClipboard(text, { appended: false }); + } + + appendDiskPathText(baseText, text) { + const lines = String(baseText || "").split(/\r?\n/).filter(Boolean); + const seen = new Set(lines); + for (const line of text.split(/\r?\n/).filter(Boolean)) { + if (!seen.has(line)) { + seen.add(line); + lines.push(line); + } + } + return lines.join("\n"); + } + + async writeDiskPathsToClipboard(text, options = {}) { if (!(await this.writeSystemClipboard(text))) { return; } const pathCount = text.split(/\r?\n/).filter(Boolean).length; + if (options.appended) { + new Notice(`Appended disk path (${pathCount} in system clipboard)`); + return; + } + new Notice(pathCount === 1 ? "Copied disk path to system clipboard" : `Copied ${pathCount} disk paths to system clipboard`); @@ -1836,7 +1888,7 @@ class CopySelectedNameSettingTab extends PluginSettingTab { new Setting(containerEl) .setName("连按判断间隔") - .setDesc("两次或三次按键之间小于这个时间,就会被识别为双击或三击。范围 200-3000 毫秒。") + .setDesc("两次或三次按键之间小于这个时间,就会被识别为双击或三击(引用复制和磁盘路径复制共用这个间隔)。范围 200-3000 毫秒。") .addText((text) => { text .setPlaceholder(String(DEFAULT_SETTINGS.pressWindowMs)) @@ -1872,7 +1924,7 @@ class CopySelectedNameSettingTab extends PluginSettingTab { containerEl.createEl("h3", { text: "物理磁盘路径复制" }); containerEl.createEl("p", { - text: "Alt+X / Option+X:复制选中文件或文件夹的真实磁盘路径,直接写入系统剪贴板。" + text: "Alt+X / Option+X:复制选中文件或文件夹的真实磁盘路径,直接写入系统剪贴板。单击覆盖;和 Alt+C 一样,在连按判断间隔内双击会把这次的路径追加到上一次复制的路径后面,方便一次攒出多个路径。" }); this.addShortcutSetting( containerEl, diff --git a/manifest.json b/manifest.json index 16e8d12..548e89a 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "copy-selected-name", "name": "Copy Selected Name", - "version": "1.7.2", + "version": "1.8.0", "minAppVersion": "1.4.5", "description": "Copy selected files or folders as @mentions with Alt+C, disk paths with Alt+X, history, and vault URL conversion.", "author": "mikeddy", diff --git a/package.json b/package.json index 5cc3ffa..483212a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-copy-selected-name", - "version": "1.7.2", + "version": "1.8.0", "description": "Copy selected files or folders as @mentions with Alt+C, disk paths with Alt+X, history, and vault URL conversion.", "main": "main.js", "scripts": { diff --git a/versions.json b/versions.json index 6f544e2..f7f86b8 100644 --- a/versions.json +++ b/versions.json @@ -7,5 +7,6 @@ "1.6.5": "1.4.5", "1.7.0": "1.4.5", "1.7.1": "1.4.5", - "1.7.2": "1.4.5" + "1.7.2": "1.4.5", + "1.8.0": "1.4.5" }