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

6
.gitignore vendored
View File

@@ -7,6 +7,9 @@ yarn-error.log*
# Build outputs # Build outputs
dist/ dist/
build/ build/
lib/dist/
insertr-cli/insertr
insertr-cli/tmp/
# Environment files # Environment files
.env .env
@@ -49,7 +52,7 @@ temp/
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
# Go specific (for future backend) # Go specific
*.exe *.exe
*.exe~ *.exe~
*.dll *.dll
@@ -59,6 +62,7 @@ temp/
*.out *.out
go.work go.work
vendor/ vendor/
insertr-cli/build-errors.log
# Database files # Database files
*.db *.db

76
CLEANUP_SUMMARY.md Normal file
View File

@@ -0,0 +1,76 @@
# Code Cleanup Summary
## Artifacts Removed from Architecture Pivot
### 🗑️ **Obsolete Assets Removed**
- `insertr-cli/assets/editor/insertr-editor.js` - Legacy inline editor script
- `insertr-cli/assets/editor/` directory - No longer needed
- Unused `marked` npm dependency from root package.json
### 🧹 **Root Package.json Cleanup**
**Before:**
```json
{
"description": "Edit-in-place CMS for client websites - simple integration with class-based content editing",
"main": "demo-site/insertr/insertr.js",
"keywords": ["cms", "edit-in-place", "inline-editing", "client-websites", "go", "htmx", "alpine"],
"dependencies": {
"marked": "^16.2.1"
}
}
```
**After:**
```json
{
"description": "The Tailwind of CMS - Zero-configuration content editing for any static site",
"main": "lib/dist/insertr.js",
"keywords": ["cms", "headless-cms", "static-site-generator", "build-time-enhancement", "zero-config", "go", "javascript"],
"dependencies": {}
}
```
### ✅ **Legacy Code Preserved (Intentionally)**
- `demo-site/archive/` - Contains original prototype for reference
- `scripts/dev.js` - Still useful for testing original prototype
- Legacy npm scripts (`dev:*`) - Kept for prototype development
- `demo-site/README.md` - Accurately describes prototype functionality
### 🔧 **Enhanced .gitignore**
Added new build artifacts:
```gitignore
# Build outputs
lib/dist/ # Library build output
insertr-cli/insertr # CLI executable
insertr-cli/tmp/ # Air temporary files
insertr-cli/build-errors.log # Air error logs
```
### 🎯 **What Was NOT Removed**
- **Archive directory**: `demo-site/archive/` kept for reference
- **Development scripts**: Legacy scripts still work for prototype testing
- **Documentation references**: Accurate historical context preserved
- **Go embed assets**: `insertr-cli/pkg/content/assets/` must stay for embedding
## Validation
### ✅ **Build System Verified**
```bash
npm run build # ✅ Works
cd insertr-cli && air # ✅ Hot reload works
./insertr enhance # ✅ CLI works with embedded library
```
### ✅ **Architecture Clean**
- No dead code or unused imports in Go
- No obsolete script references
- Clean dependency tree (1 package removed)
- Updated project metadata reflects current architecture
### 🎯 **Result**
The codebase now cleanly separates:
1. **Current Architecture**: `lib/` + `insertr-cli/` with proper embedding
2. **Legacy Prototype**: `demo-site/archive/` for reference
3. **Test Content**: `demo-site/` HTML files for enhancement testing
No functionality was lost, and the transition from prototype to production architecture is complete.

View File

