GNU/skin/board/routine/write/write.script.php
<script>
(function() {
    const canvas = document.getElementById('space-canvas');
    if (canvas) {
        const ctx = canvas.getContext('2d');
        let stars = [];

        function initSpace() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
            stars = [];
            for (let index = 0; index < 150; index++) {
                stars.push({
                    x: Math.random() * canvas.width,
                    y: Math.random() * canvas.height,
                    size: Math.random() * 2,
                    speed: Math.random() * 0.4
                });
            }
        }

        function animateSpace() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = 'white';
            stars.forEach(function(star) {
                ctx.beginPath();
                ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2);
                ctx.fill();
                star.y += star.speed;
                if (star.y > canvas.height) star.y = 0;
            });
            requestAnimationFrame(animateSpace);
        }

        window.addEventListener('resize', initSpace);
        initSpace();
        animateSpace();
    }

    window.updateFileName = function(input, index) {
        const fileName = input.files[0] ? input.files[0].name : 'No file selected';
        const fileNameElement = document.getElementById('file_name_' + index);
        if (!fileNameElement) return;
        fileNameElement.innerText = fileName;
        fileNameElement.style.color = '#00f2ff';
    };

    window.file_add = function() {
        const rows = document.querySelectorAll('.file-row:not(.active)');
        if (rows.length > 0) rows[0].classList.add('active');
    };

    function getCurrentDateText() {
        const now = new Date();
        const year = now.getFullYear();
        const month = String(now.getMonth() + 1).padStart(2, '0');
        const day = String(now.getDate()).padStart(2, '0');
        return year + '-' + month + '-' + day;
    }

    function getCurrentTimeText() {
        const now = new Date();
        const hour = String(now.getHours()).padStart(2, '0');
        const minute = String(now.getMinutes()).padStart(2, '0');
        const second = String(now.getSeconds()).padStart(2, '0');
        return hour + ':' + minute + ':' + second;
    }

    function createWorkLineRow(dateValue, contentValue, titleValue, timeValue) {
        const wrap = document.createElement('div');
        wrap.className = 'work-line-row';

        const topWrap = document.createElement('div');
        topWrap.className = 'work-line-top-wrap';

        const titleInput = document.createElement('input');
        titleInput.type = 'text';
        titleInput.className = 'work-line-title';
        titleInput.placeholder = '작업 제목을 입력하세요';
        titleInput.value = titleValue || '';

        const dateWrap = document.createElement('div');
        dateWrap.className = 'work-line-date-wrap';

        const caption = document.createElement('span');
        caption.className = 'field-caption no-margin';
        caption.textContent = '날짜';

        const dateInput = document.createElement('input');
        dateInput.type = 'date';
        dateInput.className = 'work-line-date';
        dateInput.value = dateValue && String(dateValue).trim() !== '' ? dateValue : getCurrentDateText();

        const timeWrap = document.createElement('div');
        timeWrap.className = 'work-line-time-wrap';

        const timeCaption = document.createElement('span');
        timeCaption.className = 'field-caption no-margin';
        timeCaption.textContent = '시간';

        const timeInput = document.createElement('input');
        timeInput.type = 'time';
        timeInput.step = 1;
        timeInput.className = 'work-line-time';
        timeInput.value = timeValue && String(timeValue).trim() !== '' ? timeValue : getCurrentTimeText();

        const contentArea = document.createElement('textarea');
        contentArea.className = 'work-line-content';
        contentArea.rows = 6;
        contentArea.placeholder = '작업 내용을 입력하세요';
        contentArea.value = contentValue || '';

        dateWrap.appendChild(caption);
        dateWrap.appendChild(dateInput);
        timeWrap.appendChild(timeCaption);
        timeWrap.appendChild(timeInput);
        topWrap.appendChild(titleInput);
        topWrap.appendChild(dateWrap);
        topWrap.appendChild(timeWrap);
        wrap.appendChild(topWrap);
        wrap.appendChild(contentArea);

        return wrap;
    }

    function serializeWorkLine() {
        const serializedInput = document.getElementById('x2_line_serialized');
        if (!serializedInput) return;

        const rows = document.querySelectorAll('#workLineRows .work-line-row');
        const chunks = [];

        rows.forEach(function(row) {
            const dateElement = row.querySelector('.work-line-date');
            const timeElement = row.querySelector('.work-line-time');
            const contentElement = row.querySelector('.work-line-content');
            const titleElement = row.querySelector('.work-line-title');

            const lineDate = dateElement ? dateElement.value.trim() : getCurrentDateText();
            const lineTime = timeElement ? timeElement.value.trim() : getCurrentTimeText();
            const content = contentElement ? contentElement.value.trim() : '';
            const title = titleElement ? titleElement.value.trim() : '';

            if (!content && !title) return;

            const cleanedDate = (lineDate || getCurrentDateText()).replace(/[#@]/g, ' ');
            const cleanedTime = (lineTime || getCurrentTimeText()).replace(/[#@]/g, ' ');
            const cleanedContent = content.replace(/[#@]/g, ' ');
            const cleanedTitle = title.replace(/[#@]/g, ' ');
            chunks.push(cleanedDate + '#' + cleanedContent + '#' + cleanedTitle + '#' + cleanedTime);
        });

        serializedInput.value = chunks.length > 0 ? chunks.join('@') + '@' : '';
    }

    function bindMemoToggle() {
        const memoButton = document.getElementById('memo-toggle-btn');
        const memoArea = document.getElementById('memo-area');
        if (!memoButton || !memoArea) return;

        memoButton.addEventListener('click', function() {
            memoArea.classList.toggle('open');
            memoButton.setAttribute('aria-expanded', memoArea.classList.contains('open') ? 'true' : 'false');
        });
    }

    function bindWorkLineAdd() {
        const addButton = document.getElementById('btn-add-work-line');
        const rowsContainer = document.getElementById('workLineRows');
        if (!addButton || !rowsContainer) return;

        addButton.addEventListener('click', function() {
            rowsContainer.appendChild(createWorkLineRow(getCurrentDateText(), '', '', getCurrentTimeText()));
        });
    }

    function bindDateTimePicker() {
        ['wr_date_custom', 'wr_time_custom'].forEach(function(id) {
            const field = document.getElementById(id);
            if (!field) return;

            const openPicker = function() {
                if (typeof field.showPicker === 'function') {
                    field.showPicker();
                }
            };

            field.addEventListener('click', openPicker);
            field.addEventListener('focus', openPicker);
        });
    }

    window.fwrite_submit = function(f) {
        <?php echo $editor_js; ?>

        if (!f.x2_ca2.value || !f.x2_ca3.value || !f.x2_ca4.value) {
            alert('종류, 형태, 상태는 필수 입력입니다.');
            return false;
        }

        serializeWorkLine();
        return true;
    };

    bindMemoToggle();
    bindWorkLineAdd();
    bindDateTimePicker();
})();
</script>