From 4ba3725ef735b8b062b742036d92f1ffd24dd8d7 Mon Sep 17 00:00:00 2001 From: berkaygediz <121058050+berkaygediz@users.noreply.github.com> Date: Thu, 27 Feb 2025 00:57:52 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Real-time=20LLM=20response,=20ma?= =?UTF-8?q?ximized=20GPU/NPU=20performance,=20fixed=20memory=20leaks,=20en?= =?UTF-8?q?hanced=20LLM=20threading,=20improved=20hybrid=20saver=20adaptiv?= =?UTF-8?q?e=20response=20rates,=20updated=20requirements,=20bullet/number?= =?UTF-8?q?ed=20list=20improvements,=20document=20defaults,=20toolbar=20en?= =?UTF-8?q?hancements,=20gitignore,=20file=5Fversion=5Finfo,=20metadata,?= =?UTF-8?q?=20version.rc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 8 +- SolidWriting.py | 373 +++++++++++++++++++++++------------------- file_version_info.txt | 8 +- metadata.yml | 2 +- requirements.txt | 10 +- version.rc | 30 ++++ 7 files changed, 247 insertions(+), 185 deletions(-) create mode 100644 version.rc diff --git a/.gitignore b/.gitignore index 06a4ed6..b748abe 100644 --- a/.gitignore +++ b/.gitignore @@ -173,4 +173,5 @@ cython_debug/ # Built Visual Studio Code Extensions *.vsix + *.gguf \ No newline at end of file diff --git a/README.md b/README.md index e099feb..e321344 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,7 @@ SolidWriting is a supercharged word processor offering a powerful alternative to 3. Creating a executable file (Unsigned): ```bash - pyinstaller --noconfirm --onefile --windowed --icon ".\solidwriting_icon.ico" --name "SolidWriting" --clean --optimize "2" "SolidWriting.py" - ``` - -- Command for CUDA - - ```bash - pyinstaller --noconfirm --onefile --windowed --icon ".\solidwriting_icon.ico" --name "SolidWriting" --clean --optimize "2" --add-data ".\.venv\Lib\site-packages\llama_cpp\lib\;." ".\SolidWriting.py" + pyinstaller --name="SolidWriting" --onedir --windowed --icon=".\solidwriting_icon.ico" --add-data "./.venv/Lib/site-packages/PySide6/*:PySide6" --add-data "./.venv/Lib/site-packages/torch/*:torch" --add-data "./.venv/Lib/site-packages/llama_cpp/*:llama_cpp" --add-binary "./.venv/Lib/site-packages/PySide6/*:PySide6" --add-binary "./.venv/Lib/site-packages/torch/*:torch" --add-binary "./.venv/Lib/site-packages/llama_cpp/*:llama_cpp" --optimize "2" --clean --noconfirm ".\SolidWriting.py" ``` ## Usage diff --git a/SolidWriting.py b/SolidWriting.py index b573a2b..2707759 100644 --- a/SolidWriting.py +++ b/SolidWriting.py @@ -11,7 +11,7 @@ import psutil import torch from langdetect import DetectorFactory, detect -from llama_cpp import * +from llama_cpp import Llama from PySide6.QtCore import * from PySide6.QtGui import * from PySide6.QtOpenGL import * @@ -38,6 +38,28 @@ pass +class LLMThread(QThread): + result = Signal(str) + + def __init__(self, prompt, llm, parent=None): + super(LLMThread, self).__init__(parent) + self.prompt = prompt + self.llm = llm + + def run(self): + response = self.getResponseLLM() + self.result.emit(response) + + def getResponseLLM(self): + try: + response = self.llm.create_chat_completion( + messages=[{"role": "user", "content": self.prompt}] + ) + return response["choices"][0]["message"]["content"] + except Exception as e: + return f"Error: {str(e)}" + + class SW_ControlInfo(QMainWindow): def __init__(self, parent=None): super(SW_ControlInfo, self).__init__(parent) @@ -100,7 +122,7 @@ def __init__(self, parent=None): self.about_label.setText( "
" f"{app.applicationDisplayName()}

" - "Real-time computing and formatting supported word processor.

" + "A supercharged word processor with AI integration, supporting real-time computing and advanced formatting.

" "Made by Berkay Gediz