@@ -2,30 +2,52 @@
## Quick Start ## Quick Start
### Full-Stack Development (Recommended)
1. **Install dependencies**: 1. **Install dependencies**:
```bash ```bash
npm install npm install # Root project dependencies
cd lib && npm install # Library dependencies
``` ```
2. **Start development server**: 2. **Start hot reload development**:
```bash ```bash
npm run dev cd insertr-cli && air # Starts server with library hot reload
``` ```
This will start live-server on http://localhost:3000 and automatically open the demo site. - Watches both Go CLI changes AND JavaScript library changes
- Automatically rebuilds library → CLI → serves enhanced content
- Visit http://localhost:3000 to see results
3. **Alternative commands**: ### Library-Only Development
```bash ```bash
npm run dev:about # Open about page directly cd lib && npm run watch # Just develop the JavaScript library
npm run serve # Alias for npm run dev ```
### Legacy Frontend Development
```bash
npm run dev # Legacy live-server (demo-site only)
``` ```
## Development Workflow ## Development Workflow
### Frontend Development (Current Phase) ### Hot Reload Development (Current Phase)
**🔥 NEW**: Full-stack hot reload with Air integration!
- **Library Source**: `lib/src/` - Independent JavaScript library
- **CLI Integration**: `insertr-cli/` - Go CLI with embedded library
- **Demo Site**: `demo-site/` - Test website for enhancement
- **Automatic Rebuilds**: Changes to JS library trigger full rebuild cycle
#### How Hot Reload Works:
1. Edit JavaScript in `lib/src/`
2. Air detects changes → Rebuilds library → Copies to CLI → Rebuilds CLI
3. Enhanced development server serves updated content
4. Manual browser refresh shows changes
### Legacy Frontend Development
- **Demo Site**: `demo-site/` contains the prototype website - **Demo Site**: `demo-site/` contains the prototype website
- **Core Library**: `demo-site/insertr/insertr.js` - the main Insertr library - **Core Library**: `demo-site/insertr/insertr.js` - the old prototype library
- **Styling**: `demo-site/insertr/insertr.css` - edit interface styles
- **Mock API**: `demo-site/mock-api/content.json` - sample backend data structure - **Mock API**: `demo-site/mock-api/content.json` - sample backend data structure
### Testing the Three User Types ### Testing the Three User Types
@@ -36,9 +58,15 @@
### Making Changes ### Making Changes
#### Modern Development (lib/ + insertr-cli/)
- **Library Changes**: Edit `lib/src/**/*.js` → Air automatically rebuilds everything
- **CLI Changes**: Edit Go files in `insertr-cli/` → Air rebuilds and restarts server
- **Demo Content**: Edit `demo-site/*.html` → Air serves enhanced versions
- **Hot Reload**: Changes trigger full rebuild cycle (library → CLI → enhanced content)
#### Legacy Development (demo-site/)
- **Live Reload**: The server automatically refreshes when you save changes - **Live Reload**: The server automatically refreshes when you save changes
- **JavaScript**: Edit `demo-site/insertr/insertr.js` to modify core functionality - **JavaScript**: Edit `demo-site/insertr/insertr.js` for prototype functionality
- **Styling**: Edit `demo-site/insertr/insertr.css` for UI changes
- **Content**: Edit HTML files to test different content structures - **Content**: Edit HTML files to test different content structures
### Adding New Features ### Adding New Features
@@ -51,40 +79,67 @@
``` ```
insertr/ insertr/
├── INITIAL.md # Project requirements and research
├── DEVELOPMENT.md # This file ├── DEVELOPMENT.md # This file
├── package.json # Node.js project configuration ├── LIBRARY.md # Library architecture documentation
├── .gitignore # Git ignore rules ├── package.json # Root project configuration
├── demo-site/ # Frontend prototype ├── lib/ # 🆕 Independent JavaScript Library
│ ├── src/
│ │ ├── core/
│ │ │ ├── insertr.js # Core library functionality
│ │ │ ├── editor.js # Visual editing interface
│ │ │ └── api-client.js # Content API client
│ │ └── index.js # Library entry point
│ ├── dist/
│ │ ├── insertr.js # Built library (development)
│ │ └── insertr.min.js # Built library (production)
│ ├── package.json # Library dependencies
│ └── rollup.config.js # Build configuration
├── insertr-cli/ # 🆕 Go CLI with Embedded Library
│ ├── pkg/content/
│ │ ├── assets/ # Embedded library files (auto-copied)
│ │ ├── library.go # Go embed declarations
│ │ ├── enhancer.go # HTML enhancement orchestrator
│ │ ├── injector.go # Content injection logic
│ │ └── ...
│ ├── scripts/
│ │ └── rebuild-library.sh # Air helper script
│ ├── .air.toml # Hot reload configuration
│ └── insertr # Built CLI executable
├── demo-site/ # Test website for enhancement
│ ├── index.html # Demo homepage │ ├── index.html # Demo homepage
│ ├── about.html # Demo about page │ ├── about.html # Demo about page
── README.md # Demo usage instructions ── assets/style.css # Demo site styling
│ │
│ ├── assets/ # Demo site assets
│ │ └── style.css # Demo site styling
│ │
│ ├── insertr/ # Core Insertr library
│ │ ├── insertr.js # Main library file
│ │ ├── insertr.css # Edit interface styles
│ │ └── components/ # Future: reusable edit components
│ │
│ └── mock-api/ # Backend planning
│ └── content.json # Sample data structure
└── backend/ # Future: Go backend └── scripts/
── main.go # Future: HTTP server ── build.js # Integrated build script
├── api/ # Future: REST endpoints
└── storage/ # Future: file-based storage
``` ```
## Development Scripts ## Development Scripts
- `npm run dev` - Start development server with live reload ### Root Scripts
- `npm run dev:about` - Start server opening about page - `npm run build` - Build library + CLI with embedded assets
- `npm run build` - Future: Bundle library for distribution - `npm run dev` - Legacy live-server (demo-site only)
- `npm run test` - Future: Run tests
- `npm run lint` - Future: Code linting ### Library Scripts (cd lib/)
- `npm run build` - Build library to dist/
- `npm run watch` - Watch and rebuild library on changes
### CLI Scripts (cd insertr-cli/)
- `air` - 🔥 Hot reload server (library + CLI + enhanced content)
- `go build -o insertr` - Manual CLI build
- `./insertr enhance <dir>` - Enhance static site
- `./insertr servedev <dir>` - Development server with enhancement
### Hot Reload Workflow
```bash
cd insertr-cli && air # Start hot reload development
# Edit lib/src/**/*.js # Air detects → rebuilds → serves
# Edit cmd/**/*.go # Air detects → rebuilds → serves
# Edit ../demo-site/ # Air serves enhanced versions
```
## Testing Different Scenarios ## Testing Different Scenarios

138
LIBRARY.md Normal file
View File

@@ -0,0 +1,138 @@
# Insertr Library Architecture
## Overview
Insertr now uses a proper separation between the JavaScript library and Go CLI, following the "Tailwind of CMS" philosophy where the library can be developed independently and embedded into the CLI at build time.
## Project Structure
```
insertr/
├── lib/ # Independent JavaScript library
│ ├── src/
│ │ ├── core/
│ │ │ ├── insertr.js # Main library core
│ │ │ ├── editor.js # Visual editing functionality
│ │ │ └── api-client.js # Content API client
│ │ └── index.js # Library entry point
│ ├── dist/
│ │ ├── insertr.js # Built library (dev)
│ │ └── insertr.min.js # Built library (production)
│ ├── package.json
│ └── rollup.config.js # Build configuration
├── insertr-cli/ # Go CLI with embedded library
│ ├── pkg/content/
│ │ ├── assets/ # Embedded library files
│ │ │ ├── insertr.js # From lib/dist/
│ │ │ └── insertr.min.js # From lib/dist/
│ │ ├── library.go # Go embed declarations
│ │ ├── injector.go # HTML injection logic
│ │ └── ...
│ └── insertr # Built CLI executable
└── scripts/
└── build.js # Integrated build script
```
## Build Process
### 1. Library Development (`lib/`)
```bash
cd lib
npm install
npm run build # Creates dist/insertr.js and dist/insertr.min.js
npm run watch # Development mode with file watching
```
### 2. CLI Integration (`insertr-cli/`)
The CLI uses Go's `//go:embed` directive to embed the built library:
```go
//go:embed assets/insertr.min.js
var libraryMinJS string
//go:embed assets/insertr.js
var libraryJS string
```
### 3. Integrated Build (Root)
```bash
npm run build # Builds both library AND CLI with embedded assets
```
This script:
1. Builds the JavaScript library (`lib/dist/`)
2. Copies built assets to CLI (`insertr-cli/pkg/content/assets/`)
3. Builds Go CLI with embedded library
## Library Features
### Core Functionality (`src/core/insertr.js`)
- Element discovery: `findEnhancedElements()`
- Metadata extraction: `getElementMetadata()`
- Element management: `getAllElements()`
### Visual Editor (`src/core/editor.js`)
- Hover effects and visual indicators
- Click-to-edit functionality
- Content update handling
- Dynamic style injection
### API Client (`src/core/api-client.js`)
- RESTful content API communication
- CRUD operations for content
- Error handling and fallbacks
## CLI Integration
### Asset Embedding (`pkg/content/library.go`)
```go
func GetLibraryScript(minified bool) string {
if minified {
return libraryMinJS // Production: smaller file size
}
return libraryJS // Development: readable code
}
```
### HTML Injection (`pkg/content/injector.go`)
```go
func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool, libraryScript string) {
// Injects the embedded library as inline <script> tag
}
```
## Development Workflow
### Library Development
```bash
cd lib
npm run watch # Auto-rebuild on changes
```
### CLI Development
```bash
cd insertr-cli
go build -o insertr
./insertr enhance ../demo-site/
```
### Full Integration Testing
```bash
npm run build # Builds library + CLI
cd insertr-cli && ./insertr enhance ../demo-site/
```
## Benefits
1. **Independent Development**: Library can be developed and tested separately
2. **Version Control**: CLI embeds specific library version at build time
3. **Zero Dependencies**: Enhanced HTML contains everything needed to run
4. **CDN Ready**: Library can be served from CDN in production
5. **Development Friendly**: Unminified version for debugging
## Future Enhancements
- **CDN Integration**: Support for `<script src="https://cdn.insertr.dev/v1.0.0/insertr.min.js">`
- **Library Hot Reload**: Auto-rebuild CLI when library changes
- **Version Sync**: Automated version bumping across library and CLI
- **Production Optimization**: Tree-shaking and custom builds

