diff --git a/md/49_AI_PROMPT_GENERATOR.md b/md/49_AI_PROMPT_GENERATOR.md index 84d85a0..37f22e0 100644 --- a/md/49_AI_PROMPT_GENERATOR.md +++ b/md/49_AI_PROMPT_GENERATOR.md @@ -237,15 +237,14 @@
- - - - - - - - - +
@@ -280,18 +279,25 @@ -
-
- - + +
+
+
+
- @@ -406,8 +412,9 @@ const btnCloseModalBottom = document.getElementById('btnCloseModalBottom'); const btnModalBack = document.getElementById('btnModalBack'); const btnSavePresetSubmit = document.getElementById('btnSavePresetSubmit'); + const btnCreateStructuredPreset = document.getElementById('btnCreateStructuredPreset'); const presetNameInput = document.getElementById('presetNameInput'); - const presetCategoryInput = document.getElementById('presetCategoryInput'); + const presetCategorySelect = document.getElementById('presetCategorySelect'); const presetKeywordsInput = document.getElementById('presetKeywordsInput'); const presetBarsInput = document.getElementById('presetBarsInput'); const presetBpmInput = document.getElementById('presetBpmInput'); @@ -647,18 +654,35 @@ }, 2500); } - // Open Save Preset Modal Event Handler + // Open Save Preset Modal Event Handler — auto-populate per spec btnSavePresetModal.addEventListener('click', () => { const timeData = computeTimeMetrics(); - const currentPromptText = promptOutputText.innerText; + const currentPromptText = promptOutputText.innerText.trim(); - // Auto-populate Preset Form Fields const sectionType = inputSectionType.value.trim() || "Building Section"; const role = inputRole.value.trim() || "Composer"; - + const actionDesc = inputActionDesc.value.trim(); + + // Auto-infer Category based on Role + let category = "Orchestral / Film Score"; + const roleLower = role.toLowerCase(); + if (roleLower.includes("piano")) category = "Piano Solo"; + else if (roleLower.includes("edm") || roleLower.includes("electro")) category = "EDM / Pop"; + else if (roleLower.includes("jazz")) category = "Jazz & Swing"; + else if (roleLower.includes("lo-fi") || roleLower.includes("chillhop")) category = "Lo-fi Chillhop"; + + // Auto-generate Keyword Tags + const keywordList = [ + sectionType.toLowerCase(), + roleLower.split(' ')[0], + `${timeData.barCount} bars` + ]; + const keywordsStr = keywordList.filter(Boolean).join(', '); + + // Populate Form Fields presetNameInput.value = `${sectionType} (${timeData.barCount} Bars)`; - presetCategoryInput.value = role.includes("piano") ? "Piano Solo" : "Orchestral / Film Score"; - presetKeywordsInput.value = `${sectionType.toLowerCase()}, ${role.toLowerCase().split(' ')[0]}, ${timeData.barCount} bars`; + presetCategorySelect.value = category; + presetKeywordsInput.value = keywordsStr; presetBarsInput.value = timeData.barCount; presetBpmInput.value = 120; presetScaleInput.value = "C Minor"; @@ -677,10 +701,25 @@ } }); - // Save Preset Action Handler (Saves to daw_ai_prompt_presets localStorage) + // Create Structured Preset — replace static numbers with template variables + btnCreateStructuredPreset.addEventListener('click', () => { + let promptText = presetTemplateTextarea.value; + const bars = presetBarsInput.value || "8"; + + promptText = promptText + .replace(new RegExp(`dài ${bars} ô nhịp`, 'g'), 'dài {{bars}} ô nhịp') + .replace(/Từ Ô nhịp \d+ đến \d+/g, 'Từ Ô nhịp {{start_bar}} đến {{end_bar}}') + .replace(/Phách 0\.0 đến \d+\.0/g, 'Phách 0.0 đến {{total_beats}}') + .replace(/phách \d+\.0/g, 'phách {{total_beats}}'); + + presetTemplateTextarea.value = promptText; + showToast("Đã chuyển đổi Prompt thành mẫu biến cấu trúc!"); + }); + + // Save Preset Action Handler (Saves to daw_ai_prompt_presets localStorage & dispatches event) btnSavePresetSubmit.addEventListener('click', () => { const name = presetNameInput.value.trim(); - const category = presetCategoryInput.value.trim() || "General"; + const category = presetCategorySelect.value; const keywordsRaw = presetKeywordsInput.value.trim(); const defaultBars = parseInt(presetBarsInput.value) || 8; const defaultBpm = parseInt(presetBpmInput.value) || 120; @@ -726,6 +765,8 @@ try { localStorage.setItem('daw_ai_prompt_presets', JSON.stringify(savedPresets)); + // Dispatch Custom Event so parent window updates preset list instantly + window.dispatchEvent(new CustomEvent('AI_PRESET_SAVED', { detail: newPresetObj })); showToast(`Đã lưu Preset "${name}" thành công!`); presetManagerModal.classList.add('hidden'); } catch (e) { diff --git a/wiki.md b/wiki.md index fc5305e..a542250 100644 --- a/wiki.md +++ b/wiki.md @@ -913,3 +913,9 @@ - **Tóm tắt thay đổi:** Sửa layout right column của generator page: header thêm `shrink-0` để không bị co lại; container preview đổi thành `flex-1 flex flex-col min-h-0` thay vì `overflow-y-auto`; `#promptOutputText` đổi từ `h-full` sang `flex-1` + `overflow-y-auto` để bottom luôn neo với cạnh modal và nội dung scroll trong element. - **Các file ảnh hưởng:** `md/49_AI_PROMPT_GENERATOR.md` - **Ghi chú/Test (nếu có):** Refresh trang generator → right column fill full height → content scroll bên trong #promptOutputText. + +--- +### [2026-07-31 06:35] Task: Apply generator-to-preset-manager transfer spec +- **Tóm tắt thay đổi:** Áp dụng spec từ md/50_APPLY_GENERATOR.md vào md/49_AI_PROMPT_GENERATOR.md: (1) Category input chuyển thành `