" "GNU General Public License v3.0
GNU LESSER GENERAL PUBLIC LICENSE v3.0
Mozilla Public License Version 2.0

Libraries: mwilliamson/python-mammoth, Mimino666/langdetect, abetlen/llama-cpp-python,
pytorch/pytorch, PySide6, chardet, psutil

" "OpenGL: ON
" @@ -197,10 +219,10 @@ def initUI(self): system_language = locale.getlocale()[1] if system_language not in languages.items(): settings.setValue("appLanguage", "1252") - settings.sync() if settings.value("adaptiveResponse") == None: settings.setValue("adaptiveResponse", 1) - settings.sync() + + settings.sync() centralWidget = QOpenGLWidget(self) @@ -499,16 +521,7 @@ def updateStatistics(self): statistics += f"{translations[lang]['analysis_message_5'].format(detected_language)}" else: - self.DocumentArea.setFontFamily(fallbackValues["fontFamily"]) - self.DocumentArea.setFontPointSize(fallbackValues["fontSize"]) - self.DocumentArea.setFontWeight(75 if fallbackValues["bold"] else 50) - self.DocumentArea.setFontItalic(fallbackValues["italic"]) - self.DocumentArea.setFontUnderline(fallbackValues["underline"]) - self.DocumentArea.setAlignment(fallbackValues["contentAlign"]) - self.DocumentArea.setTextColor(QColor(fallbackValues["contentColor"])) - self.DocumentArea.setTextBackgroundColor( - QColor(fallbackValues["contentBackgroundColor"]) - ) + self.resetDocumentArea() statistics += f"{translations[lang]['statistic']}" statistics += ( @@ -706,22 +719,11 @@ def updateToolbarLabel(self, toolbar, new_label): return def initArea(self): - self.DocumentArea.setFontFamily(fallbackValues["fontFamily"]) - self.DocumentArea.setFontPointSize(fallbackValues["fontSize"]) - self.DocumentArea.setFontWeight(75 if fallbackValues["bold"] else 50) - self.DocumentArea.setFontItalic(fallbackValues["italic"]) - self.DocumentArea.setFontUnderline(fallbackValues["underline"]) - self.DocumentArea.setAlignment(fallbackValues["contentAlign"]) - self.DocumentArea.setTextColor(QColor(fallbackValues["contentColor"])) - self.DocumentArea.setTextBackgroundColor( - QColor(fallbackValues["contentBackgroundColor"]) - ) - self.DocumentArea.setTabStopDistance(27) + self.resetDocumentArea() self.DocumentArea.document().setDocumentMargin(self.width() * 0.25) def loadLLM(self): - # Consent - if settings.value("load_llm") is None or settings.value("load_llm") is True: + if settings.value("load_llm") == None or settings.value("load_llm") == "true": reply = QMessageBox.question( None, "Load LLM", @@ -731,12 +733,13 @@ def loadLLM(self): ) if reply == QMessageBox.Yes: - settings.setValue("load_llm", True) self._load_model() + settings.setValue("load_llm", True) + else: - settings.setValue("load_llm", False) - self.ai_widget.setWidget(QLabel("LLM not available.")) + pass else: + settings.setValue("load_llm", False) self.ai_widget.setWidget(QLabel("LLM not available.")) def _load_model(self): @@ -756,20 +759,29 @@ def _load_model(self): model_path = os.path.join(current_directory, model_filename) if os.path.exists(model_path): - if torch.cuda.is_available(): - max_memory = 4 * 1024 + if torch.accelerator.is_available() == False: + # max_memory = torch.cuda.get_device_properties(0).total_memory / ( + # 1024**2 + # ) # x MB VRAM + max_memory = 8192 else: available_memory = psutil.virtual_memory().available - max_memory = min(available_memory, 2 * 1024) + max_memory = min(available_memory, 4 * 1024 * 1024 * 1024) / ( + 1024**2 + ) # 4096 MB self.llm = Llama( model_path, - n_gpu_layers=33, - use_fp16=True, + n_gpu_layers=-1, + split_mode=0, + offload_kqv=True, + flash_attn=True, + n_threads=4, max_memory=max_memory, device_map="auto", - n_threads=8, + verbose=True, ) + else: self.llm = None except TypeError as e: @@ -854,7 +866,7 @@ def LLMinitDock(self): QDockWidget.NoDockWidgetFeatures | QDockWidget.DockWidgetClosable ) - def LLMmessage(self, text, is_user=True): + def LLMmessage(self, text, is_user=True, typing_speed=100): DetectorFactory.seed = 0 language = "" @@ -898,6 +910,49 @@ def LLMmessage(self, text, is_user=True): self.messages_layout.addWidget(message_widget) + if not is_user: + self.LLMdynamicMessage( + message_label, text, typing_speed * self.adaptiveResponse + ) + + if is_user: + self.full_text = "" + + def LLMdynamicMessage(self, message_label, text, typing_speed): + words = text.split() + + if not hasattr(self, "full_text"): + self.full_text = "" + + word_index = 0 + + def type_next_word(): + nonlocal word_index + if word_index < len(words): + self.full_text += words[word_index] + " " + message_label.setText(self.full_text) + word_index += 1 + else: + self.LLMmessageDatetime(message_label) + self.typing_timer.stop() + + self.typing_timer = QTimer(self) + self.typing_timer.timeout.connect(type_next_word) + self.typing_timer.start(typing_speed) + + def LLMmessageDatetime(self, message_label): + current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + language = ( + detect(message_label.text()) if len(message_label.text()) > 30 else "" + ) + + if language: + new_text = f"{message_label.text()}