View File

@@ -178,18 +178,24 @@ Static Site Build → Insertr CLI Enhancement → Enhanced Deployment
## 🔧 Development Workflow ## 🔧 Development Workflow
### **Live Development** ### **Live Development with Hot Reload**
```bash ```bash
cd insertr-cli cd insertr-cli
# Air watches Go files and rebuilds CLI # 🔥 Hot Reload: watches BOTH library AND CLI
# Post-build: starts development server
air air
# Edit parser code → Auto rebuild → Server restart # Library changes: lib/src/**/*.js → Auto rebuild library + CLI
# CLI changes: cmd/**/*.go → Auto rebuild CLI
# Both trigger server restart with latest embedded assets
# Visit localhost:3000 → Refresh to see changes # Visit localhost:3000 → Refresh to see changes
``` ```
**What Gets Hot Reloaded:**
- JavaScript library changes (`lib/src/`)
- Go CLI changes (`insertr-cli/`)
- Enhanced HTML output (real-time content injection)
### **Content ID Management** ### **Content ID Management**
**Development Phase:** **Development Phase:**
- Generated IDs are disposable and change freely - Generated IDs are disposable and change freely

128
TODO.md Normal file
View File

@@ -0,0 +1,128 @@
# Insertr Library Feature Parity TODO
## Overview
Bring the current library (`lib/`) up to feature parity with the archived prototype (`demo-site/archive/insertr-old/`). The prototype has significantly more features and professional polish than our current basic library.
## Critical Feature Gaps Identified
### Current Library Status: Basic Proof-of-Concept
- ❌ Simple prompt() editing only
- ❌ No authentication or state management
- ❌ No form system or validation
- ❌ No content persistence
- ❌ Text-only editing (no markdown, links)
- ❌ Basic hover effects only
### Prototype Status: Production-Ready
- ✅ Professional modal editing system
- ✅ Full authentication & edit mode states
- ✅ Sophisticated form renderer with validation
- ✅ LocalStorage persistence
- ✅ Multiple content types (text/markdown/link)
- ✅ Mobile-responsive design
## Implementation Plan
### 🔴 Phase 1: Critical Foundation (IMMEDIATE)
#### 1.1 Authentication System
- [ ] Add state management for authentication and edit mode
- [ ] Implement body class management (`insertr-authenticated`, `insertr-edit-mode`)
- [ ] Create authentication controls (login/logout toggle)
- [ ] Add edit mode toggle (separate from authentication)
#### 1.2 Professional Edit Forms ⭐ **HIGH IMPACT**
- [ ] Replace prompt() with professional modal overlays
- [ ] Create dynamic form renderer based on content type
- [ ] Implement smart form positioning relative to elements
- [ ] Add mobile-responsive form layouts
#### 1.3 Content Type Support
- [ ] Text fields with length validation
- [ ] Textarea fields for paragraphs
- [ ] Link fields (URL + text) with validation
- [ ] Markdown fields with live preview
#### 1.4 Data Persistence
- [ ] Implement LocalStorage-based content persistence
- [ ] Create centralized content management system
- [ ] Add content caching and invalidation
### 🟡 Phase 2: Enhanced UX (IMPORTANT)
#### 2.1 Visual Polish
- [ ] Add positioned edit buttons on element hover
- [ ] Implement professional hover effects and transitions
- [ ] Create loading states for save operations
- [ ] Add success/error toast notifications
#### 2.2 Validation System
- [ ] Input validation (length, required fields, format)
- [ ] URL validation for link fields
- [ ] Markdown syntax validation and warnings
- [ ] XSS protection and content sanitization
#### 2.3 Configuration System
- [ ] Auto-detect field types from HTML elements (H1-H6, P, A, etc.)
- [ ] CSS class-based enhancement (`.lead`, `.btn-primary`, etc.)
- [ ] Developer-extensible field type system
- [ ] Configurable validation rules per field type
### 🟢 Phase 3: Advanced Features (NICE-TO-HAVE)
#### 3.1 Markdown System
- [ ] Port full markdown processing system from prototype
- [ ] Real-time markdown preview in edit forms
- [ ] DOMPurify integration for security
- [ ] Markdown toolbar (bold, italic, links)
#### 3.2 Mobile Optimization
- [ ] Touch-friendly edit interactions
- [ ] Full-screen mobile edit experience
- [ ] Adaptive modal sizing and positioning
- [ ] Touch-optimized hover states
## Key Files to Port/Adapt
### From Prototype (`demo-site/archive/insertr-old/`)
- `config.js``lib/src/core/config.js` (field type detection)
- `form-renderer.js``lib/src/ui/form-renderer.js` (modal forms)
- `validation.js``lib/src/core/validation.js` (input validation)
- `content-manager.js``lib/src/core/content-manager.js` (persistence)
- `markdown-processor.js``lib/src/core/markdown.js` (markdown support)
- `insertr.css``lib/src/styles/` (professional styling)
### Architecture Adaptations Needed
- **Build-Time Integration**: Ensure features work with CLI-enhanced HTML
- **Hot Reload Compatibility**: Maintain development experience
- **Library Independence**: Keep self-contained for CDN usage
- **Modern ES6+ Modules**: Update from class-based to module-based architecture
## Success Criteria
After Phase 1 & 2 completion:
- ✅ Professional editing experience matching prototype quality
- ✅ All critical content types supported (text/markdown/link)
- ✅ Authentication and state management working
- ✅ Persistent content storage with LocalStorage
- ✅ Mobile-responsive editing interface
- ✅ Input validation and XSS protection
## Next Steps
1. **Start with Phase 1.2** (Professional Edit Forms) - highest user impact
2. **Port form-renderer.js** first - creates immediate professional feel
3. **Test integration** with CLI enhancement pipeline
4. **Maintain hot reload** functionality during development
## Architecture Decision
Keep the current library + CLI architecture while porting prototype features:
- Library remains independent and CDN-ready
- CLI continues build-time enhancement approach
- Hot reload development experience preserved
- "Tailwind of CMS" philosophy maintained
---
*This TODO represents bringing a basic proof-of-concept up to production-ready feature parity with the original prototype.*

