| <!doctype html><html><head><script src="https://cdn.jsdelivr.net/npm/eruda@3.4.3/eruda.min.js"></script><script>eruda.init();</script><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>PenguinMod Packager - Convert PenguinMod projects to HTML, EXE, and more</title><meta name="description" content="Converts PenguinMod projects into HTML files, zip archives, or executable programs for Windows, macOS, and Linux."><style>body[p4-splash-theme="dark"]:not([p4-loaded]) { |
| background-color: #111; |
| color-scheme: dark; |
| } |
| .input-for-remembering-project-file { |
| display: none; |
| }</style></head><body><noscript>This page requires JavaScript.</noscript><input type="file" class="input-for-remembering-project-file" autocomplete="on"><div id="app"></div><script>(function() { |
| |
| |
| var theme = 'system'; |
| try { |
| var local = localStorage.getItem('P4.theme') |
| if (typeof local === 'string') theme = local; |
| } catch (e) { } |
| if (theme === 'system') theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; |
| document.body.setAttribute('p4-splash-theme', theme); |
| })();</script><script>(function(){ |
| if (typeof window.showSaveFilePicker !== 'function') { |
| alert('このブラウザはshowSaveFilePickerに対応していません(Chrome/Edge 86+などが必要です)'); |
| return; |
| } |
| |
| function getFileNameFromUrl(url, defaultName = 'download') { |
| try { |
| if (url.includes('/')) { |
| const parts = url.split('/'); |
| const last = parts.pop(); |
| if (last && last.includes('.')) return last; |
| } |
| } catch(e) {} |
| return defaultName; |
| } |
| |
| async function downloadBlobAsFile(blobUrl, suggestedName) { |
| try { |
| const response = await fetch(blobUrl); |
| if (!response.ok) throw new Error('Fetch failed'); |
| const blob = await response.blob(); |
| |
| const fileHandle = await window.showSaveFilePicker({ |
| suggestedName: suggestedName, |
| types: [{ |
| description: 'ダウンロードファイル', |
| accept: { 'application/octet-stream': ['.' + (suggestedName.split('.').pop() || 'bin')] } |
| }] |
| }); |
| |
| const writable = await fileHandle.createWritable(); |
| await writable.write(blob); |
| await writable.close(); |
| |
| console.log('ダウンロード完了:', suggestedName); |
| return true; |
| } catch(err) { |
| if (err.name !== 'AbortError') { |
| console.error('ダウンロードエラー:', err); |
| alert('ダウンロードに失敗しました: ' + err.message); |
| } |
| return false; |
| } |
| } |
| |
| async function processLink(link) { |
| const href = link.href; |
| if (!href || !href.startsWith('blob:')) return; |
| |
| const originalClick = link.onclick; |
| const originalTarget = link.target; |
| |
| link.onclick = null; |
| link.target = '_self'; |
| |
| const fileName = link.download || getFileNameFromUrl(href, 'blob_download'); |
| |
| link.style.opacity = '0.5'; |
| const success = await downloadBlobAsFile(href, fileName); |
| link.style.opacity = ''; |
| |
| if (!success) { |
| window.open(href, originalTarget || '_blank'); |
| } |
| |
| return false; |
| } |
| |
| function attachBlobHandlers() { |
| const links = document.querySelectorAll('a[href^="blob:"]'); |
| console.log(`blobリンクを${links.length}個検出しました`); |
| |
| links.forEach(link => { |
| if (link.dataset.blobHandlerAttached) return; |
| link.dataset.blobHandlerAttached = 'true'; |
| |
| link.addEventListener('click', async (e) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
| await processLink(link); |
| return false; |
| }); |
| |
| link.style.cursor = 'pointer'; |
| }); |
| } |
| |
| attachBlobHandlers(); |
| |
| const observer = new MutationObserver(() => attachBlobHandlers()); |
| observer.observe(document.body, { childList: true, subtree: true }); |
| |
| console.log('blobリンク監視を開始しました'); |
| alert('blob: URLのリンクをshowSaveFilePickerで保存します'); |
| })();</script><script src="js/p4.ff4a8adaa005c53fac81.js"></script></body></html> |