({current_time} - {language})" + else: + new_text = f"{message_label.text()}

({current_time})" + + message_label.setText(new_text) + def LLMpredict(self): prompt = self.input_text.toPlainText().strip() @@ -909,7 +964,15 @@ def LLMpredict(self): self.predict_button.setText("...") self.predict_button.setEnabled(False) - QTimer.singleShot(100, lambda: self.LLMprompt(prompt)) + self.llm_thread = LLMThread(prompt, self.llm) + self.llm_thread.result.connect(self.LLMhandleResponse) + self.llm_thread.start() + + def LLMhandleResponse(self, response): + self.LLMmessage(response, is_user=False) + self.input_text.clear() + self.predict_button.setText("->") + self.predict_button.setEnabled(True) def LLMcontextPredict(self, action_type): selected_text = self.DocumentArea.textCursor().selectedText().strip() @@ -928,7 +991,9 @@ def LLMcontextPredict(self, action_type): self.LLMmessage("No text selected.", is_user=False) return - QTimer.singleShot(100, lambda: self.LLMprompt(prompt)) + self.llm_thread = LLMThread(prompt, self.llm) + self.llm_thread.result.connect(self.LLMhandleResponse) + self.llm_thread.start() def LLMprompt(self, prompt): if prompt: @@ -1191,27 +1256,28 @@ def initActions(self): ) def initToolbar(self): - self.file_toolbar = self.addToolBar(translations[lang]["file"]) - self.toolbarLabel( - self.file_toolbar, - translations[lang]["file"] + ": ", - ) - self.file_toolbar.addActions( - [ - self.newaction, - self.openaction, - self.saveaction, - self.saveasaction, - self.printaction, - self.undoaction, - self.redoaction, - self.findaction, - self.replaceaction, - ] - ) + def add_toolbar(name_key, actions): + toolbar = self.addToolBar(translations[lang][name_key]) + self.toolbarLabel(toolbar, translations[lang][name_key] + ": ") + toolbar.addActions(actions) + return toolbar + + file_actions = [ + self.newaction, + self.openaction, + self.saveaction, + self.saveasaction, + self.printaction, + self.undoaction, + self.redoaction, + self.findaction, + self.replaceaction, + ] + self.file_toolbar = add_toolbar("file", file_actions) self.ui_toolbar = self.addToolBar(translations[lang]["ui"]) self.toolbarLabel(self.ui_toolbar, translations[lang]["ui"] + ": ") + self.theme_action = self.createAction( translations[lang]["darklight"], translations[lang]["darklight_message"], @@ -1221,93 +1287,63 @@ def initToolbar(self): ) self.theme_action.setCheckable(True) self.theme_action.setChecked(settings.value("appTheme") == "dark") - self.ui_toolbar.addAction(self.theme_action) + self.powersaveraction = QAction( - translations[lang]["powersaver"], - self, - checkable=True, + translations[lang]["powersaver"], self, checkable=True ) self.powersaveraction.setStatusTip(translations[lang]["powersaver_message"]) self.powersaveraction.toggled.connect(self.hybridSaver) - self.ui_toolbar.addAction(self.powersaveraction) + adaptiveResponse = settings.value( "adaptiveResponse", fallbackValues["adaptiveResponse"] ) self.powersaveraction.setChecked(adaptiveResponse > 1) - self.ui_toolbar.addAction(self.powersaveraction) self.hide_ai_dock = self.createAction( - "AI", - "AI", - self.toggleDock, - QKeySequence("Ctrl+Shift+D"), - "", + "AI", "AI", self.toggleDock, QKeySequence("Ctrl+Shift+D"), "" ) self.ui_toolbar.addAction(self.hide_ai_dock) self.ui_toolbar.addAction(self.helpAction) self.ui_toolbar.addAction(self.aboutAction) + self.language_combobox = QComboBox(self) self.language_combobox.setStyleSheet("background-color:#000000; color:#FFFFFF;") for lcid, name in languages.items(): self.language_combobox.addItem(name, lcid) - self.language_combobox.currentIndexChanged.connect(self.changeLanguage) self.ui_toolbar.addWidget(self.language_combobox) self.addToolBarBreak() - self.edit_toolbar = self.addToolBar(translations[lang]["edit"]) - self.toolbarLabel( - self.edit_toolbar, - translations[lang]["edit"] + ": ", - ) - self.edit_toolbar.addActions( - [ - self.alignrightevent, - self.aligncenterevent, - self.alignleftevent, - self.alignjustifiedevent, - ] - ) - self.edit_toolbar.addSeparator() - self.font_toolbar = self.addToolBar(translations[lang]["font"]) - self.toolbarLabel( - self.font_toolbar, - translations[lang]["font"] + ": ", - ) - self.font_toolbar.addActions([self.bold, self.italic, self.underline]) - self.font_toolbar.addSeparator() - self.list_toolbar = self.addToolBar(translations[lang]["list"]) - self.toolbarLabel( - self.list_toolbar, - translations[lang]["list"] + ": ", - ) - self.list_toolbar.addActions([self.bulletevent, self.numberedevent]) + edit_actions = [ + self.alignrightevent, + self.aligncenterevent, + self.alignleftevent, + self.alignjustifiedevent, + ] + self.edit_toolbar = add_toolbar("edit", edit_actions) + + font_actions = [self.bold, self.italic, self.underline] + self.font_toolbar = add_toolbar("font", font_actions) + + list_actions = [self.bulletevent, self.numberedevent] + self.list_toolbar = add_toolbar("list", list_actions) + self.addToolBarBreak() - self.color_toolbar = self.addToolBar(translations[lang]["color"]) - self.toolbarLabel( - self.color_toolbar, - translations[lang]["color"] + ": ", - ) - self.color_toolbar.addActions( - [ - self.color, - self.backgroundcolor, - self.fontfamily, - self.inc_fontaction, - self.dec_fontaction, - ] - ) + color_actions = [ + self.color, + self.backgroundcolor, + self.fontfamily, + self.inc_fontaction, + self.dec_fontaction, + ] + self.color_toolbar = add_toolbar("color", color_actions) - self.multimedia_toolbar = self.addToolBar(translations[lang]["multimedia"]) - self.toolbarLabel( - self.multimedia_toolbar, - translations[lang]["multimedia"] + ": ", - ) - self.multimedia_toolbar.addActions([self.addimage]) + multimedia_actions = [self.addimage] + self.multimedia_toolbar = add_toolbar("multimedia", multimedia_actions) def toggleDock(self): if self.ai_widget.isHidden(): @@ -1322,13 +1358,13 @@ def hybridSaver(self, checked): if battery: if battery.percent <= 35 and not battery.power_plugged: # Ultra - self.adaptiveResponse = 12 + self.adaptiveResponse = 6 else: # Standard - self.adaptiveResponse = 6 + self.adaptiveResponse = 4 else: # Global Standard - self.adaptiveResponse = 3 + self.adaptiveResponse = 2 else: self.adaptiveResponse = fallbackValues["adaptiveResponse"] @@ -1345,20 +1381,23 @@ def detectEncoding(file_path): detector.close() return detector.result["encoding"] + def resetDocumentArea(self): + self.DocumentArea.clear() + self.DocumentArea.setFontFamily(fallbackValues["fontFamily"]) + self.DocumentArea.setFontPointSize(fallbackValues["fontSize"]) + self.DocumentArea.setFontWeight(75 if fallbackValues["bold"] else 50) + self.DocumentArea.setFontItalic(fallbackValues["italic"]) + self.DocumentArea.setFontUnderline(fallbackValues["underline"]) + self.DocumentArea.setAlignment(fallbackValues["contentAlign"]) + self.DocumentArea.setTextColor(QColor(fallbackValues["contentColor"])) + self.DocumentArea.setTextBackgroundColor( + QColor(fallbackValues["contentBackgroundColor"]) + ) + self.DocumentArea.setTabStopDistance(27) + def newFile(self): - if self.is_saved == True: - self.DocumentArea.clear() - self.DocumentArea.setFontFamily(fallbackValues["fontFamily"]) - self.DocumentArea.setFontPointSize(fallbackValues["fontSize"]) - self.DocumentArea.setFontWeight(75 if fallbackValues["bold"] else 50) - self.DocumentArea.setFontItalic(fallbackValues["italic"]) - self.DocumentArea.setFontUnderline(fallbackValues["underline"]) - self.DocumentArea.setAlignment(fallbackValues["contentAlign"]) - self.DocumentArea.setTextColor(QColor(fallbackValues["contentColor"])) - self.DocumentArea.setTextBackgroundColor( - QColor(fallbackValues["contentBackgroundColor"]) - ) - self.DocumentArea.setTabStopDistance(27) + if self.is_saved: + self.resetDocumentArea() self.directory = self.default_directory self.file_name = None self.is_saved = False @@ -1373,24 +1412,7 @@ def newFile(self): ) if reply == QMessageBox.Yes: - self.DocumentArea.clear() - self.DocumentArea.setFontFamily(fallbackValues["fontFamily"]) - self.DocumentArea.setFontPointSize(fallbackValues["fontSize"]) - self.DocumentArea.setFontWeight(75 if fallbackValues["bold"] else 50) - self.DocumentArea.setFontItalic(fallbackValues["italic"]) - self.DocumentArea.setFontUnderline(fallbackValues["underline"]) - self.DocumentArea.setAlignment(fallbackValues["contentAlign"]) - self.DocumentArea.setTextColor(QColor(fallbackValues["contentColor"])) - self.DocumentArea.setTextBackgroundColor( - QColor(fallbackValues["contentBackgroundColor"]) - ) - self.DocumentArea.setTabStopDistance(27) - self.directory = self.default_directory - self.file_name = None - self.is_saved = False - self.updateTitle() - else: - pass + self.resetDocumentArea() def openFile(self, file_to_open=None): options = QFileDialog.Options() @@ -1531,27 +1553,43 @@ def viewHelp(self): def bulletList(self): cursor = self.DocumentArea.textCursor() cursor.beginEditBlock() + selected_text = cursor.selectedText() + lines = selected_text.split("\n") char_format = cursor.charFormat() - cursor.removeSelectedText() - cursor.insertList(QTextListFormat.ListDisc) - cursor.insertText(selected_text) - new_cursor = self.DocumentArea.textCursor() - new_cursor.movePosition(QTextCursor.PreviousBlock) - new_cursor.mergeCharFormat(char_format) + + for line in lines: + if line.strip(): + cursor.insertList(QTextListFormat.ListDisc) + cursor.insertText(line) + cursor.insertBlock() + else: + cursor.insertBlock() + + cursor.mergeCharFormat(char_format) + cursor.endEditBlock() def numberedList(self): cursor = self.DocumentArea.textCursor() cursor.beginEditBlock() + selected_text = cursor.selectedText() + + lines = selected_text.split("\n") + char_format = cursor.charFormat() - cursor.removeSelectedText() - cursor.insertList(QTextListFormat.ListDecimal) - cursor.insertText(selected_text) - new_cursor = self.DocumentArea.textCursor() - new_cursor.movePosition(QTextCursor.PreviousBlock) - new_cursor.mergeCharFormat(char_format) + + for line in lines: + if line.strip(): + cursor.insertList(QTextListFormat.ListDecimal) + cursor.insertText(line) + cursor.insertBlock() + else: + cursor.insertBlock() + + cursor.mergeCharFormat(char_format) + cursor.endEditBlock() def contentAlign(self, alignment): @@ -1603,11 +1641,10 @@ def decFont(self): def find(self): self.find_dialog = QInputDialog(self) self.find_dialog.setInputMode(QInputDialog.TextInput) - app_language = lang - self.find_dialog.setLabelText(translations[app_language]["find"]) - self.find_dialog.setWindowTitle(translations[app_language]["find"]) - self.find_dialog.setOkButtonText(translations[app_language]["find"]) - self.find_dialog.setCancelButtonText(translations[app_language]["cancel"]) + self.find_dialog.setLabelText(translations[lang]["find"]) + self.find_dialog.setWindowTitle(translations[lang]["find"]) + self.find_dialog.setOkButtonText(translations[lang]["find"]) + self.find_dialog.setCancelButtonText(translations[lang]["cancel"]) self.find_dialog.textValueSelected.connect(self.findText) self.find_dialog.show() @@ -1639,7 +1676,7 @@ def replaceText(self, text): app.setOrganizationName("berkaygediz") app.setApplicationName("SolidWriting") app.setApplicationDisplayName("SolidWriting 2025.02") - app.setApplicationVersion("1.5.2025.02-1") + app.setApplicationVersion("1.5.2025.02-2") ws = SW_ControlInfo() ws.show() sys.exit(app.exec()) diff --git a/file_version_info.txt b/file_version_info.txt index 43400a2..911a64c 100644 --- a/file_version_info.txt +++ b/file_version_info.txt @@ -31,13 +31,13 @@ VSVersionInfo( StringTable( u'040904B0', [StringStruct(u'CompanyName', u'berkaygediz'), - StringStruct(u'FileDescription', u'Real-time computing and formatting supported word processor.'), - StringStruct(u'FileVersion', u'1.5.2024.11'), + StringStruct(u'FileDescription', u'A supercharged word processor with AI integration, supporting real-time computing and advanced formatting.'), + StringStruct(u'FileVersion', u'1.5.2025.02'), StringStruct(u'InternalName', u'SolidWriting'), - StringStruct(u'LegalCopyright', u'2024 © berkaygediz. All rights reserved.'), + StringStruct(u'LegalCopyright', u'2025 © berkaygediz. All rights reserved.'), StringStruct(u'OriginalFilename', u'SolidWriting.exe'), StringStruct(u'ProductName', u'SolidWriting'), - StringStruct(u'ProductVersion', u'1.5.2024.11')]) + StringStruct(u'ProductVersion', u'1.5.2025.02')]) ]), VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) ] diff --git a/metadata.yml b/metadata.yml index 10f0e79..e001329 100644 --- a/metadata.yml +++ b/metadata.yml @@ -1,6 +1,6 @@ Version: 1.4.2025.02 CompanyName: berkaygediz -FileDescription: Real-time computing and formatting supported word processor. +FileDescription: A supercharged word processor with AI integration, supporting real-time computing and advanced formatting. InternalName: SolidWriting LegalCopyright: 2025 © berkaygediz. All rights reserved. OriginalFilename: SolidWriting.exe diff --git a/requirements.txt b/requirements.txt index 3134a53..3348f6f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -PySide6==6.7.2 -mammoth==1.8.0 +PySide6==6.8.2.1 +mammoth==1.9.0 chardet==5.2.0 -psutil==6.0.0 +psutil==7.0.0 langdetect==1.0.9 -pyinstaller==6.10.0 +pyinstaller==6.12.0 llama-cpp-python==0.3.1 -torch==2.5.1 \ No newline at end of file +torch==2.6.0 \ No newline at end of file diff --git a/version.rc b/version.rc new file mode 100644 index 0000000..b277cfa --- /dev/null +++ b/version.rc @@ -0,0 +1,30 @@ +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1, 5, 2025, 02 +PRODUCTVERSION 1, 5, 2025, 02 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VS_FILEFLAG_RELEASE +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "berkaygediz" + VALUE "FileDescription", "A supercharged word processor with AI integration, supporting real-time computing and advanced formatting." + VALUE "FileVersion", "1.5.2025.02" + VALUE "InternalName", "SolidWriting" + VALUE "LegalCopyright", "Copyright (C) 2025 berkaygediz" + VALUE "OriginalFilename", "SolidWriting.exe" + VALUE "ProductName", "SolidWriting" + VALUE "ProductVersion", "1.5.2025.02" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END