Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу Пред.  1, 2, 3, 4, 5, 6  След.
 Re: Верно ли, что "использование ИИ подразумевается"?
Alex-Yu в сообщении #1730432 писал(а):
Сам пользуюсь ИИ только как поисковиком литературы
Это мерзко! Называется: "поматросил и бросил".
Я ищу литературу только так: иду в городскую библиотеку и роюсь в ящичках каталогов с бумажными карточками.
Важна не цель, важен путь!

3 Полезно / согласиеmihaild, Dmitriy40, Dedekind
 Re: Верно ли, что "использование ИИ подразумевается"?
Gepidium в сообщении #1730435 писал(а):
Alex-Yu в сообщении #1730432

писал(а):
Сам пользуюсь ИИ только как поисковиком литературы Это мерзко!



Я вам еще более страшную вещь скажу: я часто еще и расчеты делаю на компьютере! Правда, обычно предпочитаю запрограммировать самостоятельно. Очень редко пользуюсь программами, которые не я писал. Но ВСЕГДА только такими, которые я бы, в принципе, мог бы и сам написать, если бы у меня было достаточно на это время. Причем написать мог бы (если уж без ограничения по времени, лишь в принципе,) в машинных кодах, даже без ассемблера (впрочем, можно сначала написать ассемблер-транслятор, я как-то писал, можно и компилятор ЯВУ). Компьютер я тоже сам могу сделать (лишь в принципе, конечно, за нереальное время, столько не живут), причем стартуя от песка и прочей руды (хотя если маленький, примитивненький и не из руды а транзисторов-диодов, то могу и за обозримое время) .

 Re: Верно ли, что "использование ИИ подразумевается"?
Аватара пользователя
Gepidium в сообщении #1730435 писал(а):
Alex-Yu в сообщении #1730432 писал(а):
Сам пользуюсь ИИ только как поисковиком литературы
Это мерзко! Называется: "поматросил и бросил".
Я ищу литературу только так: иду в городскую библиотеку и роюсь в ящичках каталогов с бумажными карточками.
Важна не цель, важен путь!

Мне понравился Ваш сарказм. Но в использовании ИИ исключительно как поисковика сегодня есть рациональное зерно: в объёме эрудиции с ним ни один человек не сравнится, а вот как только речь заходит о содержательных выводах, то нужно проверять и перепроверять, ибо галлюцинируют, собаки.
С писанием кода тоже, кстати, такая же фигня: сам запаришься вспоминать и искать по справочникам правильный синтаксис, особенно если постоянно не работаешь с этим языком. А интеллекту поставь задачу в пару предложений и он тебе за полсекунды нафигачит код. Но после нужно проверять и перепроверять, ибо обязательно напортачит. Так мне gemini написал отличный html с JavaScript - хранилище паролей. Но пришлось раз пятнадцать перепроверять и просить исправить. Сам бы я такую штуку неделю писал, с учётом того, что уже много лет на JavaScript не программировал и всё забыл на фиг.

Так что программисты - уходящая профессия, а вот тестировщики ещё побарахтаются. :wink:

 Re: Верно ли, что "использование ИИ подразумевается"?
epros в сообщении #1730438 писал(а):
С писанием кода тоже, кстати, такая же фигня:


Вот это писание кода я вообще не понимаю зачем. На естественном языке выразить алгоритм почти невозможно. Не предназначен естественный язык для этого, на языке программирования написать, что тебе надо, намного проще. Если, конечно, есть более-менее развитые мозги. Беда, првда, в том, что все технологии и вообще вся организация жизни так и ориентируются на тех, у кого таких мозгов нет.

-- добавлено через 3 минуты --

epros в сообщении #1730438 писал(а):
а вот тестировщики ещё побарахтаются.


Сама идея тестировщиков патологическая. Невозможно перебрать ВСЕ варианты входных данных (а иначе компьютер был бы вообще не нужен). Тестирование ВСЕГДА будет дырявым. Писать программы нужно так, чтобы тестирование и отладка не требовалась вообще. Да, это посложнее, чем написать какую-нибудь чепуху, а потом ее долго тестировать, отлаживать и в итоге все же чепуху получить едва-едва пригодную.

-- добавлено через 2 минуты --

epros в сообщении #1730438 писал(а):
Сам бы я такую штуку неделю писал,


Ну и что, можно и неделю. Можно и месяц. Можно и год, ничего страшного. А можно вообще без хранилища паролей обойтись. Голова-то зачем :D

-- добавлено через 3 минуты --

epros в сообщении #1730438 писал(а):
А интеллекту поставь задачу в пару предложений


За пару предложений КОРРЕКТНО поставить задачу невозможно! Некорректно -- можно. Ну так фигня в итоге и получается, которую править нужно.

 Re: Верно ли, что "использование ИИ подразумевается"?
Gepidium в сообщении #1730435 писал(а):
Я ищу литературу только так: иду в городскую библиотеку и роюсь в ящичках каталогов с бумажными карточками.
Важна не цель, важен путь!
:D Отвечу тоже. Эта фраза возникла в контексте обсуждения с Mikhail_K редактирования и оптимизации полученных доказательств с помощью ИИ, а также прогонки работ через нейросеть с целью оптимизации. Абсолютизировать её не надо, всё же мы не самураи и желательно использовать современные технологии. Хотя, если кому-то хочется и есть время, то можно и в ящичках каталога порыться. Пока роешься может какая мысль осенит)

 Re: Верно ли, что "использование ИИ подразумевается"?
Аватара пользователя
Alex-Yu в сообщении #1730439 писал(а):
Вот это писание кода я вообще не понимаю зачем. На естественном языке выразить алгоритм почти невозможно. Не предназначен естественный язык для этого, на языке программирования написать, что тебе надо, намного проще.

Практика показывает иное. 32 килобайта кода - это, конечно, не бог весть что, но возился бы я с этим довольно долго. А вот на естественном языке постановка задачи получается весьма легко. Попробуйте сами.

Alex-Yu в сообщении #1730439 писал(а):
Сама идея тестировщиков патологическая. Невозможно перебрать ВСЕ варианты входных данных (а иначе компьютер был бы вообще не нужен). Тестирование ВСЕГДА будет дырявым.