View File

@@ -7,21 +7,21 @@ tmp_dir = "tmp"
bin = "./tmp/insertr" bin = "./tmp/insertr"
cmd = "go build -o ./tmp/insertr ." cmd = "go build -o ./tmp/insertr ."
delay = 1000 delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"] exclude_dir = ["tmp", "vendor", "testdata", "node_modules", "dist"]
exclude_file = [] exclude_file = []
exclude_regex = ["_test.go"] exclude_regex = ["_test.go"]
exclude_unchanged = false exclude_unchanged = false
follow_symlink = false follow_symlink = false
full_bin = "./tmp/insertr servedev -i ../demo-site -p 3000" full_bin = "./tmp/insertr servedev -i ../demo-site -p 3000"
include_dir = [] include_dir = ["../lib/src"]
include_ext = ["go", "tpl", "tmpl", "html"] include_ext = ["go", "tpl", "tmpl", "html", "js"]
include_file = [] include_file = []
kill_delay = "0s" kill_delay = "0s"
log = "build-errors.log" log = "build-errors.log"
poll = false poll = false
poll_interval = 0 poll_interval = 0
post_cmd = [] post_cmd = []
pre_cmd = [] pre_cmd = ["./scripts/rebuild-library.sh"]
rerun = false rerun = false
rerun_delay = 500 rerun_delay = 500
send_interrupt = false send_interrupt = false

