├── App ├── main.tiff ├── appstore.png ├── playstore.png ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 512.png │ │ ├── 64.png │ │ ├── 1024.png │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── UI │ ├── ReadSize.swift │ ├── Toast.swift │ └── ContentView.swift ├── main.swift ├── MainMenu.xib ├── DataBaseClient.swift └── AnInputController.swift ├── README ├── 1.png ├── 2.png └── 3.png ├── .gitignore ├── Tests ├── Info.plist └── Tests.swift ├── ihelper.entitlements ├── ihelper.xcodeproj ├── project.xcworkspace │ ├── xcuserdata │ │ └── admin.xcuserdatad │ │ │ ├── UserInterfaceState.xcuserstate │ │ │ └── WorkspaceSettings.xcsettings │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── admin.xcuserdatad │ │ ├── xcschemes │ │ └── xcschememanagement.plist │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist └── project.pbxproj ├── makedmg.sh ├── Info.plist └── README.md /App/main.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/main.tiff -------------------------------------------------------------------------------- /README/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/README/1.png -------------------------------------------------------------------------------- /README/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/README/2.png -------------------------------------------------------------------------------- /README/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/README/3.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store? 3 | ./build 4 | ihelper.app 5 | ihelper.dmg 6 | -------------------------------------------------------------------------------- /App/appstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/appstore.png -------------------------------------------------------------------------------- /App/playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/playstore.png -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/App/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /App/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ihelper.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ihelper.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fumeboy/ihelper/HEAD/ihelper.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /makedmg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # brew install create-dmg 3 | test -f ihelper.dmg && rm ihelper.dmg 4 | create-dmg \ 5 | --volname "ihelper" \ 6 | --window-pos 200 120 \ 7 | --window-size 800 400 \ 8 | "ihelper.dmg" \ 9 | "./build/" 10 | -------------------------------------------------------------------------------- /ihelper.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ihelper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ihelper.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseTargetSettings 7 | 8 | 9 | -------------------------------------------------------------------------------- /ihelper.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | aaaa.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | ihelper.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /App/UI/ReadSize.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct SizePreferenceKey: PreferenceKey { 4 | static var defaultValue: CGSize = .zero 5 | static func reduce(value: inout CGSize, nextValue: () -> CGSize) {} 6 | } 7 | 8 | extension View { 9 | func readSize(onChange: @escaping (CGSize) -> Void) -> some View { 10 | background( 11 | GeometryReader { geometryProxy in 12 | Color.clear 13 | .preference(key: SizePreferenceKey.self, value: geometryProxy.size) 14 | } 15 | ) 16 | .onPreferenceChange(SizePreferenceKey.self, perform: onChange) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /App/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Cocoa 3 | import SwiftUI 4 | import InputMethodKit 5 | 6 | class AppDelegate: NSObject, NSApplicationDelegate { 7 | var server: IMKServer! 8 | 9 | func applicationDidFinishLaunching(_ aNotification: Notification) { 10 | server = IMKServer( 11 | name: "iHelper_1_Connection", bundleIdentifier: Bundle.main.bundleIdentifier) 12 | NSLog("start") 13 | } 14 | 15 | func applicationWillTerminate(_ aNotification: Notification) { 16 | } 17 | 18 | } 19 | 20 | let app = NSApplication.shared 21 | let d = AppDelegate() 22 | app.delegate = d 23 | _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) 24 | -------------------------------------------------------------------------------- /ihelper.xcodeproj/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /App/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // aaaaTests.swift 3 | // aaaaTests 4 | // 5 | // Created by admin on 2022/5/31. 6 | // 7 | 8 | import XCTest 9 | @testable import ihelper 10 | 11 | class Tests: XCTestCase { 12 | 13 | func testCSV() throws { 14 | guard let spans = parseCSVline("a,v,c,")else {throw ""} 15 | guard spans.3 else {throw ""} 16 | guard let spans = parseCSVline("a,v,c") else {throw ""} 17 | guard spans.3 else {throw ""} 18 | guard let spans = parseCSVline("a,v,c,d") else {throw ""} 19 | guard spans.3 == false else {throw ""} 20 | guard let spans = parseCSVline(#"a,v,c,",d""#) else {throw ""} 21 | guard spans.2 == ",d" else {throw spans.2} 22 | guard let spans = parseCSVline(#"a,v,c,",""d""#) else {throw ""} 23 | guard spans.2 == #","d"# else {throw spans.2} 24 | guard let spans = parseCSVline(#"a,v,c,",""""""d""#) else {throw ""} 25 | guard spans.2 == #","""d"# else {throw spans.2} 26 | guard let spans = parseCSVline(#"a,v,c,","""""",d""#) else {throw ""} 27 | guard spans.2 == #",""",d"# else {throw spans.2} 28 | 29 | print(parseCSVline("")) 30 | 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /App/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | {"images":[{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | InputMethodConnectionName 24 | iHelper_1_Connection 25 | InputMethodServerControllerClass 26 | $(PRODUCT_MODULE_NAME).AnInputController 27 | InputMethodServerDelegateClass 28 | $(PRODUCT_MODULE_NAME).AnInputController 29 | LSApplicationCategoryType 30 | public.app-category.utilities 31 | LSBackgroundOnly 32 | 33 | LSMinimumSystemVersion 34 | 10.9 35 | LSUIElement 36 | 37 | NSMainNibFile 38 | MainMenu 39 | NSPrincipalClass 40 | NSApplication 41 | TISInputSourceID 42 | fumeboy.inputmethod.ihelper 43 | TISIntendedLanguage 44 | zh-Hans 45 | tsInputMethodCharacterRepertoireKey 46 | 47 | Hans 48 | Latn 49 | 50 | tsInputMethodIconFileKey 51 | main.tiff 52 | 53 | 54 | -------------------------------------------------------------------------------- /App/UI/Toast.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import SwiftUI 3 | 4 | class ToastWindow: NSWindow { 5 | struct ToastView: View { 6 | var text: String 7 | var body: some View { 8 | VStack { 9 | Text(text) 10 | .font(.system(size: 50)) 11 | .foregroundColor(.white) 12 | } 13 | .fixedSize() 14 | .padding(20) 15 | .background(Color.black.opacity(0.5)) 16 | .clipped() 17 | .cornerRadius(20) 18 | } 19 | } 20 | 21 | struct ToastView_Previews: PreviewProvider { 22 | static var previews: some View { 23 | ToastView(text: "banning mode") 24 | } 25 | } 26 | 27 | private var timer: Timer? 28 | 29 | private let hostingView = NSHostingView(rootView: ToastView(text: "normal mode")) 30 | override var acceptsFirstResponder: Bool { 31 | return false 32 | } 33 | 34 | private func initWindow() { 35 | isOpaque = false 36 | backgroundColor = NSColor.clear 37 | styleMask = .init(arrayLiteral: .borderless, .fullSizeContentView) 38 | hasShadow = false 39 | ignoresMouseEvents = true 40 | isReleasedWhenClosed = false 41 | level = NSWindow.Level(rawValue: NSWindow.Level.RawValue(CGShieldingWindowLevel())) 42 | } 43 | override init( 44 | contentRect: NSRect, 45 | styleMask style: NSWindow.StyleMask, 46 | backing backingStoreType: NSWindow.BackingStoreType, 47 | defer flag: Bool 48 | ) { 49 | super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag) 50 | initWindow() 51 | contentView = hostingView 52 | } 53 | 54 | private func positionWindow() { 55 | guard let screen = getScreenFromPoint(NSEvent.mouseLocation) else { 56 | return 57 | } 58 | let cx = (screen.frame.minX + screen.frame.maxX) / 2 - frame.width / 2 59 | let cy = (screen.frame.maxY - screen.frame.minY) / 5 + screen.frame.minY 60 | self.setFrameOrigin(NSPoint(x: cx, y: cy)) 61 | } 62 | 63 | func show(_ text: String) { 64 | timer?.invalidate() 65 | hostingView.rootView.text = text 66 | positionWindow() 67 | orderFront(nil) 68 | timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { (_) in 69 | self.close() 70 | }) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOTE: ihelper -> ihelper-raycast 2 | 3 | 我决定给 raycast(一个类似于 Alfred 的软件) 写插件来实现之前开发输入法所实现的功能。这个项目后面可能不再更新。 4 | 5 | 插件已经写好在这里 https://github.com/fumeboy/ihelper-raycast 6 | 7 | (但我又发现 raycast 限制了 UI 的表达,所以这个项目可能还会更新) 8 | 9 | # ihelper 10 | 11 | 效率工具;辅助记忆;快速输入; 12 | 13 | ![screenshot](https://github.com/fumeboy/ihelper/blob/main/README/3.png) 14 | 15 | ![screenshot](https://github.com/fumeboy/ihelper/blob/main/README/2.png) 16 | 17 | ## 功能简介 18 | 19 | 用户可以自定义字典在 CSV 文件 `~/.ihelper.csv`;字典行记录 .tag、.desc、.output、.output-type 四项 20 | 21 | 启用 ihelper 输入法,输入能匹配 .tag 的文本查找出匹配的字典行;使用方向键选择要输出一项;敲回车键输出 output 文本或执行对应的 hotkey 22 | 23 | ## 场景举例 24 | 25 | 比如因为我不熟悉 vim 的操作,于是配置了 `vim;cursor;,光标移动到非空格符的下一行,+,` 这一行到字典文件 26 | 27 | 使用时,首先启用输入法并输入 `v` 匹配到 .tag `vim`, 然后选择该行并回车,输入法将输出 .output `+`, vim 窗口接收到 `+`, 执行 “光标移动到非空格符的下一行” 28 | 29 | ## 使用细节 30 | 31 | ### 初始化 32 | 33 | 1. 编译得到的 app 文件放置在 `/Library/Input Methods` 目录 34 | 35 | 2. 字典文件放置在 `~/.ihelper.csv` (第一次使用可以复制本仓库的 `.ihelper.csv`) 36 | 37 | 3. 在系统设置中启用输入法;*需要注意,系统需要重启(或注销重新登陆)才会加载新输入法* 38 | 39 | ### 字典格式 40 | 41 | 每一行为一个字典项,按顺序依次是 tag、desc、output、output-type 四列; 42 | 43 | #### tag 44 | 45 | tag 作为索引项使用,允许多值,按照符号 ; 分割 46 | 47 | #### desc 48 | 49 | 描述该字典行的 text 或 hotkey;无格式要求 50 | 51 | #### output 52 | 53 | 当 output-type == 1 时,输出 output 原文本 54 | 55 | 当 output-type == 2 时,`ctrl+C` 这样的 hotkey 会被执行 56 | 57 | hotkey 格式为 `+` 或 `` 58 | 59 | 其中 `` 的内容可以是:`[shift,ctrl,control,command,cmd,option]` 中的一项 60 | 61 | 其中 `key` 的内容格式请见源文件 `AnInputController.swift` 中的 `keycode` 62 | 63 | ### 按键功能 64 | 65 | ascii(32~126) 对应的按键输入字符 66 | 67 | `space`、`tab`、`enter` 确认输入并输出 68 | 69 | `esc` 清空输入、切换输入模式 70 | 71 | `del` 删除末尾字符 72 | 73 | `arrow up`、`arrow down` 选择输出项 74 | 75 | `arrow left`、 `arrow right` 切页 76 | 77 | ### 输入模式 78 | 79 | 输入模式分为 normal mode 和 banning mode 80 | 81 | normal mode 下输入文本索引 origin,会进行字典查询;也可以直接输出 origin 82 | 83 | banning mode 下输入纯文本,输入法不进行响应;目的是省略切换英文输入法 84 | 85 | *需要注意,在 normal node 下直接输出 origin 时,会自动切换到 banning mode* 86 | 87 | ### 控制模式 88 | 89 | 连续按两次 `esc` 进入控制模式。控制模式下: 90 | 91 | 按 `w` 键打开 `~/.ihelper.csv` 编辑窗口 92 | 93 | 按 `s` 键读取 `~/.ihelper.csv` 94 | 95 | ## 常见问题 96 | 97 | 1. 切换到该输入法但是输入无响应 -> 到 Input Methods 文件夹手动启动 app,应该会弹出系统报错信息;一般是系统版本不匹配 98 | 2. 系统设置里找不到或者无法切换到该输入法 -> 可能是没有注销重新登录,系统未加载该输入法 99 | 3. 怎么判断系统有无加载输入法 -> 查看是否有 ihelper 进程,如果有就是已经加载 100 | 4. hotkey 不生效 -> 输出 hotkey 时,系统会告诉你需要在 “安全性与隐私-辅助功能” 里为 ihelper 添加权限;如果重新安装了输入法,需要删除原权限,注销重新登录后再添加权限 101 | 5. 字典行未加载 -> 检查格式是否正确,特殊符号需要满足 csv 转义规则 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /App/DataBaseClient.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | var configCandidateNum: Int = 10; 4 | let dict_filename = ".ihelper.csv" 5 | 6 | struct Candidate: Hashable { 7 | let tag: String 8 | let desc: String 9 | let output: String 10 | let iftext: Bool 11 | let id: Int 12 | } 13 | 14 | class DataBaseClient{ 15 | static let shared = DataBaseClient() 16 | 17 | var filecontent: [(String,String,String,Bool)]! 18 | 19 | func getCandidates(origin: String = String()) -> [Candidate] { 20 | let keywords = origin.components(separatedBy: ";") 21 | var reexp = "" 22 | for (i,k) in keywords.enumerated() { 23 | // (?=.*key1).*;(?=.*key2).*;(?=.*key3) 24 | reexp += "(?=.*"+NSRegularExpression.escapedPattern(for: k)+")" 25 | if i != keywords.count-1 { 26 | reexp += ".*;" 27 | } 28 | } 29 | let RE = try! NSRegularExpression(pattern: reexp, options: .caseInsensitive) 30 | var candidates:[Candidate] = [] 31 | for (index,item) in filecontent.enumerated() { 32 | if RE.firstMatch(in: item.0, range: NSRange(location:0,length:item.0.count)) != nil { 33 | candidates.append(Candidate(tag: item.0, desc: item.1, output: item.2, iftext: item.3, id:index)) 34 | } 35 | } 36 | 37 | return candidates 38 | } 39 | 40 | func loadDict(){ // parse .csv 41 | let path = NSHomeDirectory()+"/" + dict_filename 42 | if !FileManager.default.fileExists(atPath:path) { 43 | FileManager.default.createFile(atPath: path, contents: nil, attributes: nil) 44 | } 45 | let text = try! String(contentsOfFile:path, encoding: String.Encoding.utf8) 46 | let lines = text.split{ [Character("\n"), Character("\r"), Character("\r\n")].contains($0) } 47 | var kvs = [(String,String,String,Bool)]() 48 | for l in lines{ 49 | if let s = parseCSVline(String(l)) { kvs.append(s)} 50 | } 51 | filecontent = kvs 52 | } 53 | 54 | init() { 55 | loadDict() 56 | } 57 | } 58 | 59 | func parseCSVline(_ l: String) -> (String,String,String,Bool)?{ 60 | var spans = ["","","",""] 61 | var spanIndex = 0 62 | var openclose = false 63 | for char in l.appending(",") { 64 | switch char { 65 | case "\"": 66 | if openclose == false { 67 | openclose = true 68 | }else{ 69 | openclose = false 70 | spans[spanIndex] += "\"" 71 | } 72 | case ",": 73 | if openclose == false { 74 | if spans[spanIndex].last == "\""{ 75 | spans[spanIndex].remove(at: spans[spanIndex].index(before: spans[spanIndex].endIndex)) 76 | } 77 | if spanIndex == 3 { 78 | break 79 | } 80 | spanIndex+=1 81 | }else{ 82 | spans[spanIndex] += "," 83 | } 84 | default: 85 | spans[spanIndex] += String(char) 86 | } 87 | } 88 | 89 | if spans[1].count == 0 { 90 | return nil 91 | } 92 | if spans[3] == "2" { 93 | return ((spans[0],spans[1],spans[2],false)) 94 | } else { 95 | return ((spans[0],spans[1],spans[2],true)) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /App/UI/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | let w :CGFloat = 400 4 | let color1 = Color(red:255,green:255,blue:255,opacity:0.9) 5 | let color2 = Color(red:255,green:255,blue:255,opacity:0.7) 6 | let itemselectedbg = Color.purple.opacity(0.3) 7 | let tagtextcolor = Color(red:194/255,green:194/255,blue:240/255) 8 | let itemHeight: CGFloat = 34 9 | let horizontalmargin: CGFloat = 10 10 | let itemIftextIconWidth: CGFloat = 30 11 | 12 | struct TruncableItem: View { 13 | let candidate: Candidate 14 | let ifselected: Bool 15 | 16 | @State private var intrinsicSize: CGSize = .zero 17 | @State private var truncatedSize: CGSize = .zero 18 | @State private var isTruncated: Bool = false 19 | var body: some View { 20 | let bg = ifselected ? itemselectedbg : Color.clear 21 | Group{ 22 | if isTruncated && ifselected { 23 | ScrollView{ 24 | VStack(alignment: .leading, spacing: 5) { 25 | Text(candidate.desc).foregroundColor(color1).fixedSize(horizontal: false, vertical: true) 26 | Text(candidate.tag).foregroundColor(tagtextcolor) 27 | Text(candidate.output).foregroundColor(color2).font(.system(size: CGFloat(15))) 28 | }.frame(maxWidth:w-horizontalmargin*2, alignment: .leading) 29 | } 30 | .padding(5) 31 | .padding(.leading,5) 32 | .padding(.trailing,5) 33 | .frame(width:w-horizontalmargin*2, alignment: .leading) 34 | .fixedSize() 35 | }else{ 36 | HStack(alignment:.center,spacing: 0){ 37 | HStack(alignment:.firstTextBaseline){ 38 | Text("\(candidate.desc)") 39 | .foregroundColor(color1) 40 | .frame(maxWidth: w - itemIftextIconWidth - horizontalmargin*2 - 1, alignment: .leading) 41 | .fixedSize() 42 | 43 | Text(":: "+candidate.tag).foregroundColor(tagtextcolor) 44 | .padding(2).padding(.leading,3).padding(.trailing,3) 45 | .background(Color(red:63/255,green:62/255,blue:61/255,opacity: 0.9)) 46 | 47 | Text(candidate.output).foregroundColor(Color.gray) 48 | } 49 | .readSize { size in 50 | isTruncated = (CGFloat(size.width) > w-horizontalmargin*2-itemIftextIconWidth) 51 | } 52 | .frame(width: w - itemIftextIconWidth - horizontalmargin*2, alignment: .leading) 53 | .clipped() 54 | 55 | Text(candidate.iftext ? "↩︎" : "⌘").foregroundColor(Color.white.opacity(0.5)).frame(width:itemIftextIconWidth) 56 | } 57 | .padding(5) 58 | .padding(.leading,5) 59 | .padding(.trailing,5) 60 | .fixedSize(horizontal: true, vertical: false) 61 | .frame(width:w, height:itemHeight, alignment: .leading) 62 | } 63 | } 64 | .frame(width: w, alignment: .leading) 65 | .background(bg) 66 | .font(.system(size: CGFloat(16))) 67 | } 68 | } 69 | 70 | struct ContentView: View { 71 | var candidates: [Candidate] 72 | var origin: String 73 | var curPage: Int = 1 74 | var hasNext: Bool = false 75 | var selectedI: Int = -1 76 | var error = "" 77 | 78 | var body: some View { 79 | let headerbg = -1 == selectedI 80 | ? itemselectedbg 81 | : Color(red:55/255,green: 53/255,blue: 54/255) 82 | 83 | return HStack(alignment:.top, spacing:20){ 84 | VStack(alignment: .leading, spacing: 20) { 85 | if error.count > 0 { 86 | Text("error :: " + error).foregroundColor(Color.white).font(.system(size: CGFloat(20))) 87 | .padding(.top, 3) 88 | .padding(.bottom, 3) 89 | .padding(.leading, 10) 90 | .frame(width:w, alignment: .leading) 91 | }else{ 92 | // header 93 | HStack(alignment:.center){ 94 | Text("> " + (selectedI > -1 ? candidates[selectedI].output : origin)).foregroundColor(color1).frame(maxWidth:w-24-20, alignment: .leading).fixedSize(horizontal: true, vertical: false) 95 | Spacer() 96 | Text("\(curPage)").foregroundColor(Color.gray) 97 | }.font(.system(size: CGFloat(20))) 98 | .padding(.top, 3) 99 | .padding(.bottom, 5) 100 | .padding(.leading, 12) 101 | .padding(.trailing, 12) 102 | .frame(width:w-20, alignment: .leading) 103 | .fixedSize(horizontal: false, vertical: false) 104 | .background(headerbg) 105 | .cornerRadius(6) 106 | .padding(10) 107 | .padding(.top,3) 108 | .padding(.bottom,-20) 109 | 110 | 111 | // plane 112 | HStack(alignment:.top){ 113 | // list 114 | VStack(alignment: .leading, spacing: 0) { 115 | ForEach(Array(candidates.enumerated()), id: \.element.id) { (index, candidate) in 116 | 117 | TruncableItem(candidate: candidate, ifselected: selectedI == index) 118 | 119 | } 120 | } 121 | } 122 | }} 123 | .padding(.bottom,5) 124 | .frame(width:w, alignment: .leading) 125 | .cornerRadius(6) 126 | .background(Color(red:37/255,green:36/255,blue:34/255)) 127 | .padding(6) 128 | .cornerRadius(6) 129 | .border(Color.gray.opacity(0.3),width:6) 130 | .cornerRadius(6) 131 | } 132 | } 133 | } 134 | 135 | struct ContentView_Previews: PreviewProvider { 136 | static var previews: some View { 137 | ContentView(candidates: [ 138 | Candidate(tag: "vim;", desc: "跳转至最后一行 跳转至最后一跳转至最后一行 跳转至最后一行跳转至最后一行 跳转至最后一跳转至最后一行 跳转至最后一行", output: "G 跳转至最后一至最后一行 跳转至最后一行 跳转至最后一行 跳转至最后一行 ", iftext: true,id:0), 139 | Candidate(tag: "vim;ae;", desc: "跳转至第一行", output: "gg",iftext: false,id:1), 140 | Candidate(tag: "vim;appe;", desc: "跳转至行尾", output: "g$" , iftext: true,id:2), 141 | Candidate(tag: "vim;", desc: "shift+V 进入可视化", output: "" , iftext: false,id:4), 142 | Candidate(tag: "vim;", desc: "shift+V 进入可视化", output: "" , iftext: false,id:3), 143 | Candidate(tag: "vim;", desc: "shift+V 进入可视化", output: "" , iftext: true,id:5), 144 | Candidate(tag: "vim;", desc: "shift+V 进入可视化", output: "" , iftext: true,id:6), 145 | Candidate(tag: "vim;vim;vim;vim;vim;vim;vivim;vim;vim;vim;vim;vim;vim;vim;vim;", desc: "shift+V 进入可视化", output: "" , iftext: true,id:7), 146 | Candidate(tag: "vim;", desc: "shift+V 进入可视化", output: "12345678912345678911111" , iftext: true,id:8), 147 | Candidate(tag: "vim;", desc: "shift+V 进入可视化可视化可视化可", output: "12345678" , iftext: true,id:9), 148 | ], origin:"vim1234567891234567891111112345678912345678911111", selectedI:7,error: "") 149 | } 150 | } 151 | 152 | class CandidatesWindow: NSWindow, NSWindowDelegate { 153 | let hostingView = NSHostingView(rootView: ContentView(candidates: [], origin: "")) 154 | 155 | func setCandidates( 156 | _ candidatesData: [Candidate], 157 | curPage: Int, 158 | hasNext: Bool, 159 | originalString: String, 160 | topLeft_: NSPoint 161 | ) { 162 | self.hostingView.rootView.error = "" 163 | self.hostingView.rootView.selectedI = -1 164 | self.hostingView.rootView.candidates = candidatesData 165 | self.hostingView.rootView.origin = originalString 166 | self.hostingView.rootView.hasNext = hasNext 167 | self.hostingView.rootView.curPage = curPage 168 | 169 | let origin = transformTopLeft(originalTopLeft:topLeft_) 170 | self.setFrameTopLeftPoint(origin) 171 | 172 | self.orderFront(nil) 173 | NSApp.setActivationPolicy(.prohibited) 174 | } 175 | 176 | func setSelector(i: Int){ 177 | hostingView.rootView.selectedI = i 178 | } 179 | 180 | func setError(err: String){ 181 | hostingView.rootView.error = err 182 | } 183 | 184 | override init( 185 | contentRect: NSRect, 186 | styleMask style: NSWindow.StyleMask, 187 | backing backingStoreType: NSWindow.BackingStoreType, 188 | defer flag: Bool 189 | ) { 190 | super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag) 191 | 192 | level = NSWindow.Level(rawValue: NSWindow.Level.RawValue(CGShieldingWindowLevel())) 193 | styleMask = .init(arrayLiteral: .fullSizeContentView, .borderless) 194 | isReleasedWhenClosed = false 195 | backgroundColor = .clear 196 | delegate = self 197 | 198 | // 窗口大小可根据内容变化 199 | hostingView.translatesAutoresizingMaskIntoConstraints = false 200 | guard self.contentView != nil else { 201 | return 202 | } 203 | self.contentView?.addSubview(hostingView) 204 | self.contentView?.leftAnchor.constraint(equalTo: hostingView.leftAnchor).isActive = true 205 | self.contentView?.rightAnchor.constraint(equalTo: hostingView.rightAnchor).isActive = true 206 | self.contentView?.topAnchor.constraint(equalTo: hostingView.topAnchor).isActive = true 207 | self.contentView?.bottomAnchor.constraint(equalTo: hostingView.bottomAnchor).isActive = true 208 | } 209 | 210 | private func transformTopLeft(originalTopLeft: NSPoint) -> NSPoint { 211 | var left = originalTopLeft.x 212 | var top = originalTopLeft.y 213 | 214 | if let curScreen = getScreenFromPoint(originalTopLeft) { 215 | let screen = curScreen.frame 216 | left = (screen.maxX - screen.minX)/2 - (w)/2 217 | top = (screen.maxY - screen.minY)/2 + 300 218 | } 219 | return NSPoint(x: left, y: top) 220 | } 221 | 222 | static let shared = CandidatesWindow() 223 | } 224 | 225 | func getScreenFromPoint(_ point: NSPoint) -> NSScreen? { 226 | // find current screen 227 | for screen in NSScreen.screens { 228 | if screen.frame.contains(point) { 229 | return screen 230 | } 231 | } 232 | return NSScreen.main 233 | } 234 | -------------------------------------------------------------------------------- /App/AnInputController.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import InputMethodKit 3 | 4 | extension String: LocalizedError { 5 | public var errorDescription: String? { return self } 6 | } 7 | 8 | class AnInputController: IMKInputController { 9 | private var _candidates: [Candidate] = [] 10 | private var _candidatesNow: [Candidate] = [] 11 | private var _hasNext: Bool = false 12 | private var _selector: Int = -1 13 | private var ifbanned: Bool = false 14 | private var err: String = "" { 15 | didSet{ 16 | if self.err.count == 0 { 17 | CandidatesWindow.shared.close() 18 | }else{ 19 | CandidatesWindow.shared.setError(err: err) 20 | } 21 | } 22 | } 23 | 24 | private var _originalString = "" { 25 | didSet(old){ 26 | curPage = 1 27 | _selector = -1 28 | client()?.setMarkedText(_originalString, selectionRange:NSMakeRange(_originalString.count, 0), replacementRange: replacementRange()) 29 | if self._originalString.count == 0{ 30 | CandidatesWindow.shared.close() 31 | return 32 | } 33 | if _candidates.count != 0 34 | || (old.count == 0 && self._originalString.count > 0) 35 | || old.count > self._originalString.count { 36 | _candidates = DataBaseClient.shared.getCandidates(origin: self._originalString) 37 | } 38 | refreshCandidatesWindow() 39 | } 40 | } 41 | private var curPage: Int = 1 42 | private var pageItemNum: Int = 0 43 | func refreshCandidatesWindow() { 44 | let starti = (curPage-1)*configCandidateNum 45 | let endi = curPage*configCandidateNum 46 | _hasNext = _candidates.count > endi 47 | 48 | _candidatesNow = Array(_candidates.count > starti ? 49 | _hasNext ? _candidates[starti.. Bool? { 62 | if _originalString.count > 0{ 63 | if event.keyCode == kVK_RightArrow { 64 | if _hasNext { 65 | curPage += 1 66 | _selector = -1 67 | self.refreshCandidatesWindow() 68 | } 69 | return true 70 | } 71 | if event.keyCode == kVK_LeftArrow { 72 | if curPage > 1 { 73 | curPage -= 1 74 | _selector = -1 75 | self.refreshCandidatesWindow() 76 | } 77 | return true 78 | } 79 | if event.keyCode == kVK_UpArrow { 80 | if _selector >= 0 { 81 | _selector -= 1 82 | CandidatesWindow.shared.setSelector(i: _selector) 83 | } 84 | return true 85 | } 86 | if event.keyCode == kVK_DownArrow { 87 | if _selector < _candidatesNow.count-1 { 88 | _selector += 1 89 | CandidatesWindow.shared.setSelector(i: _selector) 90 | } 91 | return true 92 | } 93 | } 94 | return nil 95 | } 96 | 97 | private func deleteHandler(event: NSEvent) -> Bool? { 98 | if event.keyCode == kVK_Delete { 99 | if _originalString.count > 0 { 100 | _originalString = String(_originalString.dropLast()) 101 | return true 102 | } 103 | return false 104 | } 105 | return nil 106 | } 107 | 108 | private func escHandler(event: NSEvent) -> Bool? { 109 | if event.keyCode == kVK_Escape { 110 | ctrl_press_times+=1 111 | if _originalString.count > 0 { 112 | clean() 113 | }else{ 114 | if ctrl_press_times >= 2 { 115 | ifbanned = false 116 | toast.show("control mode") 117 | }else{ 118 | ifbanned = !ifbanned 119 | if ifbanned { 120 | toast.show("banning mode") 121 | }else{ 122 | toast.show("normal mode") 123 | } 124 | } 125 | } 126 | return true 127 | } 128 | return nil 129 | } 130 | 131 | static let ascii:String = String(Array(32...126).map { Character(Unicode.Scalar($0)) }) 132 | private func inputHandler(event: NSEvent) -> Bool? { 133 | if event.modifierFlags.rawValue != 0 && event.modifierFlags != .shift { 134 | // check if .command .option .control is pressing 135 | return nil 136 | } 137 | if event.characters != nil{ 138 | if AnInputController.ascii.contains(event.characters!) { 139 | _originalString += event.characters! 140 | return true 141 | } 142 | } 143 | return nil 144 | } 145 | 146 | 147 | static let keycode: [String: UInt16] = [ 148 | // a-z 149 | "a": UInt16(kVK_ANSI_A), 150 | "b": UInt16(kVK_ANSI_B), 151 | "c": UInt16(kVK_ANSI_C), 152 | "d": UInt16(kVK_ANSI_D), 153 | "e": UInt16(kVK_ANSI_E), 154 | "f": UInt16(kVK_ANSI_F), 155 | "g": UInt16(kVK_ANSI_G), 156 | "h": UInt16(kVK_ANSI_H), 157 | "i": UInt16(kVK_ANSI_I), 158 | "j": UInt16(kVK_ANSI_J), 159 | "k": UInt16(kVK_ANSI_K), 160 | "l": UInt16(kVK_ANSI_L), 161 | "m": UInt16(kVK_ANSI_M), 162 | "n": UInt16(kVK_ANSI_N), 163 | "o": UInt16(kVK_ANSI_O), 164 | "p": UInt16(kVK_ANSI_P), 165 | "q": UInt16(kVK_ANSI_Q), 166 | "r": UInt16(kVK_ANSI_R), 167 | "s": UInt16(kVK_ANSI_S), 168 | "t": UInt16(kVK_ANSI_T), 169 | "u": UInt16(kVK_ANSI_U), 170 | "v": UInt16(kVK_ANSI_V), 171 | "w": UInt16(kVK_ANSI_W), 172 | "x": UInt16(kVK_ANSI_X), 173 | "y": UInt16(kVK_ANSI_Y), 174 | "z": UInt16(kVK_ANSI_Z), 175 | // Numbers 0-9 176 | "0": UInt16(kVK_ANSI_0), 177 | "1": UInt16(kVK_ANSI_1), 178 | "2": UInt16(kVK_ANSI_2), 179 | "3": UInt16(kVK_ANSI_3), 180 | "4": UInt16(kVK_ANSI_4), 181 | "5": UInt16(kVK_ANSI_5), 182 | "6": UInt16(kVK_ANSI_6), 183 | "7": UInt16(kVK_ANSI_7), 184 | "8": UInt16(kVK_ANSI_8), 185 | "9": UInt16(kVK_ANSI_9), 186 | // Arithmetic operators 187 | "+": UInt16(kVK_ANSI_KeypadPlus), 188 | "plus": UInt16(kVK_ANSI_KeypadPlus), 189 | "-": UInt16(kVK_ANSI_Minus), 190 | "*": UInt16(kVK_ANSI_KeypadMultiply), 191 | "=": UInt16(kVK_ANSI_Equal), 192 | // F1-F20 193 | "f1": UInt16(kVK_F1), 194 | "f2": UInt16(kVK_F2), 195 | "f3": UInt16(kVK_F3), 196 | "f4": UInt16(kVK_F4), 197 | "f5": UInt16(kVK_F5), 198 | "f6": UInt16(kVK_F6), 199 | "f7": UInt16(kVK_F7), 200 | "f8": UInt16(kVK_F8), 201 | "f9": UInt16(kVK_F9), 202 | "f10": UInt16(kVK_F10), 203 | "f11": UInt16(kVK_F11), 204 | "f12": UInt16(kVK_F12), 205 | "f13": UInt16(kVK_F13), 206 | "f14": UInt16(kVK_F14), 207 | "f15": UInt16(kVK_F15), 208 | "f16": UInt16(kVK_F16), 209 | "f17": UInt16(kVK_F17), 210 | "f18": UInt16(kVK_F18), 211 | "f19": UInt16(kVK_F19), 212 | "f20": UInt16(kVK_F20), 213 | // Other keys 214 | "'": UInt16(kVK_ANSI_Quote), 215 | "`": UInt16(kVK_ANSI_Grave), 216 | "capslock": UInt16(kVK_CapsLock), 217 | "delete": UInt16(kVK_ForwardDelete), 218 | "return": UInt16(kVK_Return), 219 | "tab": UInt16(kVK_Tab), 220 | "esc": UInt16(kVK_Escape), 221 | ",": UInt16(kVK_ANSI_Comma), 222 | ".": UInt16(kVK_ANSI_KeypadDecimal), 223 | "(": UInt16(kVK_ANSI_LeftBracket), 224 | ")": UInt16(kVK_ANSI_RightBracket), 225 | ";": UInt16(kVK_ANSI_Semicolon), 226 | "/": UInt16(kVK_ANSI_Slash), 227 | "space": UInt16(kVK_Space), 228 | "command": UInt16(kVK_Command), 229 | "cmd": UInt16(kVK_Command), 230 | "control": UInt16(kVK_Control), 231 | "ctrl": UInt16(kVK_Control), 232 | "fn": UInt16(kVK_Function), 233 | "option": UInt16(kVK_Option), 234 | "shift": UInt16(kVK_Shift), 235 | "down": UInt16(kVK_DownArrow), 236 | "left": UInt16(kVK_LeftArrow), 237 | "right": UInt16(kVK_RightArrow), 238 | "up": UInt16(kVK_UpArrow), 239 | ] 240 | 241 | private func insertHandler(event: NSEvent) throws -> Bool? { 242 | if (event.keyCode == kVK_Return || event.keyCode == kVK_Tab || event.keyCode == kVK_Space) { 243 | if _originalString.count == 0 { 244 | return false 245 | } 246 | if _selector == -1 { 247 | client()?.insertText(NSAttributedString(string: _originalString), replacementRange: replacementRange()) 248 | clean() 249 | ifbanned = true 250 | toast.show("banning mode") 251 | 252 | if event.keyCode == kVK_Tab || event.keyCode == kVK_Space { 253 | return false 254 | } 255 | }else{ 256 | if _candidatesNow[_selector].iftext { 257 | client()?.insertText(NSAttributedString(string: _candidatesNow[_selector].output), replacementRange: replacementRange()) 258 | clean() 259 | }else{ 260 | let keys: [String] = _candidatesNow[_selector].output.components(separatedBy: "+") 261 | let press = keys.last! 262 | var flags: UInt64 = 0 263 | for k in keys.dropLast() { 264 | switch k { 265 | case "shift": 266 | flags = CGEventFlags.maskShift.rawValue | flags 267 | case "ctrl","control": 268 | flags = CGEventFlags.maskControl.rawValue | flags 269 | case "command","cmd": 270 | flags = CGEventFlags.maskCommand.rawValue | flags 271 | case "option": 272 | flags = CGEventFlags.maskAlternate.rawValue | flags 273 | default: throw "bad expression of hotkey: " + "\(keys)" 274 | } 275 | } 276 | if let v = AnInputController.keycode[press] { 277 | clean() 278 | let up = CGEvent(keyboardEventSource: nil, virtualKey: CGKeyCode(v), keyDown: false); 279 | let down = CGEvent(keyboardEventSource: nil, virtualKey: CGKeyCode(v), keyDown: true); 280 | if flags != 0 { 281 | down?.flags = CGEventFlags(rawValue: flags); 282 | up?.flags = CGEventFlags(rawValue: flags); 283 | } 284 | down?.post(tap: CGEventTapLocation.cghidEventTap); 285 | up?.post(tap: CGEventTapLocation.cghidEventTap); 286 | }else{ 287 | throw "bad expression of hotkey (at the last key): " + _candidatesNow[_selector].output 288 | } 289 | } 290 | } 291 | return true 292 | } 293 | return nil 294 | } 295 | 296 | private var ctrl_press_times : Int = 0 297 | override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { 298 | if err.count > 0 { 299 | err = "" 300 | return true 301 | } 302 | if let r = escHandler(event: event) { 303 | return r 304 | } 305 | if ctrl_press_times >= 2 { // press ESC twice to open control mode 306 | ctrl_press_times = 0 307 | if event.characters == nil{ 308 | return false 309 | } 310 | 311 | func shell(_ command: String) -> String { 312 | let task = Process() 313 | let pipe = Pipe() 314 | task.standardOutput = pipe 315 | task.standardError = pipe 316 | task.arguments = ["-c", command] 317 | task.launchPath = "/bin/bash" 318 | task.standardInput = nil 319 | task.launch() 320 | let data = pipe.fileHandleForReading.readDataToEndOfFile() 321 | let output = String(data: data, encoding: .utf8)! 322 | return output 323 | } 324 | 325 | switch event.characters!{ 326 | case "s","S": 327 | DataBaseClient.shared.loadDict() 328 | clean() 329 | return true 330 | case "w", "W": 331 | _ = shell("open -a TextEdit ~/"+dict_filename) 332 | return true 333 | default: 334 | return false 335 | } 336 | } 337 | ctrl_press_times = 0 338 | if !ifbanned { 339 | for handler in [ 340 | arrowKeyHandler, 341 | insertHandler, 342 | deleteHandler, 343 | inputHandler, 344 | ] { 345 | do { 346 | let r = try handler(event) 347 | if r != nil{ 348 | return r! 349 | } 350 | }catch{ 351 | err = error.localizedDescription 352 | return true 353 | } 354 | } 355 | } 356 | 357 | return false 358 | } 359 | 360 | // 获取当前输入的光标位置 361 | private func getOriginPoint() -> NSPoint { 362 | let xd: CGFloat = 12 363 | let yd: CGFloat = 4 364 | var rect = NSRect() 365 | client()?.attributes(forCharacterIndex: 0, lineHeightRectangle: &rect) 366 | return NSPoint(x: rect.minX + xd, y: rect.minY - yd) 367 | } 368 | 369 | func clean() { 370 | _originalString = "" 371 | } 372 | 373 | let toast = ToastWindow() 374 | 375 | override func deactivateServer(_ sender: Any!) { 376 | clean() 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /ihelper.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8462AE462845ECF9004D41D7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8462AE452845ECF9004D41D7 /* Assets.xcassets */; }; 11 | 8462AE542845ECF9004D41D7 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8462AE532845ECF9004D41D7 /* Tests.swift */; }; 12 | 8462AE742845F170004D41D7 /* DataBaseClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8462AE6F2845F170004D41D7 /* DataBaseClient.swift */; }; 13 | 8462AE752845F170004D41D7 /* main.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 8462AE702845F170004D41D7 /* main.tiff */; }; 14 | 847111FD28468EA100C43026 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847111FC28468EA100C43026 /* Toast.swift */; }; 15 | 84E8C4782845F5C500B87E5E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E8C4772845F5C500B87E5E /* main.swift */; }; 16 | 84E8C47B2845F66C00B87E5E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84E8C47A2845F66C00B87E5E /* MainMenu.xib */; }; 17 | 84E8C47E2845F68300B87E5E /* AnInputController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E8C47C2845F68300B87E5E /* AnInputController.swift */; }; 18 | 84E8C47F2845F68300B87E5E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E8C47D2845F68300B87E5E /* ContentView.swift */; }; 19 | 84F6710B28471E6D008DA61F /* ReadSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F6710A28471E6D008DA61F /* ReadSize.swift */; }; 20 | 84F6710D284739CE008DA61F /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 84F6710C284739CE008DA61F /* README.md */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 8462AE502845ECF9004D41D7 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 8462AE362845ECF8004D41D7 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 8462AE3D2845ECF8004D41D7; 29 | remoteInfo = aaaa; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 8462AE3E2845ECF8004D41D7 /* ihelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ihelper.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 8462AE452845ECF9004D41D7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 36 | 8462AE482845ECF9004D41D7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 37 | 8462AE4F2845ECF9004D41D7 /* ihelperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ihelperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 8462AE532845ECF9004D41D7 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 39 | 8462AE6E2845F0A4004D41D7 /* ihelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ihelper.entitlements; sourceTree = ""; }; 40 | 8462AE6F2845F170004D41D7 /* DataBaseClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataBaseClient.swift; sourceTree = ""; }; 41 | 8462AE702845F170004D41D7 /* main.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = main.tiff; sourceTree = ""; }; 42 | 8462AE722845F170004D41D7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 847111FC28468EA100C43026 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 44 | 84E8C4772845F5C500B87E5E /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 45 | 84E8C4792845F62B00B87E5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | 84E8C47A2845F66C00B87E5E /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 47 | 84E8C47C2845F68300B87E5E /* AnInputController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnInputController.swift; sourceTree = ""; }; 48 | 84E8C47D2845F68300B87E5E /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 49 | 84F6710A28471E6D008DA61F /* ReadSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadSize.swift; sourceTree = ""; }; 50 | 84F6710C284739CE008DA61F /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 8462AE3B2845ECF8004D41D7 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 8462AE4C2845ECF9004D41D7 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 8462AE352845ECF8004D41D7 = { 72 | isa = PBXGroup; 73 | children = ( 74 | 84F6710C284739CE008DA61F /* README.md */, 75 | 8462AE722845F170004D41D7 /* Info.plist */, 76 | 8462AE6E2845F0A4004D41D7 /* ihelper.entitlements */, 77 | 8462AE402845ECF8004D41D7 /* App */, 78 | 8462AE522845ECF9004D41D7 /* Tests */, 79 | 8462AE3F2845ECF8004D41D7 /* Products */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | 8462AE3F2845ECF8004D41D7 /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 8462AE3E2845ECF8004D41D7 /* ihelper.app */, 87 | 8462AE4F2845ECF9004D41D7 /* ihelperTests.xctest */, 88 | ); 89 | name = Products; 90 | sourceTree = ""; 91 | }; 92 | 8462AE402845ECF8004D41D7 /* App */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 847111FB28468E7F00C43026 /* UI */, 96 | 84E8C47C2845F68300B87E5E /* AnInputController.swift */, 97 | 84E8C47A2845F66C00B87E5E /* MainMenu.xib */, 98 | 84E8C4772845F5C500B87E5E /* main.swift */, 99 | 8462AE6F2845F170004D41D7 /* DataBaseClient.swift */, 100 | 8462AE702845F170004D41D7 /* main.tiff */, 101 | 8462AE452845ECF9004D41D7 /* Assets.xcassets */, 102 | 8462AE472845ECF9004D41D7 /* Preview Content */, 103 | ); 104 | path = App; 105 | sourceTree = ""; 106 | }; 107 | 8462AE472845ECF9004D41D7 /* Preview Content */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 8462AE482845ECF9004D41D7 /* Preview Assets.xcassets */, 111 | ); 112 | path = "Preview Content"; 113 | sourceTree = ""; 114 | }; 115 | 8462AE522845ECF9004D41D7 /* Tests */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 84E8C4792845F62B00B87E5E /* Info.plist */, 119 | 8462AE532845ECF9004D41D7 /* Tests.swift */, 120 | ); 121 | path = Tests; 122 | sourceTree = ""; 123 | }; 124 | 847111FB28468E7F00C43026 /* UI */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 84E8C47D2845F68300B87E5E /* ContentView.swift */, 128 | 847111FC28468EA100C43026 /* Toast.swift */, 129 | 84F6710A28471E6D008DA61F /* ReadSize.swift */, 130 | ); 131 | path = UI; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXNativeTarget section */ 137 | 8462AE3D2845ECF8004D41D7 /* ihelper */ = { 138 | isa = PBXNativeTarget; 139 | buildConfigurationList = 8462AE632845ECF9004D41D7 /* Build configuration list for PBXNativeTarget "ihelper" */; 140 | buildPhases = ( 141 | 8462AE3A2845ECF8004D41D7 /* Sources */, 142 | 8462AE3B2845ECF8004D41D7 /* Frameworks */, 143 | 8462AE3C2845ECF8004D41D7 /* Resources */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = ihelper; 150 | productName = aaaa; 151 | productReference = 8462AE3E2845ECF8004D41D7 /* ihelper.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | 8462AE4E2845ECF9004D41D7 /* ihelperTests */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = 8462AE662845ECF9004D41D7 /* Build configuration list for PBXNativeTarget "ihelperTests" */; 157 | buildPhases = ( 158 | 8462AE4B2845ECF9004D41D7 /* Sources */, 159 | 8462AE4C2845ECF9004D41D7 /* Frameworks */, 160 | 8462AE4D2845ECF9004D41D7 /* Resources */, 161 | ); 162 | buildRules = ( 163 | ); 164 | dependencies = ( 165 | 8462AE512845ECF9004D41D7 /* PBXTargetDependency */, 166 | ); 167 | name = ihelperTests; 168 | productName = aaaaTests; 169 | productReference = 8462AE4F2845ECF9004D41D7 /* ihelperTests.xctest */; 170 | productType = "com.apple.product-type.bundle.unit-test"; 171 | }; 172 | /* End PBXNativeTarget section */ 173 | 174 | /* Begin PBXProject section */ 175 | 8462AE362845ECF8004D41D7 /* Project object */ = { 176 | isa = PBXProject; 177 | attributes = { 178 | BuildIndependentTargetsInParallel = 1; 179 | LastSwiftUpdateCheck = 1340; 180 | LastUpgradeCheck = 1340; 181 | TargetAttributes = { 182 | 8462AE3D2845ECF8004D41D7 = { 183 | CreatedOnToolsVersion = 13.4; 184 | }; 185 | 8462AE4E2845ECF9004D41D7 = { 186 | CreatedOnToolsVersion = 13.4; 187 | TestTargetID = 8462AE3D2845ECF8004D41D7; 188 | }; 189 | }; 190 | }; 191 | buildConfigurationList = 8462AE392845ECF8004D41D7 /* Build configuration list for PBXProject "ihelper" */; 192 | compatibilityVersion = "Xcode 13.0"; 193 | developmentRegion = en; 194 | hasScannedForEncodings = 0; 195 | knownRegions = ( 196 | en, 197 | Base, 198 | ); 199 | mainGroup = 8462AE352845ECF8004D41D7; 200 | productRefGroup = 8462AE3F2845ECF8004D41D7 /* Products */; 201 | projectDirPath = ""; 202 | projectRoot = ""; 203 | targets = ( 204 | 8462AE3D2845ECF8004D41D7 /* ihelper */, 205 | 8462AE4E2845ECF9004D41D7 /* ihelperTests */, 206 | ); 207 | }; 208 | /* End PBXProject section */ 209 | 210 | /* Begin PBXResourcesBuildPhase section */ 211 | 8462AE3C2845ECF8004D41D7 /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 8462AE752845F170004D41D7 /* main.tiff in Resources */, 216 | 8462AE462845ECF9004D41D7 /* Assets.xcassets in Resources */, 217 | 84F6710D284739CE008DA61F /* README.md in Resources */, 218 | 84E8C47B2845F66C00B87E5E /* MainMenu.xib in Resources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | 8462AE4D2845ECF9004D41D7 /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | /* End PBXResourcesBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | 8462AE3A2845ECF8004D41D7 /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 84E8C4782845F5C500B87E5E /* main.swift in Sources */, 237 | 8462AE742845F170004D41D7 /* DataBaseClient.swift in Sources */, 238 | 847111FD28468EA100C43026 /* Toast.swift in Sources */, 239 | 84F6710B28471E6D008DA61F /* ReadSize.swift in Sources */, 240 | 84E8C47F2845F68300B87E5E /* ContentView.swift in Sources */, 241 | 84E8C47E2845F68300B87E5E /* AnInputController.swift in Sources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | 8462AE4B2845ECF9004D41D7 /* Sources */ = { 246 | isa = PBXSourcesBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | 8462AE542845ECF9004D41D7 /* Tests.swift in Sources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | /* End PBXSourcesBuildPhase section */ 254 | 255 | /* Begin PBXTargetDependency section */ 256 | 8462AE512845ECF9004D41D7 /* PBXTargetDependency */ = { 257 | isa = PBXTargetDependency; 258 | target = 8462AE3D2845ECF8004D41D7 /* ihelper */; 259 | targetProxy = 8462AE502845ECF9004D41D7 /* PBXContainerItemProxy */; 260 | }; 261 | /* End PBXTargetDependency section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | 8462AE612845ECF9004D41D7 /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ALWAYS_SEARCH_USER_PATHS = NO; 268 | CLANG_ANALYZER_NONNULL = YES; 269 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 271 | CLANG_ENABLE_MODULES = YES; 272 | CLANG_ENABLE_OBJC_ARC = YES; 273 | CLANG_ENABLE_OBJC_WEAK = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INFINITE_RECURSION = YES; 284 | CLANG_WARN_INT_CONVERSION = YES; 285 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 286 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 287 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 288 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 289 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 290 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 291 | CLANG_WARN_STRICT_PROTOTYPES = YES; 292 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 293 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 294 | CLANG_WARN_UNREACHABLE_CODE = YES; 295 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 296 | CONFIGURATION_BUILD_DIR = "/Library/Input Methods"; 297 | COPY_PHASE_STRIP = NO; 298 | DEBUG_INFORMATION_FORMAT = dwarf; 299 | ENABLE_STRICT_OBJC_MSGSEND = YES; 300 | ENABLE_TESTABILITY = YES; 301 | GCC_C_LANGUAGE_STANDARD = gnu11; 302 | GCC_DYNAMIC_NO_PIC = NO; 303 | GCC_NO_COMMON_BLOCKS = YES; 304 | GCC_OPTIMIZATION_LEVEL = 0; 305 | GCC_PREPROCESSOR_DEFINITIONS = ( 306 | "DEBUG=1", 307 | "$(inherited)", 308 | ); 309 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 310 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 311 | GCC_WARN_UNDECLARED_SELECTOR = YES; 312 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 313 | GCC_WARN_UNUSED_FUNCTION = YES; 314 | GCC_WARN_UNUSED_VARIABLE = YES; 315 | INFOPLIST_FILE = Info.plist; 316 | INFOPLIST_PREPROCESS = NO; 317 | MACOSX_DEPLOYMENT_TARGET = 12.3; 318 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 319 | MTL_FAST_MATH = YES; 320 | ONLY_ACTIVE_ARCH = YES; 321 | SDKROOT = macosx; 322 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 323 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 324 | SYMROOT = "/Library/Input Methods"; 325 | }; 326 | name = Debug; 327 | }; 328 | 8462AE622845ECF9004D41D7 /* Release */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ALWAYS_SEARCH_USER_PATHS = NO; 332 | CLANG_ANALYZER_NONNULL = YES; 333 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 334 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 335 | CLANG_ENABLE_MODULES = YES; 336 | CLANG_ENABLE_OBJC_ARC = YES; 337 | CLANG_ENABLE_OBJC_WEAK = YES; 338 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 339 | CLANG_WARN_BOOL_CONVERSION = YES; 340 | CLANG_WARN_COMMA = YES; 341 | CLANG_WARN_CONSTANT_CONVERSION = YES; 342 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 344 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 345 | CLANG_WARN_EMPTY_BODY = YES; 346 | CLANG_WARN_ENUM_CONVERSION = YES; 347 | CLANG_WARN_INFINITE_RECURSION = YES; 348 | CLANG_WARN_INT_CONVERSION = YES; 349 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 350 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 351 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 353 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 354 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 355 | CLANG_WARN_STRICT_PROTOTYPES = YES; 356 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 357 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 358 | CLANG_WARN_UNREACHABLE_CODE = YES; 359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 360 | CONFIGURATION_BUILD_DIR = "/Library/Input Methods"; 361 | COPY_PHASE_STRIP = NO; 362 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 363 | ENABLE_NS_ASSERTIONS = NO; 364 | ENABLE_STRICT_OBJC_MSGSEND = YES; 365 | GCC_C_LANGUAGE_STANDARD = gnu11; 366 | GCC_NO_COMMON_BLOCKS = YES; 367 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 368 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 369 | GCC_WARN_UNDECLARED_SELECTOR = YES; 370 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 371 | GCC_WARN_UNUSED_FUNCTION = YES; 372 | GCC_WARN_UNUSED_VARIABLE = YES; 373 | INFOPLIST_FILE = Info.plist; 374 | INFOPLIST_PREPROCESS = NO; 375 | MACOSX_DEPLOYMENT_TARGET = 12.3; 376 | MTL_ENABLE_DEBUG_INFO = NO; 377 | MTL_FAST_MATH = YES; 378 | SDKROOT = macosx; 379 | SWIFT_COMPILATION_MODE = wholemodule; 380 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 381 | SYMROOT = "/Library/Input Methods"; 382 | }; 383 | name = Release; 384 | }; 385 | 8462AE642845ECF9004D41D7 /* Debug */ = { 386 | isa = XCBuildConfiguration; 387 | buildSettings = { 388 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 389 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 390 | CODE_SIGN_ENTITLEMENTS = ihelper.entitlements; 391 | CODE_SIGN_IDENTITY = "Apple Development"; 392 | CODE_SIGN_STYLE = Automatic; 393 | COMBINE_HIDPI_IMAGES = YES; 394 | CURRENT_PROJECT_VERSION = 1; 395 | DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; 396 | DEVELOPMENT_TEAM = ZTMJD8G3H6; 397 | ENABLE_PREVIEWS = YES; 398 | GENERATE_INFOPLIST_FILE = YES; 399 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 400 | LD_RUNPATH_SEARCH_PATHS = ( 401 | "$(inherited)", 402 | "@executable_path/../Frameworks", 403 | ); 404 | MACOSX_DEPLOYMENT_TARGET = 10.15; 405 | MARKETING_VERSION = 1.0; 406 | PRODUCT_BUNDLE_IDENTIFIER = fumeboy.inputmethod.ihelper; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | SWIFT_EMIT_LOC_STRINGS = YES; 409 | SWIFT_VERSION = 5.0; 410 | }; 411 | name = Debug; 412 | }; 413 | 8462AE652845ECF9004D41D7 /* Release */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 417 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 418 | CODE_SIGN_ENTITLEMENTS = ihelper.entitlements; 419 | CODE_SIGN_IDENTITY = "Apple Development"; 420 | CODE_SIGN_STYLE = Automatic; 421 | COMBINE_HIDPI_IMAGES = YES; 422 | CURRENT_PROJECT_VERSION = 1; 423 | DEVELOPMENT_ASSET_PATHS = "\"App/Preview Content\""; 424 | DEVELOPMENT_TEAM = ZTMJD8G3H6; 425 | ENABLE_PREVIEWS = YES; 426 | GENERATE_INFOPLIST_FILE = YES; 427 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 428 | LD_RUNPATH_SEARCH_PATHS = ( 429 | "$(inherited)", 430 | "@executable_path/../Frameworks", 431 | ); 432 | MACOSX_DEPLOYMENT_TARGET = 10.15; 433 | MARKETING_VERSION = 1.0; 434 | PRODUCT_BUNDLE_IDENTIFIER = fumeboy.inputmethod.ihelper; 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | SWIFT_EMIT_LOC_STRINGS = YES; 437 | SWIFT_VERSION = 5.0; 438 | }; 439 | name = Release; 440 | }; 441 | 8462AE672845ECF9004D41D7 /* Debug */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 445 | BUNDLE_LOADER = "$(TEST_HOST)"; 446 | CODE_SIGN_STYLE = Automatic; 447 | CURRENT_PROJECT_VERSION = 1; 448 | GENERATE_INFOPLIST_FILE = YES; 449 | INFOPLIST_FILE = Tests/Info.plist; 450 | MACOSX_DEPLOYMENT_TARGET = 12.3; 451 | MARKETING_VERSION = 1.0; 452 | PRODUCT_BUNDLE_IDENTIFIER = a.aaaaTests; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | SWIFT_EMIT_LOC_STRINGS = NO; 455 | SWIFT_VERSION = 5.0; 456 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ihelper.app/Contents/MacOS/ihelper"; 457 | }; 458 | name = Debug; 459 | }; 460 | 8462AE682845ECF9004D41D7 /* Release */ = { 461 | isa = XCBuildConfiguration; 462 | buildSettings = { 463 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 464 | BUNDLE_LOADER = "$(TEST_HOST)"; 465 | CODE_SIGN_STYLE = Automatic; 466 | CURRENT_PROJECT_VERSION = 1; 467 | GENERATE_INFOPLIST_FILE = YES; 468 | INFOPLIST_FILE = Tests/Info.plist; 469 | MACOSX_DEPLOYMENT_TARGET = 12.3; 470 | MARKETING_VERSION = 1.0; 471 | PRODUCT_BUNDLE_IDENTIFIER = a.aaaaTests; 472 | PRODUCT_NAME = "$(TARGET_NAME)"; 473 | SWIFT_EMIT_LOC_STRINGS = NO; 474 | SWIFT_VERSION = 5.0; 475 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ihelper.app/Contents/MacOS/ihelper"; 476 | }; 477 | name = Release; 478 | }; 479 | /* End XCBuildConfiguration section */ 480 | 481 | /* Begin XCConfigurationList section */ 482 | 8462AE392845ECF8004D41D7 /* Build configuration list for PBXProject "ihelper" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | 8462AE612845ECF9004D41D7 /* Debug */, 486 | 8462AE622845ECF9004D41D7 /* Release */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | 8462AE632845ECF9004D41D7 /* Build configuration list for PBXNativeTarget "ihelper" */ = { 492 | isa = XCConfigurationList; 493 | buildConfigurations = ( 494 | 8462AE642845ECF9004D41D7 /* Debug */, 495 | 8462AE652845ECF9004D41D7 /* Release */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | 8462AE662845ECF9004D41D7 /* Build configuration list for PBXNativeTarget "ihelperTests" */ = { 501 | isa = XCConfigurationList; 502 | buildConfigurations = ( 503 | 8462AE672845ECF9004D41D7 /* Debug */, 504 | 8462AE682845ECF9004D41D7 /* Release */, 505 | ); 506 | defaultConfigurationIsVisible = 0; 507 | defaultConfigurationName = Release; 508 | }; 509 | /* End XCConfigurationList section */ 510 | }; 511 | rootObject = 8462AE362845ECF8004D41D7 /* Project object */; 512 | } 513 | --------------------------------------------------------------------------------