Речь же не об исчерпывающем переборе всех вариантов, а о важных сценариях. Вот заказанное мной хранилище паролей работает так: При первом входе определяется мастер-пароль, потом я имею возможность добавить запись и сохранить новый html. При следующем входе я должен предъявить мастер-пароль чтобы расшифровать базу, потом я могу добавить запись, отредактировать или удалить существующую или изменить мастер-пароль, после чего опять сохраняю новый html. Много внимания я уделил доработке интерфейса, чтобы можно было редактируемую запись переместить в любое место. Так что сценариев использования не так много.

Попробуйте и убедитесь, что это примерно в 100 раз проще, чем написать все эти 32К кода самому.

Alex-Yu в сообщении #1730439 писал(а):
А можно вообще без хранилища паролей обойтись. Голова-то зачем :D

Уж точно не для запоминания 150 сложных паролей к разным ресурсам, у каждого из которых свои требования к тому, какой должен быть пароль.

Alex-Yu в сообщении #1730439 писал(а):
За пару предложений КОРРЕКТНО поставить задачу невозможно! Некорректно -- можно. Ну так фигня в итоге и получается, которую править нужно.

Уверяю Вас, я ничего руками не правил. Я даже со структурой кода не стал до конца разбираться - незачем, достаточно и того, что работает как нужно и я в целом понимаю как.

 Re: Верно ли, что "использование ИИ подразумевается"?
epros в сообщении #1730448 писал(а):
работает как нужно


А откуда Вы это знаете, что как нужно? Нет иного способа в этом убедиться, кроме как прогнать весь код в голове. Кстати, я ничего не имею против подпрограмм (и макросов), если они написаны людьми. С помощью них, при хорошей библиотеке, всегда можно написать очень быстро без всяких там ИИ.

Я конечно же обычно не стану писать 32к в машинных кодах. Но я МОГУ это сделать, вот что важно. Боюсь, что в очень близком будущем такое "могу" будет утеряно. Да уже утеряно.

 Re: Верно ли, что "использование ИИ подразумевается"?
Alex-Yu в сообщении #1730437 писал(а):
стартуя от песка и прочей руды

Меня терзают смутные сомнения

 Re: Верно ли, что "использование ИИ подразумевается"?
Cuprum2020 в сообщении #1730452 писал(а):
Меня терзают смутные сомнения


Не сомневайтесь. Лет за 300-400 запросто. Хотя, как я это и писал, столько не живут. Но речь шла о том, что в принципе.

-- добавлено через 8 минут --

lel0lel в сообщении #1730447 писал(а):
всё же мы не самураи и желательно использовать современные технологии.


Не желательно, а приходится. Улавливаете разницу? И очень часто (хотя и не всегда) это "приходится" очень печально. Особенно печально, когда понимаешь, что причина этого "приходится" не объективна, а есть следствие дурости людской.

 Re: Верно ли, что "использование ИИ подразумевается"?
Аватара пользователя
Alex-Yu в сообщении #1730437 писал(а):
Очень редко пользуюсь программами, которые не я писал
Браузер Вы тоже написали сами?
Alex-Yu в сообщении #1730439 писал(а):
На естественном языке выразить алгоритм почти невозможно
Следствие: заказов на разработку ПО не бывает.

(Оффтоп)

Alex-Yu в сообщении #1730439 писал(а):
Если, конечно, есть более-менее развитые мозги. Беда, првда, в том, что все технологии и вообще вся организация жизни так и ориентируются на тех, у кого таких мозгов нет
Поскольку, по всей видимости, "более-менее развитые мозги" по Вашему определению есть ровно у одного человека на Земле, я не вижу ничего удивительного в том, что организация жизни ориентируется на других.
Alex-Yu в сообщении #1730439 писал(а):
Писать программы нужно так, чтобы тестирование и отладка не требовалась вообще
Кнут писал(а):
Beware of bugs in the above code; I have only proved it correct, not tried it
Торвальдс писал(а):
Nobody actually creates perfect code the first time around, except me. But there's only one of me.
Поскольку нам нужно больше софта, чем может написать Торвальдс, приходится использовать какие-то способы разработки, позволяющие в ней участвовать кому-то, кроме Торвальдса.
Alex-Yu в сообщении #1730439 писал(а):
А можно вообще без хранилища паролей обойтись
А можно жить в лесу охотой и собирательством, а не рассказывать на dxdy, как все вокруг неправильно живут.
Alex-Yu в сообщении #1730439 писал(а):
Ну так фигня в итоге и получается, которую править нужно
И это часто получается быстрее, чем писать всё с нуля.
Alex-Yu в сообщении #1730451 писал(а):
Кстати, я ничего не имею против подпрограмм (и макросов), если они написаны людьми
Чем код, написанный человеком, принципиально отличается от кода, написанного LLM? Вы сможете их отличить хотя бы?
Alex-Yu в сообщении #1730453 писал(а):
Не желательно, а приходится. Улавливаете разницу?
За исключением некоторых странных мест, где есть метрика "сколько токенов потратили", я не очень понимаю, почему "приходится".

2 Полезно / согласиеDmitriy40, Dedekind
 Re: Верно ли, что "использование ИИ подразумевается"?
Аватара пользователя
Alex-Yu

(можете потестировать)