View File

@@ -1,85 +0,0 @@
/**
* Insertr Editor - Development Mode
* Loads editing capabilities for elements marked with data-insertr-enhanced="true"
*/
(function() {
'use strict';
console.log('🔧 Insertr Editor loaded (development mode)');
// Initialize editor when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initEditor);
} else {
initEditor();
}
function initEditor() {
console.log('🚀 Initializing Insertr Editor');
// Find all enhanced elements
const enhancedElements = document.querySelectorAll('[data-insertr-enhanced="true"]');
console.log(`📝 Found ${enhancedElements.length} editable elements`);
// Add visual indicators for development
enhancedElements.forEach(addEditIndicator);
// Add global styles
addEditorStyles();
}
function addEditIndicator(element) {
const contentId = element.getAttribute('data-content-id');
const contentType = element.getAttribute('data-content-type');
// Add hover effect
element.style.cursor = 'pointer';
element.style.position = 'relative';
// Add click handler for development demo
element.addEventListener('click', function(e) {
e.preventDefault();
alert(`Edit: ${contentId}\nType: ${contentType}\nCurrent: "${element.textContent.trim()}"`);
});
// Add visual indicator on hover
element.addEventListener('mouseenter', function() {
element.classList.add('insertr-editing-hover');
});
element.addEventListener('mouseleave', function() {
element.classList.remove('insertr-editing-hover');
});
}
function 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);
}
})();

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...');
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;
})();

