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:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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
76
CLEANUP_SUMMARY.md
Normal 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.
|
||||||
129
DEVELOPMENT.md
129
DEVELOPMENT.md
@@ -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
138
LIBRARY.md
Normal 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
|
||||||
14
README.md
14
README.md
@@ -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
128
TODO.md
Normal 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.*
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
197
insertr-cli/assets/lib/insertr.js
Normal file
197
insertr-cli/assets/lib/insertr.js
Normal 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
1
insertr-cli/assets/lib/insertr.min.js
vendored
Normal 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}();
|
||||||
197
insertr-cli/pkg/content/assets/insertr.js
Normal file
197
insertr-cli/pkg/content/assets/insertr.js
Normal 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;
|
||||||
|
|
||||||
|
})();
|
||||||
1
insertr-cli/pkg/content/assets/insertr.min.js
vendored
Normal file
1
insertr-cli/pkg/content/assets/insertr.min.js
vendored
Normal 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}();
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
26
insertr-cli/pkg/content/library.go
Normal file
26
insertr-cli/pkg/content/library.go
Normal 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"
|
||||||
|
}
|
||||||
20
insertr-cli/scripts/rebuild-library.sh
Executable file
20
insertr-cli/scripts/rebuild-library.sh
Executable 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
417
lib/package-lock.json
generated
Normal 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
30
lib/package.json
Normal 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
30
lib/rollup.config.js
Normal 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()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
57
lib/src/core/api-client.js
Normal file
57
lib/src/core/api-client.js
Normal 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
110
lib/src/core/editor.js
Normal 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
32
lib/src/core/insertr.js
Normal 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
49
lib/src/index.js
Normal 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
15
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
20
package.json
20
package.json
@@ -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
58
scripts/build.js
Executable 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
46
test-hot-reload.sh
Executable 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"
|
||||||
Reference in New Issue
Block a user