Код: [ скачать ] [ спрятать ]
Используется синтаксис HTML5
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Crypto Storage</title>
    <style>
        body { font-family: sans-serif; max-width: 800px; margin: 20px auto; padding: 0 10px; background: #f9f9f9; color: #333; }
        .box { background: white; padding: 15px; border-radius: 5px; box-shadow: 0 2px 5px rgba&#40;0,0,0,0.1&#41;; margin-bottom: 20px; }
        input, textarea, button { width: 100%; padding: 8px; margin: 5px 0 15px 0; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px; }
       
        input.masked-password {
            -webkit-text-security: disk !important;
            text-security: disk !important;
            font-family: monospace;
        }
       
        button { background: #0066cc; color: white; border: none; cursor: pointer; font-weight: bold; }
        button:hover { background: #0052a3; }
        button.delete { background: #cc3333; }
        button.delete:hover { background: #992222; }
        button.secondary { background: #666; }
        button.secondary:hover { background: #444; }
        .hidden { display: none !important; }
        .entry { border-bottom: 1px solid #eee; padding: 15px 0; }
        .entry:last-child { border: none; }
        .row { display: flex; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
        .row button { width: auto; flex-grow: 1; margin: 0; }
        .inline-btn { width: auto; padding: 4px 10px; margin: 5px 5px 0 0; }
       
        .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba&#40;0,0,0,0.5&#41;; display: flex; justify-content: center; align-items: center; z-index: 1000; }
        .modal-content { background: white; padding: 20px; border-radius: 5px; max-width: 400px; width: 100%; box-shadow: 0 4px 15px rgba&#40;0,0,0,0.2&#41;; }
        .modal-buttons { display: flex; gap: 10px; margin-top: 10px; }
        .modal-buttons button { margin: 0; }
    </style>
</head>
<body>

    <h2>Локальное хранилище паролей</h2>

    <div id="auth-panel" class="box">
        <label for="master-password" id="auth-label">Введите мастер-пароль:</label>
        <input type="text" id="master-password" class="masked-password" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus>
        <button id="auth-submit-btn" onclick="handleAuthSubmit&#40;&#41;">Открыть хранилище</button>
        <div id="auth-error" style="color: red; margin-top: 5px;"></div>
    </div>

    <div id="main-panel" class="box hidden">
        <div class="row">
            <button onclick="lock&#40;&#41;" class="secondary">Скрыть</button>
            <button onclick="openChangePasswordModal&#40;&#41;">Сменить мастер-пароль</button>
        </div>
       
        <div class="row" style="align-items: center; margin-top: 20px;">
            <h3 style="margin: 0;">Записи</h3>
            <button class="inline-btn actionable-btn" id="add-btn-top" onclick="showFormAt&#40;0, &#39;add&#39;&#41;">Добавить</button>
        </div>

        <div id="editor-block" class="box hidden" style="border: 1px dashed #0066cc; margin: 10px 0;">
            <h4 id="editor-form-title">Обработка записи</h4>
            <form id="editor-form" onsubmit="handleFormSubmit&#40;event&#41;">
                <label>Название ресурса:</label>
                <input type="text" id="editor-title" placeholder="Например: Почта, Банк" autocomplete="off" required>
               
                <label>Гиперссылка &#40;необязательно&#41;:</label>
                <input type="url" id="editor-url" placeholder="https://example.com" autocomplete="off">
               
                <label>Логин или Email:</label>
                <input type="text" id="editor-login" placeholder="username" autocomplete="off" required>
               
                <label>Пароль:</label>
                <input type="text" id="editor-pass" placeholder="password" autocomplete="off" required>
               
                <label>Комментарий:</label>
                <textarea id="editor-comment" placeholder="Дополнительные сведения" rows="2" spellcheck="false"></textarea>
               
                <div class="row" style="margin: 10px 0 0 0;">
                    <div style="display: flex; gap: 10px; flex-grow: 1;">
                        <button type="submit" style="margin:0;">Сохранить</button>
                        <button type="button" class="secondary" onclick="closeEditorForm&#40;&#41;" style="margin:0;">Отменить</button>
                    </div>
                    <div style="display: flex; gap: 5px; width: auto;">
                        <button type="button" id="move-up-btn" class="secondary inline-btn" onclick="moveFormPosition&#40;-1&#41;" style="margin:0; padding: 8px 15px;"></button>
                        <button type="button" id="move-down-btn" class="secondary inline-btn" onclick="moveFormPosition&#40;1&#41;" style="margin:0; padding: 8px 15px;"></button>
                    </div>
                </div>
            </form>
        </div>

        <div id="entries-container"></div>
    </div>

    <div id="custom-modal" class="modal-overlay hidden">
        <div class="modal-content">
            <h3 id="modal-title">Контекстное окно</h3>
            <div id="modal-body"></div>
            <div class="modal-buttons" id="modal-actions"></div>
        </div>
    </div>

    <script id="encrypted-data" type="application/json">""</script>

    <script>
        let decryptedEntries = [];
        let backupEntries = []; // Для полной отмены перемещений при редактировании
        let currentMasterPassword = &#39;&#39;;
        let currentTargetIndex = 0;
        let currentFormMode = &#39;add&#39;;
        let activeTriggerButtonId = null;
        let isInitialized = false;

        const enc = new TextEncoder&#40;&#41;;
        const dec = new TextDecoder&#40;&#41;;
        const buf2hex = buf => Array.from&#40;new Uint8Array&#40;buf&#41;&#41;.map&#40;b => b.toString&#40;16&#41;.padStart&#40;2, &#39;0&#39;&#41;&#41;.join&#40;&#39;&#39;&#41;;
        const hex2buf = hex => new Uint8Array&#40;hex.match&#40;/.{1,2}/g&#41;.map&#40;byte => parseInt&#40;byte, 16&#41;&#41;&#41;;

        window.addEventListener&#40;&#39;DOMContentLoaded&#39;, &#40;&#41; => {
            checkInitializationState&#40;&#41;;
        }&#41;;

        function checkInitializationState&#40;&#41; {
            const rawData = document.getElementById&#40;&#39;encrypted-data&#39;&#41;.textContent.trim&#40;&#41;;
            if &#40;rawData === &#39;""&#39; || rawData === &#39;&#39; || rawData === &#39;{"ciphertext":"","salt":"","iv":""}&#39;&#41; {
                isInitialized = false;
                document.getElementById&#40;&#39;auth-label&#39;&#41;.textContent = &#39;Задайте мастер-пароль:&#39;;
                document.getElementById&#40;&#39;auth-submit-btn&#39;&#41;.textContent = &#39;Сохранить мастер-пароль&#39;;
            } else {
                isInitialized = true;
                document.getElementById&#40;&#39;auth-label&#39;&#41;.textContent = &#39;Введите мастер-пароль:&#39;;
                document.getElementById&#40;&#39;auth-submit-btn&#39;&#41;.textContent = &#39;Открыть хранилище&#39;;
            }
        }

        async function deriveKey&#40;password, salt&#41; {
            const baseKey = await crypto.subtle.importKey&#40;"raw", enc.encode&#40;password&#41;, "PBKDF2", false, ["deriveKey"]&#41;;
            return crypto.subtle.deriveKey&#40;
                { name: "PBKDF2", salt: salt, iterations: 100000, hash: "SHA-256" },
                baseKey,
                { name: "AES-GCM", length: 128 },
                false,
                ["encrypt", "decrypt"]
            &#41;;
        }

        async function decryptData&#40;ciphertextHex, password, saltHex, ivHex&#41; {
            try {
                const salt = hex2buf&#40;saltHex&#41;;
                const iv = hex2buf&#40;ivHex&#41;;
                const key = await deriveKey&#40;password, salt&#41;;
                const decrypted = await crypto.subtle.decrypt&#40;{ name: "AES-GCM", iv: iv }, key, hex2buf&#40;ciphertextHex&#41;&#41;;
                return JSON.parse&#40;dec.decode&#40;decrypted&#41;&#41;;
            } catch &#40;e&#41; {
                return null;
            }
        }

        async function encryptData&#40;dataObject, password&#41; {
            const salt = crypto.getRandomValues&#40;new Uint8Array&#40;16&#41;&#41;;
            const iv = crypto.getRandomValues&#40;new Uint8Array&#40;12&#41;&#41;;
            const key = await deriveKey&#40;password, salt&#41;;
            const plaintext = enc.encode&#40;JSON.stringify&#40;dataObject&#41;&#41;;
            const ciphertext = await crypto.subtle.encrypt&#40;{ name: "AES-GCM", iv: iv }, key, plaintext&#41;;
           
            return {
                ciphertext: buf2hex&#40;ciphertext&#41;,
                salt: buf2hex&#40;salt&#41;,
                iv: buf2hex&#40;iv&#41;
            };
        }

        function showModal&#40;title, bodyHtml, actionsArray&#41; {
            document.getElementById&#40;&#39;modal-title&#39;&#41;.textContent = title;
            document.getElementById&#40;&#39;modal-body&#39;&#41;.innerHTML = bodyHtml;
            const actionsContainer = document.getElementById&#40;&#39;modal-actions&#39;&#41;;
            actionsContainer.innerHTML = &#39;&#39;;
           
            actionsArray.forEach&#40;action => {
                const btn = document.createElement&#40;&#39;button&#39;&#41;;
                btn.textContent = action.label;
                if &#40;action.className&#41; btn.className = action.className;
                btn.onclick = &#40;&#41; => {
                    closeModal&#40;&#41;;
                    if &#40;action.callback&#41; action.callback&#40;&#41;;
                };
                actionsContainer.appendChild&#40;btn&#41;;
            }&#41;;
            document.getElementById&#40;&#39;custom-modal&#39;&#41;.classList.remove&#40;&#39;hidden&#39;&#41;;
        }

        function closeModal&#40;&#41; {
            document.getElementById&#40;&#39;custom-modal&#39;&#41;.classList.add&#40;&#39;hidden&#39;&#41;;
        }

        // Авторизация
        function handleAuthSubmit&#40;&#41; {
            if &#40;!isInitialized&#41; {
                openInitializationModal&#40;&#41;;
            } else {
                unlock&#40;&#41;;
            }
        }

        function openInitializationModal&#40;&#41; {
            const passInput = document.getElementById&#40;&#39;master-password&#39;&#41;;
            const tmpPassword = passInput.value;
            if &#40;!tmpPassword&#41; return;

            const bodyHtml = `
                <p>Повторите ввод для подтверждения мастер-пароля:</p>
                <input type="text" id="init-confirm-pass" class="masked-password" autocomplete="off" autocapitalize="none" spellcheck="false" style="width:100%; padding:8px;">
            `;
            showModal&#40;&#39;Инициализация базы&#39;, bodyHtml, [
                {
                    label: &#39;Сохранить&#39;,
                    callback: async &#40;&#41; => {
                        const confirmPass = document.getElementById&#40;&#39;init-confirm-pass&#39;&#41;.value;
                        if &#40;tmpPassword !== confirmPass&#41; {
                            document.getElementById&#40;&#39;master-password&#39;&#41;.value = &#39;&#39;;
                            showModal&#40;&#39;Ошибка&#39;, &#39;<p>Пароли не совпадают. Инициализация прервана.</p>&#39;, [{ label: &#39;ОК&#39; }]&#41;;
                            return;
                        }
                        currentMasterPassword = tmpPassword;
                        decryptedEntries = [];
                        isInitialized = true;
                       
                        const encryptedPackage = await encryptData&#40;decryptedEntries, currentMasterPassword&#41;;
                        document.getElementById&#40;&#39;encrypted-data&#39;&#41;.textContent = JSON.stringify&#40;encryptedPackage&#41;;

                        showMainPanel&#40;&#41;;
                        renderEntries&#40;&#41;;
                    }
                },
                {
                    label: &#39;Отменить&#39;,
                    className: &#39;secondary&#39;,
                    callback: &#40;&#41; => { document.getElementById&#40;&#39;master-password&#39;&#41;.value = &#39;&#39;; }
                }
            ]&#41;;
           
            setTimeout&#40;&#40;&#41; => {
                const initInput = document.getElementById&#40;&#39;init-confirm-pass&#39;&#41;;
                if &#40;initInput&#41; initInput.focus&#40;&#41;;
            }, 50&#41;;
        }

        async function unlock&#40;&#41; {
            const passInput = document.getElementById&#40;&#39;master-password&#39;&#41;;
            const errorDiv = document.getElementById&#40;&#39;auth-error&#39;&#41;;
            const rawData = document.getElementById&#40;&#39;encrypted-data&#39;&#41;.textContent.trim&#40;&#41;;
           
            const tmpPassword = passInput.value;
            if &#40;!tmpPassword&#41; return;

            try {
                const encryptedObj = JSON.parse&#40;rawData&#41;;
                const result = await decryptData&#40;encryptedObj.ciphertext, tmpPassword, encryptedObj.salt, encryptedObj.iv&#41;;
               
                if &#40;result !== null&#41; {
                    currentMasterPassword = tmpPassword;
                    decryptedEntries = result;
                    errorDiv.textContent = &#39;&#39;;
                    showMainPanel&#40;&#41;;
                    renderEntries&#40;&#41;;
                } else {
                    errorDiv.textContent = &#39;Неверный мастер-пароль.&#39;;
                }
            } catch&#40;e&#41; {
                errorDiv.textContent = &#39;Ошибка структуры данных.&#39;;
            }
        }

        function showMainPanel&#40;&#41; {
            document.getElementById&#40;&#39;auth-panel&#39;&#41;.classList.add&#40;&#39;hidden&#39;&#41;;
            document.getElementById&#40;&#39;main-panel&#39;&#41;.classList.remove&#40;&#39;hidden&#39;&#41;;
        }

        function lock&#40;&#41; {
            decryptedEntries = [];
            backupEntries = [];
            currentMasterPassword = &#39;&#39;;
            document.getElementById&#40;&#39;master-password&#39;&#41;.value = &#39;&#39;;
            document.getElementById&#40;&#39;entries-container&#39;&#41;.innerHTML = &#39;&#39;;
            closeEditorForm&#40;&#41;;
            document.getElementById&#40;&#39;main-panel&#39;&#41;.classList.add&#40;&#39;hidden&#39;&#41;;
            document.getElementById&#40;&#39;auth-panel&#39;&#41;.classList.remove&#40;&#39;hidden&#39;&#41;;
            document.getElementById&#40;&#39;auth-error&#39;&#41;.textContent = &#39;&#39;;
           
            if &#40;isInitialized&#41; {
                document.getElementById&#40;&#39;auth-label&#39;&#41;.textContent = &#39;Введите мастер-пароль:&#39;;
                document.getElementById&#40;&#39;auth-submit-btn&#39;&#41;.textContent = &#39;Открыть хранилище&#39;;
            }
        }

        // Рендеринг списка БЕЗ дублирующих кнопок "Добавить"
        function renderEntries&#40;&#41; {
            const container = document.getElementById&#40;&#39;entries-container&#39;&#41;;
            container.innerHTML = &#39;&#39;;
           
            decryptedEntries.forEach&#40;&#40;entry, index&#41; => {
                const div = document.createElement&#40;&#39;div&#39;&#41;;
                div.className = &#39;entry&#39;;
                div.id = `entry-${index}`;
               
                const titleRender = entry.url && entry.url.trim&#40;&#41; !== &#39;&#39;
                    ? `<a href="${escapeHtml&#40;entry.url&#41;}" target="_blank" rel="noopener">${escapeHtml&#40;entry.title&#41;}</a>`
                    : `<strong>${escapeHtml&#40;entry.title&#41;}</strong>`;

                div.innerHTML = `
                    <div class="view-mode" id="view-mode-${index}">
                        ${titleRender}
                        <div style="margin-top: 5px;">
                            <small>Логин:</small> <input type="text" value="${escapeHtml&#40;entry.login&#41;}" readonly onclick="this.select&#40;&#41;" autocomplete="off" spellcheck="false" style="width:auto; margin:0 5px;">
                            <small>Пароль:</small> <input type="text" value="${escapeHtml&#40;entry.pass&#41;}" readonly onclick="this.select&#40;&#41;" autocomplete="off" spellcheck="false" style="width:auto; margin:0 5px;">
                        </div>
                        <div style="font-size: 0.9em; color: #666; margin: 5px 0; white-space: pre-wrap;">${escapeHtml&#40;entry.comment || &#39;&#39;&#41;}</div>
                        <button class="inline-btn actionable-btn" id="edit-btn-${index}" onclick="showFormAt&#40;${index}, &#39;edit&#39;&#41;">Редактировать</button>
                        <button class="delete inline-btn actionable-btn" id="del-btn-${index}" onclick="openDeleteModal&#40;${index}&#41;">Удалить</button>
                    </div>
                `;
                container.appendChild&#40;div&#41;;
            }&#41;;
        }

        function showFormAt&#40;index, mode&#41; {
            if &#40;document.getElementById&#40;&#39;editor-block&#39;&#41;.classList.contains&#40;&#39;hidden&#39;&#41; === false&#41; {
                closeEditorForm&#40;&#41;;
            }
   
            currentFormMode = mode;
            currentTargetIndex = index;
   
            const editorBlock = document.getElementById&#40;&#39;editor-block&#39;&#41;;
            const titleElement = document.getElementById&#40;&#39;editor-form-title&#39;&#41;;
   
            // ПРИНУДИТЕЛЬНО РАЗБЛОКИРУЕМ кнопки самой формы перед показом
            const formButtons = editorBlock.querySelectorAll&#40;&#39;button&#39;&#41;;
            formButtons.forEach&#40;btn => {
                btn.disabled = false;
                btn.style.opacity = "1";
                btn.style.pointerEvents = "auto";
            }&#41;;
   
            if &#40;mode === &#39;add&#39;&#41; {
                titleElement.textContent = &#39;Добавление записи&#39;;
                activeTriggerButtonId = &#39;add-btn-top&#39;;
       
                if &#40;index === 0&#41; {
                    const btnTop = document.getElementById&#40;&#39;add-btn-top&#39;&#41;;
                    btnTop.parentNode.insertAdjacentElement&#40;&#39;afterend&#39;, editorBlock&#41;;
                } else {
                    const targetEntry = document.getElementById&#40;`entry-${index - 1}`&#41;;
                    targetEntry.insertAdjacentElement&#40;&#39;afterend&#39;, editorBlock&#41;;
                }
            } else if &#40;mode === &#39;edit&#39;&#41; {
                titleElement.textContent = &#39;Редактирование записи&#39;;
                activeTriggerButtonId = `edit-btn-${index}`;
       
                backupEntries = JSON.parse&#40;JSON.stringify&#40;decryptedEntries&#41;&#41;;
       
                const data = decryptedEntries[index];
                document.getElementById&#40;&#39;editor-title&#39;&#41;.value = data.title;
                document.getElementById&#40;&#39;editor-url&#39;&#41;.value = data.url || &#39;&#39;;
                document.getElementById&#40;&#39;editor-login&#39;&#41;.value = data.login;
                document.getElementById&#40;&#39;editor-pass&#39;&#41;.value = data.pass;
                document.getElementById&#40;&#39;editor-comment&#39;&#41;.value = data.comment || &#39;&#39;;
       
                const targetEntryView = document.getElementById&#40;`view-mode-${index}`&#41;;
                targetEntryView.insertAdjacentElement&#40;&#39;afterend&#39;, editorBlock&#41;;
            }
   
            if &#40;activeTriggerButtonId&#41; {
                const btn = document.getElementById&#40;activeTriggerButtonId&#41;;
                if &#40;btn&#41; btn.classList.add&#40;&#39;hidden&#39;&#41;;
            }
   
            editorBlock.classList.remove&#40;&#39;hidden&#39;&#41;;
            document.getElementById&#40;&#39;editor-title&#39;&#41;.focus&#40;&#41;;
            updateMoveButtonsState&#40;&#41;;
   
            // Включаем безопасную блокировку остальных элементов
            toggleAllInterfaceButtons&#40;true&#41;;
        }        
       
        // Транзакционная отмена &#40;Пункт 1&#41;
        function closeEditorForm&#40;&#41; {
            const editorBlock = document.getElementById&#40;&#39;editor-block&#39;&#41;;
            editorBlock.classList.add&#40;&#39;hidden&#39;&#41;;
            document.getElementById&#40;&#39;editor-form&#39;&#41;.reset&#40;&#41;;
           
            // Если мы редактировали и двигали запись — откатываем массив назад
            if &#40;currentFormMode === &#39;edit&#39; && backupEntries.length > 0&#41; {
                decryptedEntries = JSON.parse&#40;JSON.stringify&#40;backupEntries&#41;&#41;;
                backupEntries = [];
            }
           
            // Возвращаем все скрытые кнопки на место
            if &#40;activeTriggerButtonId&#41; {
                const btn = document.getElementById&#40;activeTriggerButtonId&#41;;
                if &#40;btn&#41; btn.classList.remove&#40;&#39;hidden&#39;&#41;;
                activeTriggerButtonId = null;
            }
            const topBtn = document.getElementById&#40;&#39;add-btn-top&#39;&#41;;
            if &#40;topBtn&#41; topBtn.classList.remove&#40;&#39;hidden&#39;&#41;;
           
            // Перерисовываем DOM в исходное состояние
            renderEntries&#40;&#41;;
           
            document.getElementById&#40;&#39;main-panel&#39;&#41;.insertBefore&#40;editorBlock, document.getElementById&#40;&#39;entries-container&#39;&#41;&#41;;
           
            document.getElementById&#40;&#39;move-up-btn&#39;&#41;.disabled = false;
            document.getElementById&#40;&#39;move-down-btn&#39;&#41;.disabled = false;
            document.getElementById&#40;&#39;move-up-btn&#39;&#41;.style.opacity = "1";
            document.getElementById&#40;&#39;move-down-btn&#39;&#41;.style.opacity = "1";
           
            // РАЗБЛОКИРУЕМ интерфейс обратно
            toggleAllInterfaceButtons&#40;false&#41;;
        }

        // Логика перемещения с защитой DOM
        function moveFormPosition&#40;direction&#41; {
            const totalEntries = decryptedEntries.length;
            const editorBlock = document.getElementById&#40;&#39;editor-block&#39;&#41;;
   
            document.getElementById&#40;&#39;main-panel&#39;&#41;.insertBefore&#40;editorBlock, document.getElementById&#40;&#39;entries-container&#39;&#41;&#41;;
   
            if &#40;currentFormMode === &#39;edit&#39;&#41; {
                const oldIndex = currentTargetIndex;
                const newIndex = oldIndex + direction;
       
                if &#40;newIndex < 0 || newIndex >= totalEntries&#41; {
                    restoreEditorPosition&#40;&#41;;
                    return;
                }
       
                const temp = decryptedEntries[oldIndex];
                decryptedEntries[oldIndex] = decryptedEntries[newIndex];
                decryptedEntries[newIndex] = temp;
       
                currentTargetIndex = newIndex;
       
                renderEntries&#40;&#41;;
                restoreEditorPosition&#40;&#41;;
       
            } else if &#40;currentFormMode === &#39;add&#39;&#41; {
                const oldIndex = currentTargetIndex;
                const newIndex = oldIndex + direction;
       
                if &#40;newIndex < 0 || newIndex > totalEntries&#41; {
                    restoreEditorPosition&#40;&#41;;
                    return;
                }
       
                currentTargetIndex = newIndex;
       
                renderEntries&#40;&#41;;
                restoreEditorPosition&#40;&#41;;
            }
        }        

        function restoreEditorPosition&#40;&#41; {
            const editorBlock = document.getElementById&#40;&#39;editor-block&#39;&#41;;
            const topBtn = document.getElementById&#40;&#39;add-btn-top&#39;&#41;;
            if &#40;topBtn&#41; topBtn.classList.remove&#40;&#39;hidden&#39;&#41;;
   
            if &#40;currentFormMode === &#39;edit&#39;&#41; {
                activeTriggerButtonId = `edit-btn-${currentTargetIndex}`;
                const targetEntryView = document.getElementById&#40;`view-mode-${currentTargetIndex}`&#41;;
                if &#40;targetEntryView&#41; targetEntryView.insertAdjacentElement&#40;&#39;afterend&#39;, editorBlock&#41;;
            } else if &#40;currentFormMode === &#39;add&#39;&#41; {
                if &#40;currentTargetIndex === 0&#41; {
                    activeTriggerButtonId = &#39;add-btn-top&#39;;
                    if &#40;topBtn&#41; topBtn.parentNode.insertAdjacentElement&#40;&#39;afterend&#39;, editorBlock&#41;;
                } else {
                    activeTriggerButtonId = null;
                    const targetEntry = document.getElementById&#40;`entry-${currentTargetIndex - 1}`&#41;;
                    if &#40;targetEntry&#41; targetEntry.insertAdjacentElement&#40;&#39;afterend&#39;, editorBlock&#41;;
                }
            }
   
            if &#40;activeTriggerButtonId&#41; {
                const btn = document.getElementById&#40;activeTriggerButtonId&#41;;
                if &#40;btn&#41; btn.classList.add&#40;&#39;hidden&#39;&#41;;
            }
   
            // КРИТИЧНО: Принудительно закрепляем блокировку интерфейса
            // поверх только что перерисованного через renderEntries&#40;&#41; DOM-дерева
            toggleAllInterfaceButtons&#40;true&#41;;
   
            // И обновляем состояние стрелок &#40;чтобы заблокировать их на границах&#41;
            updateMoveButtonsState&#40;&#41;;
        }

        function updateMoveButtonsState&#40;&#41; {
            const upBtn = document.getElementById&#40;&#39;move-up-btn&#39;&#41;;
            const downBtn = document.getElementById&#40;&#39;move-down-btn&#39;&#41;;
            const totalEntries = decryptedEntries.length;
           
            if &#40;currentFormMode === &#39;edit&#39;&#41; {
                upBtn.disabled = &#40;currentTargetIndex === 0&#41;;
                downBtn.disabled = &#40;currentTargetIndex === totalEntries - 1&#41;;
            } else {
                upBtn.disabled = &#40;currentTargetIndex === 0&#41;;
                downBtn.disabled = &#40;currentTargetIndex === totalEntries&#41;;
            }
           
            upBtn.style.opacity = upBtn.disabled ? "0.4" : "1";
            downBtn.style.opacity = downBtn.disabled ? "0.4" : "1";
        }

        async function handleFormSubmit&#40;e&#41; {
            e.preventDefault&#40;&#41;;
           
            const record = {
                title: document.getElementById&#40;&#39;editor-title&#39;&#41;.value,
                url: document.getElementById&#40;&#39;editor-url&#39;&#41;.value,
                login: document.getElementById&#40;&#39;editor-login&#39;&#41;.value,
                pass: document.getElementById&#40;&#39;editor-pass&#39;&#41;.value,
                comment: document.getElementById&#40;&#39;editor-comment&#39;&#41;.value
            };
           
            if &#40;currentFormMode === &#39;add&#39;&#41; {
                decryptedEntries.splice&#40;currentTargetIndex, 0, record&#41;;
            } else if &#40;currentFormMode === &#39;edit&#39;&#41; {
                decryptedEntries[currentTargetIndex] = record;
            }
           
            // Успешно сохранено — очищаем бэкап отмены
            backupEntries = [];
            await saveAndClose&#40;&#41;;
        }

        function openDeleteModal&#40;index&#41; {
            showModal&#40;&#39;Подтверждение удаления&#39;, `<p>Удалить запись "${escapeHtml&#40;decryptedEntries[index].title&#41;}"?</p>`, [
                { label: &#39;Удалить&#39;, className: &#39;delete&#39;, callback: async &#40;&#41; => { decryptedEntries.splice&#40;index, 1&#41;; await saveAndClose&#40;&#41;; } },
                { label: &#39;Отменить&#39;, className: &#39;secondary&#39; }
            ]&#41;;
        }

        function openChangePasswordModal&#40;&#41; {
            const bodyHtml = `
                <input type="text" id="new-master-pass" class="masked-password" placeholder="Новый мастер-пароль" autocomplete="off" autocapitalize="none" spellcheck="false" style="width:100%; padding:8px; margin-bottom:10px;">
                <input type="text" id="confirm-master-pass" class="masked-password" placeholder="Повторите новый мастер-пароль" autocomplete="off" autocapitalize="none" spellcheck="false" style="width:100%; padding:8px;">
            `;
            showModal&#40;&#39;Смена мастер-пароля&#39;, bodyHtml, [
                {
                    label: &#39;Сохранить&#39;,
                    callback: async &#40;&#41; => {
                        const newPass = document.getElementById&#40;&#39;new-master-pass&#39;&#41;.value;
                        const confirmPass = document.getElementById&#40;&#39;confirm-master-pass&#39;&#41;.value;
                        if &#40;!newPass&#41; return;
                        if &#40;newPass !== confirmPass&#41; {
                            showModal&#40;&#39;Ошибка&#39;, &#39;<p>Пароли не совпадают!</p>&#39;, [{ label: &#39;ОК&#39; }]&#41;;
                            return;
                        }
                        currentMasterPassword = newPass;
                        await saveAndClose&#40;&#41;;
                    }
                },
                { label: &#39;Отменить&#39;, className: &#39;secondary&#39; }
            ]&#41;;
        }

        async function saveAndClose&#40;&#41; {
            if &#40;!currentMasterPassword&#41; return;

            const encryptedPackage = await encryptData&#40;decryptedEntries, currentMasterPassword&#41;;
            const jsonString = JSON.stringify&#40;encryptedPackage, null, 2&#41;;
   
            const editorBlock = document.getElementById&#40;&#39;editor-block&#39;&#41;;
            document.getElementById&#40;&#39;main-panel&#39;&#41;.insertBefore&#40;editorBlock, document.getElementById&#40;&#39;entries-container&#39;&#41;&#41;;
   
            const topBtn = document.getElementById&#40;&#39;add-btn-top&#39;&#41;;
            if &#40;topBtn&#41; topBtn.classList.remove&#40;&#39;hidden&#39;&#41;;
   
            // КРИТИЧНО: Снимаем любые блокировки в текущем DOM перед клонированием
            toggleAllInterfaceButtons&#40;false&#41;;
   
            const cloneDoc = document.documentElement.cloneNode&#40;true&#41;;
   
            cloneDoc.querySelector&#40;&#39;#main-panel&#39;&#41;.classList.add&#40;&#39;hidden&#39;&#41;;
            cloneDoc.querySelector&#40;&#39;#auth-panel&#39;&#41;.classList.remove&#40;&#39;hidden&#39;&#41;;
            cloneDoc.querySelector&#40;&#39;#custom-modal&#39;&#41;.classList.add&#40;&#39;hidden&#39;&#41;;
   
            cloneDoc.querySelector&#40;&#39;#master-password&#39;&#41;.value = &#39;&#39;;
   
            const cloneEditor = cloneDoc.querySelector&#40;&#39;#editor-block&#39;&#41;;
            cloneEditor.classList.add&#40;&#39;hidden&#39;&#41;;
            cloneDoc.querySelector&#40;&#39;#editor-form&#39;&#41;.reset&#40;&#41;;
            cloneDoc.querySelector&#40;&#39;#entries-container&#39;&#41;.innerHTML = &#39;&#39;;
   
            cloneDoc.querySelector&#40;&#39;#encrypted-data&#39;&#41;.textContent = jsonString;
   
            const finalHtml = &#39;<!DOCTYPE html>\n&#39; + cloneDoc.outerHTML;

            lock&#40;&#41;;

            const blob = new Blob&#40;[finalHtml], { type: &#39;text/html&#39; }&#41;;
            const url = URL.createObjectURL&#40;blob&#41;;
            const a = document.createElement&#40;&#39;a&#39;&#41;;
            a.href = url;
            a.download = &#39;passwords.html&#39;;
            document.body.appendChild&#40;a&#41;;
            a.click&#40;&#41;;
            document.body.removeChild&#40;a&#41;;
            URL.revokeObjectURL&#40;url&#41;;

            setTimeout&#40;&#40;&#41; => {
                window.close&#40;&#41;;
                document.open&#40;&#41;;
                document.write&#40;&#39;<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body style="font-family:sans-serif; text-align:center; margin-top:50px; color:#666;"><h3>Сессия завершена. Изменения сохранены в файл passwords.html.</h3></body></html>&#39;&#41;;
                document.close&#40;&#41;;
            }, 300&#41;;
        }        

        function escapeHtml&#40;str&#41; {
            return str.replace&#40;/&/g, "&amp;"&#41;.replace&#40;/</g, "&lt;"&#41;.replace&#40;/>/g, "&gt;"&#41;.replace&#40;/"/g, "&quot;"&#41;.replace&#40;/&#39;/g, "&#039;"&#41;;
        }
       
        function toggleAllInterfaceButtons&#40;disable&#41; {
            // 1. Блокируем/разблокируем только верхние кнопки управления &#40;Скрыть, Сменить пароль, Добавить&#41;
            const topRow = document.querySelector&#40;&#39;#main-panel > .row&#39;&#41;;
            if &#40;topRow&#41; {
                topRow.querySelectorAll&#40;&#39;button&#39;&#41;.forEach&#40;btn => {
                    btn.disabled = disable;
                    btn.style.opacity = disable ? "0.5" : "1";
                    btn.style.pointerEvents = disable ? "none" : "auto";
                }&#41;;
            }
            const addBtnTop = document.getElementById&#40;&#39;add-btn-top&#39;&#41;;
            if &#40;addBtnTop&#41; {
                addBtnTop.disabled = disable;
                addBtnTop.style.opacity = disable ? "0.5" : "1";
                addBtnTop.style.pointerEvents = disable ? "none" : "auto";
            }

            // 2. Блокируем/разблокируем кнопки "Редактировать" и "Удалить" внутри всех записей
            const entries = document.querySelectorAll&#40;&#39;.entry&#39;&#41;;
            entries.forEach&#40;entry => {
                // Ищем кнопки только в режиме просмотра &#40;view-mode&#41;, не трогая форму, если она внутри записи
                const viewMode = entry.querySelector&#40;&#39;.view-mode&#39;&#41;;
                if &#40;viewMode&#41; {
                    viewMode.querySelectorAll&#40;&#39;button&#39;&#41;.forEach&#40;btn => {
                        btn.disabled = disable;
                        btn.style.opacity = disable ? "0.5" : "1";
                        btn.style.pointerEvents = disable ? "none" : "auto";
                    }&#41;;
                }
            }&#41;;
        }
    </script>
