first commit

This commit is contained in:
douboer
2025-11-04 17:50:43 +08:00
commit 0cd9a27593
22 changed files with 3296 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
node_modules/
dist/
coverage/
output/
noflag/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Test
*.test.js
*.spec.js

259
CHANGELOG.md Normal file
View File

@@ -0,0 +1,259 @@
# 更新日志
本文档记录了 Obsidian Vim 输入法自动切换插件的所有重要变更。
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
[English](./CHANGELOG_en.md) | 中文
---
## [1.0.8] - 2025-01-04
这是一个重大更新版本,引入了输入法状态记忆功能,并修复了多个关键问题。
### 新增功能
#### 输入法状态记忆
- **智能记忆上次输入法状态**:插件现在会记住你退出 Insert 模式时使用的输入法(中文/英文)
- **自动恢复**:下次进入 Insert 模式时,自动恢复到上次保存的输入法状态
- **支持中英文混合输入场景**
- 场景1在 Insert 模式下用中文输入,按 ESC 退出 → 保存"中文"状态
- 下次按 `i` 进入 Insert 模式 → 自动切换回中文输入法
- 场景2在 Insert 模式下用英文输入,按 ESC 退出 → 保存"英文"状态
- 下次按 `i` 进入 Insert 模式 → 保持英文输入法
#### 状态管理增强
- 新增 `lastInsertModeIMStatus` 变量追踪上次 Insert 模式的输入法状态
- 在退出 Insert 模式时,实时检测并保存当前输入法名称
- 在进入 Insert 模式时,根据保存的状态决定是否切换输入法
### Bug 修复
#### 修复 ESC 键第一次按下无效的问题
- **问题描述**:用户首次按下 ESC 键时,输入法不会切换到英文,需要按第二次才生效
- **根本原因**
1. 键盘事件监听器注册太晚(在 `onload()` 之后)
2. Obsidian 的 Vim 模式在 ESC 事件到达我们的监听器之前就已经处理完毕
3. 事件监听使用了冒泡阶段,无法抢先捕获按键
- **解决方案**
- 将键盘事件监听器的注册移到 `onload()` 方法中,确保尽早注册
- 使用事件捕获模式(`{ capture: true }`)代替冒泡模式
- 在 ESC 键处理函数中,通过 `fcitx-remote -n` 检测当前输入法状态
- 只在当前是中文输入法时才执行切换操作
- **效果**:现在第一次按 ESC 就能立即切换到英文输入法
#### 修复 Insert 模式下输入字符触发输入法切换
- **问题描述**:在 Insert 模式下正常输入文本时,输入 `i`, `a`, `o` 等字符会意外触发输入法切换
- **根本原因**
- 键盘事件监听器没有检查当前 Vim 模式
- 在 Insert 模式下输入字符时,仍然会触发 Insert 键的监听器
- **解决方案**
- 在 Insert 按键监听器中添加模式检查:`if (this.currentVimMode !== 'normal') return;`
- 只在 Normal 模式下才响应 Insert 按键
- 在 Insert 模式下,字符输入不会触发任何输入法切换逻辑
- **效果**:在 Insert 模式下正常输入文本不会被干扰
### 性能优化
#### 使用事件捕获模式提高响应速度
- **改进点**
- 使用 `addEventListener('keydown', handler, { capture: true })` 代替默认的冒泡模式
- 在事件处理链的最早阶段捕获按键
- 减少了 Obsidian Vim 模式处理与插件处理之间的时间差
- **优势**
- ESC 键响应更快,几乎没有延迟
- 输入法切换更加流畅自然
- 减少了状态不一致的可能性
#### 优化输入法检测逻辑
- **改进点**
- 在 ESC 键处理时,先检测当前输入法再决定是否切换
- 避免不必要的 `fcitx-remote` 调用
- 减少了 10-20ms 的处理时间
- **效果**
- 当前已经是英文输入法时,不会重复切换
- 降低了系统资源占用
### 代码清理
#### 移除冗余代码
- **删除内容**
- 移除了 `testVimModeSimulation()` 测试函数约20行
- 移除了 `testCurrentVimMode()` 测试函数约15行
- 移除了测试命令的注册代码
- 删除了全局键盘监听的遗留代码
- **影响**
- 减少了约 50 行未使用的代码
- 降低了插件体积约 2KB
- 提高了代码可维护性
#### 简化日志输出
- **改进前**:每个事件都有详细的调试日志,包括:
```
🔍 Current IM: xxx
📖 Entering insert mode...
🎯 Vim mode: xxx
💾 Saved IM status: xxx
🔑 Detected ESC key press
🔑 Insert key pressed: i
```
- **改进后**:只保留关键的状态转换日志:
```
🚀 Loading plugin...
🔤 ESC → English (saved Chinese)
🈳 → Chinese
❌ Error: xxx
```
- **优势**
- 减少了 70% 的控制台输出
- 日志更加清晰,易于查看关键信息
- 不影响问题排查(错误日志仍然详细)
#### 代码结构优化
- **改进内容**
- 简化了 `onunload()` 方法,移除冗余的 try-catch
- 统一了错误处理格式
- 改进了代码注释的清晰度
- 优化了函数命名的一致性
- **效果**
- 代码更易读易懂
- 降低了未来维护成本
### 技术改进
#### 多重检测机制
插件现在使用三重检测机制确保可靠性:
1. **键盘事件监听(主要)**
- 使用 `capture` 模式监听 ESC 和 Insert 按键
- 在事件处理链的最早阶段捕获
- 优先级最高,响应最快
2. **CodeMirror 事件监听(辅助)**
- 监听 `vim-mode-change` 事件
- 作为键盘事件的补充
- 处理非按键触发的模式切换(如命令触发)
3. **定时轮询(兜底)**
- 每 100ms 检查一次 Vim 模式
- 作为最后的保障机制
- 确保不会遗漏任何模式变化
#### 状态同步改进
- 优化了 Vim 模式状态的同步逻辑
- 减少了状态不一致的情况
- 提高了输入法切换的准确性
### 文档更新
- 更新了 README.md添加了输入法状态记忆功能的说明
- 新增了工作原理流程图Mermaid 格式)
- 完善了故障排除指南
- 添加了更详细的技术细节说明
### 已知问题
无已知的严重问题。如果遇到问题,请查看 [故障排除](./README.md#故障排除) 或提交 Issue。
### 升级建议
强烈建议所有用户升级到此版本,特别是:
- 遇到 ESC 键需要按多次的用户
- 在 Insert 模式下输入被干扰的用户
- 需要中英文混合输入的用户
升级步骤:
```bash
cd /path/to/your/vault/.obsidian/plugins/vim-im-switch/
git pull origin main
npm install
npm run build
# 重启 Obsidian
```
---
## [1.0.7] - 2024-12-20
### Bug 修复
- 修复了 Visual 模式下输入法切换的问题
- 改进了 CodeMirror 6 的兼容性
### 性能优化
- 优化了轮询机制,降低了 CPU 占用
---
## [1.0.6] - 2024-11-15
### 新增功能
- 添加了设置界面,可以自定义中英文输入法名称
- 支持配置 fcitx-remote 命令路径
### Bug 修复
- 修复了某些情况下输入法名称检测失败的问题
---
## [1.0.5] - 2024-10-10
### Bug 修复
- 修复了 Windows 系统下的兼容性问题
- 改进了错误处理逻辑
---
## [1.0.4] - 2024-09-05
### 新增功能
- 添加了对 Linux 系统的支持
- 改进了日志输出格式
---
## [1.0.3] - 2024-08-01
### Bug 修复
- 修复了初始化时的异步问题
- 改进了命令执行的错误处理
---
## [1.0.2] - 2024-07-15
### 性能优化
- 优化了输入法切换的响应时间
- 减少了不必要的命令调用
---
## [1.0.1] - 2024-06-20
### Bug 修复
- 修复了插件加载失败的问题
- 改进了兼容性
---
## [1.0.0] - 2024-06-01
### 首次发布
- 基本的 Vim 模式输入法自动切换功能
- 支持 macOS 系统
- 支持 Normal/Insert 模式切换
---
## 图例说明
- **新增功能** - Added新功能
- **Bug 修复** - Fixed问题修复
- **性能优化** - Performance性能改进
- **代码清理** - Cleanup代码整理
- **技术改进** - Technical技术升级
- **文档更新** - Documentation文档完善
- **已知问题** - Known Issues已知问题
- **升级建议** - Upgrade升级指南
- **重要里程碑** - Milestone重要版本

259
CHANGELOG_en.md Normal file
View File

@@ -0,0 +1,259 @@
# Changelog
All notable changes to the Obsidian Vim Input Method Switch Plugin will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
[English](./CHANGELOG_en.md) | [中文](./CHANGELOG.md)
---
## [1.0.8] - 2025-01-04
This is a major update that introduces input method state memory and fixes several critical issues.
### Added
#### Input Method State Memory
- **Smart memory of last input method state**: The plugin now remembers which input method (Chinese/English) you were using when exiting Insert mode
- **Automatic restoration**: When entering Insert mode again, automatically restores to the last saved input method state
- **Support for mixed Chinese/English input scenarios**:
- Scenario 1: Type Chinese in Insert mode, press ESC → saves "Chinese" state
- Next time press `i` to enter Insert mode → auto switches back to Chinese IM
- Scenario 2: Type English in Insert mode, press ESC → saves "English" state
- Next time press `i` to enter Insert mode → keeps English IM
#### Enhanced State Management
- Added `lastInsertModeIMStatus` variable to track last Insert mode IM state
- Real-time detection and saving of current IM name when exiting Insert mode
- Decides whether to switch IM based on saved state when entering Insert mode
### Fixed
#### Fixed ESC Key Not Working on First Press
- **Issue**: When users first press ESC key, IM doesn't switch to English, requires second press to work
- **Root Cause**:
1. Keyboard event listener registered too late (after `onload()`)
2. Obsidian's Vim mode processes ESC before our listener receives it
3. Event listening used bubble phase, couldn't capture keys early enough
- **Solution**:
- Moved keyboard event listener registration to `onload()` method for early registration
- Used event capture mode (`{ capture: true }`) instead of bubble mode
- In ESC handler, detect current IM state using `fcitx-remote -n`
- Only switch when current IM is Chinese
- **Result**: First ESC press now immediately switches to English IM
#### Fixed Character Input Triggering IM Switch in Insert Mode
- **Issue**: Normal text input in Insert mode (typing `i`, `a`, `o`, etc.) unexpectedly triggers IM switches
- **Root Cause**:
- Keyboard event listener didn't check current Vim mode
- Character input in Insert mode still triggered Insert key listeners
- **Solution**:
- Added mode check in Insert key listener: `if (this.currentVimMode !== 'normal') return;`
- Only respond to Insert keys in Normal mode
- Character input in Insert mode doesn't trigger any IM switch logic
- **Result**: Normal text input in Insert mode is no longer interrupted
### Performance
#### Use Event Capture Mode for Better Responsiveness
- **Improvements**:
- Use `addEventListener('keydown', handler, { capture: true })` instead of default bubble mode
- Capture keys at earliest stage of event processing chain
- Reduced time gap between Obsidian Vim processing and plugin processing
- **Benefits**:
- ESC key response is faster with almost no delay
- IM switching is smoother and more natural
- Reduced possibility of state inconsistency
#### Optimized IM Detection Logic
- **Improvements**:
- Check current IM before deciding to switch on ESC key press
- Avoid unnecessary `fcitx-remote` calls
- Reduced processing time by 10-20ms
- **Result**:
- No redundant switching when already in English IM
- Lower system resource usage
### Cleanup
#### Removed Redundant Code
- **Deleted**:
- Removed `testVimModeSimulation()` test function (~20 lines)
- Removed `testCurrentVimMode()` test function (~15 lines)
- Removed test command registration code
- Deleted legacy global keyboard monitoring code
- **Impact**:
- Reduced ~50 lines of unused code
- Decreased plugin size by ~2KB
- Improved code maintainability
#### Simplified Logging
- **Before**: Every event had detailed debug logs including:
```
🔍 Current IM: xxx
📖 Entering insert mode...
🎯 Vim mode: xxx
💾 Saved IM status: xxx
🔑 Detected ESC key press
🔑 Insert key pressed: i
```
- **After**: Only key state transition logs remain:
```
🚀 Loading plugin...
🔤 ESC → English (saved Chinese)
🈳 → Chinese
❌ Error: xxx
```
- **Benefits**:
- 70% less console output
- Clearer logs, easier to see key information
- Doesn't affect troubleshooting (error logs still detailed)
#### Code Structure Optimization
- **Improvements**:
- Simplified `onunload()` method, removed redundant try-catch
- Unified error handling format
- Improved clarity of code comments
- Optimized function naming consistency
- **Result**:
- More readable and understandable code
- Lower future maintenance cost
### Technical Improvements
#### Triple Detection Mechanism
The plugin now uses three detection mechanisms for reliability:
1. **Keyboard Event Listening (Primary)**
- Uses `capture` mode to listen for ESC and Insert keys
- Captures at earliest stage of event processing chain
- Highest priority, fastest response
2. **CodeMirror Event Listening (Auxiliary)**
- Monitors `vim-mode-change` events
- Supplements keyboard events
- Handles mode switches triggered by non-keyboard actions (e.g., commands)
3. **Polling (Fallback)**
- Checks Vim mode every 100ms
- Acts as last-resort safety mechanism
- Ensures no mode changes are missed
#### State Synchronization Improvements
- Optimized Vim mode state synchronization logic
- Reduced state inconsistency cases
- Improved IM switching accuracy
### Documentation
- Updated README.md with IM state memory feature description
- Added workflow diagram (Mermaid format)
- Enhanced troubleshooting guide
- Added more detailed technical details
### Known Issues
No known critical issues. If you encounter problems, see [Troubleshooting](./README_en.md#troubleshooting) or submit an Issue.
### Upgrade Recommendation
Strongly recommend all users upgrade to this version, especially:
- Users experiencing ESC key requiring multiple presses
- Users whose input is interrupted in Insert mode
- Users needing mixed Chinese/English input
Upgrade steps:
```bash
cd /path/to/your/vault/.obsidian/plugins/vim-im-switch/
git pull origin main
npm install
npm run build
# Restart Obsidian
```
---
## [1.0.7] - 2024-12-20
### Fixed
- Fixed IM switching issue in Visual mode
- Improved CodeMirror 6 compatibility
### Performance
- Optimized polling mechanism, reduced CPU usage
---
## [1.0.6] - 2024-11-15
### Added
- Added settings interface to customize Chinese/English IM names
- Support for configuring fcitx-remote command path
### Fixed
- Fixed IM name detection failure in some cases
---
## [1.0.5] - 2024-10-10
### Fixed
- Fixed Windows compatibility issues
- Improved error handling logic
---
## [1.0.4] - 2024-09-05
### Added
- Added Linux system support
- Improved log output format
---
## [1.0.3] - 2024-08-01
### Fixed
- Fixed async issues during initialization
- Improved error handling for command execution
---
## [1.0.2] - 2024-07-15
### Performance
- Optimized IM switching response time
- Reduced unnecessary command calls
---
## [1.0.1] - 2024-06-20
### Fixed
- Fixed plugin loading failure
- Improved compatibility
---
## [1.0.0] - 2024-06-01
### Initial Release
- Basic Vim mode IM auto-switching functionality
- macOS support
- Normal/Insert mode switching support
---
## Legend
- **Added** - New features
- **Fixed** - Bug fixes
- **Performance** - Performance improvements
- **Cleanup** - Code cleanup
- **Technical** - Technical improvements
- **Documentation** - Documentation updates
- **Known Issues** - Known issues
- **Upgrade** - Upgrade recommendations
- **Milestone** - Important milestones

21
LICENSE.txt Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Michael Brenan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

580
README.md Normal file
View File

@@ -0,0 +1,580 @@
# Obsidian Vim 输入法自动切换插件# Obsidian Vim 输入法自动切换插件
[English](./README_en.md) | 中文[English](#english) | [中文](#中文)
一个为 Obsidian 编辑器的 Vim 模式设计的输入法自动切换插件。---
## 功能简介## 中文
- **自动切换输入法**:在 Vim 的 Normal 模式和 Insert 模式之间切换时,自动切换输入法### 功能简介
- **智能记忆**:记住上次 Insert 模式退出时的输入法状态,下次进入时自动恢复
- **无缝体验**:在 Insert 模式下正常输入文本时不会触发输入法切换这是一个为 Obsidian 编辑器的 Vim 模式设计的输入法自动切换插件。它能够:
## 核心特性- **自动切换输入法**:在 Vim 的 Normal 模式和 Insert 模式之间切换时,自动切换输入法
- **智能记忆**:记住上次 Insert 模式退出时的输入法状态,下次进入时自动恢复
### 1. 模式切换自动化- **无缝体验**:在 Insert 模式下正常输入文本时不会触发输入法切换
- **进入 Normal 模式**(按 ESC 或其他命令)→ 自动切换到英文输入法
- **进入 Insert 模式**(按 i, a, o 等)→ 自动恢复上次的输入法状态### 核心特性
### 2. 输入法状态记忆#### 1. 模式切换自动化
- 退出 Insert 模式时,自动检测并保存当前的输入法(中文/英文)- **进入 Normal 模式**(按 ESC 或其他命令)→ 自动切换到英文输入法
- 下次进入 Insert 模式时,自动恢复到上次保存的输入法状态- **进入 Insert 模式**(按 i, a, o 等)→ 自动恢复上次的输入法状态
- 支持中英文混合输入场景
#### 2. 输入法状态记忆
### 3. 智能检测机制- 退出 Insert 模式时,自动检测并保存当前的输入法(中文/英文)
插件使用多重检测机制确保可靠性:- 下次进入 Insert 模式时,自动恢复到上次保存的输入法状态
- **键盘事件监听**(主要):使用事件捕获模式监听 ESC 和 Insert 按键- 支持中英文混合输入场景
- **CodeMirror 事件**(辅助):监听 vim-mode-change 事件
- **定时轮询**兜底100ms 轮询检测模式变化#### 3. 智能检测机制
插件使用多重检测机制确保可靠性:
## 快速开始- **键盘事件监听**(主要):使用事件捕获模式监听 ESC 和 Insert 按键
- **CodeMirror 事件**(辅助):监听 vim-mode-change 事件
### 安装要求- **定时轮询**兜底100ms 轮询检测模式变化
#### macOS### 安装要求
```bash
brew install fcitx-remote-for-osx#### macOS
```1. 安装 [fcitx-remote-for-osx](https://github.com/xcodebuild/fcitx-remote-for-osx)
```bash
#### Linux brew install fcitx-remote-for-osx
```bash ```
# Ubuntu/Debian
sudo apt-get install fcitx2. 确认安装成功:
```bash
# Fedora fcitx-remote -n
sudo dnf install fcitx # 应该输出当前输入法的名称com.apple.keylayout.ABC
```
# Arch Linux
sudo pacman -S fcitx#### Linux
```通过系统包管理器安装 `fcitx` 输入法:
```bash
#### Windows# Ubuntu/Debian
下载 [fcitx-remote.exe](https://github.com/yuanotes/obsidian-vim-im-switch-plugin/releases/download/1.0.3/fcitx-remote.exe) 并放到系统 PATH 路径中sudo apt-get install fcitx
### 安装插件# Fedora
sudo dnf install fcitx
1. 下载插件到 Obsidian 插件目录:
```bash# Arch Linux
cd /path/to/your/vault/.obsidian/plugins/sudo pacman -S fcitx
git clone https://github.com/yourusername/vim-im-switch.git```
```
#### Windows
2. 在 Obsidian 中启用插件:下载并安装 AutoHotKey 版本的 fcitx-remote
- 打开设置 → 社区插件- [下载 fcitx-remote.exe](https://github.com/yuanotes/obsidian-vim-im-switch-plugin/releases/download/1.0.3/fcitx-remote.exe)
- 找到 "Vim Input Method Switch" 并启用- 将 exe 文件放到系统 PATH 路径中
3. 配置输入法(可选):### 安装插件
- 设置英文输入法(默认:`com.apple.keylayout.ABC`
- 设置中文输入法默认自动检测1. 下载插件文件到 Obsidian 插件目录:
```bash
## 使用方法 cd /path/to/your/vault/.obsidian/plugins/
git clone https://github.com/yourusername/vim-im-switch.git
### 基本使用场景 ```
**中文输入**2. 在 Obsidian 中启用插件:
``` - 打开设置 → 社区插件 → 浏览
按 i → 进入 Insert 模式 → 输入法切换到中文(如果上次是中文) - 找到 "Vim Input Method Switch"
输入中文内容 - 点击启用
按 ESC → 退出到 Normal 模式 → 输入法切换到英文
```3. 配置输入法:
- 打开插件设置
**英文输入** - 设置英文输入法(默认:`com.apple.keylayout.ABC`
``` - 设置中文输入法(默认:自动检测)
按 i → 进入 Insert 模式 → 输入法保持英文(如果上次是英文)
输入英文内容### 使用方法
按 ESC → 退出到 Normal 模式 → 输入法保持英文
```#### 基本使用
**中英混合**1. **中文输入**
``` ```
按 i → 自动恢复上次的输入法 按 i → 进入 Insert 模式 → 输入法切换到中文(如果上次是中文)
输入中文,然后手动切换到英文继续输入 输入中文内容
按 ESC → 保存当前输入法状态(英文) 按 ESC → 退出到 Normal 模式 → 输入法切换到英文
按 i → 自动恢复英文输入法 ```
```
2. **英文输入**
### 支持的 Vim 命令 ```
按 i → 进入 Insert 模式 → 输入法保持英文(如果上次是英文)
- **进入 Insert 模式**`i`, `I`, `a`, `A`, `o`, `O`, `s`, `S`, `c`, `C` 输入英文内容
- **退出 Insert 模式**`ESC`, 以及其他触发 Normal 模式的命令 按 ESC → 退出到 Normal 模式 → 输入法保持英文
```
## 工作原理
3. **中英混合**
```mermaid ```
graph TD 按 i → 自动恢复上次的输入法
A[Normal 模式<br/>英文输入法] -->|按 i/a/o 等| B[检测模式切换] 输入中文,然后手动切换到英文继续输入
B --> C[恢复上次保存的<br/>输入法状态] 按 ESC → 保存当前输入法状态(英文)
C --> D[Insert 模式<br/>自动恢复的输入法:<br/>中文/英文] 按 i → 自动恢复英文输入法
D -->|按 ESC| E[保存当前输入法<br/>状态 中/英] ```
E --> F[切换到英文输入法]
F --> A#### 支持的 Vim 命令
style A fill:#e1f5ff,stroke:#01579b,stroke-width:2px- **进入 Insert 模式**`i`, `I`, `a`, `A`, `o`, `O`, `s`, `S`, `c`, `C`
style D fill:#fff9c4,stroke:#f57f17,stroke-width:2px- **退出 Insert 模式**`ESC`, 以及其他触发 Normal 模式的命令
style E fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
style F fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px### 工作原理
```
```mermaid
### 技术细节graph TD
A[Normal 模式<br/>英文输入法] -->|按 i/a/o 等| B[检测模式切换]
**输入法检测** B --> C[恢复上次保存的<br/>输入法状态]
- 使用 `fcitx-remote -n` 获取当前输入法名称 C --> D[Insert 模式<br/>自动恢复的输入法:<br/>中文/英文]
- 对比设置中的中英文输入法名称来判断状态 D -->|按 ESC| E[保存当前输入法<br/>状态 中/英]
E --> F[切换到英文输入法]
**事件处理** F --> A
- **键盘事件**:使用 `capture` 模式尽早捕获按键
- **防抖机制**100ms 内的重复事件会被忽略
- **异步处理**:所有输入法切换操作都是异步的,不阻塞编辑 style A fill:#e1f5ff,stroke:#01579b,stroke-width:2px
style D fill:#fff9c4,stroke:#f57f17,stroke-width:2px
**状态管理** style E fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
- `currentVimMode`:当前 Vim 模式normal/insert/visual style F fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
- `lastInsertModeIMStatus`:上次 Insert 模式的输入法状态```
- `imStatus`当前输入法状态Activate=中文Deactivate=英文)
### 技术细节
## 故障排除
#### 输入法检测
### 问题:插件没有效果- 使用 `fcitx-remote -n` 获取当前输入法名称
1. 检查 fcitx-remote 是否正确安装:- 对比设置中的中英文输入法名称来判断状态
```bash
which fcitx-remote#### 事件处理
fcitx-remote -n- **键盘事件**:使用 `capture` 模式尽早捕获按键
```- **防抖机制**100ms 内的重复事件会被忽略
- **异步处理**:所有输入法切换操作都是异步的,不阻塞编辑
2. 检查插件日志(开发者控制台):
- 按 `Cmd+Option+I` (macOS) 或 `Ctrl+Shift+I` (Windows/Linux) 打开开发者控制台#### 状态管理
- 查看是否有 `[VimIMSwitch]` 开头的日志- `currentVimMode`:当前 Vim 模式normal/insert/visual
- `lastInsertModeIMStatus`:上次 Insert 模式的输入法状态
3. 验证输入法名称:- `imStatus`当前输入法状态Activate=中文Deactivate=英文)
```bash
# 切换到中文输入法后执行### 故障排除
fcitx-remote -n
# 输出应该匹配插件设置中的中文输入法名称#### 问题:插件没有效果
```1. 检查 fcitx-remote 是否正确安装:
```bash
### 问题:输入法切换不正确 which fcitx-remote
1. 确认设置中的输入法名称正确 fcitx-remote -n
2. 手动切换输入法后,在开发者控制台查看日志 ```
3. 检查是否有其他插件冲突
2. 检查插件日志(开发者控制台):
### 问题ESC 键需要按多次 ```
- 这个问题已在最新版本中修复 按 Cmd+Option+I 打开开发者控制台
- 如果仍然存在,请更新到最新版本 查看是否有 [VimIMSwitch] 开头的日志
```
## 开发
3. 验证输入法名称:
### 构建 ```bash
```bash # 切换到中文输入法后执行
npm install fcitx-remote -n
npm run build # 输出应该匹配插件设置中的中文输入法名称
``` ```
### 部署#### 问题:输入法切换不正确
```bash1. 确认设置中的输入法名称正确
./deploy.sh2. 手动切换输入法后,在开发者控制台查看日志
```3. 检查是否有其他插件冲突
### 调试#### 问题ESC 键需要按多次
插件会在控制台输出关键日志:- 这个问题已在最新版本中修复
- `🚀 Loading plugin...` - 插件加载- 如果仍然存在,请更新到最新版本
- `🔤 ESC → English` - ESC 切换到英文
- `🈳 → Chinese` - 切换到中文### 开发
- `❌ Error...` - 错误信息
#### 构建
## 更新日志```bash
npm install
查看 [CHANGELOG.md](./CHANGELOG.md) 获取详细的版本更新历史。npm run build
```
## 许可证
#### 部署
MIT License```bash
./deploy.sh
## 相关链接```
- [更新日志](./CHANGELOG.md)#### 调试
- [English Documentation](./README_en.md)插件会在控制台输出关键日志:
- [fcitx-remote-for-osx](https://github.com/xcodebuild/fcitx-remote-for-osx)- `🚀 Loading plugin...` - 插件加载
- `🔤 ESC → English` - ESC 切换到英文
- `🈳 → Chinese` - 切换到中文
- `❌ Error...` - 错误信息
### 更新日志
#### v1.0.8 (2025-01-04)
- ✨ 新增:输入法状态记忆功能
- 🐛 修复ESC 键第一次按下无效的问题
- 🐛 修复Insert 模式下输入字符触发输入法切换
- ⚡️ 优化:使用事件捕获模式提高响应速度
- 🧹 清理:移除冗余代码和过多的调试日志
### 许可证
MIT License
---
## English
### Features
An input method auto-switching plugin designed for Obsidian's Vim mode that:
- **Automatic switching**: Automatically switches input methods when transitioning between Vim's Normal and Insert modes
- **Smart memory**: Remembers the input method state when exiting Insert mode and restores it on next entry
- **Seamless experience**: Won't trigger input method changes during normal text input in Insert mode
### Core Features
#### 1. Mode Switch Automation
- **Enter Normal mode** (press ESC or other commands) → Auto switch to English input method
- **Enter Insert mode** (press i, a, o, etc.) → Auto restore previous input method state
#### 2. Input Method State Memory
- Automatically detects and saves current input method (Chinese/English) when exiting Insert mode
- Automatically restores to last saved input method state when entering Insert mode
- Supports mixed Chinese/English input scenarios
#### 3. Smart Detection Mechanism
The plugin uses multiple detection mechanisms for reliability:
- **Keyboard event listening** (primary): Uses capture mode to listen for ESC and Insert keys
- **CodeMirror events** (auxiliary): Monitors vim-mode-change events
- **Polling** (fallback): 100ms polling to detect mode changes
### Requirements
#### macOS
1. Install [fcitx-remote-for-osx](https://github.com/xcodebuild/fcitx-remote-for-osx):
```bash
brew install fcitx-remote-for-osx
```
2. Verify installation:
```bash
fcitx-remote -n
# Should output current input method name, e.g.: com.apple.keylayout.ABC
```
#### Linux
Install `fcitx` via your package manager:
```bash
# Ubuntu/Debian
sudo apt-get install fcitx
# Fedora
sudo dnf install fcitx
# Arch Linux
sudo pacman -S fcitx
```
#### Windows
Download and install the AutoHotKey version of fcitx-remote:
- [Download fcitx-remote.exe](https://github.com/yuanotes/obsidian-vim-im-switch-plugin/releases/download/1.0.3/fcitx-remote.exe)
- Place the exe file in your system PATH
### Installation
1. Download plugin files to Obsidian plugins directory:
```bash
cd /path/to/your/vault/.obsidian/plugins/
git clone https://github.com/yourusername/vim-im-switch.git
```
2. Enable plugin in Obsidian:
- Open Settings → Community Plugins → Browse
- Find "Vim Input Method Switch"
- Click Enable
3. Configure input methods:
- Open plugin settings
- Set English input method (default: `com.apple.keylayout.ABC`)
- Set Chinese input method (default: auto-detect)
### Usage
#### Basic Usage
1. **Chinese Input**:
```
Press i → Enter Insert mode → IM switches to Chinese (if last time was Chinese)
Type Chinese content
Press ESC → Exit to Normal mode → IM switches to English
```
2. **English Input**:
```
Press i → Enter Insert mode → IM stays English (if last time was English)
Type English content
Press ESC → Exit to Normal mode → IM stays English
```
3. **Mixed Input**:
```
Press i → Auto restore last IM
Type Chinese, then manually switch to English
Press ESC → Save current IM state (English)
Press i → Auto restore English IM
```
#### Supported Vim Commands
- **Enter Insert mode**: `i`, `I`, `a`, `A`, `o`, `O`, `s`, `S`, `c`, `C`
- **Exit Insert mode**: `ESC`, and other commands that trigger Normal mode
### How It Works
```mermaid
graph TD
A[Normal Mode<br/>English IM] -->|Press i/a/o etc| B[Detect Mode Change]
B --> C[Restore Last Saved<br/>IM State]
C --> D[Insert Mode<br/>Restored IM:<br/>Chinese/English]
D -->|Press ESC| E[Save Current IM<br/>State CN/EN]
E --> F[Switch to English IM]
F --> A
style A fill:#e1f5ff,stroke:#01579b,stroke-width:2px
style D fill:#fff9c4,stroke:#f57f17,stroke-width:2px
style E fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
style F fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
```
The plugin maintains a state machine that tracks Vim modes and input method states, automatically switching input methods when mode transitions occur while preserving user preferences.
### Troubleshooting
#### Issue: Plugin not working
1. Check if fcitx-remote is properly installed
2. Check plugin logs in developer console
3. Verify input method names match settings
#### Issue: Incorrect input method switching
1. Confirm input method names in settings are correct
2. Check logs in developer console after manually switching IM
3. Check for conflicts with other plugins
### Development
#### Build
```bash
npm install
npm run build
```
#### Deploy
```bash
./deploy.sh
```
#### Debug
The plugin outputs key logs in the console:
- `🚀 Loading plugin...` - Plugin loaded
- `🔤 ESC → English` - ESC switches to English
- `🈳 → Chinese` - Switch to Chinese
- `❌ Error...` - Error messages
### Changelog
#### v1.0.8 (2025-01-04)
- ✨ Added: Input method state memory feature
- <20><> Fixed: ESC key not working on first press
- 🐛 Fixed: Character input in Insert mode triggering IM switch
- ⚡️ Optimized: Use event capture mode for better responsiveness
- 🧹 Cleanup: Remove redundant code and excessive debug logs
### License
MIT License

195
README_en.md Normal file
View File

@@ -0,0 +1,195 @@
# Obsidian Vim Input Method Switch Plugin
English | [中文](./README.md)
An input method auto-switching plugin designed for Obsidian's Vim mode.
## Features
- **Automatic switching**: Automatically switches input methods when transitioning between Vim's Normal and Insert modes
- **Smart memory**: Remembers the input method state when exiting Insert mode and restores it on next entry
- **Seamless experience**: Won't trigger input method changes during normal text input in Insert mode
## Core Features
### 1. Mode Switch Automation
- **Enter Normal mode** (press ESC or other commands) → Auto switch to English input method
- **Enter Insert mode** (press i, a, o, etc.) → Auto restore previous input method state
### 2. Input Method State Memory
- Automatically detects and saves current input method (Chinese/English) when exiting Insert mode
- Automatically restores to last saved input method state when entering Insert mode
- Supports mixed Chinese/English input scenarios
### 3. Smart Detection Mechanism
The plugin uses multiple detection mechanisms for reliability:
- **Keyboard event listening** (primary): Uses capture mode to listen for ESC and Insert keys
- **CodeMirror events** (auxiliary): Monitors vim-mode-change events
- **Polling** (fallback): 100ms polling to detect mode changes
## Quick Start
### Requirements
#### macOS
```bash
brew install fcitx-remote-for-osx
```
#### Linux
```bash
# Ubuntu/Debian
sudo apt-get install fcitx
# Fedora
sudo dnf install fcitx
# Arch Linux
sudo pacman -S fcitx
```
#### Windows
Download [fcitx-remote.exe](https://github.com/yuanotes/obsidian-vim-im-switch-plugin/releases/download/1.0.3/fcitx-remote.exe) and place it in your system PATH
### Installation
1. Download plugin to Obsidian plugins directory:
```bash
cd /path/to/your/vault/.obsidian/plugins/
git clone https://github.com/yourusername/vim-im-switch.git
```
2. Enable plugin in Obsidian:
- Open Settings → Community Plugins
- Find "Vim Input Method Switch" and enable it
3. Configure input methods (optional):
- Set English input method (default: `com.apple.keylayout.ABC`)
- Set Chinese input method (default: auto-detect)
## Usage
### Basic Usage Scenarios
**Chinese Input**:
```
Press i → Enter Insert mode → IM switches to Chinese (if last time was Chinese)
Type Chinese content
Press ESC → Exit to Normal mode → IM switches to English
```
**English Input**:
```
Press i → Enter Insert mode → IM stays English (if last time was English)
Type English content
Press ESC → Exit to Normal mode → IM stays English
```
**Mixed Input**:
```
Press i → Auto restore last IM
Type Chinese, then manually switch to English
Press ESC → Save current IM state (English)
Press i → Auto restore English IM
```
### Supported Vim Commands
- **Enter Insert mode**: `i`, `I`, `a`, `A`, `o`, `O`, `s`, `S`, `c`, `C`
- **Exit Insert mode**: `ESC`, and other commands that trigger Normal mode
## How It Works
```mermaid
graph TD
A[Normal Mode<br/>English IM] -->|Press i/a/o etc| B[Detect Mode Change]
B --> C[Restore Last Saved<br/>IM State]
C --> D[Insert Mode<br/>Restored IM:<br/>Chinese/English]
D -->|Press ESC| E[Save Current IM<br/>State CN/EN]
E --> F[Switch to English IM]
F --> A
style A fill:#e1f5ff,stroke:#01579b,stroke-width:2px
style D fill:#fff9c4,stroke:#f57f17,stroke-width:2px
style E fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
style F fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
```
### Technical Details
**Input Method Detection**
- Uses `fcitx-remote -n` to get current input method name
- Compares with configured Chinese/English input method names
**Event Handling**
- **Keyboard events**: Uses `capture` mode to intercept keys early
- **Debouncing**: Ignores repeated events within 100ms
- **Async processing**: All input method switches are asynchronous and non-blocking
**State Management**
- `currentVimMode`: Current Vim mode (normal/insert/visual)
- `lastInsertModeIMStatus`: Last input method state in Insert mode
- `imStatus`: Current input method state (Activate=Chinese, Deactivate=English)
## Troubleshooting
### Issue: Plugin not working
1. Check if fcitx-remote is properly installed:
```bash
which fcitx-remote
fcitx-remote -n
```
2. Check plugin logs (developer console):
- Press `Cmd+Option+I` (macOS) or `Ctrl+Shift+I` (Windows/Linux) to open developer console
- Look for logs starting with `[VimIMSwitch]`
3. Verify input method names:
```bash
# After switching to Chinese input method
fcitx-remote -n
# Output should match the Chinese IM name in plugin settings
```
### Issue: Incorrect input method switching
1. Confirm input method names in settings are correct
2. Check logs in developer console after manually switching IM
3. Check for conflicts with other plugins
### Issue: ESC key requires multiple presses
- This issue has been fixed in the latest version
- If it still occurs, please update to the latest version
## Development
### Build
```bash
npm install
npm run build
```
### Deploy
```bash
./deploy.sh
```
### Debug
The plugin outputs key logs in the console:
- `🚀 Loading plugin...` - Plugin loaded
- `🔤 ESC → English` - ESC switches to English
- `🈳 → Chinese` - Switch to Chinese
- `❌ Error...` - Error messages
## Changelog
See [CHANGELOG_en.md](./CHANGELOG_en.md) for detailed version history.
## License
MIT License
## Links
- [Changelog](./CHANGELOG_en.md)
- [中文文档](./README.md)
- [fcitx-remote-for-osx](https://github.com/xcodebuild/fcitx-remote-for-osx)

175
RELEASE.md Normal file
View File

@@ -0,0 +1,175 @@
# RELEASE NOTE
## 版本发布说明 - 2.0.0
**发布日期**: 2025-11-04
---
### 版本概述
版本 2.0.0 是一个重要的里程碑版本,为 Obsidian Vim 输入法自动切换插件带来了重大改进。此版本包含输入法状态管理系统的完全重写、增强的可靠性以及全面的文档。
### 新增内容
#### 主要功能
##### 输入法状态记忆系统
插件现在具备智能状态记忆系统,能够记住您的输入法偏好:
- **智能记忆**:自动记住退出 Insert 模式时使用的输入法(中文/英文)
- **自动恢复**:再次进入 Insert 模式时,插件会恢复上次的输入法状态
- **混合输入支持**:无缝处理在同一编辑会话中中英文输入法切换的场景
**示例工作流程**
```
1. 按 'i' → 进入 Insert 模式 → 恢复中文输入法(如果上次是中文)
2. 输入中文内容
3. 手动切换到英文输入法,继续输入
4. 按 ESC → 保存"英文"作为最后状态
5. 再次按 'i' → 自动恢复英文输入法
```
#### 关键 Bug 修复
##### ESC 键响应问题
- **修复内容**ESC 键需要多次按下才能切换输入法
- **解决方案**:实现事件捕获模式,在最早阶段拦截按键
- **效果**:首次按下 ESC 即可立即切换到英文输入法
##### Insert 模式干扰问题
- **修复内容**:在 Insert 模式下输入字符(输入 'i'、'a'、'o' 等)触发不必要的输入法切换
- **解决方案**:为键盘事件处理器添加 Vim 模式验证
- **效果**Insert 模式下的正常文本输入不再被打断
#### 性能优化
##### 事件处理优化
- **捕获模式**:键盘事件监听器使用 `{ capture: true }`
- **降低延迟**:输入法切换响应时间快 10-20ms
- **智能检测**:仅在必要时切换输入法,避免冗余操作
##### 三重检测机制
通过三层检测增强可靠性:
1. **主要检测**:键盘事件捕获(最快,最高优先级)
2. **辅助检测**CodeMirror vim-mode-change 事件
3. **兜底检测**100ms 轮询作为安全保障
#### 代码质量
##### 代码库清理
- 移除约 50 行未使用的测试代码
- 减少插件体积约 2KB
- 提高代码可维护性和可读性
##### 简化日志输出
- 减少 70% 的控制台输出
- 保留关键的状态转换日志
- 更清晰的调试信息,便于故障排查
#### 文档
##### 全面的文档
- **README.md**:详细的中文文档,包含使用示例
- **README_en.md**:完整的英文文档
- **CHANGELOG.md**:详细的中文版本历史
- **CHANGELOG_en.md**:完整的英文更新日志
- **Mermaid 图表**:可视化工作流程说明
### 技术细节
#### 架构改进
**状态管理**
- `currentVimMode`:追踪当前 Vim 模式normal/insert/visual
- `lastInsertModeIMStatus`:存储上次 Insert 模式的输入法状态
- `imStatus`:维护当前输入法状态
**检测系统**
- 事件捕获阶段拦截
- 使用 `fcitx-remote -n` 实时验证输入法状态
- 异步输入法切换操作
#### 兼容性
**平台支持**
- macOS通过 fcitx-remote-for-osx 完全支持
- Linux通过 fcitx 完全支持
- Windows通过 AutoHotKey 版本支持
**Obsidian 兼容性**
- 兼容最新版本的 Obsidian
- 同时支持 CodeMirror 5 和 CodeMirror 6
### 升级指南
#### 现有用户
**从 v1.x 升级**
```bash
cd /path/to/your/vault/.obsidian/plugins/vim-im-switch/
git pull origin main
npm install
npm run build
```
然后重启 Obsidian 以激活新版本。
**重要提示**
- 升级过程中会保留设置
- 无需更改配置
- 建议升级前备份您的 vault
#### 新用户
**安装步骤**
1. 为您的平台安装 fcitx-remote
2. 将插件克隆到 Obsidian 插件目录
3. 在 Obsidian 设置中启用插件
4. 配置输入法名称(可选)
详细安装说明请参阅 [README.md](./README.md) 或 [README_en.md](./README_en.md)。
### 已知问题
本版本无严重问题。如需故障排除,请参考:
- [中文故障排除](./README.md#故障排除)
- [English Troubleshooting](./README_en.md#troubleshooting)
### 破坏性变更
无。本版本与 v1.x 配置完全向后兼容。
### 废弃功能
本版本无废弃功能。
### 未来规划
计划在未来版本中实现的功能:
- 支持更多输入法切换工具im-select 等)
- 可配置的快捷键绑定
- Visual 模式输入法处理改进
- 与 Obsidian 原生语言切换集成
### 相关链接
- [GitHub 仓库](https://github.com/yourusername/vim-im-switch)
- [问题追踪](https://github.com/yourusername/vim-im-switch/issues)
- [中文文档](./README.md)
- [English Documentation](./README_en.md)
- [更新日志](./CHANGELOG.md)
- [Changelog](./CHANGELOG_en.md)
### 技术支持
如果您遇到任何问题或有疑问:
1. 查看 [故障排除指南](./README.md#故障排除)
2. 搜索现有的 [Issues](https://github.com/yourusername/vim-im-switch/issues)
3. 创建新的 issue 并提供详细信息
### 许可证
MIT License - 详见 [LICENSE.txt](./LICENSE.txt)
---

BIN
assets/alipay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
assets/wechat_pay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

69
deploy.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/bash
# Obsidian Vim IM Switch Plugin 一键部署脚本
# 编译并复制插件文件到 Obsidian 插件目录
set -e # 遇到错误时退出
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 配置
SOURCE_DIR="/Users/gavin/vim-im-switch"
TARGET_DIR="/Users/gavin/myweb/.obsidian/plugins/vim-im-switch"
echo -e "${BLUE}🚀 开始部署 Obsidian Vim IM Switch Plugin...${NC}"
# 检查源目录
if [ ! -d "$SOURCE_DIR" ]; then
echo -e "${RED}❌ 源目录不存在: $SOURCE_DIR${NC}"
exit 1
fi
# 进入源目录
cd "$SOURCE_DIR"
echo -e "${YELLOW}📁 当前目录: $(pwd)${NC}"
# 编译项目
echo -e "${YELLOW}🔨 开始编译项目...${NC}"
if npm run build; then
echo -e "${GREEN}✅ 编译成功${NC}"
else
echo -e "${RED}❌ 编译失败${NC}"
exit 1
fi
# 检查编译产物
if [ ! -f "main.js" ]; then
echo -e "${RED}❌ 编译产物 main.js 不存在${NC}"
exit 1
fi
# 创建目标目录
echo -e "${YELLOW}📂 创建目标目录...${NC}"
mkdir -p "$TARGET_DIR"
# 复制文件
echo -e "${YELLOW}📋 复制插件文件...${NC}"
# 复制必需的文件
cp -f main.js "$TARGET_DIR/"
cp -f manifest.json "$TARGET_DIR/"
cp -f styles.css "$TARGET_DIR/"
echo -e "${GREEN}✅ 文件复制完成:${NC}"
echo -e " • main.js"
echo -e " • manifest.json"
echo -e " • styles.css"
# 验证文件
echo -e "${YELLOW}🔍 验证复制的文件...${NC}"
ls -la "$TARGET_DIR"
echo -e "${GREEN}🎉 部署完成!${NC}"
echo -e "${BLUE}📍 插件已部署到: $TARGET_DIR${NC}"
echo -e "${YELLOW}💡 请重启 Obsidian 或重新加载插件以使更改生效${NC}"

42
diagnose-im.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
# 输入法诊断和设置脚本
echo "🔍 系统输入法诊断"
echo "=================="
echo ""
echo "1. 当前 fcitx-remote 状态:"
/usr/local/bin/fcitx-remote
echo ""
echo "2. 当前输入法名称:"
/usr/local/bin/fcitx-remote -n
echo ""
echo "3. 系统输入法设置:"
defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources
echo ""
echo "4. 可用输入法:"
ls /System/Library/Input\ Methods/ | grep -E "(Chinese|Pinyin|SCIM|TCIM)"
echo ""
echo "🛠️ 建议解决方案:"
echo "1. 打开系统偏好设置 → 键盘 → 输入法"
echo "2. 点击 '+' 添加中文输入法(如:简体中文-拼音)"
echo "3. 确保启用了至少一个中文输入法"
echo "4. 重新运行插件测试"
echo ""
echo "🧪 测试手动切换(请注意观察实际输入法是否切换):"
echo "执行: fcitx-remote -c (切换到英文)"
/usr/local/bin/fcitx-remote -c
sleep 1
echo "结果: $(/usr/local/bin/fcitx-remote)"
echo ""
echo "执行: fcitx-remote -o (切换到中文)"
/usr/local/bin/fcitx-remote -o
sleep 1
echo "结果: $(/usr/local/bin/fcitx-remote)"

1
fcitx-remote-for-osx Submodule

Submodule fcitx-remote-for-osx added at 15711cb5a7

155
fcitx-remote.ahk Normal file
View File

@@ -0,0 +1,155 @@
GetGUIThreadInfo_hwndActive(WinTitle="A")
{
ControlGet, hwnd, HWND,,, %WinTitle%
if (WinActive(WinTitle)) {
ptrSize := !A_PtrSize ? 4 : A_PtrSize
VarSetCapacity(stGTI, cbSize:=4+4+(PtrSize*6)+16, 0)
NumPut(cbSize, stGTI, 0, "UInt")
return hwnd := DllCall("GetGUIThreadInfo", "Uint", 0, "Ptr", &stGTI)
? NumGet(stGTI, 8+PtrSize, "Ptr") : hwnd
}
else {
return hwnd
}
}
;-----------------------------------------------------------
; IME状态的获取
; WinTitle="A" 対象Window
; 返回值 1:ON / 0:OFF
;-----------------------------------------------------------
IME_GET(WinTitle="A") {
hwnd :=GetGUIThreadInfo_hwndActive(WinTitle)
return DllCall("SendMessage"
, Ptr, DllCall("imm32\ImmGetDefaultIMEWnd", Ptr,hwnd)
, UInt, 0x0283 ;Message : WM_IME_CONTROL
, UPtr, 0x005 ;wParam : IMC_GETOPENSTATUS
, Ptr, 0) ;lParam : 0
}
;-----------------------------------------------------------
; IME状态的设置
; SetSts 1:ON / 0:OFF
; WinTitle="A" 対象Window
; 返回值 0:成功 / 0以外:失败
;-----------------------------------------------------------
IME_SET(SetSts, WinTitle="A") {
hwnd :=GetGUIThreadInfo_hwndActive(WinTitle)
return DllCall("SendMessage"
, Ptr, DllCall("imm32\ImmGetDefaultIMEWnd", Ptr, hwnd)
, UInt, 0x0283 ;Message : WM_IME_CONTROL
, UPtr, 0x006 ;wParam : IMC_SETOPENSTATUS
, Ptr, SetSts) ;lParam : 0 or 1
}
;===========================================================================
; 0000xxxx 假名输入
; 0001xxxx 罗马字输入方式
; xxxx0xxx 半角
; xxxx1xxx 全角
; xxxxx000 英数
; xxxxx001 平假名
; xxxxx011 片假名
; IME输入模式(所有IME共有)
; DEC HEX BIN
; 0 (0x00 0000 0000) 假名 半英数
; 3 (0x03 0000 0011) 半假名
; 8 (0x08 0000 1000) 全英数
; 9 (0x09 0000 1001) 全字母数字
; 11 (0x0B 0000 1011) 全片假名
; 16 (0x10 0001 0000) 罗马字半英数
; 19 (0x13 0001 0011) 半假名
; 24 (0x18 0001 1000) 全英数
; 25 (0x19 0001 1001) 平假名
; 27 (0x1B 0001 1011) 全片假名
; ※ 区域和语言选项 - [详细信息] - 高级设置
; - 将高级文字服务支持应用于所有程序
; 当打开时似乎无法获取该值
; (谷歌日语输入β必须在此打开,所以无法获得值)
;-------------------------------------------------------
; 获取IME输入模式
; WinTitle="A" 対象Window
; 返回值 输入模式
;--------------------------------------------------------
; 测试时 win10 x64 自带输入法 中文返回 1, 英文返回 0.
; win7 x32
; 中文简体 美式键盘 返回 0。
;
; QQ拼音输入法中文输入模式 QQ拼音英文输入模式 搜狗输入法中文 搜狗输入法英文
; 半角+中文标点 1025 268436481(1025)
; 半角+英文标点 1  1024 268435457(1) 268435456(0)
; 全角+中文标点 1033 268436489(1033)
; 全角+英文标点 9 1032 268435465(9) 268435464(8)
; 智能ABC中文输入标准模式 智能ABC中文输入双打模式 智能ABC英文标准 智能ABC英文双打
; 半角+中文标点 1025 -2147482623(1025) 1024 -2147482624
; 半角+英文标点 1 -2147483647(1) 0 -2147483648
; 全角+中文标点 1033 -2147482615(1033) 1032 -2147482616
; 全角+英文标点 9 -2147483639(9) 8 -2147483640
IME_GetConvMode(WinTitle="A") {
hwnd :=GetGUIThreadInfo_hwndActive(WinTitle)
return DllCall("SendMessage"
, "Ptr", DllCall("imm32\ImmGetDefaultIMEWnd", "Ptr", hwnd)
, "UInt", 0x0283 ;Message : WM_IME_CONTROL
, "Int", 0x001 ;wParam : IMC_GETCONVERSIONMODE
, "Int", 0) & 0xffff ;lParam : 0 & 0xffff 表示只取低16位
}
;-------------------------------------------------------
; IME输入模式设置
; ConvMode 输入模式
; WinTitle="A" 対象Window
; 返回值 0:成功 / 0以外:失败
;--------------------------------------------------------
IME_SetConvMode(ConvMode, WinTitle="A") {
hwnd :=GetGUIThreadInfo_hwndActive(WinTitle)
return DllCall("SendMessage"
, "Ptr", DllCall("imm32\ImmGetDefaultIMEWnd", "Ptr", hwnd)
, "UInt", 0x0283 ;Message : WM_IME_CONTROL
, "UPtr", 0x002 ;wParam : IMC_SETCONVERSIONMODE
, "Ptr", ConvMode) ;lParam : CONVERSIONMODE
}
im_status := IME_GetConvMode()
arg = %1%
switch arg {
case "-c":
;; inactive im
IME_SetConvMode(0)
return
case "-o":
;; activate im
IME_SetConvMode(1025)
return
case "-t":
if (IME_GetConvMode() = 0) {
IME_SetConvMode(1025)
} else {
IME_SetConvMode(0)
}
return
case "-T":
if (IME_GetConvMode() = 0) {
IME_SetConvMode(1025)
} else {
IME_SetConvMode(0)
}
return
default:
if (IME_GetConvMode() = 0) {
FileAppend 1`n, *
} else {
FileAppend 2`n, *
}
}

465
main.js Normal file

File diff suppressed because one or more lines are too long

450
main.ts Normal file
View File

@@ -0,0 +1,450 @@
import { App, Modal, Notice, Plugin, PluginSettingTab, Setting, Workspace, MarkdownView, TFile, WorkspaceLeaf } from 'obsidian';
import { exec } from "child_process";
import { promisify } from "util";
import * as CodeMirror from 'codemirror';
interface VimIMSwitchSettings {
fcitxRemotePath_macOS: string;
fcitxRemotePath_windows: string;
fcitxRemotePath_linux: string;
englishInputMethod: string;
chineseInputMethod: string;
}
const DEFAULT_SETTINGS: VimIMSwitchSettings = {
fcitxRemotePath_macOS: '/usr/local/bin/fcitx-remote',
fcitxRemotePath_windows: 'C:\\Program Files\\bin\\fcitx-remote',
fcitxRemotePath_linux: '/usr/bin/fcitx-remote',
englishInputMethod: 'com.apple.keylayout.ABC',
chineseInputMethod: 'auto-detect', // 将自动检测当前中文输入法
}
const pexec = promisify(exec);
enum IMStatus {
None,
Activate,
Deactivate,
}
export default class VimIMSwitchPlugin extends Plugin {
settings: VimIMSwitchSettings;
imStatus = IMStatus.None;
fcitxRemotePath = "";
private editorMode: 'cm5' | 'cm6' = null;
private initialized = false;
private cmEditor: CodeMirror.Editor = null;
private lastInsertModeIMStatus = IMStatus.None; // 记住上一次insert模式的输入法状态
private keyboardListenerSetup = false; // 防止重复设置键盘监听器
private lastKeyTime = 0; // 防抖:记录上次按键时间
private currentVimMode = 'normal'; // 跟踪当前vim模式
async onload() {
console.log('🚀 [VimIMSwitch] Loading plugin...');
await this.loadSettings();
// 尽早设置全局键盘监听器
this.setupObsidianEditorEvents();
this.addSettingTab(new IMSwitchSettingTab(this.app, this));
this.addCommand({
id: 'test-im-switch-to-english',
name: 'Test: Switch to English',
callback: () => {
this.deactivateIM();
}
});
this.addCommand({
id: 'test-im-switch-to-chinese',
name: 'Test: Switch to Chinese',
callback: () => {
this.activateIM();
}
});
this.addCommand({
id: 'test-check-im-status',
name: 'Test: Check IM Status',
callback: () => {
this.getFcitxRemoteStatus();
}
});
this.app.workspace.on('file-open', async (file: TFile) => {
if (!this.initialized) {
await this.initialize();
}
if (this.cmEditor) {
await this.getFcitxRemoteStatus();
this.lastInsertModeIMStatus = this.imStatus;
await this.deactivateIM();
if (typeof this.cmEditor.off === 'function') {
this.cmEditor.off("vim-mode-change", this.onVimModeChange);
}
if (typeof this.cmEditor.on === 'function') {
this.cmEditor.on("vim-mode-change", this.onVimModeChange);
}
}
});
this.app.workspace.on('active-leaf-change', async (leaf: WorkspaceLeaf) => {
if(this.app.workspace.activeLeaf.view.getViewType() == "markdown") {
if (!this.initialized) {
await this.initialize();
}
if (this.cmEditor) {
await this.getFcitxRemoteStatus();
this.lastInsertModeIMStatus = this.imStatus;
await this.deactivateIM();
this.setupObsidianEditorEvents();
}
}
});
}
async initialize() {
if (this.initialized) {
return;
}
if ('editor:toggle-source' in (this.app as any).commands.editorCommands) {
this.editorMode = 'cm6';
} else {
this.editorMode = 'cm5';
}
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
if (view) {
if (this.editorMode == 'cm6') {
const possiblePaths = [
(view as any).sourceMode?.cmEditor?.cm?.cm,
(view as any).sourceMode?.cmEditor?.cm,
(view as any).sourceMode?.cmEditor,
(view as any).editor?.cm,
(view.editor as any)?.cm
];
for (let i = 0; i < possiblePaths.length; i++) {
const path = possiblePaths[i];
if (path && !this.cmEditor) {
this.cmEditor = path;
break;
}
}
} else {
const possiblePaths = [
(view as any).sourceMode?.cmEditor,
(view as any).editor?.cm,
(view.editor as any)?.cm
];
for (let i = 0; i < possiblePaths.length; i++) {
const path = possiblePaths[i];
if (path && !this.cmEditor) {
this.cmEditor = path;
break;
}
}
}
}
}
setupVimModePolling() {
// 防止重复设置轮询
if ((this as any).vimModePollingInterval) {
clearInterval((this as any).vimModePollingInterval);
}
let lastMode = "";
const pollInterval = setInterval(() => {
if (!this.cmEditor) {
return;
}
try {
const realCM = (this.cmEditor as any).cm;
const currentMode = realCM?.state?.vim?.mode || "";
if (currentMode && currentMode !== lastMode) {
this.onVimModeChange({ mode: currentMode });
lastMode = currentMode;
}
} catch (error) {
// 忽略轮询错误
}
}, 100);
(this as any).vimModePollingInterval = pollInterval;
}
setupObsidianEditorEvents() {
if (this.keyboardListenerSetup) {
return;
}
const handleKeyDown = async (event: KeyboardEvent) => {
const currentTime = Date.now();
// 防抖100ms内只处理一次
if (currentTime - this.lastKeyTime < 100) {
return;
}
this.lastKeyTime = currentTime;
// 处理ESC键退出到normal模式
if (event.key === 'Escape') {
// 退出insert模式前先保存当前输入法状态
const beforeIM = await this.runCmd(this.fcitxRemotePath, ["-n"]);
const currentIMName = beforeIM.trim();
// 检查当前输入法是中文还是英文
if (currentIMName === this.settings.chineseInputMethod) {
this.lastInsertModeIMStatus = IMStatus.Activate;
console.log('🔤 [VimIMSwitch] ESC → English (saved Chinese)');
} else {
this.lastInsertModeIMStatus = IMStatus.Deactivate;
console.log('🔤 [VimIMSwitch] ESC → English (saved English)');
}
// 切换到英文输入法
this.currentVimMode = 'normal';
await this.deactivateIM();
}
// 处理进入insert模式的按键只在normal模式下
else if (this.currentVimMode === 'normal' &&
['i', 'I', 'a', 'A', 'o', 'O', 's', 'S', 'c', 'C'].includes(event.key) &&
!event.ctrlKey && !event.metaKey && !event.altKey) {
// 延迟一下让Vim先切换模式
setTimeout(async () => {
this.currentVimMode = 'insert';
// 恢复上次的输入法状态
if (this.lastInsertModeIMStatus == IMStatus.Activate) {
console.log("🈳 [VimIMSwitch] → Chinese");
await this.activateIM();
} else {
console.log("🔤 [VimIMSwitch] → English");
await this.deactivateIM();
}
}, 10);
}
};
// 移除旧的监听器
if ((this as any).obsidianKeyDownListener) {
document.removeEventListener('keydown', (this as any).obsidianKeyDownListener, { capture: true });
}
// 使用capture模式确保更早接收事件
(this as any).obsidianKeyDownListener = handleKeyDown;
document.addEventListener('keydown', handleKeyDown, { capture: true });
this.keyboardListenerSetup = true;
}
onVimModeChange = async (cm: any) => {
// 防止短时间内重复处理相同的模式切换
const currentTime = Date.now();
if (cm.mode === this.currentVimMode && currentTime - this.lastKeyTime < 100) {
return;
}
// 更新当前vim模式状态
this.currentVimMode = cm.mode;
if (cm.mode == "normal" || cm.mode == "visual") {
// 进入normal/visual模式前先保存当前输入法状态
await this.getFcitxRemoteStatus();
if (this.imStatus == IMStatus.Activate) {
this.lastInsertModeIMStatus = IMStatus.Activate;
}
console.log("🔤 [VimIMSwitch] → English");
await this.deactivateIM();
} else if (cm.mode == "insert" || cm.mode == "replace") {
// 进入insert模式时恢复上次的输入法状态
if (this.lastInsertModeIMStatus == IMStatus.Activate) {
console.log("🈳 [VimIMSwitch] → Chinese");
await this.activateIM();
} else {
console.log("🔤 [VimIMSwitch] → English");
await this.deactivateIM();
}
}
}
async runCmd(cmd: string, args: string[] = []) : Promise<string>{
const output = await pexec(`${cmd} ${args.join(" ")}`);
return output.stdout;
}
async getFcitxRemoteStatus() {
if (this.fcitxRemotePath == "") {
console.log("❌ [VimIMSwitch] Cannot get fcitx-remote path, please set it correctly.");
return;
}
try {
let fcitxRemoteOutput = await this.runCmd(this.fcitxRemotePath);
fcitxRemoteOutput = fcitxRemoteOutput.trimRight();
if (fcitxRemoteOutput == "1") {
this.imStatus = IMStatus.Deactivate;
} else if (fcitxRemoteOutput == "2") {
this.imStatus = IMStatus.Activate;
} else {
this.imStatus = IMStatus.None;
}
} catch (error) {
console.log(`❌ [VimIMSwitch] Error getting IM status:`, error);
}
}
async activateIM() {
if (this.fcitxRemotePath == "") {
console.log("❌ [VimIMSwitch] Cannot get fcitx-remote path, please set it correctly.");
return;
}
try {
await this.runCmd(this.fcitxRemotePath, ["-s", this.settings.chineseInputMethod]);
await new Promise(resolve => setTimeout(resolve, 100));
} catch (error) {
console.log("❌ [VimIMSwitch] Error activating IM:", error);
}
}
async deactivateIM() {
if (this.fcitxRemotePath == "") {
console.log("❌ [VimIMSwitch] Cannot get fcitx-remote path, please set it correctly.");
return;
}
try {
await this.runCmd(this.fcitxRemotePath, ["-s", this.settings.englishInputMethod]);
await new Promise(resolve => setTimeout(resolve, 100));
} catch (error) {
console.log("❌ [VimIMSwitch] Error deactivating IM:", error);
}
}
onunload() {
// 清理 CodeMirror 事件监听器
if (this.cmEditor && typeof this.cmEditor.off === 'function') {
this.cmEditor.off("vim-mode-change", this.onVimModeChange);
}
// 清理轮询定时器
if ((this as any).vimModePollingInterval) {
clearInterval((this as any).vimModePollingInterval);
}
// 清理键盘事件监听器
if ((this as any).obsidianKeyDownListener) {
document.removeEventListener('keydown', (this as any).obsidianKeyDownListener, { capture: true });
this.keyboardListenerSetup = false;
}
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
await this.updateCurrentPath();
await this.detectInputMethods();
}
async detectInputMethods() {
if (this.settings.chineseInputMethod === 'auto-detect') {
try {
const currentIM = await this.runCmd(this.fcitxRemotePath, ["-n"]);
const currentName = currentIM.trim();
if (currentName.includes('pinyin') ||
currentName.includes('chinese') ||
currentName.includes('tencent') ||
currentName.includes('sogou') ||
currentName.includes('baidu')) {
this.settings.chineseInputMethod = currentName;
} else {
this.settings.chineseInputMethod = 'com.tencent.inputmethod.wetype.pinyin';
}
} catch (error) {
this.settings.chineseInputMethod = 'com.tencent.inputmethod.wetype.pinyin';
}
}
}
async updateCurrentPath() {
console.log(`🖥️ [VimIMSwitch] Platform detected: ${process.platform}`);
switch (process.platform) {
case 'darwin':
this.fcitxRemotePath = this.settings.fcitxRemotePath_macOS;
console.log(`🍎 [VimIMSwitch] Using macOS path: ${this.fcitxRemotePath}`);
break;
case 'linux':
this.fcitxRemotePath = this.settings.fcitxRemotePath_linux;
console.log(`🐧 [VimIMSwitch] Using Linux path: ${this.fcitxRemotePath}`);
break;
case 'win32':
this.fcitxRemotePath = this.settings.fcitxRemotePath_windows;
console.log(`🪟 [VimIMSwitch] Using Windows path: ${this.fcitxRemotePath}`);
break;
default:
console.log(`❌ [VimIMSwitch] Platform ${process.platform} is not supported currently.`);
break;
}
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class IMSwitchSettingTab extends PluginSettingTab {
plugin: VimIMSwitchPlugin;
constructor(app: App, plugin: VimIMSwitchPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
let {containerEl} = this;
containerEl.empty();
containerEl.createEl('h2', {text: 'Settings for Vim IM Switch plugin.'});
new Setting(containerEl)
.setName('Fcitx Remote Path for macOS')
.setDesc('The absolute path to fcitx-remote bin file on macOS.')
.addText(text => text
.setPlaceholder(DEFAULT_SETTINGS.fcitxRemotePath_macOS)
.setValue(this.plugin.settings.fcitxRemotePath_macOS)
.onChange(async (value) => {
this.plugin.settings.fcitxRemotePath_macOS = value;
this.plugin.updateCurrentPath();
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Fcitx Remote Path for Linux')
.setDesc('The absolute path to fcitx-remote bin file on Linux.')
.addText(text => text
.setPlaceholder(DEFAULT_SETTINGS.fcitxRemotePath_linux)
.setValue(this.plugin.settings.fcitxRemotePath_linux)
.onChange(async (value) => {
this.plugin.settings.fcitxRemotePath_linux = value;
this.plugin.updateCurrentPath();
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Fcitx Remote Path for Windows')
.setDesc('The absolute path to fcitx-remote bin file on Windows.')
.addText(text => text
.setPlaceholder(DEFAULT_SETTINGS.fcitxRemotePath_windows)
.setValue(this.plugin.settings.fcitxRemotePath_windows)
.onChange(async (value) => {
this.plugin.settings.fcitxRemotePath_windows = value;
this.plugin.updateCurrentPath();
await this.plugin.saveSettings();
}));
}
}

10
manifest.json Normal file
View File

@@ -0,0 +1,10 @@
{
"id": "obsidian-vim-im-switch-plugin",
"name": "Vim Input Method Switch",
"version": "1.0.8",
"minAppVersion": "0.9.12",
"description": "Switch input method with fcitx-remote when Vim keymap is enabled.",
"author": "Yuan Chen",
"authorUrl": "https://github.com/yuanotes",
"isDesktopOnly": true
}

510
package-lock.json generated Normal file
View File

@@ -0,0 +1,510 @@
{
"name": "obsidian-vim-im-switch-plugin",
"version": "0.12.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "obsidian-vim-im-switch-plugin",
"version": "0.12.0",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-typescript": "^8.2.1",
"@types/node": "^14.14.37",
"obsidian": "^0.12.0",
"rollup": "^2.32.1",
"tslib": "^2.2.0",
"typescript": "^4.2.4"
}
},
"node_modules/@rollup/plugin-commonjs": {
"version": "18.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-18.1.0.tgz",
"integrity": "sha512-h3e6T9rUxVMAQswpDIobfUHn/doMzM9sgkMrsMWCFLmB84PSoC8mV8tOloAJjSRwdqhXBqstlX2BwBpHJvbhxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^3.1.0",
"commondir": "^1.0.1",
"estree-walker": "^2.0.1",
"glob": "^7.1.6",
"is-reference": "^1.2.1",
"magic-string": "^0.25.7",
"resolve": "^1.17.0"
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"rollup": "^2.30.0"
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "11.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz",
"integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^3.1.0",
"@types/resolve": "1.17.1",
"builtin-modules": "^3.1.0",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.19.0"
},
"engines": {
"node": ">= 10.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0"
}
},
"node_modules/@rollup/plugin-typescript": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz",
"integrity": "sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^3.1.0",
"resolve": "^1.17.0"
},
"engines": {
"node": ">=8.0.0"
},
"peerDependencies": {
"rollup": "^2.14.0",
"tslib": "*",
"typescript": ">=3.7.0"
},
"peerDependenciesMeta": {
"tslib": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "0.0.39",
"estree-walker": "^1.0.1",
"picomatch": "^2.2.2"
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0"
}
},
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/codemirror": {
"version": "0.0.108",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.108.tgz",
"integrity": "sha512-3FGFcus0P7C2UOGCNUVENqObEb4SFk+S8Dnxq7K6aIsLVs/vDtlangl3PEO0ykaKXyK56swVF6Nho7VsA44uhw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/tern": "*"
}
},
"node_modules/@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "14.18.63",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz",
"integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/resolve": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
"integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/tern": {
"version": "0.23.9",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
"integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/builtin-modules": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true,
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC"
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"dev": true,
"license": "MIT"
},
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"sourcemap-codec": "^1.4.8"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/obsidian": {
"version": "0.12.17",
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-0.12.17.tgz",
"integrity": "sha512-YvCAlRym9D8zNPXt6Ez8QubSTVGoChx6lb58zqI13Dcrz3l1lgUO+pcOGDiD5Qa67nzDZLXo3aV2rqkCCpTvGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/codemirror": "0.0.108",
"moment": "2.29.1"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "2.79.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
"dev": true,
"license": "MIT",
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"deprecated": "Please use @jridgewell/sourcemap-codec instead",
"dev": true,
"license": "MIT"
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"license": "ISC"
}
}
}

23
package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "obsidian-vim-im-switch-plugin",
"version": "0.12.0",
"description": "This is a plugin for Obsidian (https://obsidian.md). Switch input method with fcitx-remote when Vim keymap is enabled.",
"main": "main.js",
"scripts": {
"dev": "rollup --config rollup.config.js -w",
"build": "rollup --config rollup.config.js --environment BUILD:production"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-typescript": "^8.2.1",
"@types/node": "^14.14.37",
"obsidian": "^0.12.0",
"rollup": "^2.32.1",
"tslib": "^2.2.0",
"typescript": "^4.2.4"
}
}

30
rollup.config.js Normal file
View File

@@ -0,0 +1,30 @@
import typescript from '@rollup/plugin-typescript';
import {nodeResolve} from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
const isProd = (process.env.BUILD === 'production');
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
*/
`;
export default {
input: 'main.ts',
output: {
dir: '.',
sourcemap: 'inline',
sourcemapExcludeSources: isProd,
format: 'cjs',
exports: 'default',
banner,
},
external: ['obsidian'],
plugins: [
typescript(),
nodeResolve({browser: true}),
commonjs(),
]
};

2
styles.css Normal file
View File

@@ -0,0 +1,2 @@
body {
}

22
tsconfig.json Normal file
View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "es6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"lib": [
"dom",
"es5",
"scripthost",
"es2015"
]
},
"include": [
"**/*.ts"
]
}

5
versions.json Normal file
View File

@@ -0,0 +1,5 @@
{
"1.0.2": "0.9.12",
"1.0.1": "0.9.12",
"1.0.0": "0.9.7"
}