diff --git a/internal/build/assets/js/main.js b/internal/build/assets/js/main.js deleted file mode 100644 index 3695c68..0000000 --- a/internal/build/assets/js/main.js +++ /dev/null @@ -1,127 +0,0 @@ -(function() { - 'use strict'; - - // Live reload when studio saves settings - const channel = new BroadcastChannel('writekit-studio'); - channel.onmessage = function(event) { - if (event.data.type === 'settings-changed') { - location.reload(); - } - }; - - document.addEventListener('DOMContentLoaded', initSearch); - - function initSearch() { - const trigger = document.getElementById('search-trigger'); - const modal = document.getElementById('search-modal'); - const backdrop = modal?.querySelector('.search-modal-backdrop'); - const input = document.getElementById('search-input'); - const results = document.getElementById('search-results'); - - if (!trigger || !modal || !input || !results) return; - - let debounceTimer; - - function open() { - modal.classList.add('active'); - document.body.style.overflow = 'hidden'; - input.value = ''; - results.innerHTML = ''; - setTimeout(() => input.focus(), 10); - } - - function close() { - modal.classList.remove('active'); - document.body.style.overflow = ''; - } - - trigger.addEventListener('click', open); - backdrop.addEventListener('click', close); - - document.addEventListener('keydown', function(e) { - if (e.key === '/' && !modal.classList.contains('active') && - !['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) { - e.preventDefault(); - open(); - } - if (e.key === 'Escape' && modal.classList.contains('active')) { - close(); - } - }); - - input.addEventListener('input', function() { - const query = this.value.trim(); - clearTimeout(debounceTimer); - - if (query.length < 2) { - results.innerHTML = ''; - return; - } - - debounceTimer = setTimeout(() => search(query), 150); - }); - - input.addEventListener('keydown', function(e) { - const items = results.querySelectorAll('.search-result'); - const focused = results.querySelector('.search-result.focused'); - - if (e.key === 'ArrowDown') { - e.preventDefault(); - if (!focused && items.length) { - items[0].classList.add('focused'); - } else if (focused?.nextElementSibling) { - focused.classList.remove('focused'); - focused.nextElementSibling.classList.add('focused'); - } - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - if (focused?.previousElementSibling) { - focused.classList.remove('focused'); - focused.previousElementSibling.classList.add('focused'); - } - } else if (e.key === 'Enter' && focused) { - e.preventDefault(); - const link = focused.querySelector('a'); - if (link) window.location.href = link.href; - } - }); - - async function search(query) { - try { - const res = await fetch('/api/reader/search?q=' + encodeURIComponent(query)); - const data = await res.json(); - - if (!data || data.length === 0) { - results.innerHTML = '
No results found
'; - return; - } - - results.innerHTML = data.map(r => ` -
- -
${highlight(r.title, query)}
- ${r.description ? `
${highlight(r.description, query)}
` : ''} -
-
- `).join(''); - } catch (e) { - results.innerHTML = '
Search failed
'; - } - } - - function highlight(text, query) { - if (!text) return ''; - const escaped = escapeHtml(text); - const tokens = query.split(/\s+/).filter(t => t.length > 0); - if (!tokens.length) return escaped; - const pattern = tokens.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'); - return escaped.replace(new RegExp(`(${pattern})`, 'gi'), '$1'); - } - - function escapeHtml(text) { - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; - } - } -})(); diff --git a/internal/build/templates/base.html b/internal/build/templates/base.html deleted file mode 100644 index 2effa31..0000000 --- a/internal/build/templates/base.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - {{.Title}} - - - - {{if .NoIndex}}{{end}} - - - - - - - {{if .OGImage}}{{end}} - - - - - - - {{if .OGImage}}{{end}} - - - - - {{if .FontURL}}{{end}} - - - - - - {{if .StructuredData}} - - {{end}} - - - - -
- {{template "content" .}} -
- - - -
-
-
- -
-
Press ESC to close
-
-
- - - - - {{block "scripts" .}}{{end}} - - diff --git a/internal/build/assets/assets.go b/internal/tenant/assets/assets.go similarity index 81% rename from internal/build/assets/assets.go rename to internal/tenant/assets/assets.go index a7eed10..a8c055c 100644 --- a/internal/build/assets/assets.go +++ b/internal/tenant/assets/assets.go @@ -6,6 +6,8 @@ import ( "net/http" ) +// Embedded static assets for tenant blogs +// //go:embed css js var staticFS embed.FS diff --git a/internal/build/assets/css/style.css b/internal/tenant/assets/css/style.css similarity index 85% rename from internal/build/assets/css/style.css rename to internal/tenant/assets/css/style.css index a52612e..5382977 100644 --- a/internal/build/assets/css/style.css +++ b/internal/tenant/assets/css/style.css @@ -392,7 +392,7 @@ main { margin: 0; } -.prose pre { +.prose pre:not(.chroma) { margin: var(--content-spacing) 0; padding: 1.125rem 1.25rem; background: var(--bg-secondary); @@ -404,6 +404,13 @@ main { line-height: 1.6; } +.prose pre.chroma { + font-family: var(--font-mono); + font-size: 0.875rem; + line-height: 1.6; + overflow-x: auto; +} + .prose code { font-family: var(--font-mono); font-size: 0.875em; @@ -419,6 +426,64 @@ main { font-size: inherit; } +/* Enhanced Code Block */ +.code-block { + margin: var(--content-spacing) 0; + border: 1px solid var(--border); + border-radius: 0.5rem; + overflow: hidden; + background: var(--bg-secondary); +} + +.code-block pre { + margin: 0; + border: none; + border-radius: 0; + padding: 1rem 1.25rem; +} + +.code-header { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.625rem 1rem; + background: var(--bg); + border-bottom: 1px solid var(--border); + font-size: 0.8125rem; + color: var(--text-muted); +} + +.code-icon { + width: 1rem; + height: 1rem; + flex-shrink: 0; +} + +.code-title { + flex: 1; + font-family: var(--font-mono); + font-size: 0.8125rem; +} + +.code-copy { + padding: 0.25rem 0.625rem; + font-size: 0.75rem; + font-weight: 500; + color: var(--text-muted); + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 0.25rem; + cursor: pointer; + transition: all 0.15s; + font-family: var(--font-body); +} + +.code-copy:hover { + color: var(--text); + background: var(--bg); + border-color: var(--text-muted); +} + .prose img { max-width: 100%; height: auto; @@ -512,147 +577,6 @@ main { font-weight: 500; } -/* Search */ -.search-trigger { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.375rem 0.75rem; - border: 1px solid var(--border); - border-radius: 0.375rem; - background: var(--bg); - color: var(--text-muted); - cursor: pointer; - font-size: 0.875rem; - transition: border-color 0.15s; -} - -.search-trigger:hover { - border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); -} - -.search-trigger kbd { - padding: 0.125rem 0.375rem; - background: var(--bg-secondary); - border-radius: 0.25rem; - font-family: var(--font-mono); - font-size: 0.6875rem; -} - -.search-modal { - display: none; - position: fixed; - inset: 0; - z-index: 100; -} - -.search-modal.active { - display: block; -} - -.search-modal-backdrop { - position: absolute; - inset: 0; - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(2px); -} - -.search-modal-content { - position: absolute; - top: 15%; - left: 50%; - transform: translateX(-50%); - width: 90%; - max-width: 520px; - background: var(--bg); - border-radius: 0.5rem; - box-shadow: 0 20px 40px -8px rgba(0, 0, 0, 0.2); - overflow: hidden; -} - -#search-input { - width: 100%; - padding: 1rem 1.25rem; - border: none; - font-size: 1rem; - outline: none; - background: var(--bg); - color: var(--text); -} - -#search-input::placeholder { - color: var(--text-muted); -} - -.search-results { - max-height: 320px; - overflow-y: auto; - border-top: 1px solid var(--border); -} - -.search-result { - border-bottom: 1px solid var(--border); -} - -.search-result:last-child { - border-bottom: none; -} - -.search-result a { - display: block; - padding: 0.875rem 1.25rem; - color: inherit; -} - -.search-result a:hover { - text-decoration: none; -} - -.search-result:hover, .search-result.focused { - background: var(--bg-secondary); -} - -.search-result-title { - font-weight: 500; - font-size: 0.9375rem; -} - -.search-result-snippet { - font-size: 0.8125rem; - color: var(--text-muted); - margin-top: 0.25rem; - line-height: 1.5; -} - -.search-result mark { - background: color-mix(in srgb, var(--accent) 25%, transparent); - color: inherit; - border-radius: 0.125rem; - padding: 0 0.125rem; -} - -.search-hint { - padding: 0.625rem 1.25rem; - font-size: 0.75rem; - color: var(--text-muted); - text-align: center; - border-top: 1px solid var(--border); -} - -.search-hint kbd { - padding: 0.125rem 0.375rem; - background: var(--bg-secondary); - border-radius: 0.25rem; - font-family: var(--font-mono); -} - -.search-no-results { - padding: 1.5rem 1.25rem; - text-align: center; - color: var(--text-muted); - font-size: 0.9375rem; -} - /* Comment Form */ .comment-form textarea { width: 100%; diff --git a/internal/tenant/assets/js/tenant-blog.js b/internal/tenant/assets/js/tenant-blog.js new file mode 100644 index 0000000..661d189 --- /dev/null +++ b/internal/tenant/assets/js/tenant-blog.js @@ -0,0 +1,10 @@ +(function() { + 'use strict'; + + const channel = new BroadcastChannel('writekit-studio'); + channel.onmessage = function(event) { + if (event.data.type === 'settings-changed') { + location.reload(); + } + }; +})(); diff --git a/internal/build/assets/js/post.js b/internal/tenant/assets/js/tenant-post.js similarity index 100% rename from internal/build/assets/js/post.js rename to internal/tenant/assets/js/tenant-post.js diff --git a/internal/tenant/templates/base.html b/internal/tenant/templates/base.html new file mode 100644 index 0000000..cfd08db --- /dev/null +++ b/internal/tenant/templates/base.html @@ -0,0 +1,63 @@ + + + + + + {{.Title}} + + + + {{if .NoIndex}}{{end}} + + + + + + + {{if .OGImage}}{{end}} + + + + + + + {{if .OGImage}}{{end}} + + + + + {{if .FontURL}}{{end}} + + + + {{if .StructuredData}} + + {{end}} + + +
+ + + +
+ {{template "content" .}} +
+ + + + + {{block "scripts" .}}{{end}} +
+ + diff --git a/internal/build/templates/blog.html b/internal/tenant/templates/blog.html similarity index 100% rename from internal/build/templates/blog.html rename to internal/tenant/templates/blog.html diff --git a/internal/build/templates/home.html b/internal/tenant/templates/home.html similarity index 100% rename from internal/build/templates/home.html rename to internal/tenant/templates/home.html diff --git a/internal/build/templates/post.html b/internal/tenant/templates/post.html similarity index 97% rename from internal/build/templates/post.html rename to internal/tenant/templates/post.html index 10f1791..dc89b7c 100644 --- a/internal/build/templates/post.html +++ b/internal/tenant/templates/post.html @@ -42,7 +42,7 @@ {{define "scripts"}} {{if or .InteractionConfig.ReactionsEnabled .InteractionConfig.CommentsEnabled}} - +