</body>
</html>


Это совсем не "машинные коды", поэтому это в принципе можно было бы написать и руками. Но всё же оцените, что проще: написать самому или просто протестировать и убедиться, что работает. Я ни одной буквы руками не правил.

 Re: Верно ли, что "использование ИИ подразумевается"?
mihaild в сообщении #1730456 писал(а):
Поскольку нам нужно больше софта, чем может написать Торвальдс, приходится использовать какие-то способы разработки, позволяющие в ней участвовать кому-то, кроме Торвальдса.

А можно жить в лесу охотой и собирательством, а не рассказывать на dxdy, как все вокруг неправильно живут.


Вам нужно больше софта, а мне - нет. И того, что есть, слишком много. И почему это у вас есть право рассказывать (в частности на dxdy) как жить правильно, а как - нет, а у меня такого права нет?

1 Полезно / согласиеwarlock66613
 Re: Верно ли, что "использование ИИ подразумевается"?
Alex-Yu в сообщении #1730453 писал(а):
Не желательно, а приходится. Улавливаете разницу? И очень часто (хотя и не всегда) это "приходится" очень печально.
Но приходится-то почему? Похоже потому, что нам хочется быть конкурентными, или всё же из-за исчезновения предшествующих возможностей (например, библиотек с бумажными книгами)? Может быть поэтому ИИ-технологии воспринимаются несколько настороженно теми, кто и до их появления успешно справлялся с задачами? Я согласен, что во многих сферах деятельности должен быть аккуратный подход к использованию ИИ-технологии, но и её появление у меня не вызывает печали.

 Re: Верно ли, что "использование ИИ подразумевается"?
