Spaces:
Sleeping
Sleeping
| /** | |
| * Inference Comparator - カスタムJavaScript | |
| * モーダル表示とズーム機能を提供 | |
| */ | |
| /** | |
| * ズームボタンを作成するヘルパー関数 | |
| * UI: 「−」「+」「100%表示」ボタン | |
| * 初期を100%として、+/-で10%刻みで拡大縮小(70%〜170%) | |
| */ | |
| function createZoomControls(targetElement) { | |
| var baseSize = 14; // 初期フォントサイズ(px)= 100% | |
| var zoomPercent = 100; // 現在のズーム率(%) | |
| var minPercent = 70; | |
| var maxPercent = 170; | |
| var step = 10; | |
| var zoomArea = document.createElement('div'); | |
| zoomArea.style.cssText = ` | |
| display: flex; gap: 8px; align-items: center; | |
| margin-right: auto; | |
| `; | |
| // −ボタン(縮小) | |
| var zoomOutBtn = document.createElement('button'); | |
| zoomOutBtn.style.cssText = ` | |
| background: #fff; color: #374151; border: 1px solid #d1d5db; | |
| border-radius: 8px; padding: 8px 16px; font-size: 1rem; | |
| font-weight: 600; cursor: pointer; min-width: 44px; | |
| `; | |
| zoomOutBtn.textContent = '−'; | |
| zoomOutBtn.onclick = function() { | |
| if (zoomPercent > minPercent) { | |
| zoomPercent -= step; | |
| var newSize = baseSize * zoomPercent / 100; | |
| targetElement.style.fontSize = newSize + 'px'; | |
| } | |
| }; | |
| // +ボタン(拡大) | |
| var zoomInBtn = document.createElement('button'); | |
| zoomInBtn.style.cssText = ` | |
| background: #fff; color: #374151; border: 1px solid #d1d5db; | |
| border-radius: 8px; padding: 8px 16px; font-size: 1rem; | |
| font-weight: 600; cursor: pointer; min-width: 44px; | |
| `; | |
| zoomInBtn.textContent = '+'; | |
| zoomInBtn.onclick = function() { | |
| if (zoomPercent < maxPercent) { | |
| zoomPercent += step; | |
| var newSize = baseSize * zoomPercent / 100; | |
| targetElement.style.fontSize = newSize + 'px'; | |
| } | |
| }; | |
| // 100%表示ボタン(リセット) | |
| var resetBtn = document.createElement('button'); | |
| resetBtn.style.cssText = ` | |
| background: #fff; color: #374151; border: 1px solid #d1d5db; | |
| border-radius: 8px; padding: 8px 16px; font-size: 0.9rem; | |
| font-weight: 500; cursor: pointer; | |
| `; | |
| resetBtn.textContent = '100%表示'; | |
| resetBtn.onclick = function() { | |
| zoomPercent = 100; | |
| targetElement.style.fontSize = baseSize + 'px'; | |
| }; | |
| zoomArea.appendChild(zoomOutBtn); | |
| zoomArea.appendChild(zoomInBtn); | |
| zoomArea.appendChild(resetBtn); | |
| return zoomArea; | |
| } | |
| /** | |
| * クエリモーダルを表示する | |
| * @param {string} tid_b64 - Base64エンコードされたtask_id | |
| * @param {string} query_b64 - Base64エンコードされたクエリ | |
| */ | |
| function showQueryModal(tid_b64, query_b64) { | |
| // Base64デコード(UTF-8対応) | |
| function b64decode(str) { | |
| return decodeURIComponent(escape(atob(str))); | |
| } | |
| var tid = b64decode(tid_b64); | |
| var query = b64decode(query_b64); | |
| // 既存のモーダルを削除 | |
| var existing = document.getElementById('query-modal-overlay'); | |
| if (existing) existing.remove(); | |
| // オーバーレイ作成 | |
| var overlay = document.createElement('div'); | |
| overlay.id = 'query-modal-overlay'; | |
| overlay.style.cssText = ` | |
| position: fixed; top: 0; left: 0; width: 100%; height: 100%; | |
| background: rgba(0,0,0,0.6); display: flex; | |
| align-items: center; justify-content: center; z-index: 10000; | |
| `; | |
| overlay.onclick = function(e) { | |
| if (e.target === overlay) overlay.remove(); | |
| }; | |
| // モーダルコンテンツ | |
| var modal = document.createElement('div'); | |
| modal.style.cssText = ` | |
| background: #fff; border-radius: 16px; padding: 32px; | |
| max-width: 80vw; width: 900px; max-height: 85vh; | |
| box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25); | |
| display: flex; flex-direction: column; | |
| font-family: Inter, Noto Sans JP, system-ui, sans-serif; | |
| `; | |
| // タイトル | |
| var title = document.createElement('div'); | |
| title.style.cssText = ` | |
| font-size: 1.25rem; font-weight: 700; color: #1f2937; | |
| margin-bottom: 24px; padding-bottom: 16px; | |
| border-bottom: 2px solid #e5e7eb; | |
| `; | |
| title.textContent = 'task_id: ' + tid + ' のクエリ'; | |
| // クエリ表示エリア | |
| var queryPre = document.createElement('pre'); | |
| queryPre.style.cssText = ` | |
| background: #f0f9ff; border: 1px solid #bae6fd; | |
| border-radius: 8px; padding: 20px; margin: 0; | |
| font-family: JetBrains Mono, Fira Code, monospace; | |
| font-size: 14px; line-height: 1.7; color: #0c4a6e; | |
| white-space: pre-wrap; word-break: break-all; | |
| overflow-y: auto; flex: 1; min-height: 200px; | |
| user-select: text; cursor: text; | |
| `; | |
| queryPre.textContent = query; | |
| // ボタンエリア | |
| var btnArea = document.createElement('div'); | |
| btnArea.style.cssText = ` | |
| margin-top: 20px; display: flex; gap: 12px; | |
| align-items: center; | |
| `; | |
| // ズームコントロールを追加 | |
| var zoomControls = createZoomControls(queryPre); | |
| btnArea.appendChild(zoomControls); | |
| // コピーボタン | |
| var copyBtn = document.createElement('button'); | |
| copyBtn.style.cssText = ` | |
| background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; | |
| border-radius: 8px; padding: 12px 24px; font-size: 0.95rem; | |
| font-weight: 600; cursor: pointer; transition: all 0.2s; | |
| `; | |
| copyBtn.textContent = '📋 コピー'; | |
| copyBtn.onmouseover = function() { | |
| this.style.background = '#e5e7eb'; | |
| }; | |
| copyBtn.onmouseout = function() { | |
| this.style.background = '#f3f4f6'; | |
| }; | |
| copyBtn.onclick = function() { | |
| navigator.clipboard.writeText(query).then(function() { | |
| copyBtn.textContent = '✓ コピーしました'; | |
| setTimeout(function() { | |
| copyBtn.textContent = '📋 コピー'; | |
| }, 2000); | |
| }); | |
| }; | |
| // 閉じるボタン | |
| var closeBtn = document.createElement('button'); | |
| closeBtn.style.cssText = ` | |
| background: #3b82f6; color: #fff; border: none; | |
| border-radius: 8px; padding: 12px 32px; font-size: 0.95rem; | |
| font-weight: 600; cursor: pointer; transition: background 0.2s; | |
| `; | |
| closeBtn.textContent = '閉じる'; | |
| closeBtn.onmouseover = function() { | |
| this.style.background = '#2563eb'; | |
| }; | |
| closeBtn.onmouseout = function() { | |
| this.style.background = '#3b82f6'; | |
| }; | |
| closeBtn.onclick = function() { | |
| overlay.remove(); | |
| }; | |
| btnArea.appendChild(copyBtn); | |
| btnArea.appendChild(closeBtn); | |
| modal.appendChild(title); | |
| modal.appendChild(queryPre); | |
| modal.appendChild(btnArea); | |
| overlay.appendChild(modal); | |
| document.body.appendChild(overlay); | |
| } | |
| /** | |
| * エラーモーダルを表示する | |
| * @param {string} tid_b64 - Base64エンコードされたtask_id | |
| * @param {string} err_b64 - Base64エンコードされたエラーメッセージ | |
| * @param {string} gen_b64 - Base64エンコードされた推論結果 | |
| */ | |
| function showErrorModal(tid_b64, err_b64, gen_b64) { | |
| // Base64デコード(UTF-8対応) | |
| function b64decode(str) { | |
| return decodeURIComponent(escape(atob(str))); | |
| } | |
| var tid = b64decode(tid_b64); | |
| var err = b64decode(err_b64); | |
| var gen = b64decode(gen_b64); | |
| // 既存のモーダルを削除 | |
| var existing = document.getElementById('error-modal-overlay'); | |
| if (existing) existing.remove(); | |
| // オーバーレイ作成 | |
| var overlay = document.createElement('div'); | |
| overlay.id = 'error-modal-overlay'; | |
| overlay.style.cssText = ` | |
| position: fixed; top: 0; left: 0; width: 100%; height: 100%; | |
| background: rgba(0,0,0,0.6); display: flex; | |
| align-items: center; justify-content: center; z-index: 10000; | |
| `; | |
| overlay.onclick = function(e) { | |
| if (e.target === overlay) overlay.remove(); | |
| }; | |
| // モーダルコンテンツ | |
| var modal = document.createElement('div'); | |
| modal.style.cssText = ` | |
| background: #fff; border-radius: 16px; padding: 32px; | |
| max-width: 80vw; width: 900px; max-height: 85vh; | |
| box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25); | |
| display: flex; flex-direction: column; | |
| font-family: 'Inter', 'Noto Sans JP', system-ui, sans-serif; | |
| `; | |
| // タイトル | |
| var title = document.createElement('div'); | |
| title.style.cssText = ` | |
| font-size: 1.25rem; font-weight: 700; color: #1f2937; | |
| margin-bottom: 24px; padding-bottom: 16px; | |
| border-bottom: 2px solid #e5e7eb; | |
| `; | |
| title.textContent = 'task_id: ' + tid + ' のエラー内容'; | |
| // スクロールエリア | |
| var scrollArea = document.createElement('div'); | |
| scrollArea.style.cssText = ` | |
| flex: 1; overflow-y: auto; display: flex; | |
| flex-direction: column; gap: 20px; | |
| `; | |
| // エラーセクション | |
| var errSection = document.createElement('div'); | |
| var errLabel = document.createElement('div'); | |
| errLabel.style.cssText = ` | |
| font-size: 0.875rem; font-weight: 600; color: #6b7280; | |
| margin-bottom: 8px; | |
| `; | |
| errLabel.textContent = 'エラー'; | |
| var errPre = document.createElement('pre'); | |
| errPre.style.cssText = ` | |
| background: #fef2f2; border: 1px solid #fecaca; | |
| border-radius: 8px; padding: 16px; margin: 0; | |
| font-family: 'JetBrains Mono', 'Fira Code', monospace; | |
| font-size: 0.875rem; line-height: 1.6; color: #991b1b; | |
| white-space: pre-wrap; word-break: break-all; | |
| overflow-y: auto; max-height: 150px; | |
| user-select: text; cursor: text; | |
| `; | |
| errPre.textContent = err; | |
| errSection.appendChild(errLabel); | |
| errSection.appendChild(errPre); | |
| // 推論結果セクション | |
| var genSection = document.createElement('div'); | |
| genSection.style.cssText = 'flex: 1; display: flex; flex-direction: column;'; | |
| var genLabel = document.createElement('div'); | |
| genLabel.style.cssText = ` | |
| font-size: 0.875rem; font-weight: 600; color: #6b7280; | |
| margin-bottom: 8px; | |
| `; | |
| genLabel.textContent = '推論結果'; | |
| var genPre = document.createElement('pre'); | |
| genPre.style.cssText = ` | |
| background: #f9fafb; border: 1px solid #e5e7eb; | |
| border-radius: 8px; padding: 16px; margin: 0; | |
| font-family: JetBrains Mono, Fira Code, monospace; | |
| font-size: 14px; line-height: 1.5; color: #374151; | |
| white-space: pre-wrap; word-break: break-all; | |
| overflow-y: auto; flex: 1; min-height: 200px; | |
| user-select: text; cursor: text; | |
| `; | |
| genPre.textContent = gen; | |
| genSection.appendChild(genLabel); | |
| genSection.appendChild(genPre); | |
| scrollArea.appendChild(errSection); | |
| scrollArea.appendChild(genSection); | |
| // ボタンエリア | |
| var btnArea = document.createElement('div'); | |
| btnArea.style.cssText = ` | |
| margin-top: 20px; display: flex; gap: 12px; | |
| align-items: center; | |
| `; | |
| // ズームコントロールを追加(推論結果用) | |
| var zoomControls = createZoomControls(genPre); | |
| btnArea.appendChild(zoomControls); | |
| // コピーボタン | |
| var copyBtn = document.createElement('button'); | |
| copyBtn.style.cssText = ` | |
| background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; | |
| border-radius: 8px; padding: 12px 24px; font-size: 0.95rem; | |
| font-weight: 600; cursor: pointer; transition: all 0.2s; | |
| `; | |
| copyBtn.textContent = '📋 全てコピー'; | |
| copyBtn.onmouseover = function() { | |
| this.style.background = '#e5e7eb'; | |
| }; | |
| copyBtn.onmouseout = function() { | |
| this.style.background = '#f3f4f6'; | |
| }; | |
| copyBtn.onclick = function() { | |
| var text = 'task_id: ' + tid + ' のエラー内容\n\n'; | |
| text += '【エラー】\n' + err + '\n\n'; | |
| text += '【推論結果】\n' + gen; | |
| navigator.clipboard.writeText(text).then(function() { | |
| copyBtn.textContent = '✓ コピーしました'; | |
| setTimeout(function() { | |
| copyBtn.textContent = '📋 全てコピー'; | |
| }, 2000); | |
| }); | |
| }; | |
| // 閉じるボタン | |
| var closeBtn = document.createElement('button'); | |
| closeBtn.style.cssText = ` | |
| background: #3b82f6; color: #fff; border: none; | |
| border-radius: 8px; padding: 12px 32px; font-size: 0.95rem; | |
| font-weight: 600; cursor: pointer; transition: background 0.2s; | |
| `; | |
| closeBtn.textContent = '閉じる'; | |
| closeBtn.onmouseover = function() { | |
| this.style.background = '#2563eb'; | |
| }; | |
| closeBtn.onmouseout = function() { | |
| this.style.background = '#3b82f6'; | |
| }; | |
| closeBtn.onclick = function() { | |
| overlay.remove(); | |
| }; | |
| btnArea.appendChild(copyBtn); | |
| btnArea.appendChild(closeBtn); | |
| modal.appendChild(title); | |
| modal.appendChild(scrollArea); | |
| modal.appendChild(btnArea); | |
| overlay.appendChild(modal); | |
| document.body.appendChild(overlay); | |
| } | |