diff --git a/cmd/serve.go b/cmd/serve.go
index beb7b51..e29ddfc 100644
--- a/cmd/serve.go
+++ b/cmd/serve.go
@@ -6,6 +6,7 @@ import (
"net/http"
"os"
"os/signal"
+ "strings"
"syscall"
"github.com/go-chi/chi/v5"
@@ -143,6 +144,7 @@ func runServe(cmd *cobra.Command, args []string) {
// Static library serving (for demo sites)
router.Get("/insertr.js", contentHandler.ServeInsertrJS)
+ router.Get("/insertr.css", contentHandler.ServeInsertrCSS)
// API routes
router.Route("/api", func(apiRouter chi.Router) {
@@ -163,11 +165,21 @@ func runServe(cmd *cobra.Command, args []string) {
})
// Static site serving - serve registered sites at /sites/{site_id}
- // This fixes the MIME type issues with Chi's FileServer
+ // Custom file server that fixes CSS MIME types
for siteID, siteConfig := range siteManager.GetAllSites() {
log.Printf("📁 Serving site %s from %s at /sites/%s/", siteID, siteConfig.Path, siteID)
+
+ // Create custom file server with MIME type fixing
fileServer := http.FileServer(http.Dir(siteConfig.Path))
- router.Handle("/sites/"+siteID+"/*", http.StripPrefix("/sites/"+siteID+"/", fileServer))
+ customHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Fix MIME type for CSS files (including extensionless ones in css/ directory)
+ if strings.Contains(r.URL.Path, "/css/") {
+ w.Header().Set("Content-Type", "text/css; charset=utf-8")
+ }
+ fileServer.ServeHTTP(w, r)
+ })
+
+ router.Handle("/sites/"+siteID+"/*", http.StripPrefix("/sites/"+siteID+"/", customHandler))
}
// Start server
diff --git a/internal/api/handlers.go b/internal/api/handlers.go
index 306e8c8..dd9877c 100644
--- a/internal/api/handlers.go
+++ b/internal/api/handlers.go
@@ -731,3 +731,30 @@ func (h *ContentHandler) ServeInsertrJS(w http.ResponseWriter, r *http.Request)
// Copy file contents to response
io.Copy(w, file)
}
+
+// ServeInsertrCSS handles GET /insertr.css - serves the insertr CSS stylesheet
+func (h *ContentHandler) ServeInsertrCSS(w http.ResponseWriter, r *http.Request) {
+ // Path to the built insertr.css file
+ cssPath := "lib/dist/insertr.css"
+
+ // Check if file exists
+ if _, err := os.Stat(cssPath); os.IsNotExist(err) {
+ http.Error(w, "insertr.css not found - run 'just build-lib' to build the library", http.StatusNotFound)
+ return
+ }
+
+ // Open and serve the file
+ file, err := os.Open(cssPath)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("Failed to open insertr.css: %v", err), http.StatusInternalServerError)
+ return
+ }
+ defer file.Close()
+
+ // Set appropriate headers
+ w.Header().Set("Content-Type", "text/css")
+ w.Header().Set("Cache-Control", "no-cache") // For development
+
+ // Copy file contents to response
+ io.Copy(w, file)
+}
diff --git a/internal/api/middleware.go b/internal/api/middleware.go
index dc4d9fa..25327f5 100644
--- a/internal/api/middleware.go
+++ b/internal/api/middleware.go
@@ -61,6 +61,7 @@ func ContentTypeMiddleware(next http.Handler) http.Handler {
if r.URL.Path != "/" &&
!strings.HasPrefix(r.URL.Path, "/sites/") &&
!strings.HasPrefix(r.URL.Path, "/insertr.js") &&
+ !strings.HasPrefix(r.URL.Path, "/insertr.css") &&
(r.Method == "GET" || r.Method == "POST" || r.Method == "PUT") {
w.Header().Set("Content-Type", "application/json")
}
diff --git a/internal/engine/injector.go b/internal/engine/injector.go
index ed22e3b..95c2c18 100644
--- a/internal/engine/injector.go
+++ b/internal/engine/injector.go
@@ -364,19 +364,20 @@ func (i *Injector) InjectEditorScript(doc *html.Node) {
return
}
- // Create script element that loads insertr.js from our server with site configuration
- scriptHTML := fmt.Sprintf(``, i.siteID)
+ // Create CSS and script elements that load from our server with site configuration
+ insertrHTML := fmt.Sprintf(`
+`, i.siteID)
- // Parse and inject the script
- scriptDoc, err := html.Parse(strings.NewReader(scriptHTML))
+ // Parse and inject the CSS and script elements
+ insertrDoc, err := html.Parse(strings.NewReader(insertrHTML))
if err != nil {
log.Printf("Error parsing editor script HTML: %v", err)
return
}
- // Extract and inject all script elements
- if err := i.injectAllScriptElements(scriptDoc, headNode); err != nil {
- log.Printf("Error injecting script elements: %v", err)
+ // Extract and inject all CSS and script elements
+ if err := i.injectAllHeadElements(insertrDoc, headNode); err != nil {
+ log.Printf("Error injecting CSS and script elements: %v", err)
return
}
@@ -415,6 +416,38 @@ func (i *Injector) findAllScriptElements(node *html.Node) []*html.Node {
return scripts
}
+// injectAllHeadElements finds and injects all head elements (link, script) from parsed HTML
+func (i *Injector) injectAllHeadElements(doc *html.Node, targetNode *html.Node) error {
+ elements := i.findAllHeadElements(doc)
+
+ for _, element := range elements {
+ // Remove from original parent
+ if element.Parent != nil {
+ element.Parent.RemoveChild(element)
+ }
+ // Add to target node
+ targetNode.AppendChild(element)
+ }
+
+ return nil
+}
+
+// findAllHeadElements recursively finds all link and script elements
+func (i *Injector) findAllHeadElements(node *html.Node) []*html.Node {
+ var elements []*html.Node
+
+ if node.Type == html.ElementNode && (node.Data == "script" || node.Data == "link") {
+ elements = append(elements, node)
+ }
+
+ for child := node.FirstChild; child != nil; child = child.NextSibling {
+ childElements := i.findAllHeadElements(child)
+ elements = append(elements, childElements...)
+ }
+
+ return elements
+}
+
// hasInsertrGate checks if document has .insertr-gate elements
func (i *Injector) hasInsertrGate(node *html.Node) bool {
if node.Type == html.ElementNode {
diff --git a/justfile b/justfile
index e26ecc7..d7ba1e1 100644
--- a/justfile
+++ b/justfile
@@ -69,7 +69,7 @@ check:
build:
npm run build
-# Build only the JavaScript library
+# Build only the JavaScript library (includes CSS)
build-lib:
npm run build:lib
diff --git a/lib/package.json b/lib/package.json
index afba4c8..b14a733 100644
--- a/lib/package.json
+++ b/lib/package.json
@@ -10,7 +10,9 @@
"src/"
],
"scripts": {
- "build": "rollup -c",
+ "build": "npm run build:js && npm run build:css",
+ "build:js": "rollup -c",
+ "build:css": "cp src/styles/insertr.css dist/insertr.css",
"build:only": "rollup -c",
"watch": "rollup -c -w",
"dev": "rollup -c -w"
diff --git a/lib/src/core/auth.js b/lib/src/core/auth.js
index 4ba2022..237ae0a 100644
--- a/lib/src/core/auth.js
+++ b/lib/src/core/auth.js
@@ -218,7 +218,7 @@ export class InsertrAuth {
document.body.insertAdjacentHTML('beforeend', controlsHtml);
// Add styles for controls
- this.addControlStyles();
+
}
/**
@@ -417,136 +417,7 @@ export class InsertrAuth {
document.head.appendChild(styleSheet);
}
- /**
- * Add styles for authentication controls
- */
- addControlStyles() {
- const styles = `
- .insertr-auth-controls {
- position: fixed;
- bottom: 20px;
- right: 20px;
- z-index: 9999;
- display: flex;
- flex-direction: column;
- gap: 8px;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- }
- .insertr-auth-btn {
- background: #4f46e5;
- color: white;
- border: none;
- padding: 8px 16px;
- border-radius: 6px;
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.2s;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
- }
-
- .insertr-auth-btn:hover {
- background: #4338ca;
- transform: translateY(-1px);
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
- }
-
- .insertr-auth-btn.insertr-authenticated {
- background: #059669;
- }
-
- .insertr-auth-btn.insertr-authenticated:hover {
- background: #047857;
- }
-
- .insertr-auth-btn.insertr-edit-active {
- background: #dc2626;
- }
-
- .insertr-auth-btn.insertr-edit-active:hover {
- background: #b91c1c;
- }
-
- .insertr-status-controls {
- position: fixed;
- bottom: 20px;
- left: 20px;
- z-index: 9999;
- display: flex;
- flex-direction: column;
- gap: 8px;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- }
-
- .insertr-status {
- background: white;
- border: 1px solid #e5e7eb;
- border-radius: 8px;
- padding: 8px 12px;
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
- max-width: 200px;
- }
-
- .insertr-status-content {
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .insertr-status-text {
- font-size: 12px;
- font-weight: 500;
- color: #374151;
- }
-
- .insertr-status-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: #9ca3af;
- }
-
- .insertr-status-dot.insertr-status-visitor {
- background: #9ca3af;
- }
-
- .insertr-status-dot.insertr-status-authenticated {
- background: #059669;
- }
-
- .insertr-status-dot.insertr-status-editing {
- background: #dc2626;
- animation: insertr-pulse 2s infinite;
- }
-
- @keyframes insertr-pulse {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0.5; }
- }
-
- /* Hide editing interface when not in edit mode */
- body:not(.insertr-edit-mode) .insertr:hover::after {
- display: none !important;
- }
-
- /* Only show editing features when in edit mode */
- .insertr-authenticated.insertr-edit-mode .insertr {
- cursor: pointer;
- }
-
- .insertr-authenticated.insertr-edit-mode .insertr:hover {
- outline: 2px dashed #007cba !important;
- outline-offset: 2px !important;
- background-color: rgba(0, 124, 186, 0.05) !important;
- }
- `;
-
- const styleSheet = document.createElement('style');
- styleSheet.type = 'text/css';
- styleSheet.innerHTML = styles;
- document.head.appendChild(styleSheet);
- }
/**
* OAuth integration placeholder
diff --git a/lib/src/core/editor.js b/lib/src/core/editor.js
index 7161c73..76a96f0 100644
--- a/lib/src/core/editor.js
+++ b/lib/src/core/editor.js
@@ -19,8 +19,7 @@ export class InsertrEditor {
console.log('🚀 Starting Insertr Editor');
this.isActive = true;
- // Add editor styles
- this.addEditorStyles();
+
// Initialize all enhanced elements
const elements = this.core.getAllElements();
@@ -144,205 +143,5 @@ export class InsertrEditor {
}
- addEditorStyles() {
- const styles = `
- .insertr-editing-hover {
- outline: 2px dashed #007cba !important;
- outline-offset: 2px !important;
- background-color: rgba(0, 124, 186, 0.05) !important;
- }
-
- .insertr:hover::after {
- content: "✏️ " attr(data-content-type);
- position: absolute;
- top: -25px;
- left: 0;
- background: #007cba;
- color: white;
- padding: 2px 6px;
- font-size: 11px;
- border-radius: 3px;
- white-space: nowrap;
- z-index: 1000;
- font-family: monospace;
- }
- /* Version History Modal Styles */
- .insertr-version-modal {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 10001;
- }
-
- .insertr-version-backdrop {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 20px;
- }
-
- .insertr-version-content-modal {
- background: white;
- border-radius: 8px;
- max-width: 600px;
- width: 100%;
- max-height: 80vh;
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
- display: flex;
- flex-direction: column;
- }
-
- .insertr-version-header {
- padding: 20px 20px 0;
- border-bottom: 1px solid #eee;
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-shrink: 0;
- }
-
- .insertr-version-header h3 {
- margin: 0 0 20px;
- color: #333;
- font-size: 18px;
- }
-
- .insertr-btn-close {
- background: none;
- border: none;
- font-size: 24px;
- cursor: pointer;
- color: #666;
- padding: 0;
- width: 30px;
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .insertr-btn-close:hover {
- color: #333;
- }
-
- .insertr-version-list {
- overflow-y: auto;
- padding: 20px;
- flex: 1;
- }
-
- .insertr-version-item {
- border: 1px solid #e1e5e9;
- border-radius: 6px;
- padding: 16px;
- margin-bottom: 12px;
- background: #f8f9fa;
- }
-
- .insertr-version-meta {
- display: flex;
- align-items: center;
- gap: 12px;
- margin-bottom: 8px;
- font-size: 13px;
- }
-
- .insertr-version-label {
- font-weight: 600;
- color: #0969da;
- }
-
- .insertr-version-date {
- color: #656d76;
- }
-
- .insertr-version-user {
- color: #656d76;
- }
-
- .insertr-version-content {
- margin-bottom: 12px;
- padding: 8px;
- background: white;
- border-radius: 4px;
- font-family: monospace;
- font-size: 14px;
- color: #24292f;
- white-space: pre-wrap;
- }
-
- .insertr-version-actions {
- display: flex;
- gap: 8px;
- }
-
- .insertr-btn-restore {
- background: #0969da;
- color: white;
- border: none;
- padding: 6px 12px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 12px;
- font-weight: 500;
- }
-
- .insertr-btn-restore:hover {
- background: #0860ca;
- }
-
- .insertr-btn-view-diff {
- background: #f6f8fa;
- color: #24292f;
- border: 1px solid #d1d9e0;
- padding: 6px 12px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 12px;
- font-weight: 500;
- }
-
- .insertr-btn-view-diff:hover {
- background: #f3f4f6;
- }
-
- .insertr-version-empty {
- text-align: center;
- color: #656d76;
- font-style: italic;
- padding: 40px 20px;
- }
-
- /* History Button in Form */
- .insertr-btn-history {
- background: #6f42c1;
- color: white;
- border: none;
- padding: 8px 16px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 14px;
- font-weight: 500;
- margin-left: auto;
- }
-
- .insertr-btn-history:hover {
- background: #5a359a;
- }
- `;
-
- const styleSheet = document.createElement('style');
- styleSheet.type = 'text/css';
- styleSheet.innerHTML = styles;
- document.head.appendChild(styleSheet);
- }
}
\ No newline at end of file
diff --git a/lib/src/index.js b/lib/src/index.js
index aee8c13..d40c78b 100644
--- a/lib/src/index.js
+++ b/lib/src/index.js
@@ -20,6 +20,9 @@ window.Insertr = {
init(options = {}) {
console.log('🔧 Insertr v1.0.0 initializing... (Hot Reload Ready)');
+ // Load CSS first
+ this.loadStyles(options);
+
this.core = new InsertrCore(options);
this.auth = new InsertrAuth(options);
this.apiClient = new ApiClient(options);
@@ -35,6 +38,38 @@ window.Insertr = {
return this;
},
+ // Load Insertr CSS styles
+ loadStyles(options = {}) {
+ // Check if styles are already loaded
+ if (document.querySelector('link[href*="insertr.css"]') || document.getElementById('insertr-styles')) {
+ return;
+ }
+
+ // Determine CSS path
+ let cssPath = options.cssPath;
+ if (!cssPath) {
+ // Auto-detect based on script location
+ const insertrScript = document.querySelector('script[data-insertr-injected]');
+ if (insertrScript && insertrScript.src) {
+ // Replace insertr.min.js with insertr.css
+ cssPath = insertrScript.src.replace(/insertr(\.min)?\.js$/, 'insertr.css');
+ } else {
+ // Fallback to relative path
+ cssPath = 'lib/dist/insertr.css';
+ }
+ }
+
+ // Create and load CSS link
+ const link = document.createElement('link');
+ link.id = 'insertr-styles';
+ link.rel = 'stylesheet';
+ link.href = cssPath;
+ link.onload = () => console.log('✅ Insertr CSS loaded');
+ link.onerror = () => console.warn('⚠️ Failed to load Insertr CSS from:', cssPath);
+
+ document.head.appendChild(link);
+ },
+
// Start the system - only creates the minimal trigger
start() {
if (this.auth) {
diff --git a/lib/src/styles/insertr.css b/lib/src/styles/insertr.css
new file mode 100644
index 0000000..f8f39b6
--- /dev/null
+++ b/lib/src/styles/insertr.css
@@ -0,0 +1,662 @@
+/**
+ * Insertr - The Tailwind of CMS
+ * Unified CSS Styles with Cascade Layer Architecture
+ */
+
+@layer insertr {
+ /* =================================================================
+ CSS CUSTOM PROPERTIES - MODERN THEMING SYSTEM
+ ================================================================= */
+
+ :root {
+ /* Color System */
+ --insertr-primary: #4f46e5;
+ --insertr-primary-hover: #4338ca;
+ --insertr-primary-active: #3730a3;
+
+ --insertr-success: #10b981;
+ --insertr-success-hover: #059669;
+
+ --insertr-danger: #dc2626;
+ --insertr-danger-hover: #b91c1c;
+
+ --insertr-warning: #f59e0b;
+ --insertr-warning-hover: #d97706;
+
+ --insertr-info: #3b82f6;
+ --insertr-info-hover: #2563eb;
+
+ --insertr-purple: #6f42c1;
+ --insertr-purple-hover: #5a359a;
+
+ /* Text Colors */
+ --insertr-text-primary: #1f2937;
+ --insertr-text-secondary: #374151;
+ --insertr-text-muted: #6b7280;
+ --insertr-text-inverse: #ffffff;
+
+ /* Background Colors */
+ --insertr-bg-primary: #ffffff;
+ --insertr-bg-secondary: #f8fafc;
+ --insertr-bg-muted: #f3f4f6;
+ --insertr-bg-overlay: rgba(0, 0, 0, 0.5);
+
+ /* Border Colors */
+ --insertr-border-light: #e5e7eb;
+ --insertr-border-medium: #d1d5db;
+ --insertr-border-dark: #374151;
+
+ /* Component Colors */
+ --insertr-form-border: var(--insertr-border-medium);
+ --insertr-form-border-focus: var(--insertr-info);
+
+ /* Shadows */
+ --insertr-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
+ --insertr-shadow-md: 0 4px 8px rgba(0, 0, 0, 0.15);
+ --insertr-shadow-lg: 0 8px 25px rgba(0, 0, 0, 0.15);
+ --insertr-shadow-xl: 0 10px 25px rgba(0, 0, 0, 0.2);
+
+ /* Spacing */
+ --insertr-radius-sm: 4px;
+ --insertr-radius-md: 6px;
+ --insertr-radius-lg: 8px;
+
+ /* Z-Index Scale */
+ --insertr-z-tooltip: 1000;
+ --insertr-z-overlay: 9999;
+ --insertr-z-modal: 10000;
+ --insertr-z-modal-backdrop: 10001;
+ }
+
+ /* Dark Theme Override (when parent site uses dark theme) */
+ [data-theme="dark"] {
+ --insertr-text-primary: #f9fafb;
+ --insertr-text-secondary: #e5e7eb;
+ --insertr-text-muted: #9ca3af;
+ --insertr-bg-primary: #1f2937;
+ --insertr-bg-secondary: #374151;
+ --insertr-bg-muted: #4b5563;
+ --insertr-border-light: #4b5563;
+ --insertr-border-medium: #6b7280;
+ }
+
+ /* =================================================================
+ FOUNDATION & SPECIFIC TARGETING
+ =================================================================
+
+ HYBRID CSS ARCHITECTURE:
+
+ This CSS uses a hybrid approach for maximum browser compatibility:
+
+ 1. MODERN BROWSERS: @layer insertr provides cascade layer isolation
+ - Layered styles automatically win over unlayered styles
+ - Clean separation regardless of specificity
+ - Future-proof CSS architecture
+
+ 2. LEGACY BROWSERS: Higher specificity fallback selectors
+ - html body .insertr-* selectors (0,0,3,0) beat universal * (0,0,0,1)
+ - Works in all browsers back to IE6
+ - Ensures consistent behavior across environments
+
+ This ensures our form elements maintain proper spacing and styling
+ even on sites with aggressive CSS resets like "* {margin:0; padding:0}"
+ ================================================================= */
+
+ /* Minimal gate foundation */
+ .insertr-gate {
+ cursor: pointer;
+ user-select: none;
+ }
+
+ /* UI component protection - specific targeting only */
+ .insertr-edit-form,
+ .insertr-edit-form * {
+ color: var(--insertr-text-primary);
+ }
+
+ .insertr-auth-controls,
+ .insertr-auth-controls * {
+ color: var(--insertr-text-primary);
+ }
+
+ .insertr-version-modal,
+ .insertr-version-modal * {
+ color: var(--insertr-text-primary);
+ }
+
+ /* Component-specific color overrides */
+ .insertr-btn-save,
+ .insertr-btn-cancel,
+ .insertr-btn-history,
+ .insertr-auth-btn {
+ color: var(--insertr-text-inverse);
+ }
+
+ .insertr-form-help {
+ color: var(--insertr-text-muted);
+ }
+
+ .insertr-status-text {
+ color: var(--insertr-text-secondary);
+ }
+
+ /* =================================================================
+ AUTHENTICATION CONTROLS
+ ================================================================= */
+
+ .insertr-auth-controls {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ z-index: var(--insertr-z-overlay);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ }
+
+ .insertr-auth-btn {
+ background: var(--insertr-primary);
+ color: var(--insertr-text-inverse);
+ border: none;
+ padding: 8px 16px;
+ border-radius: var(--insertr-radius-md);
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s;
+ box-shadow: var(--insertr-shadow-sm);
+ }
+
+ .insertr-auth-btn:hover {
+ background: var(--insertr-primary-hover);
+ transform: translateY(-1px);
+ box-shadow: var(--insertr-shadow-md);
+ }
+
+ .insertr-auth-btn.insertr-authenticated {
+ background: var(--insertr-success);
+ }
+
+ .insertr-auth-btn.insertr-authenticated:hover {
+ background: var(--insertr-success-hover);
+ }
+
+ .insertr-auth-btn.insertr-edit-active {
+ background: var(--insertr-danger);
+ }
+
+ .insertr-auth-btn.insertr-edit-active:hover {
+ background: var(--insertr-danger-hover);
+ }
+
+ .insertr-status-controls {
+ position: fixed;
+ bottom: 20px;
+ left: 20px;
+ z-index: var(--insertr-z-overlay);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ }
+
+ .insertr-status {
+ background: var(--insertr-bg-primary);
+ border: 1px solid var(--insertr-border-light);
+ border-radius: var(--insertr-radius-lg);
+ padding: 8px 12px;
+ box-shadow: var(--insertr-shadow-md);
+ max-width: 200px;
+ }
+
+ .insertr-status-content {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .insertr-status-text {
+ font-size: 12px;
+ font-weight: 500;
+ color: var(--insertr-text-secondary);
+ }
+
+ .insertr-status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--insertr-text-muted);
+ }
+
+ .insertr-status-dot.insertr-status-visitor {
+ background: var(--insertr-text-muted);
+ }
+
+ .insertr-status-dot.insertr-status-authenticated {
+ background: var(--insertr-success);
+ }
+
+ .insertr-status-dot.insertr-status-editing {
+ background: var(--insertr-danger);
+ animation: insertr-pulse 2s infinite;
+ }
+
+ @keyframes insertr-pulse {
+
+ 0%,
+ 100% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0.5;
+ }
+ }
+
+ .insertr-authenticated.insertr-edit-mode .insertr {
+ cursor: pointer;
+ }
+
+ .insertr-authenticated.insertr-edit-mode .insertr:hover {
+ outline: 2px dashed #007cba !important;
+ outline-offset: 2px !important;
+ background-color: rgba(0, 124, 186, 0.05) !important;
+ }
+
+ /* =================================================================
+ EDITOR STYLES
+ ================================================================= */
+
+ .insertr-editing-hover {
+ outline: 2px dashed var(--insertr-info) !important;
+ outline-offset: 2px !important;
+ background-color: color-mix(in srgb, var(--insertr-info) 5%, transparent) !important;
+ }
+
+ .insertr:hover::after {
+ content: "✏️ " attr(data-content-type);
+ position: absolute;
+ top: -25px;
+ left: 0;
+ background: #007cba;
+ color: white;
+ padding: 2px 6px;
+ font-size: 11px;
+ border-radius: 3px;
+ white-space: nowrap;
+ z-index: 1000;
+ font-family: monospace;
+ }
+
+ /* =================================================================
+ MODAL AND FORM STYLES
+ ================================================================= */
+
+ /* Overlay and Form Container */
+ .insertr-form-overlay {
+ position: absolute;
+ z-index: 10000;
+ }
+
+ .insertr-edit-form {
+ background: var(--insertr-bg-primary);
+ border: 2px solid var(--insertr-info);
+ border-radius: var(--insertr-radius-lg);
+ padding: 1rem;
+ box-shadow: var(--insertr-shadow-lg);
+ width: 100%;
+ box-sizing: border-box;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ min-width: 600px;
+ max-width: 800px;
+ color: #1f2937;
+ }
+
+ /* Form Header */
+ .insertr-edit-form .insertr-form-header {
+ font-weight: 600;
+ color: var(--insertr-text-primary);
+ margin-bottom: 1rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid var(--insertr-border-light);
+ font-size: 0.875rem;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+
+ /* Form Groups and Fields */
+ .insertr-edit-form .insertr-form-group {
+ margin-bottom: 1rem;
+ }
+
+ .insertr-edit-form .insertr-form-group:last-child {
+ margin-bottom: 0;
+ }
+
+ /* Higher specificity fallback for browsers without cascade layer support */
+ html body .insertr-edit-form .insertr-form-group {
+ margin-bottom: 1rem;
+ }
+
+ html body .insertr-edit-form .insertr-form-group:last-child {
+ margin-bottom: 0;
+ }
+
+ .insertr-form-label {
+ display: block;
+ font-weight: 600;
+ color: var(--insertr-text-secondary);
+ margin-bottom: 0.5rem;
+ font-size: 0.875rem;
+ }
+
+ .insertr-edit-form .insertr-form-input,
+ .insertr-edit-form .insertr-form-textarea {
+ width: 100%;
+ padding: 0.75rem;
+ border: 1px solid var(--insertr-form-border);
+ border-radius: var(--insertr-radius-md);
+ font-family: inherit;
+ font-size: 1rem;
+ color: var(--insertr-text-primary);
+ background-color: var(--insertr-bg-primary);
+ transition: border-color 0.2s, box-shadow 0.2s;
+ box-sizing: border-box;
+ margin: 0;
+ }
+
+ /* Higher specificity fallback for browsers without cascade layer support */
+ html body .insertr-edit-form .insertr-form-input,
+ html body .insertr-edit-form .insertr-form-textarea {
+ padding: 0.75rem;
+ margin: 0;
+ }
+
+ .insertr-form-input:focus,
+ .insertr-form-textarea:focus {
+ outline: none;
+ border-color: var(--insertr-form-border-focus);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--insertr-info) 10%, transparent);
+ }
+
+ /* Markdown Editor Styling */
+ .insertr-form-textarea {
+ min-height: 120px;
+ resize: vertical;
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
+ }
+
+ .insertr-markdown-editor {
+ min-height: 200px;
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
+ font-size: 0.9rem;
+ line-height: 1.5;
+ color: var(--insertr-text-primary);
+ background-color: var(--insertr-bg-secondary);
+ }
+
+ /* Form Actions */
+ .insertr-edit-form .insertr-form-actions {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: flex-end;
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid var(--insertr-border-light);
+ }
+
+ /* Higher specificity fallback for browsers without cascade layer support */
+ html body .insertr-edit-form .insertr-form-actions {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ }
+
+ .insertr-edit-form .insertr-btn-save {
+ background: var(--insertr-success);
+ color: var(--insertr-text-inverse);
+ border: none;
+ padding: 0.5rem 1rem;
+ margin: 0;
+ border-radius: var(--insertr-radius-md);
+ font-size: 0.875rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+
+ /* Higher specificity fallback for browsers without cascade layer support */
+ html body .insertr-edit-form .insertr-btn-save {
+ padding: 0.5rem 1rem;
+ margin: 0;
+ }
+
+ .insertr-btn-save:hover {
+ background: var(--insertr-success-hover);
+ transform: translateY(-1px);
+ box-shadow: var(--insertr-shadow-sm);
+ }
+
+ .insertr-edit-form .insertr-btn-cancel {
+ background: var(--insertr-text-muted);
+ color: var(--insertr-text-inverse);
+ border: none;
+ padding: 0.5rem 1rem;
+ margin: 0;
+ border-radius: var(--insertr-radius-md);
+ font-size: 0.875rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+
+ /* Higher specificity fallback for browsers without cascade layer support */
+ html body .insertr-edit-form .insertr-btn-cancel {
+ padding: 0.5rem 1rem;
+ margin: 0;
+ }
+
+ .insertr-btn-cancel:hover {
+ background: var(--insertr-text-secondary);
+ transform: translateY(-1px);
+ box-shadow: var(--insertr-shadow-sm);
+ }
+
+ /* Preview Styling */
+ .insertr-preview-active {
+ background-color: rgba(16, 185, 129, 0.1) !important;
+ border: 1px dashed #10b981 !important;
+ transition: all 0.2s ease;
+ }
+
+ /* Form Help Text */
+ .insertr-form-help {
+ font-size: 0.75rem;
+ color: #6b7280;
+ margin-top: 0.25rem;
+ font-style: italic;
+ }
+
+ /* Responsive Design */
+ @media (max-width: 768px) {
+ .insertr-edit-form {
+ min-width: 300px;
+ max-width: calc(100vw - 40px);
+ }
+ }
+
+ /* =================================================================
+ VERSION HISTORY MODAL STYLES
+ ================================================================= */
+
+ .insertr-version-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 10001;
+ }
+
+ .insertr-version-backdrop {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ }
+
+ .insertr-version-content-modal {
+ background: white;
+ border-radius: 8px;
+ max-width: 600px;
+ width: 100%;
+ max-height: 80vh;
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
+ display: flex;
+ flex-direction: column;
+ }
+
+ .insertr-version-header {
+ padding: 20px 20px 0;
+ border-bottom: 1px solid #eee;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-shrink: 0;
+ }
+
+ .insertr-version-header h3 {
+ margin: 0 0 20px;
+ color: #333;
+ font-size: 18px;
+ }
+
+ .insertr-btn-close {
+ background: none;
+ border: none;
+ font-size: 24px;
+ cursor: pointer;
+ color: #666;
+ padding: 0;
+ width: 30px;
+ height: 30px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .insertr-btn-close:hover {
+ color: #333;
+ }
+
+ .insertr-version-list {
+ overflow-y: auto;
+ padding: 20px;
+ flex: 1;
+ }
+
+ .insertr-version-item {
+ border: 1px solid #e1e5e9;
+ border-radius: 6px;
+ padding: 16px;
+ margin-bottom: 12px;
+ background: #f8f9fa;
+ }
+
+ .insertr-version-meta {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 8px;
+ font-size: 13px;
+ }
+
+ .insertr-version-label {
+ font-weight: 600;
+ color: #0969da;
+ }
+
+ .insertr-version-date {
+ color: #656d76;
+ }
+
+ .insertr-version-user {
+ color: #656d76;
+ }
+
+ .insertr-version-content {
+ margin-bottom: 12px;
+ padding: 8px;
+ background: white;
+ border-radius: 4px;
+ font-family: monospace;
+ font-size: 14px;
+ color: #24292f;
+ white-space: pre-wrap;
+ }
+
+ .insertr-version-actions {
+ display: flex;
+ gap: 8px;
+ }
+
+ .insertr-btn-restore {
+ background: #0969da;
+ color: white;
+ border: none;
+ padding: 6px 12px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ font-weight: 500;
+ }
+
+ .insertr-btn-restore:hover {
+ background: #0860ca;
+ }
+
+ .insertr-btn-view-diff {
+ background: #f6f8fa;
+ color: #24292f;
+ border: 1px solid #d1d9e0;
+ padding: 6px 12px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ font-weight: 500;
+ }
+
+ .insertr-btn-view-diff:hover {
+ background: #f3f4f6;
+ }
+
+ .insertr-version-empty {
+ text-align: center;
+ color: #656d76;
+ font-style: italic;
+ padding: 40px 20px;
+ }
+
+ /* History Button in Form */
+ .insertr-btn-history {
+ background: #6f42c1;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ margin-left: auto;
+ }
+
+ .insertr-btn-history:hover {
+ background: #5a359a;
+ }
+}
diff --git a/lib/src/ui/form-renderer.js b/lib/src/ui/form-renderer.js
index 923fc09..04f8210 100644
--- a/lib/src/ui/form-renderer.js
+++ b/lib/src/ui/form-renderer.js
@@ -8,7 +8,6 @@ export class InsertrFormRenderer {
constructor(apiClient = null) {
this.apiClient = apiClient;
this.editor = new Editor();
- this.setupStyles();
}
/**
@@ -238,355 +237,5 @@ export class InsertrFormRenderer {
return div.innerHTML;
}
- /**
- * Setup form styles (consolidated and simplified)
- */
- setupStyles() {
- const styles = `
- /* Overlay and Form Container */
- .insertr-form-overlay {
- position: absolute;
- z-index: 10000;
- }
- .insertr-edit-form {
- background: white;
- border: 2px solid #007cba;
- border-radius: 8px;
- padding: 1rem;
- box-shadow: 0 8px 25px rgba(0,0,0,0.15);
- width: 100%;
- box-sizing: border-box;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- min-width: 600px;
- max-width: 800px;
- }
-
- /* Form Header */
- .insertr-form-header {
- font-weight: 600;
- color: #1f2937;
- margin-bottom: 1rem;
- padding-bottom: 0.5rem;
- border-bottom: 1px solid #e5e7eb;
- font-size: 0.875rem;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- }
-
- /* Form Groups and Fields */
- .insertr-form-group {
- margin-bottom: 1rem;
- }
-
- .insertr-form-group:last-child {
- margin-bottom: 0;
- }
-
- .insertr-form-label {
- display: block;
- font-weight: 600;
- color: #374151;
- margin-bottom: 0.5rem;
- font-size: 0.875rem;
- }
-
- .insertr-form-input,
- .insertr-form-textarea {
- width: 100%;
- padding: 0.75rem;
- border: 1px solid #d1d5db;
- border-radius: 6px;
- font-family: inherit;
- font-size: 1rem;
- transition: border-color 0.2s, box-shadow 0.2s;
- box-sizing: border-box;
- }
-
- .insertr-form-input:focus,
- .insertr-form-textarea:focus {
- outline: none;
- border-color: #007cba;
- box-shadow: 0 0 0 3px rgba(0, 124, 186, 0.1);
- }
-
- /* Markdown Editor Styling */
- .insertr-form-textarea {
- min-height: 120px;
- resize: vertical;
- font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
- }
-
- .insertr-markdown-editor {
- min-height: 200px;
- font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
- font-size: 0.9rem;
- line-height: 1.5;
- background-color: #f8fafc;
- }
-
- /* Form Actions */
- .insertr-form-actions {
- display: flex;
- gap: 0.5rem;
- justify-content: flex-end;
- margin-top: 1rem;
- padding-top: 1rem;
- border-top: 1px solid #e5e7eb;
- }
-
- .insertr-btn-save {
- background: #10b981;
- color: white;
- border: none;
- padding: 0.5rem 1rem;
- border-radius: 6px;
- font-weight: 500;
- cursor: pointer;
- transition: background-color 0.2s;
- font-size: 0.875rem;
- }
-
- .insertr-btn-save:hover {
- background: #059669;
- }
-
- .insertr-btn-cancel {
- background: #6b7280;
- color: white;
- border: none;
- padding: 0.5rem 1rem;
- border-radius: 6px;
- font-weight: 500;
- cursor: pointer;
- transition: background-color 0.2s;
- font-size: 0.875rem;
- }
-
- .insertr-btn-cancel:hover {
- background: #4b5563;
- }
-
- .insertr-btn-history {
- background: #6f42c1;
- color: white;
- border: none;
- padding: 0.5rem 1rem;
- border-radius: 6px;
- font-weight: 500;
- cursor: pointer;
- transition: background-color 0.2s;
- font-size: 0.875rem;
- }
-
- .insertr-btn-history:hover {
- background: #5a359a;
- }
-
- .insertr-form-help {
- font-size: 0.75rem;
- color: #6b7280;
- margin-top: 0.25rem;
- }
-
- /* Live Preview Styles */
- .insertr-preview-active {
- position: relative;
- background: rgba(0, 124, 186, 0.05) !important;
- outline: 2px solid #007cba !important;
- outline-offset: 2px;
- transition: all 0.3s ease;
- }
-
- .insertr-preview-active::after {
- content: "Preview";
- position: absolute;
- top: -25px;
- left: 0;
- background: #007cba;
- color: white;
- padding: 2px 8px;
- border-radius: 3px;
- font-size: 0.75rem;
- font-weight: 500;
- z-index: 10001;
- white-space: nowrap;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- }
-
- /* Responsive Design */
- @media (max-width: 768px) {
- .insertr-edit-form {
- min-width: 90vw;
- max-width: 90vw;
- }
-
- .insertr-preview-active::after {
- top: -20px;
- font-size: 0.7rem;
- padding: 1px 6px;
- }
- }
-
- /* Version History Modal Styles */
- .insertr-version-modal {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 10001;
- }
-
- .insertr-version-backdrop {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 20px;
- }
-
- .insertr-version-content-modal {
- background: white;
- border-radius: 8px;
- max-width: 600px;
- width: 100%;
- max-height: 80vh;
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
- display: flex;
- flex-direction: column;
- }
-
- .insertr-version-header {
- padding: 20px 20px 0;
- border-bottom: 1px solid #eee;
- display: flex;
- justify-content: space-between;
- align-items: center;
- flex-shrink: 0;
- }
-
- .insertr-version-header h3 {
- margin: 0 0 20px;
- color: #333;
- font-size: 18px;
- }
-
- .insertr-btn-close {
- background: none;
- border: none;
- font-size: 24px;
- cursor: pointer;
- color: #666;
- padding: 0;
- width: 30px;
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .insertr-btn-close:hover {
- color: #333;
- }
-
- .insertr-version-list {
- overflow-y: auto;
- padding: 20px;
- flex: 1;
- }
-
- .insertr-version-item {
- border: 1px solid #e1e5e9;
- border-radius: 6px;
- padding: 16px;
- margin-bottom: 12px;
- background: #f8f9fa;
- }
-
- .insertr-version-meta {
- display: flex;
- align-items: center;
- gap: 12px;
- margin-bottom: 8px;
- font-size: 13px;
- }
-
- .insertr-version-label {
- font-weight: 600;
- color: #0969da;
- }
-
- .insertr-version-date {
- color: #656d76;
- }
-
- .insertr-version-user {
- color: #656d76;
- }
-
- .insertr-version-content {
- margin-bottom: 12px;
- padding: 8px;
- background: white;
- border-radius: 4px;
- font-family: monospace;
- font-size: 14px;
- color: #24292f;
- white-space: pre-wrap;
- }
-
- .insertr-version-actions {
- display: flex;
- gap: 8px;
- }
-
- .insertr-btn-restore {
- background: #0969da;
- color: white;
- border: none;
- padding: 6px 12px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 12px;
- font-weight: 500;
- }
-
- .insertr-btn-restore:hover {
- background: #0860ca;
- }
-
- .insertr-btn-view-diff {
- background: #f6f8fa;
- color: #24292f;
- border: 1px solid #d1d9e0;
- padding: 6px 12px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 12px;
- font-weight: 500;
- }
-
- .insertr-btn-view-diff:hover {
- background: #f3f4f6;
- }
-
- .insertr-version-empty {
- text-align: center;
- color: #656d76;
- font-style: italic;
- padding: 40px 20px;
- }
- `;
-
- const styleSheet = document.createElement('style');
- styleSheet.type = 'text/css';
- styleSheet.innerHTML = styles;
- document.head.appendChild(styleSheet);
- }
}
\ No newline at end of file