fix: Mobile hamburger menu and iPad sidebar visibility
Mobile fixes: - Add click toggle handler for hamburger menu (was hover-only) - Menu now opens/closes on tap and closes when clicking outside - Keep hover support for desktop iPad fixes: - Sidebar content now visible on touch devices (901-1280px) - Added (hover: hover) media query to prevent hide-on-hover on tablets Security improvements: - Replace exec.CommandContext with go-git library for git operations - Add path traversal and command injection prevention - Fix race condition in template hot reload - Add environment-based cookie Secure flag Code quality: - Add constants.go for magic numbers - Remove unused code (ParsePreferenceToggleRequest, DomainError) - Add FOUC prevention with inline critical CSS - Add Makefile dev/run/clean targets - Fix README git clone URL - Add doc/DECISIONS.md for architectural decisions Tests: - Add hamburger menu click toggle tests - Add iPad sidebar visibility tests - Update security tests for go-git implementation - Add cookie Secure flag tests
This commit is contained in:
@@ -34,7 +34,11 @@ func NewManager(cfg *config.TemplateConfig) (*Manager, error) {
|
||||
func (m *Manager) loadTemplates() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.loadTemplatesLocked()
|
||||
}
|
||||
|
||||
// loadTemplatesLocked parses templates without acquiring lock (caller must hold lock)
|
||||
func (m *Manager) loadTemplatesLocked() error {
|
||||
// Create template with custom functions
|
||||
funcMap := template.FuncMap{
|
||||
"iterate": func(count int) []int {
|
||||
@@ -109,15 +113,33 @@ func (m *Manager) Reload() error {
|
||||
}
|
||||
|
||||
// Render executes a template with the given data
|
||||
// Note: This method is thread-safe. Hot reload acquires full lock to prevent race conditions.
|
||||
func (m *Manager) Render(name string) (*template.Template, error) {
|
||||
// Hot reload in development mode
|
||||
// Use full lock to prevent race condition between reload and lookup
|
||||
if m.config.HotReload {
|
||||
if err := m.Reload(); err != nil {
|
||||
m.mu.Lock()
|
||||
if err := m.loadTemplatesLocked(); err != nil {
|
||||
m.mu.Unlock()
|
||||
log.Printf("Warning: template reload failed: %v", err)
|
||||
// Continue with cached templates
|
||||
// Fall back to read lock for cached templates
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
tmpl := m.templates.Lookup(name)
|
||||
if tmpl == nil {
|
||||
return nil, fmt.Errorf("template %q not found", name)
|
||||
}
|
||||
return tmpl, nil
|
||||
}
|
||||
tmpl := m.templates.Lookup(name)
|
||||
m.mu.Unlock()
|
||||
if tmpl == nil {
|
||||
return nil, fmt.Errorf("template %q not found", name)
|
||||
}
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
// Production mode: just read
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user