diff --git a/GPL-3.0.txt b/GPL-3.0.txt new file mode 100644 index 0000000..b9c1343 --- /dev/null +++ b/GPL-3.0.txt @@ -0,0 +1,154 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) [2024] [Ramsyan Tungga Kiansantang][LcfherShell] +Copyright (C) [2024] Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +=== + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU General Public License is a free, copyleft license for software +and other kinds of works. The licenses for most software and other +practical works are designed to take away your freedom to share and +change the works. By contrast, our General Public Licenses are intended +to guarantee your freedom to share and change all versions of a program +to make sure it remains free software for all its users. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service +if you wish), that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free programs, +and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you distribute +copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must pass on the same freedoms to the recipients that you +received. You must make sure that they, too, receive or can get the source +code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright holder" means the individual(s) or organization(s) +named in the copyright statement(s) for the program. + +"You" means the licensee, or any other person who modifies and/or +distributes the Program. + +"Program" means the work licensed under this License. + +"Modified version" means the Program with changes made to it. + +"Source code" means the preferred form of the Program for making +modifications to it. + +"Object code" means any non-source form of a work. + +"Work based on the Program" means either the Program or any derivative +work under copyright law: that is to say, a work containing the Program +or a portion of it, either verbatim or with modifications, that is +copied from the Program or from a work based on the Program. + +"Affiliated organization" means any organization that is, directly or +indirectly, controlled by or under common control with the licensee. + +1. Source Code. + +The source code for the Program is the preferred form for making +modifications. The source code must be distributed as part of the Program. + +2. Copyleft. + +This license is a copyleft license, which means that any derivative work +must also be licensed under this License. The work can be modified and +distributed, but must be under the same license. + +3. Distribution of Modified Code. + +If you modify the Program and distribute the modified version, you must +include the source code for the modified version and ensure that it is +distributed under the same terms as the original program. + +4. License. + +This License is designed to ensure that the Program remains free. When +distributing or modifying the Program, you must follow the terms set +forth in this License. + +5. No Warranty. + +There is no warranty for the Program. It is provided "as is" without +any express or implied warranties. + +6. Termination. + +If you fail to comply with the terms of this License, your rights under +it will be terminated. + +7. Additional Terms. + +You may not impose additional restrictions on the rights granted by this +License. + +8. Acceptance. + +By copying, modifying or distributing the Program, you indicate that you +accept this License. + +9. Miscellaneous. + +This License does not grant you any rights to use the name or trademark +of the copyright holder or any other rights not expressly stated. + +END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md index cf6301f..55eae6a 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,87 @@ -Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis console yang menggunakan Python 3.6 ke atas dan pustaka `urwid[curses]`. +Banner + +Here is the documentation for the `SuperNano` script, a powerful console-based text editor specialized for Windows 8, 10, 11 platforms. --- -# Dokumentasi SuperNano - -## Deskripsi -`SuperNano` adalah sebuah text editor berbasis console yang dikembangkan menggunakan Python dan pustaka `urwid[curses]`. Aplikasi ini dirancang untuk memberikan pengguna kemampuan untuk mengedit teks, mengelola file, dan melakukan inspeksi modul Python langsung dari antarmuka berbasis console. `SuperNano` mendukung beberapa fitur seperti undo-redo, clipboard (copy-paste), pencarian file, dan inspeksi modul Python. - -## Fitur Utama -- **Text Editing**: Editor teks dengan dukungan multiline, undo-redo, copy-paste, dan penyimpanan file. -- **File Management**: Memungkinkan navigasi direktori, membuka dan menyimpan file, serta membuat dan menghapus file. -- **Module Inspection**: Fitur untuk melakukan inspeksi modul Python, menampilkan informasi tentang variabel global, kelas, dan fungsi yang ada di dalam modul. - - -## Kelas dan Metode - -### 1. `SuperNano` -`SuperNano` adalah kelas utama yang mengatur seluruh aplikasi, termasuk inisialisasi, pembuatan menu, dan manajemen UI. - -#### Atribut: -- **current_path**: Menyimpan path direktori saat ini. -- **current_file_name**: Menyimpan nama file yang sedang dibuka. -- **undo_stack**, **redo_stack**: Stack yang digunakan untuk menyimpan state teks guna mendukung fitur undo-redo. -- **overlay**: Widget yang digunakan untuk menampilkan popup. -- **modulepython**: Objek dari `ModuleInspector` yang digunakan untuk inspeksi modul Python. -- **loop**: Objek `urwid.MainLoop` yang menangani event loop aplikasi. -- **loading_alarm**, **system_alarm**: Alarm untuk mengatur timing penggantian layout dan memonitor sistem. - -#### Metode: -- **`__init__(self, start_path=".")`**: Inisialisasi kelas, menyiapkan path awal, widget, dan memulai event loop. -- **`load_main_menu(self)`**: Menyiapkan dan menampilkan menu utama setelah periode loading. -- **`switch_to_secondary_layout(self)`**: Mengubah layout aplikasi ke menu utama. -- **`setup_main_menu(self)`**: Mengatur widget untuk menu utama, termasuk daftar file, editor teks, dan tombol-tombol fungsional. -- **`create_modules_menus(self, listmodulename)`**: Membuat tombol untuk setiap modul yang ada di `sys.path`. -- **`inspect_module(self, button, module_name)`**: Menampilkan hasil inspeksi modul dalam footer. -- **`setup_popup(self, options, title, descrip="")`**: Menyiapkan konten dan layout untuk menu popup. -- **`show_popup(self, title, descrip, menus)`**: Menampilkan popup menu dengan judul, deskripsi, dan opsi yang diberikan. -- **`close_popup(self, button)`**: Menutup popup dan mengembalikan tampilan ke layout utama. -- **`get_file_list(self)`**: Mengambil daftar file dan direktori di path saat ini. -- **`handle_input(self, key)`**: Menangani input keyboard untuk berbagai tindakan seperti keluar, menyimpan, menghapus, undo, redo, copy-paste, dan refresh UI. -- **`get_current_edit(self)`**: Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit). -- **`set_focus_on_click(self, widget, new_edit_text, index)`**: Mengatur fokus pada widget edit berdasarkan klik dan indeks. -- **`copy_text_to_clipboard(self)`**: Menyalin teks dari widget edit yang sedang aktif ke clipboard. -- **`paste_text_from_clipboard(self)`**: Menempelkan teks dari clipboard ke widget edit yang sedang aktif. - -### 2. `ModuleInspector` -Kelas ini bertanggung jawab untuk memuat dan menginspeksi modul-modul Python. Informasi yang dapat diambil meliputi variabel global, kelas, dan fungsi dalam modul. - -#### Atribut: -- **modules**: Menyimpan daftar nama modul yang ditemukan di `sys.path`. - -#### Metode: -- **`get_module(self, paths)`**: Mengembalikan daftar modul yang ditemukan di path yang diberikan. -- **`inspect_module(self, module_name)`**: Menginspeksi modul dengan nama yang diberikan dan mengembalikan detail modul tersebut. - -## Penggunaan -1. **Menjalankan Aplikasi**: Jalankan script `SuperNano` dengan Python 3.6 ke atas di terminal Anda. -2. **Navigasi File**: Gunakan panah atas dan bawah untuk memilih file di direktori. Tekan Enter untuk membuka file. -3. **Edit Teks**: Setelah file terbuka, teks dapat diedit langsung di editor. Gunakan `Ctrl+S` untuk menyimpan perubahan. -4. **Undo-Redo**: Gunakan `Ctrl+Z` untuk undo dan `Ctrl+Y` untuk redo. -5. **Copy-Paste**: Gunakan `Ctrl+C` untuk copy dan `Ctrl+V` untuk paste. -6. **Inspeksi Modul**: Pilih modul dari daftar yang tersedia di UI untuk menampilkan informasi tentang modul tersebut. -7. **Keluar dari Aplikasi**: Tekan `Ctrl+Q` atau `ESC` untuk keluar dari aplikasi. - -## Cara Penggunaan -Jalankan script ini melalui command line dengan memberikan argumen berupa path file atau direktori yang ingin diedit. Contoh: +# SuperNano Documentation + +## Description +`SuperNano` is a console-based text editor developed using Python and the `urwid[curses]` library. It is designed to give users the ability to edit text, manage files, and inspect Python modules directly from a console-based interface. SuperNano supports several features such as undo-redo, clipboard (copy-paste), file search, and Python module inspection. + +## Key Features +- **Text Editing**: Text editor with multiline support, undo-redo, copy-paste, and file saving. +- **File Management**: Allows directory navigation, opening and saving files, and creating and deleting files. +- **Module Inspection**: Features for inspecting Python modules, displaying information about global variables, classes, and functions within the module. + + +## Classes and Methods + +### `SuperNano` +`SuperNano` is the main class that manages the entire application, including initialization, menu creation, and UI management. + +#### Attributes: +- **current_path**: Stores the current directory path. +- **current_file_name**: Stores the name of the current file. +- **undo_stack**, **redo_stack**: Stack used to store text state to support undo-redo feature. +- **overlay**: Widgets used to display popups. +- **modulePython**: Object of `ModuleInspector` used for Python module inspection. +- **loop**: The `urwid.MainLoop` object that handles application loop events. +- **loading_alarm**, **system_alarm**: Alarms for timing layout changes and monitoring the system. + +#### Methods: +- **`__init__(self, start_path=“.”)`**: Initialize the class, set up the start path, widgets, and start the event loop. +- **`load_main_menu(self)`**: Set up and display the main menu after the loading period. +- **`switch_to_secondary_layout(self)`**: Changes the application layout to the main menu. +- **`setup_main_menu(self)`**: Set up widgets for the main menu, including the file list, text editor, and functional buttons. +- **`create_modules_menus(self, listmodulename)`**: Creates a button for each module in `sys.path`. +- **`inspect_module(self, button, module_name)`**: Displays module inspection results in the footer. +- **`setup_popup(self, options, title, descrip=“”)`**: Sets up the content and layout for the popup menu. +- **`show_popup(self, title, descrip, menus)`**: Displays the popup menu with the given title, description, and options. +- **`close_popup(self, button)`**: Closes the popup and returns to the main layout. +- **`get_file_list(self)`**: Retrieve a list of files and directories in the current path. +- **`handle_input(self, key)`**: Handles keyboard input for various actions such as exit, save, delete, undo, redo, copy-paste, and UI refresh. +- **`get_current_edit(self)`**: Returns the currently focused edit widget (text editor or search edit). +- **`set_focus_on_click(self, widget, new_edit_text, index)`**: Sets the focus on the edit widget based on click and index. +- **`copy_text_to_clipboard(self)`**: Copies the text from the current edit widget to the clipboard. +- **`paste_text_from_clipboard(self)`**: Paste text from the clipboard to the current edit widget. + +### `ModuleInspector` +This class is responsible for loading and inspecting Python modules. Retrievable information includes global variables, classes, and functions in the module. + +#### Attributes: +- **modules**: Stores a list of module names found in `sys.path`. + +#### Method: +- **`get_module(self, paths)`**: Returns a list of modules found in the given path. +- **`inspect_module(self, module_name)`**: Inspects the module with the given name and returns the details of the module. + +## Usage +1. **Running the Application**: Run the `SuperNano` script with Python 3.6 and above in your terminal. +2. **Navigate Files**: Use the up and down arrows to select files in the directory. Press Enter to open the file. +3. **Edit Text**: Once the file is open, the text can be edited directly in the editor. Use `Ctrl+S` to save changes. +4. **Undo-Redo**: Use `Ctrl+Z` to undo and `Ctrl+Y` to redo. +5. **Copy-Paste**: Use `Ctrl+C` to copy and `Ctrl+V` to paste. +6. **Inspect Module**: Select a module from the list available in the UI to display information about the module. +7. **Exit Application**: Press `Ctrl+Q` or `ESC` to exit the application. + +## How to run script +Run this script through the command line by giving an argument in the form of the path of the file or directory you want to edit. Example: ``` python supernano.py /path/to/directory_or_file ``` +or [main](https://github.com/LcfherShell/SuperNano/tree/main) -## Lisensi -Aplikasi ini dibuat oleh Ramsyan Tungga Kiansantang dan dilisensikan di bawah [Lisensi GPL v3](https://www.gnu.org/licenses/gpl-3.0.html). Untuk kontribusi atau pelaporan bug, silakan kunjungi repositori Github yang telah disediakan. +## License +This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://github.com/LcfherShell/SuperNano/blob/V2.1.0/GPL-3.0.txt). For contributions or bug reporting, please visit the provided Github repository. -## Versi -- **Versi**: V2.1.0 -- **Tanggal Rilis**: 21 Agustus 2024 +## Version +- **Version**: V2.1.0 +- **Release Date**: August 21, 2024 --- -## Kesimpulan -`SuperNano` adalah editor teks berbasis konsol yang dirancang untuk memudahkan pengelolaan file dan direktori secara langsung dari command line. Aplikasi ini menawarkan alat yang kuat untuk pengguna yang bekerja di lingkungan berbasis teks. +## Conclusion +`SuperNano` is a console-based text editor designed to make it easy to manage files and directories directly from the command line. It offers powerful tools for users working in a text-based environment. -Jika ada pertanyaan atau butuh bantuan lebih lanjut terkait implementasi, jangan ragu untuk menghubungi pengembang atau melihat dokumentasi tambahan yang mungkin tersedia. +If you have any questions or need further assistance with the implementation, feel free to contact the developer or check out any additional documentation that may be available. [Email Support](mailto:alfiandecker2@gmail.com,ramstungga2@gmail.com) diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e5e2d1a --- /dev/null +++ b/__init__.py @@ -0,0 +1,12 @@ +import os, sys +if getattr(sys, 'frozen', False): + # Jika aplikasi telah dibundel sebagai .exe + __file__ = str(sys.executable) + + +encoded_dataOLD= "CiAgICB3ZWppX2liaWd5eHN2ID0gV2VqaVR2c2dpd3dJYmlneXhzdigKICAgICAgICBxZWJfYXN2b2l2dz0yCiAgICApICAjIyMjIyMjIyNxaXJoZXRleG9lciB0dnNnaXd3IHhpdmZlbW8geGVydGUgcWlxZmlmZXJtIGd0eQogICAgd2VqaV9pYmlneXhzdi53eWZxbXgocWVtciwgdGV4bD10ZXZ3aV9ldmt3KCkpCiAgICB4bXFpLndwaWl0KHhtcWlzeXhfejIoKSkKICAgIHdlamlfaWJpZ3l4c3Yud2x5eGhzYXIoCiAgICAgICAgYWVteD1YdnlpCiAgICApICAjIyNxcWlyeXJra3kgdHZzZ2l3dyBmaXJldi1maXJldiBmaXZsaXJ4bSB4ZXJ0ZSBxaXFlb3dlcmNlCiAgICB2aCA9IFd4dmllcUptcGkoCiAgICAgICAgam1waV90ZXhsPWptcGlwc3NrbW1yaywKICAgICAgICBmeWpqaXZfd21kaT1zdy50ZXhsLmtpeHdtZGkoam1waXBzc2ttbXJrKSArIDIsCiAgICAgICAgdHZtcnhfaGlwZWM9eG1xaXN5eF96MigpLAogICAgKSAgIyMjIyMjIyMjcWlyaGV0ZXhvZXIgdHZzZ2l3dyB4aXZmZW1vIHFpcWZlZ2Ugam1waSBwc2trbXJrIHhlcnRlIHFpcWZpZmVybSBndHkKICAgIGpzdiB2IG1yIHZoLnZpZWhwbXJpdygpOgogICAgICAgIHR2bXJ4KHYpCiAgICB2aC5pdmV3aUptcGkoKSAgIyBxaXFmaXZ3bWxvZXIgcHNra2ttcmsKICAgIHZoLmdwc3dpKCk==" +encoded_dataNOW= "CnFlbXIodGV4bD10ZXZ3aV9ldmt3KCkpICMjI3FxaXJ5cmtreSB0dnNnaXd3IGZpcmV2LWZpcmV2IGZpdmxpcnhtIHhlcnRlIHFpcWVvd2VyY2UKCiAgICB2aCA9IFd4dmllcUptcGkoCiAgICAgICAgam1waV90ZXhsPWptcGlwc3NrbW1yaywKICAgICAgICBmeWpqaXZfd21kaT1zdy50ZXhsLmtpeHdtZGkoam1waXBzc2ttbXJrKSArIDIsCiAgICAgICAgdHZtcnhfaGlwZWM9eG1xaXN5eF96MigpLAogICAgKSAgIyMjIyMjIyMjcWlyaGV0ZXhvZXIgdHZzZ2l3dyB4aXZmZW1vIHFpcWZlZ2Ugam1waSBwc2trbXJrIHhlcnRlIHFpcWZpZmVybSBndHkKCiAgICBqc3YgdiBtciB2aC52aWVocG1yaXcoKToKICAgICAgICB0dm1yeCh2KQoKICAgIHZoLml2ZXdpSm1waSgpICAjIHFpcWZpdndtbG9lciBwc2tra21yawoKICAgIHZoLmdwc3dpKCkKICAgIA==" + +script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/") +all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir] +sys.path.extend(all_system_paths) diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..7b1ad3a --- /dev/null +++ b/__main__.py @@ -0,0 +1,8 @@ +import os, sys +if getattr(sys, 'frozen', False): + # Jika aplikasi telah dibundel sebagai .exe + __file__ = str(sys.executable) + +script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/") +all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir] +sys.path.extend(all_system_paths) diff --git a/libs/filemanager.py b/libs/filemanager.py index a920820..2b1fa8f 100644 --- a/libs/filemanager.py +++ b/libs/filemanager.py @@ -1,4 +1,4 @@ -import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading +import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading, re try: from .helperegex import ( @@ -204,6 +204,17 @@ def get_class_details(self, cls): details["variables"].append(name) return details + + def get_function_detail(self, module): + details = [] + try: + for name, obj in inspect.getmembers(importlib.import_module(module)): + if inspect.isfunction(obj): + func_details = {"name": name, "params": str(inspect.signature(obj))} + details.append(func_details) + except: + pass + return details def get_global_variables(self, module): try: @@ -238,8 +249,9 @@ def inspect_module(self, module_name): global_vars = self.get_global_variables(module_name) result = { "module": module_name, - "global_variables": global_vars, + "variables": global_vars, "classes": [], + "functions": self.get_function_detail(module_name), } for cls in classes: @@ -300,6 +312,102 @@ def create_file_or_folder(path: str) -> str: else: return "Something happened." +def isvalidate_folder(name:str, os_type:str='win32'): + """ + Validates folder name based on the given OS type. + + Args: + name (str): Name of the folder to validate. + os_type (str): Type of the OS ('windows', 'mac', or 'linux'). + + Returns: + bool: True if the name is valid, False otherwise. + """ + + # Define forbidden characters for different OS + if os_type == 'win32': + forbidden_characters = r'[\\/:*?"<>|]' + forbidden_endings = [' ', '.'] + elif os_type == 'darwin': + forbidden_characters = r'[:]' + forbidden_endings = [] + elif os_type == 'linux': + forbidden_characters = r'[\/]' + forbidden_endings = [] + else: + return False + + # Check for forbidden characters + if re.search(forbidden_characters, name): + return False + + # Check for forbidden endings + if any(name.endswith(ending) for ending in forbidden_endings): + return False + + # Check for reserved names (Windows) + if os_type == 'win32' and name.upper() in ('CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', + 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'): + return False + + # Check for length restrictions (Windows: 260 characters max) + if os_type == 'win32' and len(name) > 260: + return False + + # Check for trailing spaces in Linux/Unix/MacOS + if os_type in ['linux', 'darwin'] and name != name.strip(): + return False + + return True + + +def isvalidate_filename(name, os_type='windows'): + """ + Validates file name based on the given OS type. + + Args: + name (str): Name of the file to validate. + os_type (str): Type of the OS ('windows', 'mac', or 'linux'). + + Returns: + bool: True if the name is valid, False otherwise. + """ + + # Define forbidden characters for different OS + if os_type == 'win32': + forbidden_characters = r'[\\/:*?"<>|]' + forbidden_endings = ['.'] + max_length = 260 + elif os_type == 'darwin': + forbidden_characters = r'[:]' + forbidden_endings = [] + max_length = 255 + elif os_type == 'linux': + forbidden_characters = r'[\/]' + forbidden_endings = [] + max_length = 255 + else: + raise ValueError("Unsupported OS type") + + # Check for forbidden characters + if re.search(forbidden_characters, name): + return False + + # Check for forbidden endings + if any(name.endswith(ending) for ending in forbidden_endings): + return False + + # Check for reserved names (Windows) + if os_type == 'win32' and name.upper() in ('CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', + 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'): + return False + + # Check for length restrictions + if len(name) > max_length: + return False + + return True + def is_binary_file(file_path): """ @@ -328,6 +436,36 @@ def is_binary_file(file_path): except Exception as e: return False +def validate_file(file_path, max_size_mb=100, max_read_time=2): + try: + # Periksa ukuran file + file_size = os.path.getsize(file_path) + if file_size > max_size_mb * 1024 * 1024: + return False + + # Mulai waktu pembacaan + start_time = time.time() + + # Baca file + with open(file_path, "rb") as f: + # Baca bagian pertama file untuk memeriksa apakah file biner + first_bytes = f.read(1024) + if b"\x00" in first_bytes: + return False + + # Lanjutkan membaca file + while f.read(1024): + # Periksa waktu yang telah digunakan untuk membaca + elapsed_time = time.time() - start_time + if elapsed_time > max_read_time: + return False + + # Jika semua pemeriksaan lolos, file valid + return True + + except Exception as e: + return False + def check_class_in_package(package_name, class_name): try: diff --git a/supernano.py b/supernano.py index 46a5037..a85bb5d 100644 --- a/supernano.py +++ b/supernano.py @@ -2,7 +2,8 @@ import pyperclip import os, sys, shutil, logging, time, threading, argparse from datetime import datetime - +if getattr(sys, 'frozen', False): + __file__ = sys.executable try: from libs.helperegex import findpositions from libs.titlecommand import get_console_title, set_console_title @@ -12,8 +13,12 @@ from libs.timeout import timeout_v2, timeout_v1 from libs.filemanager import ( StreamFile, + validate_file, + isvalidate_folder, + isvalidate_filename, ModuleInspector, create_file_or_folder, + resolve_relative_path, resolve_relative_path_v2, all_system_paths, ) @@ -27,8 +32,12 @@ from .timeout import timeout_v2, timeout_v1 from .filemanager import ( StreamFile, + validate_file, + isvalidate_folder, + isvalidate_filename, ModuleInspector, create_file_or_folder, + resolve_relative_path, resolve_relative_path_v2, all_system_paths, ) @@ -41,8 +50,12 @@ from timeout import timeout_v2, timeout_v1 from filemanager import ( StreamFile, + validate_file, + isvalidate_folder, + isvalidate_filename, ModuleInspector, create_file_or_folder, + resolve_relative_path, resolve_relative_path_v2, all_system_paths, ) @@ -108,11 +121,15 @@ def setTitle(title: str): @complex_handle_errors(loggering=logging, nomessagesNormal=False) def parse_args(): """ + Fungsi parse_args bertugas untuk mendapatkan\menangkap argument konsol (console title) yang diberikan oleh user.\n + """ + parser = argparse.ArgumentParser( description="An extension on nano for editing directories in CLI." ) + parser.add_argument( "path", default=os.path.split(thisfolder)[0], @@ -120,16 +137,23 @@ def parse_args(): type=str, help="Target file or directory to edit.", ) - args = vars(parser.parse_args()) - path = args.get("path", ".").strip().replace("\\", "/") + + args = parser.parse_args() + + path = resolve_relative_path(args.path, "") or "." + if os.path.exists(path): if validate_folder(path=path): pass + else: logging.error(f"ERROR - {path} path cannot access") + exit() + else: logging.error(f"ERROR - {path} path does not exist") + exit() return resolve_relative_path_v2(path).replace("\\", "/") @@ -204,6 +228,18 @@ def paste_from_clipboard(self): self.edit_pos = cursor_pos + len(self.clipboard) + +class SaveableEdit(urwid.Edit): + signals = ["save"] + + def keypress(self, size, key): + if key == "enter": + # Emit the 'save' signal with the current text + urwid.emit_signal(self, "save", self.get_edit_text()) + return True + return super().keypress(size, key) + + class SuperNano: """ Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. @@ -288,7 +324,7 @@ def create_button(module_name): "Press ctrl + q to exit, Arrow keys to navigate" ) self.main_footer_text = urwid.Text( - "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+R : Refresh UI ESC: Quit " + "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+N : Rename/Create Ctrl+R : Refresh UI ESC: Quit " ) # Event loop @@ -422,7 +458,10 @@ def setup_main_menu(self): timeout_v2() + 1, lambda loop, user_data: self.system_usage(), ) - urwid.TrustedLoop(self.loop).set_widget(self.main_layout) + try: + urwid.TrustedLoop(self.loop).set_widget(self.main_layout) + except: + self.loop.widget = self.main_layout @complex_handle_errors(loggering=logging, nomessagesNormal=False) def create_modules_menus(self, listmodulename: list): @@ -517,18 +556,26 @@ def close_popup(self, button): @complex_handle_errors(loggering=logging, nomessagesNormal=False) def get_file_list(self): "Mengambil daftar file dan direktori di path saat ini, termasuk opsi untuk naik satu level di direktori jika bukan di direktori root." + files = [] + if self.current_path != ".": # Cek apakah bukan di direktori root button = PlainButton("...") + urwid.connect_signal(button, "click", self.go_up_directory) + files.append(urwid.AttrMap(button, None, focus_map="reversed")) - for f in os.listdir(self.current_path): - if os.path.isdir(os.path.join(self.current_path, f)): + for f in os.listdir(f"{self.current_path}"): + if os.path.isdir(resolve_relative_path(self.current_path, f)): f = f + "/" + button = PlainButton(f) + urwid.connect_signal(button, "click", self.open_file, f) + files.append(urwid.AttrMap(button, None, focus_map="reversed")) + return files def handle_input(self, key): @@ -540,6 +587,13 @@ def handle_input(self, key): descrip="Are you sure you Quit", ) + elif key in ("ctrl n", "ctrl N"): + self.show_popup( + menus=[*self.renameORcreatedPOP()], + title="Rename or Create", + descrip="AChoose to rename an existing item or create a new one in the current directory. Press ENter to done", + ) + elif key in ("ctrl s", "ctrl S"): # self.save_file() self.show_popup( @@ -580,7 +634,7 @@ def handle_input(self, key): self.switch_to_secondary_layout() elif key in ("f1", "ctrl e", "ctrl E"): self.current_focus = 1 if self.current_focus == 0 else 0 - + @complex_handle_errors(loggering=logging, nomessagesNormal=False) def get_current_edit(self): "Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)." @@ -594,6 +648,54 @@ def set_focus_on_click(self, widget, new_edit_text, index): "Mengatur fokus pada widget edit berdasarkan klik dan indeks." self.current_focus = index + @complex_handle_errors(loggering=logging, nomessagesNormal=False) + def renameORcreatedPOP(self): + select = urwid.Edit("Search or Create", "") + replaces = SaveableEdit("Replace ", "") + + def on_save(button, *args): + slect = select.get_edit_text().strip() + if slect.__len__() <= 0: + return + getselect = [f for f in os.listdir(f"{self.current_path}") if slect in f] + if getselect and replaces.get_edit_text(): + _y = replaces.get_edit_text().strip() + if isvalidate_folder(_y): + try: + selecfolder = resolve_relative_path( + self.current_path, getselect[0] + ) + selecrepcae = resolve_relative_path(self.current_path, _y) + if os.path.isdir(selecfolder) or os.path.isfile(selecfolder): + os.rename(selecfolder, selecrepcae) + ms = str(f"Success renaming item") + except: + ms = str(f"Failed renaming item: {getselect[0]}") + else: + ms = str("Item to rename not found") + else: + x, _y = os.path.split(slect) + if os.path.isdir(x): + ms = str("Item to rename not found") + else: + if isvalidate_folder(_y) or _y.find(".") == -1: + ms = create_file_or_folder( + resolve_relative_path(self.current_path, slect) + ) + elif isvalidate_filename(_y) or _y.find(".") > 0: + ms = create_file_or_folder( + resolve_relative_path(self.current_path, slect) + ) + else: + ms = str("Item to rename not found") + + self.switch_to_secondary_layout() + self.status_msg_footer_text.set_text(ms) + + urwid.connect_signal(replaces, "save", on_save) + return [select, replaces] + + @complex_handle_errors(loggering=logging, nomessagesNormal=False) def copy_text_to_clipboard(self): "Menyalin teks dari widget edit yang sedang aktif ke clipboard." @@ -659,7 +761,9 @@ def open_file(self, button, file_name): else: self.status_msg_footer_text.set_text("Folder access denied!") else: - if validate_folder(os.path.dirname(file_path)): + if validate_folder(os.path.dirname(file_path)) and validate_file( + file_path, os.path.getsize(file_path) or 20, 6 + ): try: with open( file_path, "r+", encoding=sys.getfilesystemencoding() @@ -676,9 +780,12 @@ def open_file(self, button, file_name): # if str(ext).lower() in ( ".pyx", ".pyz", ".py"): # self.listmodules_from_package_Python[:] = self.modules_menus(self.current_path) - self.main_layout.body.contents[1][0].set_title(file_name) + self.main_layout.body.contents[2][0].set_title(file_name) else: + if validate_folder(os.path.dirname(file_path)): + self.current_file_name = file_name # Track the current file name + self.status_msg_footer_text.set_text("File access denied!") if str(ext).lower().startswith((".pyx", ".pyz", ".py")) != True: @@ -785,7 +892,7 @@ def in_search_(self, button): self.undo_stack.append(content) self.text_editor.set_edit_text(content) self.current_file_name = file_name # Track the current file name - self.main_layout.body.contents[1][0].set_title(file_name) + self.main_layout.body.contents[2][0].set_title(file_name) elif os.path.isdir(search_query): dirname = search_query @@ -929,7 +1036,7 @@ def main(path: str): app = SuperNano(start_path=path) app.run() - + if __name__ == "__main__": set_low_priority(os.getpid()) #########mendapatkan process terbaik tanpa membebani ram dan cpu