AIConfigWindowController.swift 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  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.window?.makeFirstResponder(nil)
  173. 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"]
  174. let menu = NSMenu.init()
  175. for idx in 0...languages.count-1 {
  176. let string = languages[idx]
  177. let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "")
  178. menuItem.tag = 1000 + idx
  179. menu.addItem(menuItem)
  180. }
  181. self.fromLanguageView?.menu = menu
  182. self.fromLanguage = "Automatic"
  183. 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"]
  184. var toMenu = NSMenu.init()
  185. for idx in 0...languages.count-1 {
  186. let string = languages[idx]
  187. let menuItem = NSMenuItem.init(title: string, action: #selector(menuItemClick(_:)), keyEquivalent: "")
  188. menuItem.tag = 3000 + idx
  189. toMenu.addItem(menuItem)
  190. }
  191. self.toLanguageView?.menu = toMenu
  192. self.toLanguage = "English"
  193. self.fromLanguageLabel.font = NSFont.SFProTextRegularFont(13)
  194. self.toLanguageLabel.font = NSFont.SFProTextRegularFont(13)
  195. self.fromLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0()
  196. self.toLanguageLabel.textColor = KMAppearance.KMColor_Layout_H0()
  197. self.fromLanguageLabel.stringValue = self.fromLanguage
  198. self.toLanguageLabel.stringValue = self.toLanguage
  199. self.removePDFBtn.isHidden = true
  200. self.filePDFHoverBtn.mouseMoveCallback = {[unowned self] mouseEntered in
  201. if mouseEntered {
  202. self.removePDFBtn.isHidden = false
  203. } else {
  204. self.removePDFBtn.isHidden = true
  205. }
  206. }
  207. self.cancelBtn.title = NSLocalizedString("Cancel", comment: "")
  208. self.saveAsTextBtn.title = NSLocalizedString("Save as TXT", comment: "")
  209. self.saveAsPDFBtn.title = NSLocalizedString("Save as PDF", comment: "")
  210. self.translateResultLabel.stringValue = NSLocalizedString("Translation Completion", comment: "")
  211. self.translateResultSubLabel.stringValue = NSLocalizedString("Click \"Save as PDF\" to save the result as a PDF file.", comment: "")
  212. self.translateResultView.isHidden = true
  213. self.translateResultView.wantsLayer = true
  214. if KMAppearance.isDarkMode() {
  215. self.translateResultLabel.textColor = NSColor.white
  216. self.translateResultSubLabel.textColor = NSColor.white
  217. } else {
  218. self.translateResultLabel.textColor = NSColor.black.withAlphaComponent(0.5)
  219. self.translateResultSubLabel.textColor = NSColor.black.withAlphaComponent(0.5)
  220. }
  221. self.translateResultLabel.font = NSFont.SFProTextSemiboldFont(13)
  222. self.translateResultSubLabel.font = NSFont.SFProTextRegularFont(13)
  223. self.aiResultTextView.font = NSFont.SFProTextRegularFont(14)
  224. self.aiResultTextView.textColor = KMAppearance.KMColor_Layout_H0()
  225. self.aiResultTextView.backgroundColor = NSColor.clear
  226. self.aiResultTextView.isEditable = false
  227. self.fileEmptyTextView.font = NSFont.SFProTextRegularFont(14)
  228. self.fileEmptyTextView.textColor = KMAppearance.KMColor_Layout_H0()
  229. }
  230. func getFileSize(atPath filePath : String) -> CGFloat {
  231. guard let dict = try? FileManager.default.attributesOfItem(atPath: filePath) as NSDictionary else {
  232. return 0
  233. }
  234. return CGFloat(dict.fileSize())
  235. }
  236. func refreshUI() -> Void {
  237. if self.configType == .summarize {
  238. self.titleLabel.stringValue = NSLocalizedString("AI Summarize", comment: "")
  239. self.fileEmptyImage.image = NSImage(named: "ai_summary_Empty")
  240. } else if self.configType == .reWriting {
  241. self.titleLabel.stringValue = NSLocalizedString("AI Rewrite", comment: "")
  242. self.fileEmptyImage.image = NSImage(named: "ai_rewriting_empty")
  243. } else if self.configType == .proofreading {
  244. self.titleLabel.stringValue = NSLocalizedString("AI Proofread", comment: "")
  245. self.fileEmptyImage.image = NSImage(named: "ai_proofreading_empty")
  246. } else if self.configType == .translate {
  247. self.titleLabel.stringValue = NSLocalizedString("AI Translate", comment: "")
  248. self.fileEmptyImage.image = NSImage(named: "ai_translate_empty")
  249. }
  250. self.translateResultView.isHidden = true
  251. self.translateConfigView.isHidden = true
  252. self.translateSepLineView.isHidden = true
  253. if self.configType == .reWriting ||
  254. self.configType == .proofreading {
  255. self.uploadBGView.isHidden = true
  256. self.fileContendViewTopConst.constant = 52
  257. self.filePDFBGView.isHidden = true
  258. } else {
  259. self.uploadBGView.isHidden = false
  260. self.fileContendViewTopConst.constant = 92
  261. self.filePDFBGView.isHidden = false
  262. if self.configType == .translate {
  263. self.fileContendViewTopConst.constant = 137
  264. self.translateConfigView.isHidden = false
  265. self.translateSepLineView.isHidden = false
  266. }
  267. }
  268. self.reloadData()
  269. }
  270. func clearData() -> Void {
  271. self.fileEmptyTextView.string = ""
  272. self.aiResultTextView.string = ""
  273. self.filePath = ""
  274. self.translateFinish = false
  275. self.translateFile = false
  276. self.cancelBtn.isEnabled = true
  277. }
  278. func reloadData() -> Void {
  279. self.clearData()
  280. self.fileEmptyTextView.isEditable = true
  281. self.inputTextCountLabel.isHidden = true
  282. if self.configType == .summarize {
  283. self.fileEmptyTextView.isEditable = false
  284. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by uploading a document (pdf).", comment: "")
  285. } else if self.configType == .reWriting {
  286. self.inputTextCountLabel.isHidden = false
  287. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "")
  288. } else if self.configType == .proofreading {
  289. self.inputTextCountLabel.isHidden = false
  290. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text", comment: "")
  291. } else if self.configType == .translate {
  292. self.inputTextCountLabel.isHidden = false
  293. self.filePlaceholdLabel.placeholderString = NSLocalizedString("Start by typing, pasting (⌘ + V) text, or uploading a document (pdf).", comment: "")
  294. }
  295. self.updateActionViewUI()
  296. self.updateResultViewUI()
  297. }
  298. //MARK: 更新左侧UI
  299. func updateActionViewUI() -> Void{
  300. self.fileSizeTipView.isHidden = true
  301. if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 {
  302. self.enableActionView(true)
  303. self.filePlaceholdLabel.isHidden = true
  304. } else {
  305. self.enableActionView(false)
  306. self.filePlaceholdLabel.isHidden = false
  307. }
  308. if self.filePath.count > 0 {
  309. self.fileEmptyTextView.isHidden = true
  310. self.filePDFBGView.isHidden = false
  311. let document = CPDFDocument.init(url: URL(fileURLWithPath: self.filePath))
  312. if document?.isLocked == true || document == nil {
  313. self.filePath = ""
  314. self.updateActionViewUI()
  315. return
  316. }
  317. DispatchQueue.main.async {
  318. let page = document?.page(at: 0)
  319. let image = page?.thumbnail(of: page!.size)
  320. self.pdfCoverImageView.image = image
  321. }
  322. let filePathURL = URL(fileURLWithPath: self.filePath)
  323. let parentDirectory = filePathURL.lastPathComponent
  324. self.pdfNameLbl.stringValue = parentDirectory
  325. self.inputTextCountLabel.isHidden = true
  326. } else {
  327. self.fileEmptyTextView.isHidden = false
  328. self.filePDFBGView.isHidden = true
  329. self.pdfCoverImageView.image = nil
  330. self.inputTextCountLabel.isHidden = false
  331. if self.configType == .summarize {
  332. self.inputTextCountLabel.isHidden = true
  333. }
  334. }
  335. self.inputTextCountLabel.stringValue = String(format: "%ld", self.fileEmptyTextView.string.count) + "/2000"
  336. if self.fileEmptyTextView.string.count == 2000 {
  337. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Status_Err()
  338. } else {
  339. if KMAppearance.isDarkMode() {
  340. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_W30()
  341. } else {
  342. self.inputTextCountLabel.textColor = KMAppearance.KMColor_Layout_B30()
  343. }
  344. }
  345. if self.aiResultTextView.string.count > 0 && (self.fileEmptyTextView.string.count > 0 || self.filePath.count > 0){
  346. self.fileActionLabel.stringValue = NSLocalizedString("Redo (1 credit)", comment: "")
  347. if self.configType == .translate && self.filePath.isEmpty == false {
  348. self.fileActionLabel.stringValue = NSLocalizedString("Redo", comment: "")
  349. }
  350. } else {
  351. if self.configType == .summarize {
  352. self.fileActionLabel.stringValue = NSLocalizedString("Summarize", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  353. } else if self.configType == .reWriting {
  354. self.fileActionLabel.stringValue = NSLocalizedString("Rewrite", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  355. } else if self.configType == .proofreading {
  356. self.fileActionLabel.stringValue = NSLocalizedString("Proofread", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  357. } else if self.configType == .translate {
  358. self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "") + NSLocalizedString("(1 credit)", comment: "")
  359. if self.filePath.isEmpty == false {
  360. self.fileActionLabel.stringValue = NSLocalizedString("Translate", comment: "")
  361. }
  362. }
  363. }
  364. }
  365. func enableActionView(_ enable: Bool) -> Void {
  366. if enable {
  367. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().cgColor
  368. self.fileActionLabel.textColor = NSColor.white
  369. self.fileActionBtn.isEnabled = true
  370. } else {
  371. self.fileActionView.layer?.backgroundColor = KMAppearance.KMColor_Interactive_M0().withAlphaComponent(0.4).cgColor
  372. self.fileActionLabel.textColor = NSColor.white.withAlphaComponent(0.4)
  373. self.fileActionBtn.isEnabled = false
  374. }
  375. self.fileActionenAble = enable
  376. if self.filePath.count > 0 || self.fileEmptyTextView.string.count > 0 {
  377. self.filePlaceholdLabel.isHidden = true
  378. self.fileEmptyImage.isHidden = true
  379. } else {
  380. self.filePlaceholdLabel.isHidden = false
  381. self.fileEmptyImage.isHidden = false
  382. }
  383. }
  384. func updateResultViewUI() -> Void{
  385. if self.aiResultTextView.string.count > 0 {
  386. self.resultEmptyImage.isHidden = true
  387. self.saveAsTextBtn.isEnabled = true
  388. self.saveAsPDFBtn.isEnabled = true
  389. } else {
  390. self.resultEmptyImage.isHidden = false
  391. self.saveAsTextBtn.isEnabled = false
  392. self.saveAsPDFBtn.isEnabled = false
  393. }
  394. if self.configType == .translate && self.translateFinish == true {
  395. self.saveAsTextBtn.isEnabled = true
  396. self.saveAsPDFBtn.isEnabled = true
  397. if self.translateFile == true {
  398. self.translateResultView.isHidden = false
  399. self.saveAsTextBtn.isEnabled = false
  400. } else {
  401. self.translateResultView.isHidden = true
  402. }
  403. if translateResultView.isHidden == false {
  404. self.resultEmptyImage.isHidden = true
  405. }
  406. }
  407. }
  408. //MARK: WaitingView
  409. func addWaingView(_ view: NSView) -> Void {
  410. self.removeWaitingView(view)
  411. let wView = WaitingView.init(frame: view.bounds)
  412. wView.autoresizingMask = [.width, .height]
  413. view.addSubview(wView)
  414. wView.startAnimation()
  415. }
  416. func removeWaitingView(_ view: NSView) -> Void {
  417. for view in view.subviews {
  418. if view.className == WaitingView.className() {
  419. view.removeFromSuperview()
  420. break
  421. }
  422. }
  423. }
  424. func aiTranslateFinish() -> Void {
  425. self.aiConvertFinish = true
  426. self.removePDFBtn.isEnabled = true
  427. self.removeWaitingView(self.resultBGView)
  428. self.updateActionViewUI()
  429. }
  430. //MARK: AI-Action
  431. func aiSummarize() -> Void {
  432. if FileManager.default.fileExists(atPath: self.filePath) {
  433. self.aiResultTextView.string = ""
  434. self.addWaingView(self.resultBGView)
  435. self.enableActionView(false)
  436. self.removePDFBtn.isEnabled = false
  437. self.aiConvertFinish = false
  438. self.updateResultViewUI()
  439. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  440. KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath, state: .extractSummaryFile) { wrapper in
  441. DispatchQueue.main.async {
  442. self.aiConvertFinish = true
  443. self.removePDFBtn.isEnabled = true
  444. self.removeWaitingView(self.resultBGView)
  445. self.updateActionViewUI()
  446. let success = wrapper.success
  447. if success {
  448. let resultStr = wrapper.content
  449. self.aiResultTextView.string = resultStr
  450. self.updateActionViewUI()
  451. self.updateResultViewUI()
  452. } else {
  453. let alert = NSAlert()
  454. alert.alertStyle = .critical
  455. alert.messageText = wrapper.content
  456. alert.runModal()
  457. return
  458. }
  459. }
  460. }
  461. }
  462. }
  463. func aiReWriting() -> Void {
  464. self.aiResultTextView.string = ""
  465. self.addWaingView(self.resultBGView)
  466. self.enableActionView(false)
  467. self.removePDFBtn.isEnabled = false
  468. self.aiConvertFinish = false
  469. self.updateResultViewUI()
  470. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  471. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  472. state: .rewrite) { wrapper in
  473. DispatchQueue.main.async {
  474. self.aiConvertFinish = true
  475. self.removePDFBtn.isEnabled = true
  476. self.removeWaitingView(self.resultBGView)
  477. self.updateActionViewUI()
  478. let success = wrapper.success
  479. if success {
  480. let resultStr = wrapper.content
  481. self.aiResultTextView.string = resultStr
  482. self.updateActionViewUI()
  483. self.updateResultViewUI()
  484. } else {
  485. let alert = NSAlert()
  486. alert.alertStyle = .critical
  487. alert.messageText = wrapper.content
  488. alert.runModal()
  489. return
  490. }
  491. }
  492. }
  493. }
  494. func aiProofreading() -> Void {
  495. self.aiResultTextView.string = ""
  496. self.addWaingView(self.resultBGView)
  497. self.enableActionView(false)
  498. self.removePDFBtn.isEnabled = false
  499. self.aiConvertFinish = false
  500. self.updateResultViewUI()
  501. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  502. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  503. state: .correctTypos) { wrapper in
  504. DispatchQueue.main.async {
  505. self.aiConvertFinish = true
  506. self.removePDFBtn.isEnabled = true
  507. self.removeWaitingView(self.resultBGView)
  508. self.updateActionViewUI()
  509. let success = wrapper.success
  510. if success {
  511. let resultStr = wrapper.content
  512. self.aiResultTextView.string = resultStr
  513. self.updateActionViewUI()
  514. self.updateResultViewUI()
  515. } else {
  516. let alert = NSAlert()
  517. alert.alertStyle = .critical
  518. alert.messageText = wrapper.content
  519. alert.runModal()
  520. return
  521. }
  522. }
  523. }
  524. }
  525. func aiTranslate() -> Void {
  526. }
  527. @objc func menuItemClick(_ item: NSMenuItem) {
  528. if item.tag < 2000 {
  529. self.fromLanguage = item.title
  530. } else {
  531. self.toLanguage = item.title
  532. }
  533. self.fromLanguageLabel.stringValue = self.fromLanguage
  534. self.toLanguageLabel.stringValue = self.toLanguage
  535. }
  536. //MARK: - IBAction
  537. @IBAction func chooseFileAction(_ sender: KMButton) {
  538. let openPanel = NSOpenPanel()
  539. openPanel.canChooseDirectories = true
  540. openPanel.canChooseFiles = true
  541. openPanel.allowsMultipleSelection = false;
  542. openPanel.allowedFileTypes = ["pdf"]
  543. openPanel.beginSheetModal(for: self.window!) { result in
  544. if result == .OK {
  545. let fileURL = openPanel.urls.first
  546. let fileSize = self.getFileSize(atPath: fileURL!.path)
  547. if fileSize/(1024*1024) > 10 {
  548. self.fileSizeTipView.isHidden = false
  549. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
  550. self.fileSizeTipView.isHidden = true
  551. }
  552. } else {
  553. self.filePath = fileURL!.path
  554. self.fileEmptyTextView.string = ""
  555. self.updateActionViewUI()
  556. }
  557. }
  558. }
  559. }
  560. @IBAction func chooseLanguageAction(_ sender: NSButton) {
  561. if sender == self.fromLanguageBtn {
  562. let menu = self.fromLanguageView?.menu
  563. menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender)
  564. } else if sender == self.toLanguageBtn {
  565. let menu = self.toLanguageView?.menu
  566. menu?.popUp(positioning: menu?.item(at: 0), at: CGPoint(x: 0, y: 15), in: sender)
  567. }
  568. }
  569. @IBAction func removePDFAction(_ sender: NSButton) {
  570. self.filePath = ""
  571. self.updateActionViewUI()
  572. self.updateResultViewUI()
  573. }
  574. @IBAction func startAIFunction(_ sender: KMButton) {
  575. let newStatus: Bool = KMCloudServer.isConnectionAvailable()
  576. if !newStatus {
  577. let alert = NSAlert()
  578. alert.alertStyle = .critical
  579. alert.messageText = NSLocalizedString("Please make sure your internet connection is available.", comment: "")
  580. alert.runModal()
  581. return
  582. }
  583. if self.configType == .summarize {
  584. self.aiSummarize()
  585. } else if self.configType == .reWriting {
  586. self.aiReWriting()
  587. } else if self.configType == .proofreading {
  588. self.aiProofreading()
  589. } else if self.configType == .translate {
  590. self.aiResultTextView.string = ""
  591. self.addWaingView(self.resultBGView)
  592. self.enableActionView(false)
  593. self.translateFinish = false
  594. self.translateResultView.isHidden = true
  595. self.updateResultViewUI()
  596. if self.filePath.count > 0 {
  597. self.translateFile = true
  598. self.removePDFBtn.isEnabled = false
  599. self.aiConvertFinish = false
  600. self.cancelBtn.isEnabled = false
  601. KMAIRequestServerManager.defaultManager.aiAction(content: self.filePath,
  602. state: .fileTranslate,
  603. from: self.fromLanguage,
  604. to: self.toLanguage) { wrapper in
  605. DispatchQueue.main.async {
  606. var success = wrapper.success
  607. let resultStr = wrapper.content
  608. if success == false && resultStr == "501" {
  609. success = true
  610. }
  611. if success {
  612. let translateTipWC = AITranslateTipWindowController.currentWC()
  613. let infoDict: NSDictionary = wrapper.result
  614. var credit: Int = 0
  615. if infoDict["credit"] != nil {
  616. credit = (infoDict["credit"] ?? "0") as! Int
  617. }
  618. var charCount: Int = 0
  619. if infoDict["charCount"] != nil {
  620. charCount = (infoDict["charCount"] ?? "0") as! Int
  621. }
  622. translateTipWC.creditValid = true
  623. if resultStr == "501" {
  624. translateTipWC.creditValid = false
  625. }
  626. translateTipWC.spendCredits = credit
  627. translateTipWC.totalChart = charCount
  628. translateTipWC.actionHandle = {[weak self] wc, result in
  629. if result == true {
  630. self?.window?.endSheet(wc.window!)
  631. KMAIRequestServerManager.defaultManager.aiTranslationFileTranslateHandle(fileKey: resultStr,
  632. from: self!.fromLanguage,
  633. to: self!.toLanguage) { wrapper in
  634. let success = wrapper.success
  635. if success {
  636. DispatchQueue.main.async {
  637. self?.aiTranslateFinish()
  638. self?.cancelBtn.isEnabled = true
  639. self!.translateFinish = true
  640. self!.translateResultView.isHidden = false
  641. let resultStr = wrapper.content
  642. self!.translateResultPath = resultStr
  643. self!.updateActionViewUI()
  644. self!.updateResultViewUI()
  645. }
  646. } else {
  647. DispatchQueue.main.async {
  648. self?.aiTranslateFinish()
  649. self?.cancelBtn.isEnabled = true
  650. let alert = NSAlert()
  651. alert.alertStyle = .critical
  652. alert.messageText = wrapper.content
  653. alert.runModal()
  654. }
  655. return
  656. }
  657. }
  658. } else {
  659. self?.cancelBtn.isEnabled = true
  660. self?.aiTranslateFinish()
  661. self?.window?.endSheet(wc.window!)
  662. }
  663. }
  664. self.window?.beginSheet(translateTipWC.window!)
  665. translateTipWC.reloadData()
  666. } else {
  667. self.cancelBtn.isEnabled = true
  668. self.aiConvertFinish = true
  669. self.removePDFBtn.isEnabled = true
  670. self.removeWaitingView(self.resultBGView)
  671. self.updateActionViewUI()
  672. let alert = NSAlert()
  673. alert.alertStyle = .critical
  674. alert.messageText = wrapper.content
  675. alert.runModal()
  676. return
  677. }
  678. }
  679. };
  680. } else {
  681. self.translateFile = false
  682. self.removePDFBtn.isEnabled = false
  683. self.aiConvertFinish = false
  684. // DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
  685. KMAIRequestServerManager.defaultManager.aiAction(content: self.fileEmptyTextView.string,
  686. state: .textTranslate,
  687. from: self.fromLanguage,
  688. to: self.toLanguage) { wrapper in
  689. DispatchQueue.main.async {
  690. self.aiConvertFinish = true
  691. self.removePDFBtn.isEnabled = true
  692. self.removeWaitingView(self.resultBGView)
  693. self.updateActionViewUI()
  694. let success = wrapper.success
  695. if success {
  696. self.translateFinish = true
  697. self.translateResultView.isHidden = false
  698. let resultStr = wrapper.content
  699. self.aiResultTextView.string = resultStr
  700. self.updateActionViewUI()
  701. self.updateResultViewUI()
  702. } else {
  703. let alert = NSAlert()
  704. alert.alertStyle = .critical
  705. alert.messageText = wrapper.content
  706. alert.runModal()
  707. self.updateResultViewUI()
  708. return
  709. }
  710. }
  711. };
  712. }
  713. }
  714. }
  715. @IBAction func languageChangeAction(_ sender: Any) {
  716. let curLan = self.fromLanguage
  717. self.fromLanguage = self.toLanguage
  718. self.toLanguage = curLan
  719. self.fromLanguageLabel.stringValue = self.fromLanguage
  720. self.toLanguageLabel.stringValue = self.toLanguage
  721. }
  722. @IBAction func cancelAction(_ sender: NSButton) {
  723. DispatchQueue.main.async {
  724. self.removeWaitingView(self.resultBGView)
  725. self.updateActionViewUI()
  726. }
  727. if self.aiConvertFinish == false {
  728. let alert = NSAlert()
  729. alert.alertStyle = .critical
  730. 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: "")
  731. alert.addButton(withTitle: NSLocalizedString("Back ", comment: ""))
  732. alert.addButton(withTitle: NSLocalizedString("Confirm Cancel", comment: ""))
  733. let response = alert.runModal()
  734. if response.rawValue == 1001 {
  735. self.confirmCancelAction()
  736. }
  737. return
  738. }
  739. if self.aiResultTextView.string.count > 0 {
  740. if KMFunctionGuideWindowController.availableShow(.aiInfoResultSave) == true {
  741. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  742. let alert = NSAlert()
  743. alert.alertStyle = .critical
  744. alert.messageText = NSLocalizedString("The results are not saved. Do you want to save them in other formats?", comment: "")
  745. alert.addButton(withTitle: NSLocalizedString("Yes", comment: ""))
  746. alert.addButton(withTitle: NSLocalizedString("No", comment: ""))
  747. let response = alert.runModal()
  748. if response.rawValue == 1001 {
  749. self.cancelAction(self.cancelBtn)
  750. }
  751. return
  752. }
  753. }
  754. self.clearData()
  755. guard let callBack = self.cancelHandle else {
  756. return
  757. }
  758. callBack(self)
  759. }
  760. func confirmCancelAction() -> Void {
  761. self.clearData()
  762. guard let callBack = self.cancelHandle else {
  763. return
  764. }
  765. callBack(self)
  766. }
  767. @IBAction func saveAsTextAction(_ sender: Any) {
  768. let fileName = "Untitled"
  769. let outputSavePanel = NSSavePanel()
  770. outputSavePanel.allowedFileTypes = ["txt"]
  771. outputSavePanel.nameFieldStringValue = fileName
  772. let result = outputSavePanel.runModal()
  773. if result == .OK {
  774. let filePath = outputSavePanel.url?.path
  775. do {
  776. try self.aiResultTextView.string.write(to: outputSavePanel.url!, atomically: true, encoding: .utf8)
  777. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  778. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  779. } catch {
  780. }
  781. }
  782. }
  783. @IBAction func saveAsPDFAction(_ sender: Any) {
  784. let fileName = "Untitled"
  785. let outputSavePanel = NSSavePanel()
  786. outputSavePanel.allowedFileTypes = ["pdf"]
  787. outputSavePanel.nameFieldStringValue = fileName
  788. let result = outputSavePanel.runModal()
  789. if result == .OK {
  790. let filePath = outputSavePanel.url?.path
  791. do {
  792. if self.translateFile == true {
  793. do {
  794. if self.translateResultPath.count > 0 {
  795. if FileManager.default.fileExists(atPath: filePath!) {
  796. do {
  797. try FileManager.default.removeItem(atPath: filePath!)
  798. // print("删除旧文件成功")
  799. } catch {
  800. // print("删除旧文件失败:\(error)")
  801. }
  802. }
  803. try FileManager.default.copyItem(atPath: self.translateResultPath, toPath: filePath!)
  804. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  805. }
  806. } catch {
  807. }
  808. } else {
  809. let text = self.aiResultTextView.string
  810. self.createPDF(from: text, saveTo: filePath!)
  811. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath!)])
  812. }
  813. KMFunctionGuideWindowController.setDidShowFor(.aiInfoResultSave)
  814. } catch {
  815. }
  816. }
  817. }
  818. func textDidChange(_ notification: Notification) {
  819. if let textView = notification.object as? NSTextView, textView == self.fileEmptyTextView {
  820. // 获取文本字段的当前字符数
  821. let currentText = textView.string
  822. let currentCount = currentText.count
  823. // 如果超过最大字符数,将文本截断为最大字符数,并设置回文本字段
  824. if currentCount > 2000 {
  825. let endIndex = currentText.index(currentText.startIndex, offsetBy: 2000)
  826. let truncatedText = String(currentText[..<endIndex])
  827. textView.string = truncatedText
  828. }
  829. }
  830. self.updateActionViewUI()
  831. }
  832. override func mouseDown(with event: NSEvent) {
  833. super.mouseDown(with: event)
  834. self.window?.makeFirstResponder(nil)
  835. }
  836. func createPDF(from text: String, saveTo path: String) {
  837. let attributedText = NSAttributedString(string: text)
  838. let pdfData = NSMutableData()
  839. let pdfConsumer = CGDataConsumer(data: pdfData)!
  840. var mediaBox = CGRect(x: 0, y: 0, width: 612, height: 792) // 设置页面尺寸,这里使用默认的美国信纸尺寸
  841. let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: &mediaBox, nil)!
  842. pdfContext.beginPage(mediaBox: &mediaBox)
  843. pdfContext.textMatrix = .identity
  844. pdfContext.setShouldAntialias(true)
  845. let frame = mediaBox.insetBy(dx: 20, dy: 20) // 设置文字边距
  846. let framesetter = CTFramesetterCreateWithAttributedString(attributedText as CFAttributedString)
  847. let framePath = CGPath(rect: frame, transform: nil)
  848. let frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedText.length), framePath, nil)
  849. CTFrameDraw(frameRef, pdfContext)
  850. pdfContext.endPage()
  851. pdfContext.closePDF()
  852. pdfData.write(toFile: path, atomically: true)
  853. print("PDF 文件已保存至:\(path)")
  854. }
  855. }