feat: complete code cleanup and create feature parity plan

Major Architecture Improvements:
- Separate JavaScript library (lib/) with proper build system
- Go CLI with embedded library using go:embed
- Hot reload development with Air integration
- Library + CLI build pipeline with npm run build

Code Cleanup:
- Remove obsolete assets (insertr-cli/assets/editor/)
- Clean up package.json metadata and dependencies
- Update .gitignore for new architecture
- Remove unused 'marked' dependency

New Documentation:
- Add comprehensive TODO.md with feature gap analysis
- Document critical gaps between prototype and current library
- Create phased implementation plan for feature parity
- Update DEVELOPMENT.md with hot reload workflow
- Add LIBRARY.md documenting new architecture

Hot Reload System:
- Air watches both Go CLI and JavaScript library
- Library changes trigger: rebuild → copy → CLI rebuild → serve
- Seamless development experience across full stack

Next Steps:
- Current library is basic proof-of-concept (prompt() editing)
- Archived prototype has production-ready features
- Phase 1 focuses on professional forms and authentication
- Phase 2 adds validation and content persistence
This commit is contained in:
2025-09-03 19:11:54 +02:00
parent f82d8bb287
commit ca3df47451
27 changed files with 1747 additions and 164 deletions

View File

@@ -0,0 +1,197 @@
var Insertr = (function () {
'use strict';
/**
* InsertrCore - Core functionality for content management
*/
class InsertrCore {
constructor(options = {}) {
this.options = {
apiEndpoint: options.apiEndpoint || '/api/content',
siteId: options.siteId || 'default',
...options
};
}
// Find all enhanced elements on the page
findEnhancedElements() {
return document.querySelectorAll('[data-insertr-enhanced="true"]');
}
// Get element metadata
getElementMetadata(element) {
return {
contentId: element.getAttribute('data-content-id'),
contentType: element.getAttribute('data-content-type'),
element: element
};
}
// Get all elements with their metadata
getAllElements() {
const elements = this.findEnhancedElements();
return Array.from(elements).map(el => this.getElementMetadata(el));
}
}
/**
* InsertrEditor - Visual editing functionality
*/
class InsertrEditor {
constructor(core, options = {}) {
this.core = core;
this.options = options;
this.isActive = false;
}
start() {
if (this.isActive) return;
console.log('🚀 Starting Insertr Editor');
this.isActive = true;
// Add editor styles
this.addEditorStyles();
// Initialize all enhanced elements
const elements = this.core.getAllElements();
console.log(`📝 Found ${elements.length} editable elements`);
elements.forEach(meta => this.initializeElement(meta));
}
initializeElement(meta) {
const { element, contentId, contentType } = meta;
// Add visual indicators
element.style.cursor = 'pointer';
element.style.position = 'relative';
// Add interaction handlers
this.addHoverEffects(element);
this.addClickHandler(element, meta);
}
addHoverEffects(element) {
element.addEventListener('mouseenter', () => {
element.classList.add('insertr-editing-hover');
});
element.addEventListener('mouseleave', () => {
element.classList.remove('insertr-editing-hover');
});
}
addClickHandler(element, meta) {
element.addEventListener('click', (e) => {
e.preventDefault();
this.openEditor(meta);
});
}
openEditor(meta) {
const { contentId, contentType, element } = meta;
const currentContent = element.textContent.trim();
// For now, use a simple prompt (will be replaced with proper modal)
const newContent = prompt(
`Edit ${contentType} content (ID: ${contentId}):`,
currentContent
);
if (newContent !== null && newContent !== currentContent) {
this.updateContent(meta, newContent);
}
}
updateContent(meta, newContent) {
const { element } = meta;
// Update the element content
element.textContent = newContent;
// TODO: Save to backend API
console.log(`💾 Content updated:`, meta.contentId, newContent);
}
addEditorStyles() {
const styles = `
.insertr-editing-hover {
outline: 2px dashed #007cba !important;
outline-offset: 2px !important;
background-color: rgba(0, 124, 186, 0.05) !important;
}
[data-insertr-enhanced="true"]: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;
}
`;
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerHTML = styles;
document.head.appendChild(styleSheet);
}
}
/**
* Insertr - The Tailwind of CMS
* Main library entry point
*/
// Create global Insertr instance
window.Insertr = {
// Core functionality
core: null,
editor: null,
// Initialize the library
init(options = {}) {
console.log('🔧 Insertr v1.0.0 initializing... (Hot Reload Ready)');
this.core = new InsertrCore(options);
this.editor = new InsertrEditor(this.core, options);
// Auto-initialize if DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.start());
} else {
this.start();
}
return this;
},
// Start the editor
start() {
if (this.editor) {
this.editor.start();
}
},
// Version info
version: '1.0.0'
};
// Auto-initialize in development mode
if (document.querySelector('[data-insertr-enhanced]')) {
window.Insertr.init();
}
var index = window.Insertr;
return index;
})();

View File

@@ -0,0 +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(t,e={}){this.core=t,this.options=e,this.isActive=!1}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=>{t.preventDefault(),this.openEditor(e)})}openEditor(t){const{contentId:e,contentType:n,element:i}=t,o=i.textContent.trim(),r=prompt(`Edit ${n} content (ID: ${e}):`,o);null!==r&&r!==o&&this.updateContent(t,r)}updateContent(t,e){const{element:n}=t;n.textContent=e,console.log("💾 Content updated:",t.contentId,e)}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)}}return window.Insertr={core:null,editor:null,init(n={}){return console.log("🔧 Insertr v1.0.0 initializing... (Hot Reload Ready)"),this.core=new t(n),this.editor=new e(this.core,n),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>this.start()):this.start(),this},start(){this.editor&&this.editor.start()},version:"1.0.0"},document.querySelector("[data-insertr-enhanced]")&&window.Insertr.init(),window.Insertr}();

View File

@@ -69,7 +69,8 @@ func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error {
}
// Inject editor assets for development
e.injector.InjectEditorAssets(doc, true)
libraryScript := GetLibraryScript(true) // Use minified for better performance
e.injector.InjectEditorAssets(doc, true, libraryScript)
// Write enhanced HTML
if err := e.writeHTML(doc, outputPath); err != nil {

View File

@@ -133,7 +133,7 @@ func (i *Injector) addContentAttributes(node *html.Node, contentID string, conte
}
// InjectEditorAssets adds editor JavaScript and CSS to HTML document
func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool) {
func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool, libraryScript string) {
if !isDevelopment {
return // Only inject in development mode for now
}
@@ -144,15 +144,21 @@ func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool) {
return
}
// Add editor JavaScript
// Add inline script with embedded library
script := &html.Node{
Type: html.ElementNode,
Data: "script",
Attr: []html.Attribute{
{Key: "src", Val: "/_insertr/insertr-editor.js"},
{Key: "defer", Val: ""},
{Key: "type", Val: "text/javascript"},
},
}
// Add the library content as text node
textNode := &html.Node{
Type: html.TextNode,
Data: libraryScript,
}
script.AppendChild(textNode)
head.AppendChild(script)
}

View File

@@ -0,0 +1,26 @@
package content
import (
_ "embed"
)
// Embedded library assets
//
//go:embed assets/insertr.min.js
var libraryMinJS string
//go:embed assets/insertr.js
var libraryJS string
// GetLibraryScript returns the appropriate library version
func GetLibraryScript(minified bool) string {
if minified {
return libraryMinJS
}
return libraryJS
}
// GetLibraryVersion returns the current embedded library version
func GetLibraryVersion() string {
return "1.0.0"
}