AIConfigWindowController.swift 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. //
  2. // AIConfigWindowController.swift
  3. // PDF Reader Pro Edition
  4. //
  5. // Created by Niehaoyu on 2024/1/17.
  6. //
  7. import Cocoa
  8. @objcMembers class AIConfigWindowController: NSWindowController, NSTextFieldDelegate, NSTextViewDelegate {
  9. @IBOutlet weak var contendBox: NSBox!
  10. @IBOutlet weak var titleLabel: NSTextField!
  11. @IBOutlet weak var titleTipBtn: KMButton!
  12. @IBOutlet weak var uploadBGView: NSView!
  13. @IBOutlet weak var uploadItemBox: KMBox!
  14. @IBOutlet weak var uploadDesLabel: NSTextField!
  15. @IBOutlet weak var uploadPDFBtn: KMButton!
  16. @IBOutlet weak var fileBGView: NSView!
  17. @IBOutlet weak var fileEmptyImage: NSImageView!
  18. @IBOutlet weak var fileActionView: NSView!
  19. @IBOutlet weak var fileActionLabel: NSTextField!
  20. @IBOutlet weak var fileActionBtn: KMButton!
  21. @IBOutlet weak var filePDFBGView: NSView!
  22. @IBOutlet weak var filePDFHoverBtn: KMButton!
  23. @IBOutlet weak var pdfCoverView: NSView!
  24. @IBOutlet weak var pdfCoverImageView: NSImageView!
  25. @IBOutlet weak var removePDFBtn: NSButton!
  26. @IBOutlet weak var inputTextCountLabel: NSTextField!
  27. @IBOutlet weak var fileSizeTipView: NSView!
  28. @IBOutlet weak var fileSizeTipLabel: NSTextField!
  29. @IBOutlet weak var pdfNameLbl: NSTextField!
  30. @IBOutlet var fileEmptyTextView: NSTextView!
  31. @IBOutlet weak var filePlaceholdLabel: NSTextField!
  32. @IBOutlet weak var fileContendViewTopConst: NSLayoutConstraint!
  33. @IBOutlet weak var translateSepLineView: NSView!
  34. @IBOutlet weak var translateConfigView: NSView!
  35. @IBOutlet weak var fromLanguageView: NSView!
  36. @IBOutlet weak var fromLanguageLabel: NSTextField!
  37. @IBOutlet weak var fromLanguageBtn: NSButton!
  38. @IBOutlet weak var toLanguageView: NSView!
  39. @IBOutlet weak var toLanguageLabel: NSTextField!
  40. @IBOutlet weak var toLanguageBtn: NSButton!
  41. @IBOutlet weak var translateResultView: NSView!
  42. @IBOutlet weak var translateResultLabel: NSTextField!
  43. @IBOutlet weak var translateResultSubLabel: NSTextField!
  44. @IBOutlet weak var resultBGView: NSView!
  45. @IBOutlet weak var resultEmptyImage: NSImageView!
  46. // @IBOutlet weak var resultLabel: NSTextField!
  47. @IBOutlet var aiResultTextView: NSTextView!
  48. @IBOutlet weak var cancelBtn: NSButton!
  49. @IBOutlet weak var saveAsTextBtn: NSButton!
  50. @IBOutlet weak var saveAsPDFBtn: NSButton!
  51. static var currentWindowController: AIConfigWindowController!
  52. var popOver: NSPopover!
  53. var configType: AIConfigType = .none
  54. var cancelHandle: ((_ windowVC: AIConfigWindowController) -> Void)?
  55. var fileActionenAble: Bool = false
  56. var aiConvertFinish: Bool = true
  57. var filePath: String = ""
  58. var fromLanguage: String = ""
  59. var toLanguage: String = ""
  60. var translateFinish: Bool = false
  61. var translateFile: Bool = false
  62. var translateResultPath: String = ""
  63. @objc static func currentWC() -> AIConfigWindowController {
  64. if currentWindowController != nil {
  65. return currentWindowController
  66. } else {
  67. let configWC: AIConfigWindowController = AIConfigWindowController.init(windowNibName: "AIConfigWindowController")
  68. currentWindowController = configWC;
  69. return currentWindowController
  70. }
  71. }
  72. override func windowDidLoad() {
  73. super.windowDidLoad()
  74. // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
  75. self.titleLabel.font = NSFont.SFProTextSemiboldFont(13)
  76. self.titleTipBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  77. if mouseEntered {
  78. var tipString = NSLocalizedString("Recommended file size: 10M or less.", comment: "")
  79. if self.configType == .reWriting {
  80. tipString = NSLocalizedString("No more than 2000 characters.", comment: "")
  81. } else if self.configType == .proofreading {
  82. tipString = NSLocalizedString("No more than 2000 characters.", comment: "")
  83. } else if self.configType == .translate {
  84. tipString = NSLocalizedString("1 credit for every 10,000 characters", comment: "")
  85. }
  86. let popViewController = KMToolbarItemPopViewController.init()
  87. if self.popOver == nil {
  88. self.popOver = NSPopover.init()
  89. }
  90. self.popOver.contentViewController = popViewController
  91. self.popOver.animates = false
  92. self.popOver.behavior = .semitransient
  93. self.popOver.contentSize = (popViewController.view.frame.size)
  94. popViewController.updateWithHelpTip(helpTip: tipString)
  95. self.popOver.show(relativeTo: self.titleTipBtn.frame, of: (self.window?.contentView)!, preferredEdge: .maxY)
  96. } else {
  97. self.popOver.close()
  98. }
  99. }
  100. self.uploadItemBox.borderWidth = 0
  101. self.uploadItemBox.cornerRadius = 1
  102. self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S0()
  103. self.uploadDesLabel.font = NSFont.SFProTextRegularFont(13)
  104. self.uploadDesLabel.textColor = KMAppearance.KMColor_Layout_H0()
  105. self.uploadDesLabel.stringValue = NSLocalizedString("Choose", comment: "")
  106. self.uploadPDFBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  107. if mouseEntered {
  108. self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S_1()
  109. } else {
  110. self.uploadItemBox.fillColor = KMAppearance.KMColor_Interactive_S0()
  111. }
  112. }
  113. self.fileBGView.wantsLayer = true
  114. self.fileBGView.layer?.cornerRadius = 6
  115. self.fileBGView.layer?.masksToBounds = true
  116. self.fileBGView.layer?.borderWidth = 1
  117. self.fileBGView.layer?.borderColor = KMAppearance.KMColor_Layout_L_2().cgColor
  118. self.fileSizeTipView.wantsLayer = true
  119. self.fileSizeTipLabel.font = NSFont.SFProTextRegularFont(12)
  120. self.fileSizeTipLabel.textColor = KMAppearance.KMColor_Layout_H0()
  121. self.fileSizeTipLabel.stringValue = NSLocalizedString("Please upload a file smaller than 10M.", comment: "")
  122. self.fileSizeTipView.isHidden = true
  123. self.pdfNameLbl.font = NSFont.SFProTextRegularFont(12)
  124. self.pdfNameLbl.textColor = KMAppearance.KMColor_Layout_H0()
  125. self.resultBGView.wantsLayer = true
  126. self.resultBGView.layer?.cornerRadius = 6
  127. self.resultBGView.layer?.masksToBounds = true
  128. self.fileActionView.wantsLayer = true
  129. self.fileActionView.layer?.cornerRadius = 1
  130. self.fileActionView.layer?.masksToBounds = true
  131. self.fileActionLabel.font = NSFont.SFProTextRegularFont(13)
  132. self.fileActionBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  133. if self.fileActionenAble == true {
  134. if mouseEntered {
  135. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M_1().cgColor
  136. } else {
  137. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().cgColor
  138. }
  139. }
  140. }
  141. self.translateSepLineView.wantsLayer = true
  142. self.pdfCoverView.wantsLayer = true
  143. self.pdfCoverView.layer?.cornerRadius = 2
  144. self.pdfCoverView.layer?.borderWidth = 2
  145. let countFormatter = TextFieldFormatter.init()
  146. countFormatter.setMaximumLength(2000)
  147. self.inputTextCountLabel.formatter = countFormatter
  148. self.inputTextCountLabel.delegate = self
  149. if KMAppearance.isDarkMode() {
  150. self.contendBox.fillColor = NSColor(red: 44/255, green: 44/255, blue: 44/255, alpha: 1)
  151. self.titleLabel.textColor = NSColor.white
  152. self.resultBGView.layer?.backgroundColor = NSColor(red: 49/255, green: 49/255, blue: 49/255, alpha: 1).cgColor
  153. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_W30()
  154. self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 251/255, green: 166/255, blue: 0, alpha: 1).cgColor
  155. self.translateSepLineView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor
  156. self.translateResultView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.05).cgColor
  157. self.pdfCoverView.layer?.borderColor = NSColor.white.withAlphaComponent(0.2).cgColor
  158. self.pdfCoverView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.1).cgColor
  159. } else {
  160. self.contendBox.fillColor = NSColor.white
  161. self.titleLabel.textColor = NSColor.black
  162. self.resultBGView.layer?.backgroundColor = NSColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor
  163. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_B30()
  164. self.fileSizeTipView.layer?.backgroundColor = NSColor(red: 253/255, green: 239/255, blue: 212/255, alpha: 1).cgColor
  165. self.translateSepLineView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.05).cgColor
  166. self.translateResultView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.05).cgColor
  167. self.pdfCoverView.layer?.borderColor = NSColor.black.withAlphaComponent(0.12).cgColor
  168. self.pdfCoverView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.04).cgColor
  169. }
  170. self.fileEmptyTextView.backgroundColor = NSColor.clear
  171. self.fileEmptyTextView.delegate = self
  172. self.fileEmptyTextView.isEditable = false
  173. self.fileEmptyTextView.frame = (self.fileEmptyTextView.enclosingScrollView?.contentView.bounds) ?? .zero
  174. self.fileEmptyTextView.autoresizingMask = [.width, .height]
  175. self.window?.makeFirstResponder(nil)
  176. var languages = ["Automatic", "English", "Simplified Chinese", "Traditional Chinese", "Japanese", "Korean", "French", "Spanish", "Italian", "German", "Portuguese", "Russian", "Vietnamese", "Thai", "Arabic", "Greek", "Bulgarian", "Finnish", "Slovene", "Dutch", "Czech", "Swedish", "Polish", "Danish", "Romanian", "Hungarian"]
  177. let menu = NSMenu.init()
  178. for idx in 0...languages.count-1 {
  179. let string = languages[idx]
  180. let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "")
  181. menuItem.tag = 1000 + idx
  182. menu.addItem(menuItem)
  183. }
  184. self.fromLanguageView?.menu = menu
  185. self.fromLanguage = "Automatic"
  186. languages = ["English", "Simplified Chinese", "Traditional Chinese", "Japanese", "Korean", "French", "Spanish", "Italian", "German", "Portuguese", "Russian", "Vietnamese", "Thai", "Arabic", "Greek", "Bulgarian", "Finnish", "Slovene", "Dutch", "Czech", "Swedish", "Polish", "Danish", "Romanian", "Hungarian"]
  187. var toMenu = NSMenu.init()
  188. for idx in 0...languages.count-1 {
  189. let string = languages[idx]
  190. let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "")
  191. menuItem.tag = 3000 + idx
  192. toMenu.addItem(menuItem)
  193. }
  194. self.toLanguageView?.menu = toMenu
  195. self.toLanguage = "English"
  196. self.fromLanguageLabel.font = NSFont.SFProTextRegularFont(13)
  197. self.toLanguageLabel.font = NSFont.SFProTextRegularFont(13)
  198. self.fromLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0()
  199. self.toLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0()
  200. self.fromLanguageLabel.stringValue = self.fromLanguage
  201. self.toLanguageLabel.stringValue = self.toLanguage
  202. self.removePDFBtn.isHidden = true
  203. self.filePDFHoverBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  204. if mouseEntered {
  205. self.removePDFBtn.isHidden = false
  206. } else {
  207. self.removePDFBtn.isHidden = true
  208. }
  209. }
  210. self.cancelBtn.title = NSLocalizedString("Cancel", comment: "")
  211. self.saveAsTextBtn.title = NSLocalizedString("Save as TXT", comment: "")
  212. self.saveAsPDFBtn.title = NSLocalizedString("Save as PDF", comment: "")
  213. self.translateResultLabel.stringValue = NSLocalizedString("Translation Completion", comment: "")
  214. self.translateResultSubLabel.stringValue = NSLocalizedString("Click \"Save as PDF\" to save the result as a PDF file.", comment: "")
  215. self.translateResultView.isHidden = true
  216. self.translateResultView.wantsLayer = true
  217. if KMAppearance.isDarkMode() {
  218. self.translateResultLabel.textColor = NSColor.white
  219. self.translateResultSubLabel.textColor = NSColor.white
  220. } else {
  221. self.translateResultLabel.textColor = NSColor.black.withAlphaComponent(0.5)
  222. self.translateResultSubLabel.textColor = NSColor.black.withAlphaComponent(0.5)
  223. }
  224. self.translateResultLabel.font = NSFont.SFProTextSemiboldFont(13)
  225. self.translateResultSubLabel.font = NSFont.SFProTextRegularFont(13)
  226. self.aiResultTextView.font = NSFont.SFProTextRegularFont(14)
  227. self.aiResultTextView.textColor = KMAppearance.KMColor_Layout_H0()
  228. self.aiResultTextView.backgroundColor = NSColor.clear
  229. self.aiResultTextView.isEditable = false
  230. self.fileEmptyTextView.font = NSFont.SFProTextRegularFont(14)
  231. self.fileEmptyTextView.textColor = KMAppearance.KMColor_Layout_H0()
  232. }
  233. func getFileSize(atPath filePath : String) -> CGFloat {
  234. guard let dict = try? FileManager.default.attributesOfItem(atPath: filePath) as NSDictionary else {
  235. return 0
  236. }
  237. return CGFloat(dict.fileSize())
  238. }
  239. func refreshUI() -> Void {
  240. if self.configType == .summarize {
  241. self.titleLabel.stringValue = NSLocalizedString("AI Summarize", comment: "")
  242. self.fileEmptyImage.image = NSImage(named: "ai_summary_Empty")
  243. } else if self.configType == .reWriting {
  244. self.titleLabel.stringValue = NSLocalizedString("AI Rewrite", comment: "")
  245. self.fileEmptyImage.image = NSImage(named: "ai_rewriting_empty")
  246. } else if self.configType == .proofreading {
  247. self.titleLabel.stringValue = NSLocalizedString("AI Proofread", comment: "")
  248. self.fileEmptyImage.image = NSImage(named: "ai_proofreading_empty")
  249. } else if self.configType == .translate {
  250. self.titleLabel.stringValue = NSLocalizedString("AI Translate", comment: "")
  251. self.fileEmptyImage.image = NSImage(named: "ai_translate_empty")
  252. }
  253. self.translateResultView.isHidden = true
  254. self.translateConfigView.isHidden = true
  255. self.translateSepLineView.isHidden = true
  256. if self.configType == .reWriting ||
  257. self.configType == .proofreading {
  258. self.uploadBGView.isHidden = true
  259. self.fileContendViewTopConst.constant = 52
  260. self.filePDFBGView.isHidden = true
  261. } else {
  262. self.uploadBGView.isHidden = false
  263. self.fileContendViewTopConst.constant = 92
  264. self.filePDFBGView.isHidden = false
  265. if self.configType == .translate {
  266. self.fileContendViewTopConst.constant = 137
  267. self.translateConfigView.isHidden = false
  268. self.translateSepLineView.isHidden = false
  269. }
  270. }
  271. self.reloadData()
  272. }
  273. func clearData() -> Void {
  274. self.fileEmptyTextView.string = ""
  275. self.aiResultTextView.string = ""
  276. self.filePath = ""
  277. self.translateFinish = false
  278. self.translateFile = false
  279. self.cancelBtn.isEnabled = true
  280. }
  281. func reloadData() -> Void {
  282. self.clearData()
  283. self.fileEmptyTextView.isEditable = true
  284. self.inputTextCountLabel.isHidden = true
  285. if self.configType == .summarize {
  286. self.fileEmptyTextView.isEditable = false
  287. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by uploading a document (pdf).", comment: "")
  288. } else if self.configType == .reWriting {
  289. self.inputTextCountLabel.isHidden = false
  290. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "")
  291. } else if self.configType == .proofreading {
  292. self.inputTextCountLabel.isHidden = false
  293. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "")
  294. } else if self.configType == .translate {
  295. self.inputTextCountLabel.isHidden = false
  296. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text, or uploading a document (pdf).", comment: "")
  297. }
  298. self.updateActionViewUI()
  299. self.updateResultViewUI()
  300. }
  301. //MARK: 更新左侧UI
  302. func updateActionViewUI() -> Void{
  303. self.fileSizeTipView.isHidden = true
  304. if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 {
  305. self.enableActionView(true)
  306. self.filePlaceholdLabel.isHidden = true
  307. } else {
  308. self.enableActionView(false)
  309. self.filePlaceholdLabel.isHidden = false
  310. }
  311. if self.filePath.count > 0 {
  312. self.fileEmptyTextView.isHidden = true
  313. self.filePDFBGView.isHidden = false
  314. let document = CPDFDocument.init(url: URL(fileURLWithPath: self.filePath))
  315. if document?.isLocked == true || document == nil {
  316. self.filePath = ""
  317. self.updateActionViewUI()
  318. return
  319. }
  320. DispatchQueue.main.async {
  321. let page = document?.page(at: 0)
  322. let image = page?.thumbnail(of: page!.size)
  323. self.pdfCoverImageView.image = image
  324. }
  325. let filePathURL = URL(fileURLWithPath: self.filePath)
  326. let parentDirectory = filePathURL.lastPathComponent
  327. self.pdfNameLbl.stringValue = parentDirectory
  328. self.inputTextCountLabel.isHidden = true
  329. } else {
  330. self.fileEmptyTextView.isHidden = false
  331. self.filePDFBGView.isHidden = true
  332. self.pdfCoverImageView.image = nil
  333. self.inputTextCountLabel.isHidden = false
  334. if self.configType == .summarize {
  335. self.inputTextCountLabel.isHidden = true
  336. }
  337. }
  338. self.inputTextCountLabel.stringValue = String(format: "%ld", self.fileEmptyTextView.string.count) + "/2000"
  339. if self.fileEmptyTextView.string.count == 2000 {
  340. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Status_Err()
  341. } else {
  342. if KMAppearance.isDarkMode() {
  343. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_W30()
  344. } else {
  345. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_B30()
  346. }
  347. }
  348. if self.aiResultTextView.string.count > 0 && (self.fileEmptyTextView.string.count > 0 || self.filePath.count > 0){
  349. self.fileActionLabel.stringValue = NSLocalizedString("Redo (1 credit)", comment: "")
  350. if self.configType == .translate && self.filePath.isEmpty == false {
  351. self.fileActionLabel.stringValue = NSLocalizedString("Redo", comment: "")
  352. }
  353. } else {
  354. if self.configType == .summarize {
  355. self.fileActionLabel.stringValue = NSLocalizedString("Summarize", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  356. } else if self.configType == .reWriting {
  357. self.fileActionLabel.stringValue = NSLocalizedString("Rewrite", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  358. } else if self.configType == .proofreading {
  359. self.fileActionLabel.stringValue = NSLocalizedString("Proofread", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  360. } else if self.configType == .translate {
  361. self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  362. if self.filePath.isEmpty == false {
  363. self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "")
  364. }
  365. }
  366. }
  367. }
  368. func enableActionView(_ enable: Bool) -> Void {
  369. if enable {
  370. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().cgColor
  371. self.fileActionLabel.textColor = NSColor.white
  372. self.fileActionBtn.isEnabled = true
  373. } else {
  374. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().withAlphaComponent(0.4).cgColor
  375. self.fileActionLabel.textColor = NSColor.white.withAlphaComponent(0.4)
  376. self.fileActionBtn.isEnabled = false
  377. }
  378. self.fileActionenAble = enable
  379. if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 {
  380. self.filePlaceholdLabel.isHidden = true
  381. self.fileEmptyImage.isHidden = true
  382. } else {
  383. self.filePlaceholdLabel.isHidden = false
  384. self.fileEmptyImage.isHidden = false
  385. }
  386. }
  387. func updateResultViewUI() -> Void{
  388. if self.aiResultTextView.string.count > 0 {
  389. self.resultEmptyImage.isHidden = true
  390. self.saveAsTextBtn.isEnabled = true
  391. self.saveAsPDFBtn.isEnabled = true
  392. } else {
  393. self.resultEmptyImage.isHidden = false
  394. self.saveAsTextBtn.isEnabled = false
  395. self.saveAsPDFBtn.isEnabled = false
  396. }
  397. if self.configType == .translate && self.translateFinish == true {
  398. self.saveAsTextBtn.isEnabled = true
  399. self.saveAsPDFBtn.isEnabled = true
  400. if self.translateFile == true {
  401. self.translateResultView.isHidden = false
  402. self.saveAsTextBtn.isEnabled = false
  403. } else {
  404. self.translateResultView.isHidden = true
  405. }
  406. if translateResultView.isHidden == false {
  407. self.resultEmptyImage.isHidden = true
  408. }
  409. }
  410. }
  411. //MARK: WaitingView
  412. func addWaingView(_ view: NSView) -> Void {
  413. self.removeWaitingView(view)
  414. let wView = WaitingView.init(frame: view.bounds)
  415. wView.autoresizingMask = [.width, .height]
  416. view.addSubview(wView)
  417. wView.startAnimation()
  418. }
  419. func removeWaitingView(_ view: NSView) -> Void {
  420. for view in view.subviews {
  421. if view.className == WaitingView.className() {
  422. view.removeFromSuperview()
  423. break
  424. }
  425. }
  426. }
  427. func aiTranslateFinish() -> Void {
  428. self.aiConvertFinish = true
  429. self.removePDFBtn.isEnabled = true
  430. self.removeWaitingView(self.resultBGView)
  431. self.updateActionViewUI()
  432. }
  433. //MARK: AI-Action
  434. func aiSummarize() -> Void {
  435. if FileManager.default.fileExists(atPath: self.filePath) {
  436. self.aiResultTextView.string = ""
  437. self.addWaingView(self.resultBGView)
  438. self.enableActionView(false)
  439. self.removePDFBtn.isEnabled = false
  440. self.aiConvertFinish = false
  441. self.updateResultViewUI()
  442. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  443. KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath, state: .extractSummaryFile) { wrapper in
  444. DispatchQueue.main.async {
  445. self.aiConvertFinish = true
  446. self.removePDFBtn.isEnabled = true
  447. self.removeWaitingView(self.resultBGView)
  448. self.updateActionViewUI()
  449. let success = wrapper.success
  450. if success {
  451. let resultStr = wrapper.content
  452. self.aiResultTextView.string = resultStr
  453. self.updateActionViewUI()
  454. self.updateResultViewUI()
  455. } else {
  456. let alert = NSAlert()
  457. alert.alertStyle = .critical
  458. alert.messageText = wrapper.content
  459. alert.runModal()
  460. return
  461. }
  462. }
  463. }
  464. }
  465. }
  466. func aiReWriting() -> Void {
  467. self.aiResultTextView.string = ""
  468. self.addWaingView(self.resultBGView)
  469. self.enableActionView(false)
  470. self.removePDFBtn.isEnabled = false
  471. self.aiConvertFinish = false
  472. self.updateResultViewUI()
  473. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  474. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  475. state: .rewrite) { wrapper in
  476. DispatchQueue.main.async {
  477. self.aiConvertFinish = true
  478. self.removePDFBtn.isEnabled = true
  479. self.removeWaitingView(self.resultBGView)
  480. self.updateActionViewUI()
  481. let success = wrapper.success
  482. if success {
  483. let resultStr = wrapper.content
  484. self.aiResultTextView.string = resultStr
  485. self.updateActionViewUI()
  486. self.updateResultViewUI()
  487. } else {
  488. let alert = NSAlert()
  489. alert.alertStyle = .critical
  490. alert.messageText = wrapper.content
  491. alert.runModal()
  492. return
  493. }
  494. }
  495. }
  496. }
  497. func aiProofreading() -> Void {
  498. self.aiResultTextView.string = ""
  499. self.addWaingView(self.resultBGView)
  500. self.enableActionView(false)
  501. self.removePDFBtn.isEnabled = false
  502. self.aiConvertFinish = false
  503. self.updateResultViewUI()
  504. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  505. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  506. state: .correctTypos) { wrapper in
  507. DispatchQueue.main.async {
  508. self.aiConvertFinish = true
  509. self.removePDFBtn.isEnabled = true
  510. self.removeWaitingView(self.resultBGView)
  511. self.updateActionViewUI()
  512. let success = wrapper.success
  513. if success {
  514. let resultStr = wrapper.content
  515. self.aiResultTextView.string = resultStr
  516. self.updateActionViewUI()
  517. self.updateResultViewUI()
  518. } else {
  519. let alert = NSAlert()
  520. alert.alertStyle = .critical
  521. alert.messageText = wrapper.content
  522. alert.runModal()
  523. return
  524. }
  525. }
  526. }
  527. }
  528. func aiTranslate() -> Void {
  529. }
  530. @objc func menuItemClick(_ item: NSMenuItem) {
  531. if item.tag < 2000 {
  532. self.fromLanguage = item.title
  533. } else {
  534. self.toLanguage = item.title
  535. }
  536. self.fromLanguageLabel.stringValue = self.fromLanguage
  537. self.toLanguageLabel.stringValue = self.toLanguage
  538. }
  539. //MARK: - IBAction
  540. @IBAction func chooseFileAction(_ sender: KMButton) {
  541. let openPanel = NSOpenPanel()
  542. openPanel.canChooseDirectories = true
  543. openPanel.canChooseFiles = true
  544. openPanel.allowsMultipleSelection = false;
  545. openPanel.allowedFileTypes = ["pdf"]
  546. openPanel.beginSheetModal(for: self.window!) { result in
  547. if result == .OK {
  548. let fileURL = openPanel.urls.first
  549. let fileSize = self.getFileSize(atPath: fileURL!.path)
  550. if fileSize/(1024*1024) > 10 {
  551. self.fileSizeTipView.isHidden = false
  552. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
  553. self.fileSizeTipView.isHidden = true
  554. }
  555. } else {
  556. self.filePath = fileURL!.path
  557. self.fileEmptyTextView.string = ""
  558. self.updateActionViewUI()
  559. }
  560. }
  561. }
  562. }
  563. @IBAction func chooseLanguageAction(_ sender: NSButton) {
  564. if sender == self.fromLanguageBtn {
  565. let menu = self.fromLanguageView?.menu
  566. menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender)
  567. } else if sender == self.toLanguageBtn {
  568. let menu = self.toLanguageView?.menu
  569. menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender)
  570. }
  571. }
  572. @IBAction func removePDFAction(_ sender: NSButton) {
  573. self.filePath = ""
  574. self.updateActionViewUI()
  575. self.updateResultViewUI()
  576. }
  577. @IBAction func startAIFunction(_ sender: KMButton) {
  578. let newStatus: Bool = KMCloudServer.isConnectionAvailable()
  579. if !newStatus {
  580. let alert = NSAlert()
  581. alert.alertStyle = .critical
  582. alert.messageText = NSLocalizedString("Please make sure your internet connection is available.", comment: "")
  583. alert.runModal()
  584. return
  585. }
  586. if self.configType == .summarize {
  587. self.aiSummarize()
  588. } else if self.configType == .reWriting {
  589. self.aiReWriting()
  590. } else if self.configType == .proofreading {
  591. self.aiProofreading()
  592. } else if self.configType == .translate {
  593. self.aiResultTextView.string = ""
  594. self.addWaingView(self.resultBGView)
  595. self.enableActionView(false)
  596. self.translateFinish = false
  597. self.translateResultView.isHidden = true
  598. self.updateResultViewUI()
  599. if self.filePath.count > 0 {
  600. self.translateFile = true
  601. self.removePDFBtn.isEnabled = false
  602. self.aiConvertFinish = false
  603. self.cancelBtn.isEnabled = false
  604. KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath,
  605. state: .fileTranslate,
  606. from: self.fromLanguage,
  607. to: self.toLanguage) { wrapper in
  608. DispatchQueue.main.async {
  609. var success = wrapper.success
  610. let resultStr = wrapper.content
  611. if success == false && resultStr == "501" {
  612. success = true
  613. }
  614. if success {
  615. let translateTipWC = AITranslateTipWindowController.currentWC()
  616. let infoDict: NSDictionary = wrapper.result
  617. var credit: Int = 0
  618. if infoDict["credit"] != nil {
  619. credit = (infoDict["credit"] ?? "0") as! Int
  620. }
  621. var charCount: Int = 0
  622. if infoDict["charCount"] != nil {
  623. charCount = (infoDict["charCount"] ?? "0") as! Int
  624. }
  625. translateTipWC.creditValid = true
  626. if resultStr == "501" {
  627. translateTipWC.creditValid = false
  628. }
  629. translateTipWC.spendCredits = credit
  630. translateTipWC.totalChart = charCount
  631. translateTipWC.actionHandle = {[weak self] wc, result in
  632. if result == true {
  633. self?.window?.endSheet(wc.window!)
  634. KMAIRequestServerManager.defaultManager.aiTranslationFileTranslateHandle(fileKey: resultStr,
  635. from: self!.fromLanguage,
  636. to: self!.toLanguage) { wrapper in
  637. let success = wrapper.success
  638. if success {
  639. DispatchQueue.main.async {
  640. self?.aiTranslateFinish()
  641. self?.cancelBtn.isEnabled = true
  642. self!.translateFinish = true
  643. self!.translateResultView.isHidden = false
  644. let resultStr = wrapper.content
  645. self!.translateResultPath = resultStr
  646. self!.updateActionViewUI()
  647. self!.updateResultViewUI()
  648. }
  649. } else {
  650. DispatchQueue.main.async {
  651. self?.aiTranslateFinish()
  652. self?.cancelBtn.isEnabled = true
  653. let alert = NSAlert()
  654. alert.alertStyle = .critical
  655. alert.messageText = wrapper.content
  656. alert.runModal()
  657. }
  658. return
  659. }
  660. }
  661. } else {
  662. self?.cancelBtn.isEnabled = true
  663. self?.aiTranslateFinish()
  664. self?.window?.endSheet(wc.window!)
  665. }
  666. }
  667. self.window?.beginSheet(translateTipWC.window!)
  668. translateTipWC.reloadData()
  669. } else {
  670. self.cancelBtn.isEnabled = true
  671. self.aiConvertFinish = true
  672. self.removePDFBtn.isEnabled = true
  673. self.removeWaitingView(self.resultBGView)
  674. self.updateActionViewUI()
  675. let alert = NSAlert()
  676. alert.alertStyle = .critical
  677. alert.messageText = wrapper.content
  678. alert.runModal()
  679. return
  680. }
  681. }
  682. };
  683. } else {
  684. self.translateFile = false
  685. self.removePDFBtn.isEnabled = false
  686. self.aiConvertFinish = false
  687. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  688. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  689. state: .textTranslate,
  690. from: self.fromLanguage,
  691. to: self.toLanguage) { wrapper in
  692. DispatchQueue.main.async {
  693. self.aiConvertFinish = true
  694. self.removePDFBtn.isEnabled = true
  695. self.removeWaitingView(self.resultBGView)
  696. self.updateActionViewUI()
  697. let success = wrapper.success
  698. if success {
  699. self.translateFinish = true
  700. self.translateResultView.isHidden = false
  701. let resultStr = wrapper.content
  702. self.aiResultTextView.string = resultStr
  703. self.updateActionViewUI()
  704. self.updateResultViewUI()
  705. } else {
  706. let alert = NSAlert()
  707. alert.alertStyle = .critical
  708. alert.messageText = wrapper.content
  709. alert.runModal()
  710. self.updateResultViewUI()
  711. return
  712. }
  713. }
  714. };
  715. }
  716. }
  717. }
  718. @IBAction func languageChangeAction(_ sender: Any) {
  719. let curLan = self.fromLanguage
  720. self.fromLanguage = self.toLanguage
  721. self.toLanguage = curLan
  722. self.fromLanguageLabel.stringValue = self.fromLanguage
  723. self.toLanguageLabel.stringValue = self.toLanguage
  724. }
  725. @IBAction func cancelAction(_ sender: NSButton) {
  726. DispatchQueue.main.async {
  727. self.removeWaitingView(self.resultBGView)
  728. self.updateActionViewUI()
  729. }
  730. if self.aiConvertFinish == false {
  731. let alert = NSAlert()
  732. alert.alertStyle = .critical
  733. alert.messageText = NSLocalizedString("The task is already in process, if you cancel the process, AI Credit will not be returned, please confirm if you still need to cancel?", comment: "")
  734. alert.addButton(withTitle: NSLocalizedString("Back ", comment: ""))
  735. alert.addButton(withTitle: NSLocalizedString("Confirm Cancel", comment: ""))
  736. let response = alert.runModal()
  737. if response.rawValue == 1001 {
  738. self.confirmCancelAction()
  739. }
  740. return
  741. }
  742. if self.aiResultTextView.string.count > 0 {
  743. if KMFunctionGuideWindowController.availableShow(.aiInfoResultSave) == true {
  744. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  745. let alert = NSAlert()
  746. alert.alertStyle = .critical
  747. alert.messageText = NSLocalizedString("The results are not saved. Do you want to save them in other formats?", comment: "")
  748. alert.addButton(withTitle: NSLocalizedString("Yes", comment: ""))
  749. alert.addButton(withTitle: NSLocalizedString("No", comment: ""))
  750. let response = alert.runModal()
  751. if response.rawValue == 1001 {
  752. self.cancelAction(self.cancelBtn)
  753. }
  754. return
  755. }
  756. }
  757. self.clearData()
  758. guard let callBack = self.cancelHandle else {
  759. return
  760. }
  761. callBack(self)
  762. }
  763. func confirmCancelAction() -> Void {
  764. self.clearData()
  765. guard let callBack = self.cancelHandle else {
  766. return
  767. }
  768. callBack(self)
  769. }
  770. @IBAction func saveAsTextAction(_ sender: Any) {
  771. let fileName = "Untitled"
  772. let outputSavePanel = NSSavePanel()
  773. outputSavePanel.allowedFileTypes = ["txt"]
  774. outputSavePanel.nameFieldStringValue = fileName
  775. let result = outputSavePanel.runModal()
  776. if result == .OK {
  777. let filePath = outputSavePanel.url?.path
  778. do {
  779. try self.aiResultTextView.string.write(to: outputSavePanel.url!, atomically: true, encoding: .utf8)
  780. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  781. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  782. } catch {
  783. }
  784. }
  785. }
  786. @IBAction func saveAsPDFAction(_ sender: Any) {
  787. let fileName = "Untitled"
  788. let outputSavePanel = NSSavePanel()
  789. outputSavePanel.allowedFileTypes = ["pdf"]
  790. outputSavePanel.nameFieldStringValue = fileName
  791. let result = outputSavePanel.runModal()
  792. if result == .OK {
  793. let filePath = outputSavePanel.url?.path
  794. do {
  795. if self.translateFile == true {
  796. do {
  797. if self.translateResultPath.count > 0 {
  798. if FileManager.default.fileExists(atPath: filePath!) {
  799. do {
  800. try FileManager.default.removeItem(atPath: filePath!)
  801. // print("删除旧文件成功")
  802. } catch {
  803. // print("删除旧文件失败:\(error)")
  804. }
  805. }
  806. try FileManager.default.copyItem(atPath: self.translateResultPath, toPath: filePath!)
  807. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  808. }
  809. } catch {
  810. }
  811. } else {
  812. let text = self.aiResultTextView.string
  813. self.createPDF(from: text, saveTo: filePath!)
  814. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  815. }
  816. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  817. } catch {
  818. }
  819. }
  820. }
  821. func textDidChange(_ notification: Notification) {
  822. if let textView = notification.object as? NSTextView, textView == self.fileEmptyTextView {
  823. // 获取文本字段的当前字符数
  824. let currentText = textView.string
  825. let currentCount = currentText.count
  826. // 如果超过最大字符数,将文本截断为最大字符数,并设置回文本字段
  827. if currentCount > 2000 {
  828. let endIndex = currentText.index(currentText.startIndex, offsetBy: 2000)
  829. let truncatedText = String(currentText[..<endIndex])
  830. textView.string = truncatedText
  831. }
  832. }
  833. self.updateActionViewUI()
  834. }
  835. override func mouseDown(with event: NSEvent) {
  836. super.mouseDown(with: event)
  837. self.window?.makeFirstResponder(nil)
  838. }
  839. func createPDF(from text: String, saveTo path: String) {
  840. let attributedText = NSAttributedString(string: text)
  841. let pdfData = NSMutableData()
  842. let pdfConsumer = CGDataConsumer(data: pdfData)!
  843. var mediaBox = CGRect(x: 0, y: 0, width: 612, height: 792) // 设置页面尺寸,这里使用默认的美国信纸尺寸
  844. let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: &mediaBox, nil)!
  845. pdfContext.beginPage(mediaBox: &mediaBox)
  846. pdfContext.textMatrix = .identity
  847. pdfContext.setShouldAntialias(true)
  848. let frame = mediaBox.insetBy(dx: 20, dy: 20) // 设置文字边距
  849. let framesetter = CTFramesetterCreateWithAttributedString(attributedText as CFAttributedString)
  850. let framePath = CGPath(rect: frame, transform: nil)
  851. let frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedText.length), framePath, nil)
  852. CTFrameDraw(frameRef, pdfContext)
  853. pdfContext.endPage()
  854. pdfContext.closePDF()
  855. pdfData.write(toFile: path, atomically: true)
  856. print("PDF 文件已保存至:\(path)")
  857. }
  858. }