```lua
-- Dependencies
local imgui = require("imgui")
local encoding = require("encoding")
local json = require("dkjson")

-- Configure Encoding
encoding.default = "CP1251"
local u8 = encoding.UTF8

-- Constants
local CHAT_COMMAND = "notepad"
local SAVE_FILE = "notes.json"

-- UI Configuration
local WINDOW_WIDTH, WINDOW_HEIGHT = 600, 700
local POPUP_WIDTH = 300
local NOTE_LIST_HEIGHT = 400

-- State Variables
local state = {
    windowVisible = imgui.ImBool(false),
    inputTitle = imgui.ImBuffer(256),
    inputTags = imgui.ImBuffer(256),
    inputText = imgui.ImBuffer(1024),
    searchQuery = imgui.ImBuffer(256),
    notes = {},
    popups = {
        import = { visible = imgui.ImBool(false), path = imgui.ImBuffer(256) },
        export = { visible = imgui.ImBool(false), path = imgui.ImBuffer(256) },
        delete = { visible = imgui.ImBool(false), index = 0 },
    }
}

-- Utility Functions

--- Checks if a file exists at the given path.
---@param filepath string
---@return boolean
local function fileExists(filepath)
    local file = io.open(filepath, "r")
    if file then
        file:close()
        return true
    end
    return false
end

--- Reads the entire content of a file.
---@param filepath string
---@return string|nil, string|nil
local function readFile(filepath)
    local file, err = io.open(filepath, "r")
    if not file then
        return nil, err
    end
    local content = file:read("*a")
    file:close()
    return content, nil
end

--- Writes content to a file.
---@param filepath string
---@param content string
---@return boolean, string|nil
local function writeFile(filepath, content)
    local file, err = io.open(filepath, "w")
    if not file then
        return false, err
    end
    file:write(content)
    file:close()
    return true, nil
end

--- Displays a notification to the user.
---@param message string
---@param color integer
local function showNotification(message, color)
    color = color or 0x00FF00
    sampAddChatMessage(u8("[Notepad] ") .. message, color)
end

--- Loads notes from the save file.
local function loadNotes()
    if not fileExists(SAVE_FILE) then
        return
    end
    local content, err = readFile(SAVE_FILE)
    if not content then
        showNotification("Не удалось открыть файл заметок: " .. (err or "Неизвестная ошибка"), 0xFF0000)
        return
    end
    local decoded, _, decode_err = json.decode(content, 1, nil)
    if not decoded or type(decoded) ~= "table" then
        showNotification("Ошибка разбора файла заметок: " .. (decode_err or "Неизвестная ошибка"), 0xFF0000)
        return
    end
    state.notes = decoded
end

--- Saves notes to the save file.
local function saveNotes()
    local encoded = json.encode(state.notes, { indent = true })
    local success, err = writeFile(SAVE_FILE, encoded)
    if not success then
        showNotification("Не удалось сохранить заметки: " .. (err or "Неизвестная ошибка"), 0xFF0000)
    else
        showNotification("Заметки успешно сохранены.", 0x00FF00)
    end
end

--- Imports notes from a specified file.
---@param filepath string
local function importNotes(filepath)
    if not fileExists(filepath) then
        showNotification("Файл импорта не найден: " .. filepath, 0xFF0000)
        return
    end

    local content, _ = readFile(filepath)
    if not content then
        showNotification("Не удалось открыть файл импорта: " .. filepath, 0xFF0000)
        return
    end

    local decoded, _, decode_err = json.decode(content, 1, nil)
    if not decoded or type(decoded) ~= "table" then
        showNotification("Ошибка разбора файла импорта: " .. (decode_err or "Неизвестная ошибка"), 0xFF0000)
        return
    end

    state.notes = decoded
    saveNotes()
    showNotification("Заметки успешно импортированы из " .. filepath, 0x00FF00)
end

--- Exports notes to a specified file.
---@param filepath string
local function exportNotes(filepath)
    local encoded = json.encode(state.notes, { indent = true })
    local success, err = writeFile(filepath, encoded)
    if success then
        showNotification("Заметки успешно экспортированы в " .. filepath, 0x00FF00)
    else
        showNotification("Не удалось создать файл экспорта: " .. (err or filepath), 0xFF0000)
    end
end

--- Adds a new note based on user input.
local function addNote()
    local title = u8(state.inputTitle.v):match("^%s*(.-)%s*$") -- Преобразование в UTF-8
    local tags = u8(state.inputTags.v):match("^%s*(.-)%s*$") -- Преобразование в UTF-8
    local content = u8(state.inputText.v):match("^%s*(.-)%s*$") -- Преобразование в UTF-8

    if title == "" or content == "" then
        showNotification("Пожалуйста, заполните все поля.", 0xFFA500)
        return
    end

    table.insert(state.notes, {
        title = title,
        tags = tags,
        content = content,
        createdAt = os.date("%Y-%m-%d %H:%M:%S")
    })

    -- Очистка полей ввода
    state.inputTitle.v = ""
    state.inputTags.v = ""
    state.inputText.v = ""

    saveNotes()
    showNotification("Заметка добавлена.", 0x00FF00)
end

--- Edits an existing note.
---@param index integer
local function editNote(index)
    local note = state.notes[index]
    if not note then
        showNotification("Заметка не найдена.", 0xFF0000)
        return
    end

    state.inputTitle.v = u8(note.title)
    state.inputTags.v = u8(note.tags)
    state.inputText.v = u8(note.content)

    table.remove(state.notes, index)
    saveNotes()
    showNotification("Заметку можно отредактировать и сохранить заново.", 0xFFA500)
end

--- Prompts the user to delete a note.
---@param index integer
local function requestDeleteNote(index)
    state.popups.delete.index = index
    state.popups.delete.visible.v = true
end

--- Deletes a note at the specified index.
---@param index integer
local function deleteNote(index)
    if not state.notes[index] then
        showNotification("Заметка не найдена.", 0xFF0000)
        return
    end

    table.remove(state.notes, index)
    saveNotes()
    showNotification("Заметка удалена.", 0xFFA500)
end

--- Clears all notes after confirmation.
local function clearAllNotes()
    -- Optional: Add confirmation dialog before clearing all notes
    state.notes = {}
    saveNotes()
    showNotification("Все заметки очищены.", 0xFFA500)
end

-- Popup Handlers

--- Handles generic pop-ups with dynamic content.
---@param title string
---@param visible imgui.ImBool
---@param contentFunction function
local function handlePopup(title, visible, contentFunction)
    if visible.v then
        imgui.OpenPopup(title)
    end

    if imgui.BeginPopupModal(title, visible, imgui.WindowFlags.AlwaysAutoResize) then
        contentFunction()
        imgui.EndPopup()
    end
end

--- Handles the import pop-up.
local function confirmImport()
    handlePopup("Импорт заметок", state.popups.import.visible, function()
        imgui.Text(u8("Введите путь к файлу импорта:"))
        imgui.InputText(u8("Путь к файлу"), state.popups.import.path, imgui.ImVec2(POPUP_WIDTH, 0))
        imgui.Separator()

        if imgui.Button(u8("Импортировать"), imgui.ImVec2(120, 0)) then
            local path = state.popups.import.path.v
            if path ~= "" then
                importNotes(path)
                state.popups.import.visible.v = false
                state.popups.import.path.v = ""
            else
                showNotification("Путь к файлу импорта не может быть пустым.", 0xFFA500)
            end
        end

        imgui.SameLine()
        if imgui.Button(u8("Отмена"), imgui.ImVec2(120, 0)) then
            state.popups.import.visible.v = false
            state.popups.import.path.v = ""
        end
    end)
end

--- Handles the export pop-up.
local function confirmExport()
    handlePopup("Экспорт заметок", state.popups.export.visible, function()
        imgui.Text(u8("Введите путь к файлу экспорта:"))
        imgui.InputText(u8("Путь к файлу"), state.popups.export.path, imgui.ImVec2(POPUP_WIDTH, 0))
        imgui.Separator()

        if imgui.Button(u8("Экспортировать"), imgui.ImVec2(120, 0)) then
            local path = state.popups.export.path.v
            if path ~= "" then
                exportNotes(path)
                state.popups.export.visible.v = false
                state.popups.export.path.v = ""
            else
                showNotification("Путь к файлу экспорта не может быть пустым.", 0xFFA500)
            end
        end

        imgui.SameLine()
        if imgui.Button(u8("Отмена"), imgui.ImVec2(120, 0)) then
            state.popups.export.visible.v = false
            state.popups.export.path.v = ""
        end
    end)
end

--- Handles the delete confirmation pop-up.
local function confirmDelete()
    handlePopup("Подтверждение удаления", state.popups.delete.visible, function()
        imgui.Text(u8("Вы уверены, что хотите удалить эту заметку?"))
        imgui.Separator()

        if imgui.Button(u8("Да"), imgui.ImVec2(120, 0)) then
            deleteNote(state.popups.delete.index)
            state.popups.delete.visible.v = false
        end

        imgui.SameLine()
        if imgui.Button(u8("Нет"), imgui.ImVec2(120, 0)) then
            state.popups.delete.visible.v = false
        end
    end)
end

--- Displays all active pop-ups.
local function showAllPopups()
    confirmImport()
    confirmExport()
    confirmDelete()
end

-- Note Rendering

--- Renders the list of notes with search functionality.
local function drawNotes()
    imgui.Text(u8("Заметки:"))
    imgui.Separator()

    if #state.notes == 0 then
        imgui.Text(u8("Нет заметок для отображения."))
        return
    end

    imgui.BeginChild("NotesList", imgui.ImVec2(0, NOTE_LIST_HEIGHT), true)

    for i = #state.notes, 1, -1 do
        local note = state.notes[i]
        local query = state.searchQuery.v:lower()
        local matches = query == "" or note.title:lower():find(query) or
                        (note.tags and note.tags:lower():find(query)) or
                        note.content:lower():find(query)

        if matches and imgui.TreeNode(u8(note.title .. "##" .. i)) then
            if note.tags and note.tags ~= "" then
                imgui.Text(u8("Теги: ") .. note.tags)
            else
                imgui.Text(u8("Теги: Отсутствуют"))
            end

            imgui.TextWrapped(note.content)
            imgui.Text(u8("Создано: ") .. note.createdAt)

            if imgui.Button(u8("Редактировать##" .. i)) then
                editNote(i)
            end

            imgui.SameLine()

            if imgui.Button(u8("Удалить##" .. i)) then
                requestDeleteNote(i)
            end

            imgui.TreePop()
        end
    end

    imgui.EndChild()
end

-- Frame Rendering

--- Renders the main UI frame.
local function drawFrame()
    if not state.windowVisible.v then return end

    local resX, resY = getScreenResolution()
    imgui.SetNextWindowPos(imgui.ImVec2(resX / 2 - WINDOW_WIDTH / 2, resY / 2 - WINDOW_HEIGHT / 2), imgui.Cond.FirstUseEver)
    imgui.SetNextWindowSize(imgui.ImVec2(WINDOW_WIDTH, WINDOW_HEIGHT), imgui.Cond.FirstUseEver)

    if imgui.Begin(u8("Блокнот для Даня Модеров"), state.windowVisible, imgui.WindowFlags.MenuBar + imgui.WindowFlags.NoResize) then

        -- Menu Bar
        if imgui.BeginMenuBar() then
            if imgui.BeginMenu(u8("Действия")) then
                if imgui.MenuItem(u8("Очистить все заметки")) then
                    clearAllNotes()
                end

                if imgui.MenuItem(u8("Импортировать заметки")) then
                    state.popups.import.visible.v = true
                end

                if imgui.MenuItem(u8("Экспортировать заметки")) then
                    state.popups.export.visible.v = true
                end

                imgui.EndMenu()
            end
            imgui.EndMenuBar()
        end

        -- Add Note Section
        if imgui.CollapsingHeader(u8("Добавить Заметку"), imgui.TreeNodeFlags.DefaultOpen) then
            imgui.InputText(u8("Название заметки"), state.inputTitle, imgui.ImVec2(-1, 0))
            imgui.InputText(u8("Теги (через запятую)"), state.inputTags, imgui.ImVec2(-1, 0))
            imgui.InputTextMultiline(u8("Текст заметки"), state.inputText, imgui.ImVec2(-1, 150))

            if imgui.Button(u8("Добавить заметку")) then
                addNote()
            end
        end

        imgui.Separator()

        -- View Notes Section
        if imgui.CollapsingHeader(u8("Просмотр Заметок"), imgui.TreeNodeFlags.DefaultOpen) then
            imgui.InputText(u8("Поиск"), state.searchQuery, imgui.ImVec2(-1, 0))
            drawNotes()
        end

        -- Handle Pop-ups
        showAllPopups()

    end
    imgui.End()
end

-- Initialization

--- Toggles the visibility of the main window.
local function toggleWindow()
    state.windowVisible.v = not state.windowVisible.v
end

--- Initializes the script: registers chat command and loads notes.
local function initialize()
    sampRegisterChatCommand(CHAT_COMMAND, toggleWindow)
    while not isSampAvailable() do
        wait(200)
    end

    loadNotes()
    imgui.Process = false
end

-- Style Configuration

--- Applies a custom style to the ImGui interface.
local function applyCustomStyle()
    local io = imgui.GetIO()
    local style = imgui.GetStyle()
    local colors = style.Colors
    local clr = imgui.Col
    local ImVec4 = imgui.ImVec4

    -- Window Styling
    style.WindowRounding = 5.0
    style.WindowTitleAlign = imgui.ImVec2(0.5, 0.5)
    style.ChildWindowRounding = 5.0
    style.FrameRounding = 5.0
    style.ItemSpacing = imgui.ImVec2(10.0, 10.0)
    style.ScrollbarSize = 15.0
    style.ScrollbarRounding = 5.0
    style.GrabMinSize = 10.0
    style.GrabRounding = 5.0

    -- Color Scheme
    colors[clr.WindowBg] = ImVec4(0.1, 0.1, 0.1, 1.0)
    colors[clr.TitleBg] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.TitleBgActive] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.TitleBgCollapsed] = ImVec4(0.15, 0.15, 0.15, 1.0)
    colors[clr.Button] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.ButtonHovered] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.ButtonActive] = ImVec4(0.4, 0.4, 0.4, 1.0)
    colors[clr.FrameBg] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.FrameBgHovered] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.FrameBgActive] = ImVec4(0.4, 0.4, 0.4, 1.0)
    colors[clr.CheckMark] = ImVec4(0.0, 0.5, 0.0, 1.0)
    colors[clr.Header] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.HeaderHovered] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.HeaderActive] = ImVec4(0.4, 0.4, 0.4, 1.0)
    colors[clr.ResizeGrip] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.ResizeGripHovered] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.ResizeGripActive] = ImVec4(0.4, 0.4, 0.4, 1.0)
    colors[clr.ScrollbarBg] = ImVec4(0.1, 0.1, 0.1, 1.0)
    colors[clr.ScrollbarGrab] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.ScrollbarGrabHovered] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.ScrollbarGrabActive] = ImVec4(0.4, 0.4, 0.4, 1.0)

    -- Load a font that supports Cyrillic characters
    local fontPath = "path/to/DejaVuSans.ttf" -- Укажите правильный путь к шрифту
    local fontSize = 16.0 -- Задайте размер шрифта по желанию

    -- Проверка существования файла шрифта
    local fontFile = io.open(fontPath, "rb")
    if not fontFile then
        showNotification("Шрифт не найден: " .. fontPath, 0xFF0000)
    else
        fontFile:close()
        local font = io.Fonts:AddFontFromFileTTF(fontPath, fontSize, nil, io.Fonts:GetGlyphRangesCyrillic())
        if not font then
            showNotification("Не удалось загрузить шрифт: " .. fontPath, 0xFF0000)
        else
            showNotification("Шрифт успешно загружен: " .. fontPath, 0x00FF00)
            imgui.PushFont(font) -- Применение загруженного шрифта
        end
    end
end

-- Main Loop

--- Main entry point of the script.
function main()
    initialize()
    while true do
        wait(0)
        imgui.Process = state.windowVisible.v
    end
end

--- Renders the frame each draw call.
function imgui.OnDrawFrame()
    drawFrame()
end

-- Apply Custom Style on Load
applyCustomStyle()
```
Fix error:
[ML] (error) notepad.lua: ...ads\cleaned originalSA by kichiro\moonloader\notepad.lua:490: attempt to call field 'open' (a nil value)
stack traceback:
	...ads\cleaned originalSA by kichiro\moonloader\notepad.lua:490: in function 'applyCustomStyle'
	...ads\cleaned originalSA by kichiro\moonloader\notepad.lua:531: in main chunk
[ML] (error) notepad.lua: Script died due to an error. (27EE711C)