1
insertr-cli/assets/lib/insertr.min.js vendored Normal file
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..."),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

@@ -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 // 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 // Write enhanced HTML
if err := e.writeHTML(doc, outputPath); err != nil { 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 // 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 { if !isDevelopment {
return // Only inject in development mode for now return // Only inject in development mode for now
} }
@@ -144,15 +144,21 @@ func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool) {
return return
} }
// Add editor JavaScript // Add inline script with embedded library
script := &html.Node{ script := &html.Node{
Type: html.ElementNode, Type: html.ElementNode,
Data: "script", Data: "script",
Attr: []html.Attribute{ Attr: []html.Attribute{
{Key: "src", Val: "/_insertr/insertr-editor.js"}, {Key: "type", Val: "text/javascript"},
{Key: "defer", Val: ""},
}, },
} }
// Add the library content as text node
textNode := &html.Node{
Type: html.TextNode,
Data: libraryScript,
}
script.AppendChild(textNode)
head.AppendChild(script) 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"
}

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Rebuild library and copy to CLI assets
# Used by Air for hot reloading when library changes
set -e
echo "🔄 Library changed, rebuilding..."
# Build the library
cd ../lib
npm run build --silent
# Copy to CLI assets
echo "📁 Copying updated library to CLI assets..."
cp dist/* ../insertr-cli/pkg/content/assets/
# Get library version for confirmation
VERSION=$(node -e "console.log(require('./package.json').version)")
echo "✅ Library v$VERSION ready for CLI embedding"

417
lib/package-lock.json generated Normal file
View File

@@ -0,0 +1,417 @@
{
"name": "@insertr/lib",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@insertr/lib",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-terser": "^0.4.0",
"rollup": "^3.0.0"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.30",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz",
"integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"@types/resolve": "1.20.2",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.22.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.78.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-terser": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"serialize-javascript": "^6.0.1",
"smob": "^1.0.0",
"terser": "^5.17.4"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true,
"license": "MIT"
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"dev": true,
"license": "MIT"
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "3.29.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true,
"license": "MIT",
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
},
"node_modules/smob": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
"integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
"dev": true,
"license": "MIT"
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/terser": {
"version": "5.44.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
"integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
}
}
}

30
lib/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "@insertr/lib",
"version": "1.0.0",
"type": "module",
"description": "The Tailwind of CMS - Client-side library for content injection and editing",
"main": "dist/insertr.js",
"module": "src/index.js",
"files": [
"dist/",
"src/"
],
"scripts": {
"build": "rollup -c",
"watch": "rollup -c -w",
"dev": "rollup -c -w"
},
"keywords": [
"cms",
"content",
"headless",
"inline-editing"
],
"author": "Insertr Team",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-terser": "^0.4.0",
"rollup": "^3.0.0"
}
}

30
lib/rollup.config.js Normal file
View File

@@ -0,0 +1,30 @@
import { nodeResolve } from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
export default [
// Development build
{
input: 'src/index.js',
output: {
file: 'dist/insertr.js',
format: 'iife',
name: 'Insertr'
},
plugins: [
nodeResolve()
]
},
// Production build (minified)
{
input: 'src/index.js',
output: {
file: 'dist/insertr.min.js',
format: 'iife',
name: 'Insertr'
},
plugins: [
nodeResolve(),
terser()
]
}
];

View File

@@ -0,0 +1,57 @@
/**
* ApiClient - Handle communication with content API
*/
export class ApiClient {
constructor(options = {}) {
this.baseUrl = options.apiEndpoint || '/api/content';
this.siteId = options.siteId || 'default';
}
async getContent(contentId) {
try {
const response = await fetch(`${this.baseUrl}/sites/${this.siteId}/content/${contentId}`);
return response.ok ? await response.json() : null;
} catch (error) {
console.warn('Failed to fetch content:', contentId, error);
return null;
}
}
async updateContent(contentId, content) {
try {
const response = await fetch(`${this.baseUrl}/sites/${this.siteId}/content/${contentId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ value: content })
});
return response.ok;
} catch (error) {
console.error('Failed to update content:', contentId, error);
return false;
}
}
async createContent(contentId, content, type) {
try {
const response = await fetch(`${this.baseUrl}/sites/${this.siteId}/content`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: contentId,
value: content,
type: type
})
});
return response.ok;
} catch (error) {
console.error('Failed to create content:', contentId, error);
return false;
}
}
}

110
lib/src/core/editor.js Normal file
View File

@@ -0,0 +1,110 @@
/**
* InsertrEditor - Visual editing functionality
*/
export 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);
}
}

32
lib/src/core/insertr.js Normal file
View File

@@ -0,0 +1,32 @@
/**
* InsertrCore - Core functionality for content management
*/
export 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));
}
}

49
lib/src/index.js Normal file
View File

@@ -0,0 +1,49 @@
/**
* Insertr - The Tailwind of CMS
* Main library entry point
*/
import { InsertrCore } from './core/insertr.js';
import { InsertrEditor } from './core/editor.js';
import { ApiClient } from './core/api-client.js';
// 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();
}
export default window.Insertr;

15
package-lock.json generated
View File

@@ -8,9 +8,6 @@
"name": "insertr", "name": "insertr",
"version": "0.1.0", "version": "0.1.0",
"license": "MIT", "license": "MIT",
"dependencies": {
"marked": "^16.2.1"
},
"devDependencies": { "devDependencies": {
"live-server": "^1.2.2" "live-server": "^1.2.2"
}, },
@@ -1147,18 +1144,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/marked": {
"version": "16.2.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-16.2.1.tgz",
"integrity": "sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 20"
}
},
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "3.1.10", "version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",

View File

@@ -1,15 +1,15 @@
{ {
"name": "insertr", "name": "insertr",
"version": "0.1.0", "version": "0.1.0",
"description": "Edit-in-place CMS for client websites - simple integration with class-based content editing", "description": "The Tailwind of CMS - Zero-configuration content editing for any static site",
"main": "demo-site/insertr/insertr.js", "main": "lib/dist/insertr.js",
"scripts": { "scripts": {
"dev": "node scripts/dev.js serve", "dev": "node scripts/dev.js serve",
"dev:about": "live-server demo-site --port=3000 --open=/about.html", "dev:about": "live-server demo-site --port=3000 --open=/about.html",
"dev:check": "node scripts/dev.js check", "dev:check": "node scripts/dev.js check",
"dev:demo": "node scripts/dev.js demo", "dev:demo": "node scripts/dev.js demo",
"dev:help": "node scripts/dev.js help", "dev:help": "node scripts/dev.js help",
"build": "echo 'Build script placeholder - will bundle insertr.js for distribution'", "build": "node scripts/build.js",
"test": "echo 'Test script placeholder - will add tests for insertr.js'", "test": "echo 'Test script placeholder - will add tests for insertr.js'",
"lint": "echo 'Linting placeholder - will add ESLint'", "lint": "echo 'Linting placeholder - will add ESLint'",
"serve": "npm run dev", "serve": "npm run dev",
@@ -17,13 +17,13 @@
}, },
"keywords": [ "keywords": [
"cms", "cms",
"edit-in-place", "headless-cms",
"inline-editing", "static-site-generator",
"content-management", "content-management",
"client-websites", "build-time-enhancement",
"zero-config",
"go", "go",
"htmx", "javascript"
"alpine"
], ],
"author": "Your Name", "author": "Your Name",
"license": "MIT", "license": "MIT",
@@ -42,7 +42,5 @@
"defaults", "defaults",
"not IE 11" "not IE 11"
], ],
"dependencies": { "dependencies": {}
"marked": "^16.2.1"
}
} }

58
scripts/build.js Executable file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env node
/**
* Build script for Insertr library and CLI integration
* This ensures the CLI always has the latest library version embedded
*/
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
console.log('🔨 Building Insertr library and CLI...\n');
// 1. Build the library
console.log('📦 Building JavaScript library...');
try {
execSync('npm run build', { cwd: './lib', stdio: 'inherit' });
console.log('✅ Library built successfully\n');
} catch (error) {
console.error('❌ Library build failed:', error.message);
process.exit(1);
}
// 2. Copy built library to CLI assets
console.log('📁 Copying library to CLI assets...');
const srcDir = './lib/dist';
const destDir = './insertr-cli/pkg/content/assets';
// Ensure destination directory exists
fs.mkdirSync(destDir, { recursive: true });
// Copy files
const files = fs.readdirSync(srcDir);
files.forEach(file => {
const src = path.join(srcDir, file);
const dest = path.join(destDir, file);
fs.copyFileSync(src, dest);
console.log(` ✅ Copied ${file}`);
});
console.log('📁 Assets copied successfully\n');
// 3. Build the CLI
console.log('🔧 Building Go CLI...');
try {
execSync('go build -o insertr', { cwd: './insertr-cli', stdio: 'inherit' });
console.log('✅ CLI built successfully\n');
} catch (error) {
console.error('❌ CLI build failed:', error.message);
process.exit(1);
}
console.log('🎉 Build complete!\n');
console.log('📋 What was built:');
console.log(' • JavaScript library (lib/dist/)');
console.log(' • Go CLI with embedded library (insertr-cli/insertr)');
console.log('\n🚀 Ready to use:');
console.log(' cd insertr-cli && ./insertr --help');

46
test-hot-reload.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
# Test script to demonstrate Air hot reload functionality
# Shows how library changes automatically trigger CLI rebuild
echo "🧪 Testing Insertr Hot Reload Functionality"
echo "============================================"
cd insertr-cli
echo "1. Starting Air in background..."
air &
AIR_PID=$!
sleep 5
echo "2. Making a test change to the library..."
cd ../lib/src
# Make a test change
sed -i 's/Hot Reload Ready/Hot Reload TESTED/g' index.js
echo "3. Waiting for Air to detect change and rebuild..."
sleep 8
echo "4. Testing if the change was embedded in CLI..."
cd ../insertr-cli
if ./insertr enhance ../demo-site/ -o /tmp/test-output 2>/dev/null && grep -q "Hot Reload TESTED" /tmp/test-output/index.html; then
echo "✅ SUCCESS: Library change was automatically embedded in CLI!"
else
echo "❌ FAILED: Library change was not embedded"
fi
echo "5. Reverting test change..."
cd ../lib/src
sed -i 's/Hot Reload TESTED/Hot Reload Ready/g' index.js
echo "6. Stopping Air..."
kill $AIR_PID 2>/dev/null
wait $AIR_PID 2>/dev/null
echo ""
echo "🎉 Hot reload test completed!"
echo " Library changes automatically trigger:"
echo " • Library rebuild (npm run build)"
echo " • Asset copy to CLI"
echo " • CLI rebuild with embedded library"
echo " • Development server restart"