diff --git a/static/css/components/recipe-modal.css b/static/css/components/recipe-modal.css index 2be60cfd..23be8846 100644 --- a/static/css/components/recipe-modal.css +++ b/static/css/components/recipe-modal.css @@ -4,15 +4,20 @@ justify-content: flex-start; align-items: flex-start; border-bottom: 1px solid var(--lora-border); - padding-bottom: 10px; - margin-bottom: 10px; + padding-bottom: var(--space-2); + margin-bottom: var(--space-3); + position: relative; } .recipe-modal-header h2 { - font-size: 1.4em; /* Reduced from default h2 size */ - line-height: 1.3; - margin: 0; - max-height: 2.6em; /* Limit to 2 lines */ + margin: 0 0 var(--space-1); + padding: var(--space-1); + border-radius: var(--border-radius-xs); + font-size: 1.5em; + font-weight: 600; + line-height: 1.2; + color: var(--text-color); + max-height: 2.8em; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; @@ -127,7 +132,7 @@ /* Recipe Tags styles */ .recipe-tags-container { position: relative; - margin-top: 6px; + margin-top: 0; margin-bottom: 10px; } @@ -225,6 +230,62 @@ overflow: hidden; } +/* Recipe Header Actions */ +.recipe-header-actions { + display: flex; + align-items: center; + gap: var(--space-2); + flex-wrap: wrap; + width: 100%; + margin-bottom: var(--space-1); + flex-shrink: 0; + min-height: 0; +} + +.recipe-header-actions:empty { + display: none; +} + +.recipe-source-url-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + background: rgba(0, 0, 0, 0.03); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: var(--border-radius-sm); + color: var(--text-color); + cursor: pointer; + font-weight: 500; + font-size: 0.9em; + transition: all 0.2s; + white-space: nowrap; +} + +[data-theme="dark"] .recipe-source-url-btn { + background: rgba(255, 255, 255, 0.03); + border: 1px solid var(--lora-border); +} + +.recipe-source-url-btn:hover { + background: oklch(var(--lora-accent-l) var(--lora-accent-c) var(--lora-accent-h) / 0.1); + border-color: var(--lora-accent); + transform: translateY(-1px); +} + +.recipe-source-url-btn i { + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; +} + +@media (max-height: 860px) { + .recipe-header-actions { + padding-bottom: 4px; + } +} + /* Top Section: Preview and Gen Params */ .recipe-top-section { display: grid; @@ -1083,13 +1144,13 @@ } .recipe-modal-header { - padding-bottom: 6px; - margin-bottom: 8px; + padding-bottom: var(--space-1); + margin-bottom: var(--space-2); } .recipe-modal-header h2 { - font-size: 1.25em; - max-height: 2.5em; + font-size: 1.3em; + max-height: 2.4em; } .recipe-tags-container { diff --git a/static/js/components/RecipeModal.js b/static/js/components/RecipeModal.js index b461d236..02f5528d 100644 --- a/static/js/components/RecipeModal.js +++ b/static/js/components/RecipeModal.js @@ -383,6 +383,7 @@ class RecipeModal { this.syncGenerationParams(hydratedRecipe.gen_params); this.syncResourcesSection(hydratedRecipe); + this.syncSourceUrlAction(); // Show the modal modalManager.showModal('recipeModal'); @@ -515,6 +516,7 @@ class RecipeModal { } else { this.updateSourceUrlDisplay(this.currentRecipe.source_path || ''); } + this.syncSourceUrlAction(); } getPreviewMediaUrl(recipe = {}) { @@ -582,6 +584,30 @@ class RecipeModal { } } + syncSourceUrlAction() { + const actionsContainer = document.getElementById('recipeHeaderActions'); + if (!actionsContainer) { + return; + } + + actionsContainer.innerHTML = ''; + + const sourcePath = this.currentRecipe?.source_path || ''; + const isValidUrl = sourcePath.startsWith('http://') || sourcePath.startsWith('https://'); + if (!isValidUrl) { + return; + } + + const btn = document.createElement('button'); + btn.className = 'recipe-source-url-btn'; + btn.title = sourcePath; + btn.innerHTML = ' Open Source URL'; + btn.addEventListener('click', () => { + window.open(sourcePath, '_blank'); + }); + actionsContainer.appendChild(btn); + } + syncTagsDisplay(tags) { const tagsContainer = document.getElementById('recipeTagsCompact'); if (!tagsContainer) { @@ -1316,6 +1342,7 @@ class RecipeModal { // Update source URL in the UI this.commitField('source_path'); this.updateSourceUrlDisplay(newSourceUrl, { forceInputSync: true }); + this.syncSourceUrlAction(); // Update the current recipe object this.currentRecipe.source_path = newSourceUrl; diff --git a/templates/components/recipe_modal.html b/templates/components/recipe_modal.html index 74ce975c..41327356 100644 --- a/templates/components/recipe_modal.html +++ b/templates/components/recipe_modal.html @@ -4,6 +4,8 @@

Recipe Details

+ +