epros в сообщении #1730459 писал(а):
просто протестировать и убедиться, что работает.


Меня всегда напрягал критерий "убедиться, что работает". В одних условиях работает, в других может и не будет. И что она еще делает, кроме того, что внешне проявляется... Про побочные эффекты, думаю, знаете.

Хотя, конечно, иной раз приходится и так. Разница лишь в том, что ПО ВОЗМОЖНОСТИ я такого подхода стараюсь избегать. Но я наблюдаю, что чаще и при наличии возможности не избегают. Тяп-ляп, побыстрее и не задумываясь... А потом постоянно достают обновлениями (как известно, правка программы -- это замена одних ошибок на другие ошибки же :D ). Ну ладно бы еще когда исключительно для себя программка...

-- добавлено через 28 секунд --

lel0lel в сообщении #1730461 писал(а):
нам хочется быть конкурентными,


Не всем. Мне, например, не хочется.

1 Отличная работаwarlock66613
 Re: Верно ли, что "использование ИИ подразумевается"?
Аватара пользователя
Alex-Yu в сообщении #1730460 писал(а):
Вам нужно больше софта, а мне - нет
Вы браузер сами написали?
Alex-Yu в сообщении #1730460 писал(а):
И того, что есть, слишком много
И чем он Вам мешает?
Alex-Yu в сообщении #1730460 писал(а):
И почему это у вас есть право рассказывать (в частности на dxdy) как жить правильно, а как - нет, а у меня такого права нет?
Право есть (я не предлагаю Вас за это банить). Но у меня есть право заметить, что Ваша позиция, если её применять последовательно, приводит к странным выводам.

 [ Сообщений: 76 ]  На страницу Пред.  1, 2, 3, 4, 5, 6  След.


Соглашение о конфиденциальности | Общие правила

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group