feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
586
modules/backend/formwidgets/codeeditor/README.md
Normal file
586
modules/backend/formwidgets/codeeditor/README.md
Normal file
@@ -0,0 +1,586 @@
|
||||
# Monaco Code Editor for Winter CMS
|
||||
|
||||
This is the Monaco Editor integration for Winter CMS Backend, replacing the legacy Ace Editor with Microsoft's Monaco Editor (the same editor that powers VS Code).
|
||||
|
||||
## Overview
|
||||
|
||||
**Monaco Editor** provides a rich, modern code editing experience with:
|
||||
- IntelliSense (code completion)
|
||||
- Syntax highlighting for 15+ languages
|
||||
- Advanced find/replace with regex support
|
||||
- Multi-cursor editing
|
||||
- Code folding
|
||||
- Bracket matching and colorization
|
||||
- Minimap overview
|
||||
- Color picker for CSS colors
|
||||
- And many more VS Code features
|
||||
|
||||
## Features
|
||||
|
||||
### Supported Languages (15)
|
||||
|
||||
1. **TypeScript** - Full TypeScript support with type checking
|
||||
2. **JavaScript** - Modern ES6+ support
|
||||
3. **CSS** - Including CSS3 properties
|
||||
4. **JSON** - With schema validation
|
||||
5. **HTML** - HTML5 support
|
||||
6. **INI** - Configuration files
|
||||
7. **LESS** - CSS preprocessor
|
||||
8. **Markdown** - Rich markdown editing
|
||||
9. **MySQL** - SQL syntax highlighting
|
||||
10. **PHP** - Full PHP support
|
||||
11. **SCSS** - Sass CSS preprocessor
|
||||
12. **Twig** - Template engine syntax
|
||||
13. **XML** - Markup language support
|
||||
14. **YAML** - Configuration file support
|
||||
|
||||
### Monaco Features (20+)
|
||||
|
||||
Enabled features include:
|
||||
- Anchor select
|
||||
- Bracket matching
|
||||
- Caret operations
|
||||
- Clipboard operations
|
||||
- Code lens
|
||||
- Color picker
|
||||
- Comment toggling
|
||||
- Context menu
|
||||
- Cursor undo/redo
|
||||
- Find and replace
|
||||
- Code folding
|
||||
- Go to symbol
|
||||
- Hover information
|
||||
- In-place replace
|
||||
- Indentation
|
||||
- Inline hints
|
||||
- Links
|
||||
- Multi-cursor editing
|
||||
- Parameter hints
|
||||
- Rename symbol
|
||||
- Smart select
|
||||
- Snippets
|
||||
- Suggest (autocomplete)
|
||||
- Word highlighter
|
||||
- Word operations
|
||||
|
||||
### Themes (35+)
|
||||
|
||||
Includes legacy tmTheme themes plus modern JSON themes
|
||||
|
||||
### User Preferences
|
||||
|
||||
All editor preferences are configurable from **Backend → Preferences → Code editor**:
|
||||
|
||||
**Appearance:**
|
||||
- Font size (default: 12px)
|
||||
- Theme selection
|
||||
- Show/hide line numbers (gutter)
|
||||
- Show/hide invisibles (whitespace)
|
||||
- Highlight active line
|
||||
- Show minimap
|
||||
- Bracket colorization
|
||||
- Color picker for CSS
|
||||
|
||||
**Behavior:**
|
||||
- Tab size (default: 4 spaces)
|
||||
- Use soft tabs (spaces) vs hard tabs
|
||||
- Word wrap
|
||||
- Auto-closing brackets/quotes
|
||||
- Code folding
|
||||
- Indent guides
|
||||
- Print margin
|
||||
|
||||
All preferences persist across sessions and are stored per-user.
|
||||
|
||||
## Editor Architecture
|
||||
|
||||
Winter CMS uses a **dual-editor architecture** to optimize for different use cases:
|
||||
|
||||
### Monaco Editor (this FormWidget)
|
||||
**Used by:** CodeEditor FormWidget
|
||||
**Location:** `/modules/backend/formwidgets/codeeditor/`
|
||||
**Purpose:** Advanced code editing with IntelliSense, syntax highlighting, and modern IDE features
|
||||
**Bundle Size:** ~15 MB gzipped (main bundle + workers)
|
||||
**Best for:** Writing PHP, JavaScript, CSS, YAML, and other code files
|
||||
|
||||
### Ace Editor (preserved)
|
||||
**Used by:** RichEditor and MarkdownEditor FormWidgets
|
||||
**Location:** `/modules/backend/assets/vendor/ace-codeeditor/`
|
||||
**Purpose:** HTML source code editing within WYSIWYG editors
|
||||
**Bundle Size:** ~500 KB (significantly lighter)
|
||||
**Best for:** Viewing/editing raw HTML in rich text contexts
|
||||
|
||||
### Why Both?
|
||||
|
||||
**Monaco for CodeEditor:**
|
||||
- Full IntelliSense and code completion
|
||||
- Advanced refactoring tools
|
||||
- Multi-cursor editing
|
||||
- Rich language support
|
||||
- Worth the bundle size for dedicated code editing
|
||||
|
||||
**Ace for RichEditor/MarkdownEditor:**
|
||||
- Users rarely need advanced IDE features for HTML source view
|
||||
- Lighter bundle improves page load performance
|
||||
- Sufficient for basic HTML editing needs
|
||||
- Reduces total application bundle by keeping WYSIWYG tools lean
|
||||
|
||||
This architecture balances modern features where they matter most (code editing) with performance optimization for general-purpose rich text editing.
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Architecture
|
||||
|
||||
```text
|
||||
modules/backend/formwidgets/codeeditor/
|
||||
├── assets/
|
||||
│ ├── css/
|
||||
│ │ └── codeeditor.css - Compiled styles
|
||||
│ ├── fonts/
|
||||
│ │ └── codicon.ttf - Monaco icons font
|
||||
│ ├── js/
|
||||
│ │ ├── codeeditor.js - Main Monaco integration
|
||||
│ │ └── build/
|
||||
│ │ ├── codeeditor.bundle.js - Main bundle (19 MB)
|
||||
│ │ ├── css.worker.js - CSS language worker
|
||||
│ │ ├── editor.worker.js - Base editor worker
|
||||
│ │ ├── html.worker.js - HTML language worker
|
||||
│ │ ├── json.worker.js - JSON language worker
|
||||
│ │ ├── ts.worker.js - TypeScript worker
|
||||
│ │ └── [language-chunks] - 15 language modules
|
||||
│ ├── less/
|
||||
│ │ └── codeeditor.less - Source styles
|
||||
│ ├── themes/
|
||||
│ │ ├── [34 .tmTheme files] - Legacy TextMate themes
|
||||
│ │ ├── one-dark-pro.json - Modern JSON theme
|
||||
│ │ └── winter.json - Modern JSON theme
|
||||
│ ├── winter.mix.js - Laravel Mix build configuration
|
||||
│ └── package.json - NPM dependencies (in parent)
|
||||
├── partials/
|
||||
│ └── codeeditor.htm - Widget template
|
||||
└── CodeEditor.php - FormWidget class
|
||||
```
|
||||
|
||||
### Build System
|
||||
|
||||
**Current:** Laravel Mix 6 with Webpack 5
|
||||
|
||||
#### Build Command
|
||||
```bash
|
||||
php artisan mix:compile --package=module-backend.formwidgets.codeeditor -f
|
||||
```
|
||||
|
||||
#### Build Configuration
|
||||
|
||||
See `assets/winter.mix.js`:
|
||||
- Uses `monaco-editor-webpack-plugin` for proper worker splitting
|
||||
- Polyfills for browser compatibility (> 0.5%, last 2 versions, Firefox ESR)
|
||||
- Removes inline codicon font CSS (post-build hook)
|
||||
- Minification and terser optimization
|
||||
|
||||
### Web Workers
|
||||
|
||||
Monaco Editor uses Web Workers for language services:
|
||||
|
||||
| Worker | Size | Purpose |
|
||||
|--------|------|---------|
|
||||
| editor.worker.js | 1.6 MB | Base editor operations |
|
||||
| ts.worker.js | 22 MB | TypeScript/JavaScript IntelliSense |
|
||||
| css.worker.js | 4.7 MB | CSS validation and completion |
|
||||
| html.worker.js | 3.3 MB | HTML validation |
|
||||
| json.worker.js | 2.2 MB | JSON schema validation |
|
||||
|
||||
Workers are loaded asynchronously and run in separate threads for better performance.
|
||||
|
||||
### Theme System
|
||||
|
||||
Themes are loaded directly as static assets via HTTP fetch (no PHP handler required). Theme preference values include the file extension (e.g., `twilight.tmTheme`, `one-dark-pro.json`).
|
||||
|
||||
#### Supported Formats
|
||||
|
||||
**1. TextMate Themes (.tmTheme)**
|
||||
Legacy XML-based themes. Converted to Monaco format at runtime using `fast-plist` library.
|
||||
|
||||
**2. JSON Themes (.json)**
|
||||
Modern VS Code theme format. Parsed and mapped to Monaco's theme structure.
|
||||
|
||||
```javascript
|
||||
// codeeditor.js - Themes loaded via static fetch
|
||||
async fetchTheme(themeName) {
|
||||
// Theme name includes extension (e.g., "twilight.tmTheme", "one-dark-pro.json")
|
||||
// Legacy values without extension default to .tmTheme
|
||||
const basePath = window.Snowboard.url().asset('/modules/backend/formwidgets/codeeditor/assets/themes/');
|
||||
const response = await fetch(`${basePath}${themeName}`);
|
||||
// Format determined from file extension
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```yaml
|
||||
# fields.yaml
|
||||
code:
|
||||
type: codeeditor
|
||||
size: giant
|
||||
language: php
|
||||
```
|
||||
|
||||
### Available Options
|
||||
|
||||
```yaml
|
||||
code:
|
||||
type: codeeditor
|
||||
# Editor size
|
||||
size: tiny|small|large|huge|giant # Default: large
|
||||
|
||||
# Programming language
|
||||
language: php|javascript|css|html|twig|yaml|etc # Default: php
|
||||
|
||||
# Theme (overrides user preference)
|
||||
theme: twilight|monokai|github|one-dark-pro|etc
|
||||
|
||||
# Line numbers
|
||||
showGutter: true|false # Default: true
|
||||
|
||||
# Word wrapping
|
||||
wordWrap: true|false # Default: true
|
||||
|
||||
# Code folding
|
||||
codeFolding: true|false # Default: true
|
||||
|
||||
# Auto-closing brackets
|
||||
autoClosing: true|false # Default: true
|
||||
|
||||
# Soft tabs (spaces)
|
||||
useSoftTabs: true|false # Default: true
|
||||
tabSize: 2|4|8 # Default: 4
|
||||
|
||||
# Font size (px)
|
||||
fontSize: 10|12|14|16|18 # Default: 12
|
||||
|
||||
# Read-only mode
|
||||
readOnly: true|false # Default: false
|
||||
disabled: true|false # Sets readOnly
|
||||
|
||||
# Display options
|
||||
showInvisibles: true|false # Default: false
|
||||
highlightActiveLine: true|false # Default: true
|
||||
displayIndentGuides: true|false # Default: true
|
||||
showPrintMargin: true|false # Default: false
|
||||
showMinimap: true|false # Default: true
|
||||
bracketColors: true|false # Default: false
|
||||
showColors: true|false # Default: true (CSS color picker)
|
||||
```
|
||||
|
||||
### JavaScript API
|
||||
|
||||
```javascript
|
||||
// Get editor instance
|
||||
const $editor = $('#my-editor');
|
||||
const wrapper = $editor.data('oc.codeeditor');
|
||||
|
||||
// Access Monaco instance directly
|
||||
const monacoEditor = wrapper.editor;
|
||||
|
||||
// Get/set content (via wrapper)
|
||||
const code = wrapper.getValue();
|
||||
wrapper.setValue('function test() {}');
|
||||
|
||||
// Get/set language
|
||||
wrapper.setLanguage('javascript');
|
||||
|
||||
// Change theme
|
||||
wrapper.setTheme('one-dark-pro');
|
||||
|
||||
// Insert at cursor
|
||||
wrapper.insert('code here');
|
||||
|
||||
// Get cursor position
|
||||
const position = wrapper.getPosition(); // { lineNumber: 1, column: 1 }
|
||||
|
||||
// Fullscreen
|
||||
wrapper.enterFullscreen();
|
||||
wrapper.exitFullscreen();
|
||||
```
|
||||
|
||||
### Migrating from ACE to Monaco API
|
||||
|
||||
Winter CMS has migrated from ACE Editor to Monaco Editor. While backward compatibility is maintained for accessing the editor instance via jQuery `.data('oc.codeEditor')`, direct ACE API calls need to be updated.
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
**ACE's Session API is Removed:**
|
||||
- `editor.getSession()` → No longer available
|
||||
- ACE used a separate "session" object for document operations
|
||||
- Monaco combines session and model into a single API
|
||||
|
||||
**Position Indexing Changed:**
|
||||
- ACE uses **0-indexed** positions (rows and columns start at 0)
|
||||
- Monaco uses **1-indexed** positions (lines and columns start at 1)
|
||||
- Example: ACE row 5 = Monaco line 6, ACE column 0 = Monaco column 1
|
||||
|
||||
**Annotations Replaced with Markers:**
|
||||
- ACE's `setAnnotations()` → Monaco's `monaco.editor.setModelMarkers()`
|
||||
- Different data structure and API
|
||||
|
||||
#### Quick Migration Guide
|
||||
|
||||
**Getting/Setting Editor Value:**
|
||||
|
||||
```javascript
|
||||
// ❌ OLD (ACE API - Deprecated)
|
||||
const editor = $('[data-control=codeeditor]').data('oc.codeEditor').editor;
|
||||
const value = editor.getSession().getValue();
|
||||
editor.getSession().setValue('new value');
|
||||
|
||||
// ✅ NEW (Recommended - Use Wrapper)
|
||||
const wrapper = $('[data-control=codeeditor]').data('oc.codeEditor');
|
||||
const value = wrapper.getValue();
|
||||
wrapper.setValue('new value');
|
||||
|
||||
// ✅ ALTERNATIVE (Direct Monaco API)
|
||||
const monacoEditor = wrapper.editor;
|
||||
const value = monacoEditor.getModel().getValue();
|
||||
monacoEditor.getModel().setValue('new value');
|
||||
```
|
||||
|
||||
**Inserting Text at Cursor:**
|
||||
|
||||
```javascript
|
||||
// ❌ OLD (ACE API)
|
||||
editor.insert('text');
|
||||
|
||||
// ✅ NEW (Wrapper provides this method)
|
||||
wrapper.insert('text');
|
||||
```
|
||||
|
||||
**Working with Annotations/Markers:**
|
||||
|
||||
```javascript
|
||||
// ❌ OLD (ACE Annotations)
|
||||
editor.getSession().setAnnotations([
|
||||
{ row: 5, column: 0, text: 'Warning message', type: 'warning' }
|
||||
]);
|
||||
|
||||
// Clear annotations
|
||||
editor.getSession().setAnnotations([]);
|
||||
|
||||
// ✅ NEW (Monaco Wrapper Method - Recommended)
|
||||
wrapper.setMarkers('sourceId', [
|
||||
{
|
||||
startLineNumber: 6, // ACE row 5 = Monaco line 6 (1-indexed!)
|
||||
startColumn: 1, // ACE column 0 = Monaco column 1
|
||||
endLineNumber: 6,
|
||||
endColumn: Number.MAX_VALUE, // End of line
|
||||
message: 'Warning message',
|
||||
severity: wrapper.monaco.MarkerSeverity.Warning // Info, Warning, or Error
|
||||
}
|
||||
]);
|
||||
|
||||
// Clear markers
|
||||
wrapper.setMarkers('sourceId', []);
|
||||
```
|
||||
|
||||
**Getting Cursor Position:**
|
||||
|
||||
```javascript
|
||||
// ❌ OLD (ACE API)
|
||||
const cursor = editor.getCursorPosition(); // { row: 5, column: 10 } (0-indexed)
|
||||
|
||||
// ✅ NEW (Wrapper)
|
||||
const position = wrapper.getPosition(); // { lineNumber: 6, column: 11 } (1-indexed)
|
||||
|
||||
// ✅ ALTERNATIVE (Direct Monaco)
|
||||
const position = wrapper.editor.getPosition();
|
||||
```
|
||||
|
||||
**Getting Selection:**
|
||||
|
||||
```javascript
|
||||
// ❌ OLD (ACE API)
|
||||
const range = editor.getSelection().getRange();
|
||||
|
||||
// ✅ NEW (Wrapper)
|
||||
const selection = wrapper.getSelection();
|
||||
|
||||
// ✅ ALTERNATIVE (Direct Monaco)
|
||||
const selection = wrapper.editor.getSelection();
|
||||
```
|
||||
|
||||
#### API Comparison Table
|
||||
|
||||
| Operation | ACE API (Deprecated) | Monaco Wrapper (Recommended) | Direct Monaco API |
|
||||
|-----------|---------------------|------------------------------|-------------------|
|
||||
| Get value | `editor.getSession().getValue()` | `wrapper.getValue()` | `editor.getModel().getValue()` |
|
||||
| Set value | `editor.getSession().setValue(v)` | `wrapper.setValue(v)` | `editor.getModel().setValue(v)` |
|
||||
| Insert text | `editor.insert(text)` | `wrapper.insert(text)` | Complex - use wrapper |
|
||||
| Get position | `editor.getCursorPosition()` | `wrapper.getPosition()` | `editor.getPosition()` |
|
||||
| Get selection | `editor.getSelection()` | `wrapper.getSelection()` | `editor.getSelection()` |
|
||||
| Set annotations | `editor.getSession().setAnnotations()` | `wrapper.setMarkers(id, markers)` | `monaco.editor.setModelMarkers()` |
|
||||
| Focus editor | `editor.focus()` | `wrapper.focus()` | `editor.focus()` |
|
||||
| Set language | N/A | `wrapper.setLanguage(lang)` | Complex - use wrapper |
|
||||
|
||||
#### Migration Checklist for Plugin Developers
|
||||
|
||||
If your plugin interacts with the CodeEditor widget, follow these steps:
|
||||
|
||||
1. **Update Editor Instance Access:**
|
||||
- ✅ Keep: `.data('oc.codeEditor')` - Returns the wrapper
|
||||
- ⚠️ Avoid: `.data('oc.codeEditor').editor` - Returns raw Monaco (advanced use only)
|
||||
|
||||
2. **Replace ACE Session Methods:**
|
||||
- ❌ Remove all: `getSession().getValue()` → ✅ Use: `getValue()`
|
||||
- ❌ Remove all: `getSession().setValue()` → ✅ Use: `setValue()`
|
||||
|
||||
3. **Update Annotations:**
|
||||
- ❌ Remove: `getSession().setAnnotations(annotations)`
|
||||
- ✅ Add: `wrapper.setMarkers(sourceId, markers)`
|
||||
- ⚠️ Remember: Convert 0-indexed row/column to 1-indexed line/column
|
||||
- Use `wrapper.monaco.MarkerSeverity` for severity constants
|
||||
|
||||
4. **Test Thoroughly:**
|
||||
- Verify all editor interactions work
|
||||
- Check that cursor operations use correct indexing
|
||||
- Ensure markers/warnings display correctly
|
||||
|
||||
#### Available Wrapper Methods
|
||||
|
||||
The Monaco Snowboard editor wrapper provides these convenience methods:
|
||||
|
||||
```javascript
|
||||
const wrapper = $('[data-control=codeeditor]').data('oc.codeEditor');
|
||||
|
||||
// Content
|
||||
wrapper.getValue() // Get editor content
|
||||
wrapper.setValue(value) // Set editor content
|
||||
wrapper.insert(text) // Insert at cursor position
|
||||
|
||||
// Position & Selection
|
||||
wrapper.getPosition() // Get cursor position (1-indexed)
|
||||
wrapper.getSelection() // Get selection range
|
||||
|
||||
// Markers (Errors/Warnings/Info with squiggly underlines)
|
||||
wrapper.setMarkers(sourceId, markers) // Set error/warning markers in editor
|
||||
// Example: wrapper.setMarkers('myPlugin', [{ startLineNumber: 5, startColumn: 1,
|
||||
// endLineNumber: 5, endColumn: Number.MAX_VALUE, message: 'Warning',
|
||||
// severity: wrapper.monaco.MarkerSeverity.Warning }])
|
||||
|
||||
// Decorations (Visual highlights WITHOUT error semantics)
|
||||
wrapper.setDecorations(sourceId, decorations) // Set visual highlights (no squiggles)
|
||||
// Example: wrapper.setDecorations('myHighlight', [{ range: new monaco.Range(5, 1, 5, Number.MAX_VALUE),
|
||||
// options: { isWholeLine: true, className: 'myHighlightClass',
|
||||
// linesDecorationsClassName: 'myGutterClass' } }])
|
||||
|
||||
// Configuration
|
||||
wrapper.setLanguage(lang) // Change syntax highlighting language
|
||||
wrapper.setTheme(theme) // Change color theme
|
||||
wrapper.focus() // Focus the editor
|
||||
|
||||
// View
|
||||
wrapper.enterFullscreen() // Enter fullscreen mode
|
||||
wrapper.exitFullscreen() // Exit fullscreen mode
|
||||
wrapper.refresh() // Refresh editor (re-create instance)
|
||||
|
||||
// Direct Access (Advanced)
|
||||
wrapper.editor // Access Monaco editor instance
|
||||
wrapper.getEditor() // Same as wrapper.editor
|
||||
wrapper.getModel() // Get Monaco model
|
||||
wrapper.monaco // Access Monaco namespace (for constants like MarkerSeverity)
|
||||
```
|
||||
|
||||
#### Example: Winter.Builder Plugin Migration
|
||||
|
||||
The Winter.Builder plugin was migrated to use Monaco API. Here's a real example:
|
||||
|
||||
**Before (ACE):**
|
||||
```javascript
|
||||
Localization.prototype.copyStringsFromDone = function(data) {
|
||||
var codeEditor = this.getCodeEditor($masterTabPane);
|
||||
|
||||
// Set value using ACE Session API
|
||||
codeEditor.getSession().setValue(responseData.strings);
|
||||
|
||||
// Set annotations using ACE
|
||||
var annotations = [];
|
||||
for (var i = 0; i < updatedLines.length; i++) {
|
||||
annotations.push({
|
||||
row: updatedLines[i], // 0-indexed
|
||||
column: 0,
|
||||
text: 'New String',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
codeEditor.getSession().setAnnotations(annotations);
|
||||
}
|
||||
```
|
||||
|
||||
**After (Monaco):**
|
||||
```javascript
|
||||
Localization.prototype.copyStringsFromDone = function(data) {
|
||||
var wrapper = this.getCodeEditor($masterTabPane);
|
||||
|
||||
// Set value using wrapper method
|
||||
wrapper.setValue(responseData.strings);
|
||||
|
||||
// Convert to Monaco decorations (visual highlights without error semantics)
|
||||
var decorations = [];
|
||||
for (var i = 0; i < updatedLines.length; i++) {
|
||||
decorations.push({
|
||||
range: new wrapper.monaco.Range(
|
||||
updatedLines[i] + 1, // Convert to 1-indexed!
|
||||
1, // Start column
|
||||
updatedLines[i] + 1, // End line (same line)
|
||||
Number.MAX_VALUE // End column (end of line)
|
||||
),
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
className: 'builder-new-translation-line', // Background highlight
|
||||
linesDecorationsClassName: 'builder-new-translation-gutter', // Gutter indicator
|
||||
hoverMessage: { value: 'New string or section' } // Tooltip on hover
|
||||
}
|
||||
});
|
||||
}
|
||||
wrapper.setDecorations('builderLocalization', decorations);
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Playwright E2E Tests
|
||||
|
||||
Comprehensive test suite with 55+ tests:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm run test:e2e
|
||||
|
||||
# Run with UI
|
||||
npm run test:e2e:ui
|
||||
|
||||
# Run specific test file
|
||||
npx playwright test fullscreen.spec.js
|
||||
|
||||
# Debug tests
|
||||
npm run test:e2e:debug
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
- **Fullscreen functionality** (6 tests)
|
||||
- **Theme loading and switching** (9 tests)
|
||||
- **Language support** (14 tests for all 15 languages)
|
||||
- **Monaco features** (14 tests: find, replace, folding, multi-cursor, etc.)
|
||||
- **Preferences persistence** (12 tests)
|
||||
|
||||
See `tests/e2e/README-TESTING.md` for full testing documentation.
|
||||
|
||||
## Resources
|
||||
|
||||
- **Monaco Editor Documentation:** https://microsoft.github.io/monaco-editor/
|
||||
- **Monaco Editor GitHub:** https://github.com/microsoft/monaco-editor
|
||||
- **VS Code Themes:** https://marketplace.visualstudio.com/search?target=VSCode&category=Themes
|
||||
- **Winter CMS Docs:** https://wintercms.com/docs
|
||||
- **PR #801:** https://github.com/wintercms/winter/pull/801
|
||||
- **Issue #431:** https://github.com/wintercms/winter/issues/431
|
||||
Reference in New Issue
Block a user