diff --git a/demo-site/about.html b/demo-site/about.html
index 06bb94f..28b2ea0 100644
--- a/demo-site/about.html
+++ b/demo-site/about.html
@@ -100,7 +100,7 @@
diff --git a/demo-site/index.html b/demo-site/index.html
index b57b284..29496f0 100644
--- a/demo-site/index.html
+++ b/demo-site/index.html
@@ -76,7 +76,7 @@
diff --git a/insertr-cli/pkg/content/assets/insertr.js b/insertr-cli/pkg/content/assets/insertr.js
index e6ccb38..0712e7f 100644
--- a/insertr-cli/pkg/content/assets/insertr.js
+++ b/insertr-cli/pkg/content/assets/insertr.js
@@ -639,7 +639,7 @@ var Insertr = (function () {
constructor(options = {}) {
this.options = {
mockAuth: options.mockAuth !== false, // Enable mock auth by default
- autoCreateControls: options.autoCreateControls !== false,
+ hideGatesAfterAuth: options.hideGatesAfterAuth === true, // Keep gates visible by default
...options
};
@@ -648,31 +648,191 @@ var Insertr = (function () {
isAuthenticated: false,
editMode: false,
currentUser: null,
- activeEditor: null
+ activeEditor: null,
+ isInitialized: false,
+ isAuthenticating: false
};
this.statusIndicator = null;
}
/**
- * Initialize authentication system
+ * Initialize gate system (called on page load)
*/
init() {
- console.log('đ Initializing Insertr Authentication');
+ console.log('đ§ Insertr: Scanning for editor gates');
- if (this.options.autoCreateControls) {
- this.createAuthControls();
+ this.setupEditorGates();
+ }
+
+ /**
+ * Initialize full editing system (called after successful OAuth)
+ */
+ initializeFullSystem() {
+ if (this.state.isInitialized) {
+ return; // Already initialized
}
+
+ console.log('đ Initializing Insertr Editing System');
+ this.createAuthControls();
this.setupAuthenticationControls();
this.createStatusIndicator();
this.updateBodyClasses();
- console.log('đą Auth controls ready - Look for buttons in top-right corner');
+ // Auto-enable edit mode after OAuth
+ this.state.editMode = true;
+ this.state.isInitialized = true;
+
+ // Start the editor system
+ if (window.Insertr && window.Insertr.startEditor) {
+ window.Insertr.startEditor();
+ }
+
+ this.updateButtonStates();
+ this.updateStatusIndicator();
+
+ console.log('đą Editing system active - Controls in bottom-right corner');
+ console.log('âī¸ Edit mode enabled - Click elements to edit');
}
/**
- * Create authentication control buttons if they don't exist
+ * Setup editor gate click handlers for any .insertr-gate elements
+ */
+ setupEditorGates() {
+ const gates = document.querySelectorAll('.insertr-gate');
+
+ if (gates.length === 0) {
+ console.log('âšī¸ No .insertr-gate elements found - editor access disabled');
+ return;
+ }
+
+ console.log(`đĒ Found ${gates.length} editor gate(s)`);
+
+ // Add gate styles
+ this.addGateStyles();
+
+ gates.forEach((gate, index) => {
+ // Store original text for later restoration
+ if (!gate.hasAttribute('data-original-text')) {
+ gate.setAttribute('data-original-text', gate.textContent);
+ }
+
+ gate.addEventListener('click', (e) => {
+ e.preventDefault();
+ this.handleGateClick(gate, index);
+ });
+
+ // Add subtle styling to indicate it's clickable
+ gate.style.cursor = 'pointer';
+ });
+ }
+
+ /**
+ * Handle click on an editor gate element
+ */
+ async handleGateClick(gateElement, gateIndex) {
+ // Prevent multiple simultaneous authentication attempts
+ if (this.state.isAuthenticating) {
+ console.log('âŗ Authentication already in progress...');
+ return;
+ }
+
+ console.log(`đ Editor gate activated (gate ${gateIndex + 1})`);
+ this.state.isAuthenticating = true;
+
+ // Store original text and show loading state
+ const originalText = gateElement.textContent;
+ gateElement.setAttribute('data-original-text', originalText);
+ gateElement.textContent = 'âŗ Signing in...';
+ gateElement.style.pointerEvents = 'none';
+
+ try {
+ // Perform OAuth authentication
+ await this.performOAuthFlow();
+
+ // Initialize full editing system
+ this.initializeFullSystem();
+
+ // Conditionally hide gates based on options
+ if (this.options.hideGatesAfterAuth) {
+ this.hideAllGates();
+ } else {
+ this.updateGateState();
+ }
+
+ } catch (error) {
+ console.error('â Authentication failed:', error);
+
+ // Restore clicked gate to original state
+ const originalText = gateElement.getAttribute('data-original-text');
+ if (originalText) {
+ gateElement.textContent = originalText;
+ }
+ gateElement.style.pointerEvents = '';
+ } finally {
+ this.state.isAuthenticating = false;
+ }
+ }
+
+ /**
+ * Perform OAuth authentication flow
+ */
+ async performOAuthFlow() {
+ // In development, simulate OAuth flow
+ if (this.options.mockAuth) {
+ console.log('đ Mock OAuth: Simulating authentication...');
+
+ // Simulate network delay
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ // Set authenticated state
+ this.state.isAuthenticated = true;
+ this.state.currentUser = {
+ name: 'Site Owner',
+ email: 'owner@example.com',
+ role: 'admin'
+ };
+
+ console.log('â
Mock OAuth: Authentication successful');
+ return;
+ }
+
+ // TODO: In production, implement real OAuth flow
+ // This would redirect to OAuth provider, handle callback, etc.
+ throw new Error('Production OAuth not implemented yet');
+ }
+
+ /**
+ * Hide all editor gates after successful authentication (optional)
+ */
+ hideAllGates() {
+ document.body.classList.add('insertr-hide-gates');
+ console.log('đĒ Editor gates hidden (hideGatesAfterAuth enabled)');
+ }
+
+ /**
+ * Update gate state after authentication (restore normal appearance)
+ */
+ updateGateState() {
+ const gates = document.querySelectorAll('.insertr-gate');
+ gates.forEach(gate => {
+ // Restore original text if it was saved
+ const originalText = gate.getAttribute('data-original-text');
+ if (originalText) {
+ gate.textContent = originalText;
+ }
+
+ // Restore interactive state
+ gate.style.pointerEvents = '';
+ gate.style.opacity = '';
+ });
+
+ console.log('đĒ Editor gates restored to original state');
+ }
+
+ /**
+ * Create authentication control buttons (bottom-right positioned)
*/
createAuthControls() {
// Check if controls already exist
@@ -853,6 +1013,32 @@ var Insertr = (function () {
return this.state.currentUser;
}
+ /**
+ * Add minimal styles for editor gates
+ */
+ addGateStyles() {
+ const styles = `
+ .insertr-gate {
+ transition: opacity 0.2s ease;
+ user-select: none;
+ }
+
+ .insertr-gate:hover {
+ opacity: 0.7;
+ }
+
+ /* Optional: Hide gates when authenticated (only if hideGatesAfterAuth option is true) */
+ body.insertr-hide-gates .insertr-gate {
+ display: none !important;
+ }
+ `;
+
+ const styleSheet = document.createElement('style');
+ styleSheet.type = 'text/css';
+ styleSheet.innerHTML = styles;
+ document.head.appendChild(styleSheet);
+ }
+
/**
* Add styles for authentication controls
*/
@@ -860,11 +1046,12 @@ var Insertr = (function () {
const styles = `
.insertr-auth-controls {
position: fixed;
- top: 20px;
+ bottom: 20px;
right: 20px;
z-index: 9999;
display: flex;
- gap: 10px;
+ flex-direction: column;
+ gap: 8px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
@@ -914,6 +1101,7 @@ var Insertr = (function () {
padding: 8px 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ max-width: 200px;
}
.insertr-status-content {
@@ -1034,12 +1222,17 @@ var Insertr = (function () {
return this;
},
- // Start the editor and authentication
+ // Start the system - only creates the minimal trigger
start() {
if (this.auth) {
- this.auth.init();
+ this.auth.init(); // Creates footer trigger only
}
- if (this.editor) {
+ // Note: Editor is NOT started here, only when trigger is clicked
+ },
+
+ // Start the full editor system (called when trigger is activated)
+ startEditor() {
+ if (this.editor && !this.editor.isActive) {
this.editor.start();
}
},
@@ -1071,9 +1264,19 @@ var Insertr = (function () {
version: '1.0.0'
};
- // Auto-initialize in development mode
- if (document.querySelector('[data-insertr-enhanced]')) {
- window.Insertr.init();
+ // Auto-initialize in development mode with proper DOM ready handling
+ function autoInitialize() {
+ if (document.querySelector('[data-insertr-enhanced="true"]')) {
+ window.Insertr.init();
+ }
+ }
+
+ // Run auto-initialization when DOM is ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', autoInitialize);
+ } else {
+ // DOM is already ready
+ autoInitialize();
}
var index = window.Insertr;
diff --git a/insertr-cli/pkg/content/assets/insertr.min.js b/insertr-cli/pkg/content/assets/insertr.min.js
index 2f98850..7b8caee 100644
--- a/insertr-cli/pkg/content/assets/insertr.min.js
+++ b/insertr-cli/pkg/content/assets/insertr.min.js
@@ -1 +1 @@
-var Insertr=function(){"use strict";class t{constructor(t={}){this.options={apiEndpoint:t.apiEndpoint||"/api/content",siteId:t.siteId||"default",...t}}findEnhancedElements(){return document.querySelectorAll('[data-insertr-enhanced="true"]')}getElementMetadata(t){return{contentId:t.getAttribute("data-content-id"),contentType:t.getAttribute("data-content-type"),element:t}}getAllElements(){const t=this.findEnhancedElements();return Array.from(t).map(t=>this.getElementMetadata(t))}}class e{constructor(){this.currentOverlay=null,this.setupStyles()}showEditForm(t,e,n,r){this.closeForm();const{element:i,contentId:o,contentType:s}=t,a=this.getFieldConfig(i,s),d=this.createEditForm(o,a,e),l=this.createOverlay(d);this.positionForm(i,l),this.setupFormHandlers(d,l,{onSave:n,onCancel:r}),document.body.appendChild(l),this.currentOverlay=l;const c=d.querySelector("input, textarea");return c&&setTimeout(()=>c.focus(),100),l}closeForm(){this.currentOverlay&&(this.currentOverlay.remove(),this.currentOverlay=null)}getFieldConfig(t,e){const n=t.tagName.toLowerCase(),r=Array.from(t.classList);let i={h1:{type:"text",label:"Headline",maxLength:60,placeholder:"Enter headline..."},h2:{type:"text",label:"Subheading",maxLength:80,placeholder:"Enter subheading..."},h3:{type:"text",label:"Section Title",maxLength:100,placeholder:"Enter title..."},h4:{type:"text",label:"Title",maxLength:100,placeholder:"Enter title..."},h5:{type:"text",label:"Title",maxLength:100,placeholder:"Enter title..."},h6:{type:"text",label:"Title",maxLength:100,placeholder:"Enter title..."},p:{type:"textarea",label:"Paragraph",rows:3,placeholder:"Enter paragraph text..."},a:{type:"link",label:"Link",placeholder:"Enter link text...",includeUrl:!0},span:{type:"text",label:"Text",placeholder:"Enter text..."},button:{type:"text",label:"Button Text",placeholder:"Enter button text..."}}[n]||{type:"text",label:"Text",placeholder:"Enter text..."};return r.includes("lead")&&(i={...i,label:"Lead Paragraph",rows:4,placeholder:"Enter lead paragraph..."}),"markdown"===e&&(i={...i,type:"markdown",label:"Markdown Content",rows:8}),i}createEditForm(t,e,n){const r=document.createElement("div");r.className="insertr-edit-form";let i=`
`;return"markdown"===e.type?i+=this.createMarkdownField(e,n):"link"===e.type&&e.includeUrl?i+=this.createLinkField(e,n):"textarea"===e.type?i+=this.createTextareaField(e,n):i+=this.createTextField(e,n),i+='\n \n \n \n
\n ',r.innerHTML=i,r}createMarkdownField(t,e){return`\n \n `}createLinkField(t,e){const n="object"==typeof e?e.text||"":e,r="object"==typeof e&&e.url||"";return`\n \n \n \n
\n \n \n \n
\n `}createTextareaField(t,e){const n="object"==typeof e?e.text||"":e;return`\n \n \n
\n `}createTextField(t,e){const n="object"==typeof e?e.text||"":e;return`\n \n \n
\n `}createOverlay(t){const e=document.createElement("div");return e.className="insertr-form-overlay",e.appendChild(t),e}positionForm(t,e){const n=t.getBoundingClientRect(),r=e.querySelector(".insertr-edit-form"),i=window.innerWidth;let o;o=i<768?Math.min(i-40,350):Math.min(Math.max(n.width,300),500),r.style.width=`${o}px`;const s=n.bottom+window.scrollY+10,a=Math.max(20,n.left+window.scrollX);e.style.position="absolute",e.style.top=`${s}px`,e.style.left=`${a}px`,e.style.zIndex="10000"}setupFormHandlers(t,e,{onSave:n,onCancel:r}){const i=t.querySelector(".insertr-btn-save"),o=t.querySelector(".insertr-btn-cancel");i&&i.addEventListener("click",()=>{const e=this.extractFormData(t);n(e)}),o&&o.addEventListener("click",()=>{r(),this.closeForm()});const s=t=>{"Escape"===t.key&&(r(),this.closeForm(),document.removeEventListener("keydown",s))};document.addEventListener("keydown",s),e.addEventListener("click",t=>{t.target===e&&(r(),this.closeForm())})}extractFormData(t){const e={},n=t.querySelector('input[name="text"]'),r=t.querySelector('input[name="url"]'),i=t.querySelector('input[name="content"], textarea[name="content"]');return n&&r?(e.text=n.value,e.url=r.value):i&&(e.text=i.value),e}escapeHtml(t){if("string"!=typeof t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}setupStyles(){const t=document.createElement("style");t.type="text/css",t.innerHTML="\n .insertr-form-overlay {\n position: absolute;\n z-index: 10000;\n }\n\n .insertr-edit-form {\n background: white;\n border: 2px solid #007cba;\n border-radius: 8px;\n padding: 1rem;\n box-shadow: 0 8px 25px rgba(0,0,0,0.15);\n width: 100%;\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .insertr-form-header {\n font-weight: 600;\n color: #1f2937;\n margin-bottom: 1rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid #e5e7eb;\n font-size: 0.875rem;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .insertr-form-group {\n margin-bottom: 1rem;\n }\n\n .insertr-form-group:last-child {\n margin-bottom: 0;\n }\n\n .insertr-form-label {\n display: block;\n font-weight: 600;\n color: #374151;\n margin-bottom: 0.5rem;\n font-size: 0.875rem;\n }\n\n .insertr-form-input, \n .insertr-form-textarea {\n width: 100%;\n padding: 0.75rem;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n font-family: inherit;\n font-size: 1rem;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n }\n\n .insertr-form-input:focus,\n .insertr-form-textarea:focus {\n outline: none;\n border-color: #007cba;\n box-shadow: 0 0 0 3px rgba(0, 124, 186, 0.1);\n }\n\n .insertr-form-textarea {\n min-height: 120px;\n resize: vertical;\n font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;\n }\n\n .insertr-markdown-editor {\n min-height: 200px;\n font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;\n font-size: 0.9rem;\n line-height: 1.5;\n background-color: #f8fafc;\n }\n\n .insertr-form-actions {\n display: flex;\n gap: 0.5rem;\n justify-content: flex-end;\n margin-top: 1rem;\n padding-top: 1rem;\n border-top: 1px solid #e5e7eb;\n }\n\n .insertr-btn-save {\n background: #10b981;\n color: white;\n border: none;\n padding: 0.5rem 1rem;\n border-radius: 6px;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 0.2s;\n font-size: 0.875rem;\n }\n\n .insertr-btn-save:hover {\n background: #059669;\n }\n\n .insertr-btn-cancel {\n background: #6b7280;\n color: white;\n border: none;\n padding: 0.5rem 1rem;\n border-radius: 6px;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 0.2s;\n font-size: 0.875rem;\n }\n\n .insertr-btn-cancel:hover {\n background: #4b5563;\n }\n\n .insertr-form-help {\n font-size: 0.75rem;\n color: #6b7280;\n margin-top: 0.25rem;\n }\n ",document.head.appendChild(t)}}class n{constructor(t,n,r={}){this.core=t,this.auth=n,this.options=r,this.isActive=!1,this.formRenderer=new e}start(){if(this.isActive)return;console.log("đ Starting Insertr Editor"),this.isActive=!0,this.addEditorStyles();const t=this.core.getAllElements();console.log(`đ Found ${t.length} editable elements`),t.forEach(t=>this.initializeElement(t))}initializeElement(t){const{element:e,contentId:n,contentType:r}=t;e.style.cursor="pointer",e.style.position="relative",this.addHoverEffects(e),this.addClickHandler(e,t)}addHoverEffects(t){t.addEventListener("mouseenter",()=>{t.classList.add("insertr-editing-hover")}),t.addEventListener("mouseleave",()=>{t.classList.remove("insertr-editing-hover")})}addClickHandler(t,e){t.addEventListener("click",t=>{this.auth.isAuthenticated()&&this.auth.isEditMode()&&(t.preventDefault(),this.openEditor(e))})}openEditor(t){const{element:e}=t,n=this.extractCurrentContent(e);this.formRenderer.showEditForm(t,n,e=>this.handleSave(t,e),()=>this.handleCancel(t))}extractCurrentContent(t){return"a"===t.tagName.toLowerCase()?{text:t.textContent.trim(),url:t.getAttribute("href")||""}:t.textContent.trim()}handleSave(t,e){console.log("đž Saving content:",t.contentId,e),this.updateElementContent(t.element,e),this.formRenderer.closeForm(),console.log("â
Content saved:",t.contentId,e)}handleCancel(t){console.log("â Edit cancelled:",t.contentId)}updateElementContent(t,e){"a"===t.tagName.toLowerCase()?(void 0!==e.text&&(t.textContent=e.text),void 0!==e.url&&t.setAttribute("href",e.url)):t.textContent=e.text||""}addEditorStyles(){const t=document.createElement("style");t.type="text/css",t.innerHTML='\n .insertr-editing-hover {\n outline: 2px dashed #007cba !important;\n outline-offset: 2px !important;\n background-color: rgba(0, 124, 186, 0.05) !important;\n }\n \n [data-insertr-enhanced="true"]:hover::after {\n content: "âī¸ " attr(data-content-type);\n position: absolute;\n top: -25px;\n left: 0;\n background: #007cba;\n color: white;\n padding: 2px 6px;\n font-size: 11px;\n border-radius: 3px;\n white-space: nowrap;\n z-index: 1000;\n font-family: monospace;\n }\n ',document.head.appendChild(t)}}class r{constructor(t={}){this.options={mockAuth:!1!==t.mockAuth,autoCreateControls:!1!==t.autoCreateControls,...t},this.state={isAuthenticated:!1,editMode:!1,currentUser:null,activeEditor:null},this.statusIndicator=null}init(){console.log("đ Initializing Insertr Authentication"),this.options.autoCreateControls&&this.createAuthControls(),this.setupAuthenticationControls(),this.createStatusIndicator(),this.updateBodyClasses(),console.log("đą Auth controls ready - Look for buttons in top-right corner")}createAuthControls(){if(document.getElementById("insertr-auth-controls"))return;document.body.insertAdjacentHTML("beforeend",'\n \n \n \n
\n '),this.addControlStyles()}setupAuthenticationControls(){const t=document.getElementById("insertr-auth-toggle"),e=document.getElementById("insertr-edit-toggle");t&&t.addEventListener("click",()=>this.toggleAuthentication()),e&&e.addEventListener("click",()=>this.toggleEditMode())}toggleAuthentication(){this.state.isAuthenticated=!this.state.isAuthenticated,this.state.currentUser=this.state.isAuthenticated?{name:"Demo User",email:"demo@example.com",role:"editor"}:null,this.state.isAuthenticated||(this.state.editMode=!1),this.updateBodyClasses(),this.updateButtonStates(),this.updateStatusIndicator(),console.log(this.state.isAuthenticated?"â
Authenticated as Demo User":"â Logged out")}toggleEditMode(){this.state.isAuthenticated?(this.state.editMode=!this.state.editMode,!this.state.editMode&&this.state.activeEditor&&(this.state.activeEditor=null),this.updateBodyClasses(),this.updateButtonStates(),this.updateStatusIndicator(),console.log(this.state.editMode?"âī¸ Edit mode ON - Click elements to edit":"đ Edit mode OFF - Read-only view")):console.warn("â Cannot enable edit mode - not authenticated")}updateBodyClasses(){document.body.classList.toggle("insertr-authenticated",this.state.isAuthenticated),document.body.classList.toggle("insertr-edit-mode",this.state.editMode)}updateButtonStates(){const t=document.getElementById("insertr-auth-toggle"),e=document.getElementById("insertr-edit-toggle");t&&(t.textContent=this.state.isAuthenticated?"Logout":"Login as Client",t.className="insertr-auth-btn "+(this.state.isAuthenticated?"insertr-authenticated":"")),e&&(e.style.display=this.state.isAuthenticated?"inline-block":"none",e.textContent="Edit Mode: "+(this.state.editMode?"On":"Off"),e.className="insertr-auth-btn "+(this.state.editMode?"insertr-edit-active":""))}createStatusIndicator(){if(document.getElementById("insertr-status"))return;document.body.insertAdjacentHTML("beforeend",'\n \n
\n Visitor Mode\n \n
\n
\n '),this.statusIndicator=document.getElementById("insertr-status"),this.updateStatusIndicator()}updateStatusIndicator(){const t=document.querySelector(".insertr-status-text"),e=document.querySelector(".insertr-status-dot");t&&e&&(this.state.isAuthenticated?this.state.editMode?(t.textContent="Editing",e.className="insertr-status-dot insertr-status-editing"):(t.textContent="Authenticated",e.className="insertr-status-dot insertr-status-authenticated"):(t.textContent="Visitor Mode",e.className="insertr-status-dot insertr-status-visitor"))}isAuthenticated(){return this.state.isAuthenticated}isEditMode(){return this.state.editMode}getCurrentUser(){return this.state.currentUser}addControlStyles(){const t=document.createElement("style");t.type="text/css",t.innerHTML="\n .insertr-auth-controls {\n position: fixed;\n top: 20px;\n right: 20px;\n z-index: 9999;\n display: flex;\n gap: 10px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .insertr-auth-btn {\n background: #4f46e5;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n }\n\n .insertr-auth-btn:hover {\n background: #4338ca;\n transform: translateY(-1px);\n box-shadow: 0 4px 8px rgba(0,0,0,0.15);\n }\n\n .insertr-auth-btn.insertr-authenticated {\n background: #059669;\n }\n\n .insertr-auth-btn.insertr-authenticated:hover {\n background: #047857;\n }\n\n .insertr-auth-btn.insertr-edit-active {\n background: #dc2626;\n }\n\n .insertr-auth-btn.insertr-edit-active:hover {\n background: #b91c1c;\n }\n\n .insertr-status {\n position: fixed;\n bottom: 20px;\n left: 20px;\n z-index: 9999;\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 8px 12px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .insertr-status-content {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .insertr-status-text {\n font-size: 12px;\n font-weight: 500;\n color: #374151;\n }\n\n .insertr-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #9ca3af;\n }\n\n .insertr-status-dot.insertr-status-visitor {\n background: #9ca3af;\n }\n\n .insertr-status-dot.insertr-status-authenticated {\n background: #059669;\n }\n\n .insertr-status-dot.insertr-status-editing {\n background: #dc2626;\n animation: insertr-pulse 2s infinite;\n }\n\n @keyframes insertr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n /* Hide editing interface when not in edit mode */\n body:not(.insertr-edit-mode) [data-insertr-enhanced]:hover::after {\n display: none !important;\n }\n\n /* Only show editing features when in edit mode */\n .insertr-authenticated.insertr-edit-mode [data-insertr-enhanced] {\n cursor: pointer;\n }\n\n .insertr-authenticated.insertr-edit-mode [data-insertr-enhanced]:hover {\n outline: 2px dashed #007cba !important;\n outline-offset: 2px !important;\n background-color: rgba(0, 124, 186, 0.05) !important;\n }\n ",document.head.appendChild(t)}async authenticateWithOAuth(t="google"){console.log(`đ Mock OAuth login with ${t}`),setTimeout(()=>{this.state.isAuthenticated=!0,this.state.currentUser={name:"OAuth User",email:"user@example.com",provider:t,role:"editor"},this.updateBodyClasses(),this.updateButtonStates(),this.updateStatusIndicator(),console.log("â
OAuth authentication successful")},1e3)}}return window.Insertr={core:null,editor:null,auth:null,init(e={}){return console.log("đ§ Insertr v1.0.0 initializing... (Hot Reload Ready)"),this.core=new t(e),this.auth=new r(e),this.editor=new n(this.core,this.auth,e),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>this.start()):this.start(),this},start(){this.auth&&this.auth.init(),this.editor&&this.editor.start()},login(){return this.auth?this.auth.toggleAuthentication():null},logout(){this.auth&&this.auth.isAuthenticated()&&this.auth.toggleAuthentication()},toggleEditMode(){return this.auth?this.auth.toggleEditMode():null},isAuthenticated(){return!!this.auth&&this.auth.isAuthenticated()},isEditMode(){return!!this.auth&&this.auth.isEditMode()},version:"1.0.0"},document.querySelector("[data-insertr-enhanced]")&&window.Insertr.init(),window.Insertr}();
+var Insertr=function(){"use strict";class t{constructor(t={}){this.options={apiEndpoint:t.apiEndpoint||"/api/content",siteId:t.siteId||"default",...t}}findEnhancedElements(){return document.querySelectorAll('[data-insertr-enhanced="true"]')}getElementMetadata(t){return{contentId:t.getAttribute("data-content-id"),contentType:t.getAttribute("data-content-type"),element:t}}getAllElements(){const t=this.findEnhancedElements();return Array.from(t).map(t=>this.getElementMetadata(t))}}class e{constructor(){this.currentOverlay=null,this.setupStyles()}showEditForm(t,e,n,i){this.closeForm();const{element:r,contentId:o,contentType:s}=t,a=this.getFieldConfig(r,s),d=this.createEditForm(o,a,e),l=this.createOverlay(d);this.positionForm(r,l),this.setupFormHandlers(d,l,{onSave:n,onCancel:i}),document.body.appendChild(l),this.currentOverlay=l;const c=d.querySelector("input, textarea");return c&&setTimeout(()=>c.focus(),100),l}closeForm(){this.currentOverlay&&(this.currentOverlay.remove(),this.currentOverlay=null)}getFieldConfig(t,e){const n=t.tagName.toLowerCase(),i=Array.from(t.classList);let r={h1:{type:"text",label:"Headline",maxLength:60,placeholder:"Enter headline..."},h2:{type:"text",label:"Subheading",maxLength:80,placeholder:"Enter subheading..."},h3:{type:"text",label:"Section Title",maxLength:100,placeholder:"Enter title..."},h4:{type:"text",label:"Title",maxLength:100,placeholder:"Enter title..."},h5:{type:"text",label:"Title",maxLength:100,placeholder:"Enter title..."},h6:{type:"text",label:"Title",maxLength:100,placeholder:"Enter title..."},p:{type:"textarea",label:"Paragraph",rows:3,placeholder:"Enter paragraph text..."},a:{type:"link",label:"Link",placeholder:"Enter link text...",includeUrl:!0},span:{type:"text",label:"Text",placeholder:"Enter text..."},button:{type:"text",label:"Button Text",placeholder:"Enter button text..."}}[n]||{type:"text",label:"Text",placeholder:"Enter text..."};return i.includes("lead")&&(r={...r,label:"Lead Paragraph",rows:4,placeholder:"Enter lead paragraph..."}),"markdown"===e&&(r={...r,type:"markdown",label:"Markdown Content",rows:8}),r}createEditForm(t,e,n){const i=document.createElement("div");i.className="insertr-edit-form";let r=``;return"markdown"===e.type?r+=this.createMarkdownField(e,n):"link"===e.type&&e.includeUrl?r+=this.createLinkField(e,n):"textarea"===e.type?r+=this.createTextareaField(e,n):r+=this.createTextField(e,n),r+='\n \n \n \n
\n ',i.innerHTML=r,i}createMarkdownField(t,e){return`\n \n `}createLinkField(t,e){const n="object"==typeof e?e.text||"":e,i="object"==typeof e&&e.url||"";return`\n \n \n \n
\n \n \n \n
\n `}createTextareaField(t,e){const n="object"==typeof e?e.text||"":e;return`\n \n \n
\n `}createTextField(t,e){const n="object"==typeof e?e.text||"":e;return`\n \n \n
\n `}createOverlay(t){const e=document.createElement("div");return e.className="insertr-form-overlay",e.appendChild(t),e}positionForm(t,e){const n=t.getBoundingClientRect(),i=e.querySelector(".insertr-edit-form"),r=window.innerWidth;let o;o=r<768?Math.min(r-40,350):Math.min(Math.max(n.width,300),500),i.style.width=`${o}px`;const s=n.bottom+window.scrollY+10,a=Math.max(20,n.left+window.scrollX);e.style.position="absolute",e.style.top=`${s}px`,e.style.left=`${a}px`,e.style.zIndex="10000"}setupFormHandlers(t,e,{onSave:n,onCancel:i}){const r=t.querySelector(".insertr-btn-save"),o=t.querySelector(".insertr-btn-cancel");r&&r.addEventListener("click",()=>{const e=this.extractFormData(t);n(e)}),o&&o.addEventListener("click",()=>{i(),this.closeForm()});const s=t=>{"Escape"===t.key&&(i(),this.closeForm(),document.removeEventListener("keydown",s))};document.addEventListener("keydown",s),e.addEventListener("click",t=>{t.target===e&&(i(),this.closeForm())})}extractFormData(t){const e={},n=t.querySelector('input[name="text"]'),i=t.querySelector('input[name="url"]'),r=t.querySelector('input[name="content"], textarea[name="content"]');return n&&i?(e.text=n.value,e.url=i.value):r&&(e.text=r.value),e}escapeHtml(t){if("string"!=typeof t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}setupStyles(){const t=document.createElement("style");t.type="text/css",t.innerHTML="\n .insertr-form-overlay {\n position: absolute;\n z-index: 10000;\n }\n\n .insertr-edit-form {\n background: white;\n border: 2px solid #007cba;\n border-radius: 8px;\n padding: 1rem;\n box-shadow: 0 8px 25px rgba(0,0,0,0.15);\n width: 100%;\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .insertr-form-header {\n font-weight: 600;\n color: #1f2937;\n margin-bottom: 1rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid #e5e7eb;\n font-size: 0.875rem;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .insertr-form-group {\n margin-bottom: 1rem;\n }\n\n .insertr-form-group:last-child {\n margin-bottom: 0;\n }\n\n .insertr-form-label {\n display: block;\n font-weight: 600;\n color: #374151;\n margin-bottom: 0.5rem;\n font-size: 0.875rem;\n }\n\n .insertr-form-input, \n .insertr-form-textarea {\n width: 100%;\n padding: 0.75rem;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n font-family: inherit;\n font-size: 1rem;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n }\n\n .insertr-form-input:focus,\n .insertr-form-textarea:focus {\n outline: none;\n border-color: #007cba;\n box-shadow: 0 0 0 3px rgba(0, 124, 186, 0.1);\n }\n\n .insertr-form-textarea {\n min-height: 120px;\n resize: vertical;\n font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;\n }\n\n .insertr-markdown-editor {\n min-height: 200px;\n font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;\n font-size: 0.9rem;\n line-height: 1.5;\n background-color: #f8fafc;\n }\n\n .insertr-form-actions {\n display: flex;\n gap: 0.5rem;\n justify-content: flex-end;\n margin-top: 1rem;\n padding-top: 1rem;\n border-top: 1px solid #e5e7eb;\n }\n\n .insertr-btn-save {\n background: #10b981;\n color: white;\n border: none;\n padding: 0.5rem 1rem;\n border-radius: 6px;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 0.2s;\n font-size: 0.875rem;\n }\n\n .insertr-btn-save:hover {\n background: #059669;\n }\n\n .insertr-btn-cancel {\n background: #6b7280;\n color: white;\n border: none;\n padding: 0.5rem 1rem;\n border-radius: 6px;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 0.2s;\n font-size: 0.875rem;\n }\n\n .insertr-btn-cancel:hover {\n background: #4b5563;\n }\n\n .insertr-form-help {\n font-size: 0.75rem;\n color: #6b7280;\n margin-top: 0.25rem;\n }\n ",document.head.appendChild(t)}}class n{constructor(t,n,i={}){this.core=t,this.auth=n,this.options=i,this.isActive=!1,this.formRenderer=new e}start(){if(this.isActive)return;console.log("đ Starting Insertr Editor"),this.isActive=!0,this.addEditorStyles();const t=this.core.getAllElements();console.log(`đ Found ${t.length} editable elements`),t.forEach(t=>this.initializeElement(t))}initializeElement(t){const{element:e,contentId:n,contentType:i}=t;e.style.cursor="pointer",e.style.position="relative",this.addHoverEffects(e),this.addClickHandler(e,t)}addHoverEffects(t){t.addEventListener("mouseenter",()=>{t.classList.add("insertr-editing-hover")}),t.addEventListener("mouseleave",()=>{t.classList.remove("insertr-editing-hover")})}addClickHandler(t,e){t.addEventListener("click",t=>{this.auth.isAuthenticated()&&this.auth.isEditMode()&&(t.preventDefault(),this.openEditor(e))})}openEditor(t){const{element:e}=t,n=this.extractCurrentContent(e);this.formRenderer.showEditForm(t,n,e=>this.handleSave(t,e),()=>this.handleCancel(t))}extractCurrentContent(t){return"a"===t.tagName.toLowerCase()?{text:t.textContent.trim(),url:t.getAttribute("href")||""}:t.textContent.trim()}handleSave(t,e){console.log("đž Saving content:",t.contentId,e),this.updateElementContent(t.element,e),this.formRenderer.closeForm(),console.log("â
Content saved:",t.contentId,e)}handleCancel(t){console.log("â Edit cancelled:",t.contentId)}updateElementContent(t,e){"a"===t.tagName.toLowerCase()?(void 0!==e.text&&(t.textContent=e.text),void 0!==e.url&&t.setAttribute("href",e.url)):t.textContent=e.text||""}addEditorStyles(){const t=document.createElement("style");t.type="text/css",t.innerHTML='\n .insertr-editing-hover {\n outline: 2px dashed #007cba !important;\n outline-offset: 2px !important;\n background-color: rgba(0, 124, 186, 0.05) !important;\n }\n \n [data-insertr-enhanced="true"]:hover::after {\n content: "âī¸ " attr(data-content-type);\n position: absolute;\n top: -25px;\n left: 0;\n background: #007cba;\n color: white;\n padding: 2px 6px;\n font-size: 11px;\n border-radius: 3px;\n white-space: nowrap;\n z-index: 1000;\n font-family: monospace;\n }\n ',document.head.appendChild(t)}}class i{constructor(t={}){this.options={mockAuth:!1!==t.mockAuth,hideGatesAfterAuth:!0===t.hideGatesAfterAuth,...t},this.state={isAuthenticated:!1,editMode:!1,currentUser:null,activeEditor:null,isInitialized:!1,isAuthenticating:!1},this.statusIndicator=null}init(){console.log("đ§ Insertr: Scanning for editor gates"),this.setupEditorGates()}initializeFullSystem(){this.state.isInitialized||(console.log("đ Initializing Insertr Editing System"),this.createAuthControls(),this.setupAuthenticationControls(),this.createStatusIndicator(),this.updateBodyClasses(),this.state.editMode=!0,this.state.isInitialized=!0,window.Insertr&&window.Insertr.startEditor&&window.Insertr.startEditor(),this.updateButtonStates(),this.updateStatusIndicator(),console.log("đą Editing system active - Controls in bottom-right corner"),console.log("âī¸ Edit mode enabled - Click elements to edit"))}setupEditorGates(){const t=document.querySelectorAll(".insertr-gate");0!==t.length?(console.log(`đĒ Found ${t.length} editor gate(s)`),this.addGateStyles(),t.forEach((t,e)=>{t.hasAttribute("data-original-text")||t.setAttribute("data-original-text",t.textContent),t.addEventListener("click",n=>{n.preventDefault(),this.handleGateClick(t,e)}),t.style.cursor="pointer"})):console.log("âšī¸ No .insertr-gate elements found - editor access disabled")}async handleGateClick(t,e){if(this.state.isAuthenticating)return void console.log("âŗ Authentication already in progress...");console.log(`đ Editor gate activated (gate ${e+1})`),this.state.isAuthenticating=!0;const n=t.textContent;t.setAttribute("data-original-text",n),t.textContent="âŗ Signing in...",t.style.pointerEvents="none";try{await this.performOAuthFlow(),this.initializeFullSystem(),this.options.hideGatesAfterAuth?this.hideAllGates():this.updateGateState()}catch(e){console.error("â Authentication failed:",e);const n=t.getAttribute("data-original-text");n&&(t.textContent=n),t.style.pointerEvents=""}finally{this.state.isAuthenticating=!1}}async performOAuthFlow(){if(this.options.mockAuth)return console.log("đ Mock OAuth: Simulating authentication..."),await new Promise(t=>setTimeout(t,1e3)),this.state.isAuthenticated=!0,this.state.currentUser={name:"Site Owner",email:"owner@example.com",role:"admin"},void console.log("â
Mock OAuth: Authentication successful");throw new Error("Production OAuth not implemented yet")}hideAllGates(){document.body.classList.add("insertr-hide-gates"),console.log("đĒ Editor gates hidden (hideGatesAfterAuth enabled)")}updateGateState(){document.querySelectorAll(".insertr-gate").forEach(t=>{const e=t.getAttribute("data-original-text");e&&(t.textContent=e),t.style.pointerEvents="",t.style.opacity=""}),console.log("đĒ Editor gates restored to original state")}createAuthControls(){if(document.getElementById("insertr-auth-controls"))return;document.body.insertAdjacentHTML("beforeend",'\n \n \n \n
\n '),this.addControlStyles()}setupAuthenticationControls(){const t=document.getElementById("insertr-auth-toggle"),e=document.getElementById("insertr-edit-toggle");t&&t.addEventListener("click",()=>this.toggleAuthentication()),e&&e.addEventListener("click",()=>this.toggleEditMode())}toggleAuthentication(){this.state.isAuthenticated=!this.state.isAuthenticated,this.state.currentUser=this.state.isAuthenticated?{name:"Demo User",email:"demo@example.com",role:"editor"}:null,this.state.isAuthenticated||(this.state.editMode=!1),this.updateBodyClasses(),this.updateButtonStates(),this.updateStatusIndicator(),console.log(this.state.isAuthenticated?"â
Authenticated as Demo User":"â Logged out")}toggleEditMode(){this.state.isAuthenticated?(this.state.editMode=!this.state.editMode,!this.state.editMode&&this.state.activeEditor&&(this.state.activeEditor=null),this.updateBodyClasses(),this.updateButtonStates(),this.updateStatusIndicator(),console.log(this.state.editMode?"âī¸ Edit mode ON - Click elements to edit":"đ Edit mode OFF - Read-only view")):console.warn("â Cannot enable edit mode - not authenticated")}updateBodyClasses(){document.body.classList.toggle("insertr-authenticated",this.state.isAuthenticated),document.body.classList.toggle("insertr-edit-mode",this.state.editMode)}updateButtonStates(){const t=document.getElementById("insertr-auth-toggle"),e=document.getElementById("insertr-edit-toggle");t&&(t.textContent=this.state.isAuthenticated?"Logout":"Login as Client",t.className="insertr-auth-btn "+(this.state.isAuthenticated?"insertr-authenticated":"")),e&&(e.style.display=this.state.isAuthenticated?"inline-block":"none",e.textContent="Edit Mode: "+(this.state.editMode?"On":"Off"),e.className="insertr-auth-btn "+(this.state.editMode?"insertr-edit-active":""))}createStatusIndicator(){if(document.getElementById("insertr-status"))return;document.body.insertAdjacentHTML("beforeend",'\n \n
\n Visitor Mode\n \n
\n
\n '),this.statusIndicator=document.getElementById("insertr-status"),this.updateStatusIndicator()}updateStatusIndicator(){const t=document.querySelector(".insertr-status-text"),e=document.querySelector(".insertr-status-dot");t&&e&&(this.state.isAuthenticated?this.state.editMode?(t.textContent="Editing",e.className="insertr-status-dot insertr-status-editing"):(t.textContent="Authenticated",e.className="insertr-status-dot insertr-status-authenticated"):(t.textContent="Visitor Mode",e.className="insertr-status-dot insertr-status-visitor"))}isAuthenticated(){return this.state.isAuthenticated}isEditMode(){return this.state.editMode}getCurrentUser(){return this.state.currentUser}addGateStyles(){const t=document.createElement("style");t.type="text/css",t.innerHTML="\n .insertr-gate {\n transition: opacity 0.2s ease;\n user-select: none;\n }\n\n .insertr-gate:hover {\n opacity: 0.7;\n }\n\n /* Optional: Hide gates when authenticated (only if hideGatesAfterAuth option is true) */\n body.insertr-hide-gates .insertr-gate {\n display: none !important;\n }\n ",document.head.appendChild(t)}addControlStyles(){const t=document.createElement("style");t.type="text/css",t.innerHTML="\n .insertr-auth-controls {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .insertr-auth-btn {\n background: #4f46e5;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n }\n\n .insertr-auth-btn:hover {\n background: #4338ca;\n transform: translateY(-1px);\n box-shadow: 0 4px 8px rgba(0,0,0,0.15);\n }\n\n .insertr-auth-btn.insertr-authenticated {\n background: #059669;\n }\n\n .insertr-auth-btn.insertr-authenticated:hover {\n background: #047857;\n }\n\n .insertr-auth-btn.insertr-edit-active {\n background: #dc2626;\n }\n\n .insertr-auth-btn.insertr-edit-active:hover {\n background: #b91c1c;\n }\n\n .insertr-status {\n position: fixed;\n bottom: 20px;\n left: 20px;\n z-index: 9999;\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 8px 12px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n max-width: 200px;\n }\n\n .insertr-status-content {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .insertr-status-text {\n font-size: 12px;\n font-weight: 500;\n color: #374151;\n }\n\n .insertr-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #9ca3af;\n }\n\n .insertr-status-dot.insertr-status-visitor {\n background: #9ca3af;\n }\n\n .insertr-status-dot.insertr-status-authenticated {\n background: #059669;\n }\n\n .insertr-status-dot.insertr-status-editing {\n background: #dc2626;\n animation: insertr-pulse 2s infinite;\n }\n\n @keyframes insertr-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n /* Hide editing interface when not in edit mode */\n body:not(.insertr-edit-mode) [data-insertr-enhanced]:hover::after {\n display: none !important;\n }\n\n /* Only show editing features when in edit mode */\n .insertr-authenticated.insertr-edit-mode [data-insertr-enhanced] {\n cursor: pointer;\n }\n\n .insertr-authenticated.insertr-edit-mode [data-insertr-enhanced]:hover {\n outline: 2px dashed #007cba !important;\n outline-offset: 2px !important;\n background-color: rgba(0, 124, 186, 0.05) !important;\n }\n ",document.head.appendChild(t)}async authenticateWithOAuth(t="google"){console.log(`đ Mock OAuth login with ${t}`),setTimeout(()=>{this.state.isAuthenticated=!0,this.state.currentUser={name:"OAuth User",email:"user@example.com",provider:t,role:"editor"},this.updateBodyClasses(),this.updateButtonStates(),this.updateStatusIndicator(),console.log("â
OAuth authentication successful")},1e3)}}function r(){document.querySelector('[data-insertr-enhanced="true"]')&&window.Insertr.init()}return window.Insertr={core:null,editor:null,auth:null,init(e={}){return console.log("đ§ Insertr v1.0.0 initializing... (Hot Reload Ready)"),this.core=new t(e),this.auth=new i(e),this.editor=new n(this.core,this.auth,e),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>this.start()):this.start(),this},start(){this.auth&&this.auth.init()},startEditor(){this.editor&&!this.editor.isActive&&this.editor.start()},login(){return this.auth?this.auth.toggleAuthentication():null},logout(){this.auth&&this.auth.isAuthenticated()&&this.auth.toggleAuthentication()},toggleEditMode(){return this.auth?this.auth.toggleEditMode():null},isAuthenticated(){return!!this.auth&&this.auth.isAuthenticated()},isEditMode(){return!!this.auth&&this.auth.isEditMode()},version:"1.0.0"},"loading"===document.readyState?document.addEventListener("DOMContentLoaded",r):r(),window.Insertr}();
diff --git a/insertr-cli/pkg/content/enhancer.go b/insertr-cli/pkg/content/enhancer.go
index 6c6815e..1de83ff 100644
--- a/insertr-cli/pkg/content/enhancer.go
+++ b/insertr-cli/pkg/content/enhancer.go
@@ -69,7 +69,7 @@ func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error {
}
// Inject editor assets for development
- libraryScript := GetLibraryScript(true) // Use minified for better performance
+ libraryScript := GetLibraryScript(false) // Use non-minified for development debugging
e.injector.InjectEditorAssets(doc, true, libraryScript)
// Write enhanced HTML
diff --git a/insertr-cli/pkg/content/injector.go b/insertr-cli/pkg/content/injector.go
index dfb2433..bbb55e6 100644
--- a/insertr-cli/pkg/content/injector.go
+++ b/insertr-cli/pkg/content/injector.go
@@ -2,6 +2,7 @@ package content
import (
"fmt"
+ "strings"
"golang.org/x/net/html"
)
@@ -145,21 +146,19 @@ func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool, librar
}
// Add inline script with embedded library
- script := &html.Node{
- Type: html.ElementNode,
- Data: "script",
- Attr: []html.Attribute{
- {Key: "type", Val: "text/javascript"},
- },
- }
+ // Note: Using html.TextNode for scripts can cause issues with HTML entity encoding
+ // Instead, we'll insert the script tag as raw HTML
+ scriptHTML := fmt.Sprintf(``, libraryScript)
- // Add the library content as text node
- textNode := &html.Node{
- Type: html.TextNode,
- Data: libraryScript,
+ // Parse the script HTML and append to head
+ scriptNodes, err := html.ParseFragment(strings.NewReader(scriptHTML), head)
+ if err == nil && len(scriptNodes) > 0 {
+ for _, node := range scriptNodes {
+ head.AppendChild(node)
+ }
}
- script.AppendChild(textNode)
- head.AppendChild(script)
}
// findHeadElement finds the element in the document
diff --git a/lib/src/core/auth.js b/lib/src/core/auth.js
index b2c26e9..9ec3e0a 100644
--- a/lib/src/core/auth.js
+++ b/lib/src/core/auth.js
@@ -6,7 +6,7 @@ export class InsertrAuth {
constructor(options = {}) {
this.options = {
mockAuth: options.mockAuth !== false, // Enable mock auth by default
- autoCreateControls: options.autoCreateControls !== false,
+ hideGatesAfterAuth: options.hideGatesAfterAuth === true, // Keep gates visible by default
...options
};
@@ -15,31 +15,191 @@ export class InsertrAuth {
isAuthenticated: false,
editMode: false,
currentUser: null,
- activeEditor: null
+ activeEditor: null,
+ isInitialized: false,
+ isAuthenticating: false
};
this.statusIndicator = null;
}
/**
- * Initialize authentication system
+ * Initialize gate system (called on page load)
*/
init() {
- console.log('đ Initializing Insertr Authentication');
+ console.log('đ§ Insertr: Scanning for editor gates');
- if (this.options.autoCreateControls) {
- this.createAuthControls();
+ this.setupEditorGates();
+ }
+
+ /**
+ * Initialize full editing system (called after successful OAuth)
+ */
+ initializeFullSystem() {
+ if (this.state.isInitialized) {
+ return; // Already initialized
}
+
+ console.log('đ Initializing Insertr Editing System');
+ this.createAuthControls();
this.setupAuthenticationControls();
this.createStatusIndicator();
this.updateBodyClasses();
- console.log('đą Auth controls ready - Look for buttons in top-right corner');
+ // Auto-enable edit mode after OAuth
+ this.state.editMode = true;
+ this.state.isInitialized = true;
+
+ // Start the editor system
+ if (window.Insertr && window.Insertr.startEditor) {
+ window.Insertr.startEditor();
+ }
+
+ this.updateButtonStates();
+ this.updateStatusIndicator();
+
+ console.log('đą Editing system active - Controls in bottom-right corner');
+ console.log('âī¸ Edit mode enabled - Click elements to edit');
}
/**
- * Create authentication control buttons if they don't exist
+ * Setup editor gate click handlers for any .insertr-gate elements
+ */
+ setupEditorGates() {
+ const gates = document.querySelectorAll('.insertr-gate');
+
+ if (gates.length === 0) {
+ console.log('âšī¸ No .insertr-gate elements found - editor access disabled');
+ return;
+ }
+
+ console.log(`đĒ Found ${gates.length} editor gate(s)`);
+
+ // Add gate styles
+ this.addGateStyles();
+
+ gates.forEach((gate, index) => {
+ // Store original text for later restoration
+ if (!gate.hasAttribute('data-original-text')) {
+ gate.setAttribute('data-original-text', gate.textContent);
+ }
+
+ gate.addEventListener('click', (e) => {
+ e.preventDefault();
+ this.handleGateClick(gate, index);
+ });
+
+ // Add subtle styling to indicate it's clickable
+ gate.style.cursor = 'pointer';
+ });
+ }
+
+ /**
+ * Handle click on an editor gate element
+ */
+ async handleGateClick(gateElement, gateIndex) {
+ // Prevent multiple simultaneous authentication attempts
+ if (this.state.isAuthenticating) {
+ console.log('âŗ Authentication already in progress...');
+ return;
+ }
+
+ console.log(`đ Editor gate activated (gate ${gateIndex + 1})`);
+ this.state.isAuthenticating = true;
+
+ // Store original text and show loading state
+ const originalText = gateElement.textContent;
+ gateElement.setAttribute('data-original-text', originalText);
+ gateElement.textContent = 'âŗ Signing in...';
+ gateElement.style.pointerEvents = 'none';
+
+ try {
+ // Perform OAuth authentication
+ await this.performOAuthFlow();
+
+ // Initialize full editing system
+ this.initializeFullSystem();
+
+ // Conditionally hide gates based on options
+ if (this.options.hideGatesAfterAuth) {
+ this.hideAllGates();
+ } else {
+ this.updateGateState();
+ }
+
+ } catch (error) {
+ console.error('â Authentication failed:', error);
+
+ // Restore clicked gate to original state
+ const originalText = gateElement.getAttribute('data-original-text');
+ if (originalText) {
+ gateElement.textContent = originalText;
+ }
+ gateElement.style.pointerEvents = '';
+ } finally {
+ this.state.isAuthenticating = false;
+ }
+ }
+
+ /**
+ * Perform OAuth authentication flow
+ */
+ async performOAuthFlow() {
+ // In development, simulate OAuth flow
+ if (this.options.mockAuth) {
+ console.log('đ Mock OAuth: Simulating authentication...');
+
+ // Simulate network delay
+ await new Promise(resolve => setTimeout(resolve, 1000));
+
+ // Set authenticated state
+ this.state.isAuthenticated = true;
+ this.state.currentUser = {
+ name: 'Site Owner',
+ email: 'owner@example.com',
+ role: 'admin'
+ };
+
+ console.log('â
Mock OAuth: Authentication successful');
+ return;
+ }
+
+ // TODO: In production, implement real OAuth flow
+ // This would redirect to OAuth provider, handle callback, etc.
+ throw new Error('Production OAuth not implemented yet');
+ }
+
+ /**
+ * Hide all editor gates after successful authentication (optional)
+ */
+ hideAllGates() {
+ document.body.classList.add('insertr-hide-gates');
+ console.log('đĒ Editor gates hidden (hideGatesAfterAuth enabled)');
+ }
+
+ /**
+ * Update gate state after authentication (restore normal appearance)
+ */
+ updateGateState() {
+ const gates = document.querySelectorAll('.insertr-gate');
+ gates.forEach(gate => {
+ // Restore original text if it was saved
+ const originalText = gate.getAttribute('data-original-text');
+ if (originalText) {
+ gate.textContent = originalText;
+ }
+
+ // Restore interactive state
+ gate.style.pointerEvents = '';
+ gate.style.opacity = '';
+ });
+
+ console.log('đĒ Editor gates restored to original state');
+ }
+
+ /**
+ * Create authentication control buttons (bottom-right positioned)
*/
createAuthControls() {
// Check if controls already exist
@@ -220,6 +380,32 @@ export class InsertrAuth {
return this.state.currentUser;
}
+ /**
+ * Add minimal styles for editor gates
+ */
+ addGateStyles() {
+ const styles = `
+ .insertr-gate {
+ transition: opacity 0.2s ease;
+ user-select: none;
+ }
+
+ .insertr-gate:hover {
+ opacity: 0.7;
+ }
+
+ /* Optional: Hide gates when authenticated (only if hideGatesAfterAuth option is true) */
+ body.insertr-hide-gates .insertr-gate {
+ display: none !important;
+ }
+ `;
+
+ const styleSheet = document.createElement('style');
+ styleSheet.type = 'text/css';
+ styleSheet.innerHTML = styles;
+ document.head.appendChild(styleSheet);
+ }
+
/**
* Add styles for authentication controls
*/
@@ -227,11 +413,12 @@ export class InsertrAuth {
const styles = `
.insertr-auth-controls {
position: fixed;
- top: 20px;
+ bottom: 20px;
right: 20px;
z-index: 9999;
display: flex;
- gap: 10px;
+ flex-direction: column;
+ gap: 8px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
@@ -281,6 +468,7 @@ export class InsertrAuth {
padding: 8px 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ max-width: 200px;
}
.insertr-status-content {
diff --git a/lib/src/index.js b/lib/src/index.js
index d66a5be..995f78f 100644
--- a/lib/src/index.js
+++ b/lib/src/index.js
@@ -33,12 +33,17 @@ window.Insertr = {
return this;
},
- // Start the editor and authentication
+ // Start the system - only creates the minimal trigger
start() {
if (this.auth) {
- this.auth.init();
+ this.auth.init(); // Creates footer trigger only
}
- if (this.editor) {
+ // Note: Editor is NOT started here, only when trigger is clicked
+ },
+
+ // Start the full editor system (called when trigger is activated)
+ startEditor() {
+ if (this.editor && !this.editor.isActive) {
this.editor.start();
}
},
@@ -70,9 +75,19 @@ window.Insertr = {
version: '1.0.0'
};
-// Auto-initialize in development mode
-if (document.querySelector('[data-insertr-enhanced]')) {
- window.Insertr.init();
+// Auto-initialize in development mode with proper DOM ready handling
+function autoInitialize() {
+ if (document.querySelector('[data-insertr-enhanced="true"]')) {
+ window.Insertr.init();
+ }
+}
+
+// Run auto-initialization when DOM is ready
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', autoInitialize);
+} else {
+ // DOM is already ready
+ autoInitialize();
}
export default window.Insertr;
\ No newline at end of file