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
|
||||
dist/
|
||||
build/
|
||||
lib/dist/
|
||||
insertr-cli/insertr
|
||||
insertr-cli/tmp/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
@@ -49,7 +52,7 @@ temp/
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Go specific (for future backend)
|
||||
# Go specific
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
@@ -59,6 +62,7 @@ temp/
|
||||
*.out
|
||||
go.work
|
||||
vendor/
|
||||
insertr-cli/build-errors.log
|
||||
|
||||
# Database files
|
||||
*.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.
|
||||
133
DEVELOPMENT.md
133
DEVELOPMENT.md
@@ -2,30 +2,52 @@
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Full-Stack Development (Recommended)
|
||||
1. **Install dependencies**:
|
||||
```bash
|
||||
npm install
|
||||
npm install # Root project dependencies
|
||||
cd lib && npm install # Library dependencies
|
||||
```
|
||||
|
||||
2. **Start development server**:
|
||||
2. **Start hot reload development**:
|
||||
```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**:
|
||||
```bash
|
||||
npm run dev:about # Open about page directly
|
||||
npm run serve # Alias for npm run dev
|
||||
```
|
||||
### Library-Only Development
|
||||
```bash
|
||||
cd lib && npm run watch # Just develop the JavaScript library
|
||||
```
|
||||
|
||||
### Legacy Frontend Development
|
||||
```bash
|
||||
npm run dev # Legacy live-server (demo-site only)
|
||||
```
|
||||
|
||||
## 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
|
||||
- **Core Library**: `demo-site/insertr/insertr.js` - the main Insertr library
|
||||
- **Styling**: `demo-site/insertr/insertr.css` - edit interface styles
|
||||
- **Core Library**: `demo-site/insertr/insertr.js` - the old prototype library
|
||||
- **Mock API**: `demo-site/mock-api/content.json` - sample backend data structure
|
||||
|
||||
### Testing the Three User Types
|
||||
@@ -36,9 +58,15 @@
|
||||
|
||||
### 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
|
||||
- **JavaScript**: Edit `demo-site/insertr/insertr.js` to modify core functionality
|
||||
- **Styling**: Edit `demo-site/insertr/insertr.css` for UI changes
|
||||
- **JavaScript**: Edit `demo-site/insertr/insertr.js` for prototype functionality
|
||||
- **Content**: Edit HTML files to test different content structures
|
||||
|
||||
### Adding New Features
|
||||
@@ -51,40 +79,67 @@
|
||||
|
||||
```
|
||||
insertr/
|
||||
├── INITIAL.md # Project requirements and research
|
||||
├── DEVELOPMENT.md # This file
|
||||
├── package.json # Node.js project configuration
|
||||
├── .gitignore # Git ignore rules
|
||||
├── LIBRARY.md # Library architecture documentation
|
||||
├── 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
|
||||
│ ├── about.html # Demo about page
|
||||
│ ├── README.md # Demo usage instructions
|
||||
│ │
|
||||
│ ├── 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
|
||||
│ └── assets/style.css # Demo site styling
|
||||
│
|
||||
└── backend/ # Future: Go backend
|
||||
├── main.go # Future: HTTP server
|
||||
├── api/ # Future: REST endpoints
|
||||
└── storage/ # Future: file-based storage
|
||||
└── scripts/
|
||||
└── build.js # Integrated build script
|
||||
```
|
||||
|
||||
## Development Scripts
|
||||
|
||||
- `npm run dev` - Start development server with live reload
|
||||
- `npm run dev:about` - Start server opening about page
|
||||
- `npm run build` - Future: Bundle library for distribution
|
||||
- `npm run test` - Future: Run tests
|
||||
- `npm run lint` - Future: Code linting
|
||||
### Root Scripts
|
||||
- `npm run build` - Build library + CLI with embedded assets
|
||||
- `npm run dev` - Legacy live-server (demo-site only)
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
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
|
||||
|
||||
### **Live Development**
|
||||
### **Live Development with Hot Reload**
|
||||
```bash
|
||||
cd insertr-cli
|
||||
|
||||
# Air watches Go files and rebuilds CLI
|
||||
# Post-build: starts development server
|
||||
# 🔥 Hot Reload: watches BOTH library AND CLI
|
||||
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
|
||||
```
|
||||
|
||||
**What Gets Hot Reloaded:**
|
||||
- JavaScript library changes (`lib/src/`)
|
||||
- Go CLI changes (`insertr-cli/`)
|
||||
- Enhanced HTML output (real-time content injection)
|
||||
|
||||
### **Content ID Management**
|
||||
**Development Phase:**
|
||||
- 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"
|
||||
cmd = "go build -o ./tmp/insertr ."
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"]
|
||||
exclude_dir = ["tmp", "vendor", "testdata", "node_modules", "dist"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = "./tmp/insertr servedev -i ../demo-site -p 3000"
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_dir = ["../lib/src"]
|
||||
include_ext = ["go", "tpl", "tmpl", "html", "js"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
pre_cmd = ["./scripts/rebuild-library.sh"]
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
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
|
||||
e.injector.InjectEditorAssets(doc, true)
|
||||
libraryScript := GetLibraryScript(true) // Use minified for better performance
|
||||
e.injector.InjectEditorAssets(doc, true, libraryScript)
|
||||
|
||||
// Write enhanced HTML
|
||||
if err := e.writeHTML(doc, outputPath); err != nil {
|
||||
|
||||
@@ -133,7 +133,7 @@ func (i *Injector) addContentAttributes(node *html.Node, contentID string, conte
|
||||
}
|
||||
|
||||
// InjectEditorAssets adds editor JavaScript and CSS to HTML document
|
||||
func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool) {
|
||||
func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool, libraryScript string) {
|
||||
if !isDevelopment {
|
||||
return // Only inject in development mode for now
|
||||
}
|
||||
@@ -144,15 +144,21 @@ func (i *Injector) InjectEditorAssets(doc *html.Node, isDevelopment bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Add editor JavaScript
|
||||
// Add inline script with embedded library
|
||||
script := &html.Node{
|
||||
Type: html.ElementNode,
|
||||
Data: "script",
|
||||
Attr: []html.Attribute{
|
||||
{Key: "src", Val: "/_insertr/insertr-editor.js"},
|
||||
{Key: "defer", Val: ""},
|
||||
{Key: "type", Val: "text/javascript"},
|
||||
},
|
||||
}
|
||||
|
||||
// Add the library content as text node
|
||||
textNode := &html.Node{
|
||||
Type: html.TextNode,
|
||||
Data: libraryScript,
|
||||
}
|
||||
script.AppendChild(textNode)
|
||||
head.AppendChild(script)
|
||||
}
|
||||
|
||||
|
||||
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",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"marked": "^16.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"live-server": "^1.2.2"
|
||||
},
|
||||
@@ -1147,18 +1144,6 @@
|
||||
"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": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
|
||||
|
||||
20
package.json
20
package.json
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "insertr",
|
||||
"version": "0.1.0",
|
||||
"description": "Edit-in-place CMS for client websites - simple integration with class-based content editing",
|
||||
"main": "demo-site/insertr/insertr.js",
|
||||
"description": "The Tailwind of CMS - Zero-configuration content editing for any static site",
|
||||
"main": "lib/dist/insertr.js",
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev.js serve",
|
||||
"dev:about": "live-server demo-site --port=3000 --open=/about.html",
|
||||
"dev:check": "node scripts/dev.js check",
|
||||
"dev:demo": "node scripts/dev.js demo",
|
||||
"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'",
|
||||
"lint": "echo 'Linting placeholder - will add ESLint'",
|
||||
"serve": "npm run dev",
|
||||
@@ -17,13 +17,13 @@
|
||||
},
|
||||
"keywords": [
|
||||
"cms",
|
||||
"edit-in-place",
|
||||
"inline-editing",
|
||||
"headless-cms",
|
||||
"static-site-generator",
|
||||
"content-management",
|
||||
"client-websites",
|
||||
"build-time-enhancement",
|
||||
"zero-config",
|
||||
"go",
|
||||
"htmx",
|
||||
"alpine"
|
||||
"javascript"
|
||||
],
|
||||
"author": "Your Name",
|
||||
"license": "MIT",
|
||||
@@ -42,7 +42,5 @@
|
||||
"defaults",
|
||||
"not IE 11"
|
||||
],
|
||||
"dependencies": {
|
||||
"marked": "^16.2.1"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
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