@wiliarko/wysiwyg-editor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WYSIWYG Editor Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,265 @@
1
+ # WYSIWYG Editor Standalone
2
+
3
+ A custom, lightweight rich-text editor built with zero external dependencies. Perfect for any projectโ€”Laravel, Vue, React, or plain HTML.
4
+
5
+ **Features:**
6
+ - โœจ Pure JavaScript (no dependencies)
7
+ - ๐Ÿ“ฆ Tiny bundle: 38 KB min, 11 KB gzipped
8
+ - ๐ŸŽจ Rich formatting (bold, italic, underline, strikethrough, headings, lists, blockquote)
9
+ - ๐Ÿ–ผ๏ธ Image upload & resize
10
+ - ๐Ÿ”— Link insertion & removal
11
+ - ๐Ÿ“Š Table creation & editing
12
+ - ๐Ÿท๏ธ Mention support (@username)
13
+ - ๐Ÿ˜Š Emoji picker (8 categories)
14
+ - ๐Ÿ’ป HTML source mode
15
+ - ๐Ÿ“ฑ Mobile friendly
16
+ - โ™ฟ Accessible
17
+
18
+ ## Quick Start
19
+
20
+ ### 1. Add to HTML
21
+
22
+ ```html
23
+ <link rel="stylesheet" href="wysiwyg.min.css">
24
+ <script src="wysiwyg.min.js"></script>
25
+ ```
26
+
27
+ ### 2a. Auto-Init (Simplest)
28
+
29
+ ```html
30
+ <div data-wysiwyg
31
+ data-wysiwyg-upload-url="/api/upload"
32
+ data-wysiwyg-height="400"
33
+ data-wysiwyg-placeholder="Write something..."
34
+ data-wysiwyg-input="#content"></div>
35
+ <input type="hidden" id="content" name="content">
36
+ ```
37
+
38
+ ### 2b. Manual Init (More Control)
39
+
40
+ ```javascript
41
+ const editor = WysiwygEditor.create('#my-editor', {
42
+ uploadUrl: '/api/upload',
43
+ minHeight: 400,
44
+ placeholder: 'Type here...',
45
+ onChange: (html) => console.log('Content:', html)
46
+ });
47
+
48
+ // Get content
49
+ const html = editor.getHTML();
50
+
51
+ // Set content
52
+ editor.setHTML('<h1>Hello</h1><p>World</p>');
53
+
54
+ // Toggle HTML source mode
55
+ editor.toggleSource();
56
+
57
+ // Cleanup
58
+ editor.destroy();
59
+ ```
60
+
61
+ ## Configuration Options
62
+
63
+ ```javascript
64
+ {
65
+ placeholder: 'Tulis sesuatu...', // Input placeholder
66
+ minHeight: 250, // Minimum editor height (px)
67
+ uploadUrl: '/upload', // Image upload endpoint
68
+ uploadFn: customUploadFunction, // Custom upload logic (async)
69
+ onChange: (html) => {}, // Content change callback
70
+ syncInput: '#hidden-input', // Hidden input selector to sync
71
+ }
72
+ ```
73
+
74
+ ## Upload Endpoint Format
75
+
76
+ Server should respond with JSON:
77
+
78
+ ```json
79
+ {
80
+ "success": true,
81
+ "url": "https://yourserver.com/uploads/image.jpg"
82
+ }
83
+ ```
84
+
85
+ Request:
86
+ ```
87
+ POST /your-upload-endpoint
88
+ Content-Type: multipart/form-data
89
+ Field: "file" (binary image)
90
+ ```
91
+
92
+ ## Toolbar Plugins
93
+
94
+ Default plugins:
95
+ - **Format**: Bold, Italic, Underline, Strikethrough
96
+ - **Heading**: H1, H2, H3
97
+ - **Lists**: Bullet list, Ordered list
98
+ - **Blocks**: Blockquote
99
+ - **Links**: Insert link, Remove link
100
+ - **Images**: Upload, URL, Resize, Alignment
101
+ - **Tables**: Insert 2x2, Add row, Delete
102
+ - **Mention**: @username
103
+ - **Emoji**: Picker with 8 categories
104
+ - **Source**: Edit raw HTML
105
+ - **Format**: Remove all formatting
106
+
107
+ ## Usage Examples
108
+
109
+ ### Laravel
110
+
111
+ ```blade
112
+ <x-wysiwyg
113
+ name="content"
114
+ label="Article Content"
115
+ :value="$post->content"
116
+ placeholder="Write article..."
117
+ :height="400"
118
+ :uploadUrl="route('wysiwyg.upload-image')"
119
+ />
120
+ ```
121
+
122
+ ### Vue 3
123
+
124
+ ```vue
125
+ <template>
126
+ <div ref="editorEl"></div>
127
+ </template>
128
+
129
+ <script setup>
130
+ import { onMounted, onUnmounted } from 'vue'
131
+
132
+ let editor
133
+
134
+ onMounted(() => {
135
+ editor = WysiwygEditor.create(editorEl, {
136
+ uploadUrl: '/api/upload',
137
+ onChange: (html) => console.log(html)
138
+ })
139
+ })
140
+
141
+ onUnmounted(() => {
142
+ editor.destroy()
143
+ })
144
+ </script>
145
+ ```
146
+
147
+ ### React
148
+
149
+ ```jsx
150
+ import { useEffect, useRef } from 'react'
151
+
152
+ export function Editor() {
153
+ const editorRef = useRef(null)
154
+ const editorInstance = useRef(null)
155
+
156
+ useEffect(() => {
157
+ if (typeof window !== 'undefined' && window.WysiwygEditor) {
158
+ editorInstance.current = WysiwygEditor.create(editorRef.current, {
159
+ uploadUrl: '/api/upload'
160
+ })
161
+
162
+ return () => editorInstance.current?.destroy()
163
+ }
164
+ }, [])
165
+
166
+ return <div ref={editorRef} style={{ height: '400px' }} />
167
+ }
168
+ ```
169
+
170
+ ### Plain HTML + Vanilla JS
171
+
172
+ ```html
173
+ <!DOCTYPE html>
174
+ <html>
175
+ <head>
176
+ <link rel="stylesheet" href="wysiwyg.min.css">
177
+ </head>
178
+ <body>
179
+ <textarea id="content" hidden></textarea>
180
+ <div id="editor"></div>
181
+
182
+ <script src="wysiwyg.min.js"></script>
183
+ <script>
184
+ const editor = WysiwygEditor.create('#editor', {
185
+ syncInput: '#content',
186
+ uploadUrl: '/upload'
187
+ })
188
+
189
+ document.querySelector('form').addEventListener('submit', (e) => {
190
+ const html = editor.getHTML()
191
+ document.getElementById('content').value = html
192
+ })
193
+ </script>
194
+ </body>
195
+ </html>
196
+ ```
197
+
198
+ ## API Reference
199
+
200
+ ### Constructor
201
+ ```javascript
202
+ const editor = new WysiwygEditor(element, options)
203
+ const editor = WysiwygEditor.create(element, options)
204
+ ```
205
+
206
+ ### Methods
207
+ ```javascript
208
+ editor.getHTML() // Get content as HTML string
209
+ editor.setHTML(html) // Set content
210
+ editor.focus() // Focus editor
211
+ editor.destroy() // Cleanup
212
+ editor.toggleSource() // Toggle HTML source mode
213
+ editor.isSourceMode() // Check if in source mode
214
+ editor.openImageDialog() // Open image insertion dialog
215
+ editor.getArea() // Get contenteditable element
216
+ editor.exec(command, value) // Execute browser command
217
+ editor.queryCommand(cmd) // Query command state
218
+ editor.getActiveBlock() // Get current block element
219
+ ```
220
+
221
+ ### Static Methods
222
+ ```javascript
223
+ WysiwygEditor.registerPlugin(pluginObject) // Register custom plugin
224
+ WysiwygEditor.getPlugins() // Get all registered plugins
225
+ ```
226
+
227
+ ## Custom Plugin
228
+
229
+ ```javascript
230
+ WysiwygEditor.registerPlugin({
231
+ name: 'highlight',
232
+ group: 'format',
233
+ title: 'Highlight',
234
+ icon: '<span style="bg:yellow;px:4px">H</span>',
235
+ action: (editor) => editor.exec('backColor', 'yellow'),
236
+ isActive: (editor) => editor.queryCommand('backColor')
237
+ })
238
+ ```
239
+
240
+ ## Browser Support
241
+
242
+ - Chrome/Edge 60+
243
+ - Firefox 55+
244
+ - Safari 11+
245
+ - Mobile browsers (iOS Safari, Chrome Mobile)
246
+
247
+ ## Performance
248
+
249
+ - **Bundle size**: 38 KB minified, 11 KB gzipped
250
+ - **No dependencies**: Faster load, zero conflicts
251
+ - **contenteditable based**: Native browser support
252
+
253
+ ## License
254
+
255
+ MIT
256
+
257
+ ## Support
258
+
259
+ For issues, features, or questions:
260
+ - GitHub: [github.com/yourusername/wysiwyg-editor](https://github.com/yourusername/wysiwyg-editor)
261
+ - Email: support@example.com
262
+
263
+ ---
264
+
265
+ **Made with โค๏ธ by [Your Team]**
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@wiliarko/wysiwyg-editor",
3
+ "version": "1.0.0",
4
+ "description": "Custom rich-text editor with zero dependencies. Supports formatting, images, tables, mentions, emojis, and more.",
5
+ "main": "wysiwyg.min.js",
6
+ "module": "wysiwyg.min.js",
7
+ "types": "wysiwyg.d.ts",
8
+ "files": [
9
+ "wysiwyg.min.js",
10
+ "wysiwyg.min.css",
11
+ "demo.html",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "test": "echo \"No tests for dist package\""
17
+ },
18
+ "keywords": [
19
+ "wysiwyg",
20
+ "editor",
21
+ "rich-text",
22
+ "rich text editor",
23
+ "contenteditable",
24
+ "markdown",
25
+ "html",
26
+ "formatting",
27
+ "table",
28
+ "emoji",
29
+ "mention",
30
+ "zero-dependency",
31
+ "vanilla-js"
32
+ ],
33
+ "author": "Your Name",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/yourusername/wysiwyg-editor-standalone.git"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/yourusername/wysiwyg-editor-standalone/issues"
41
+ },
42
+ "homepage": "https://github.com/yourusername/wysiwyg-editor-standalone#readme",
43
+ "engines": {
44
+ "node": ">=14.0.0"
45
+ }
46
+ }
@@ -0,0 +1 @@
1
+ .wysiwyg-editor{--wy-border: #e5e7eb;--wy-border-focus: #6366f1;--wy-bg: #ffffff;--wy-bg-toolbar: #f9fafb;--wy-text: #111827;--wy-muted: #9ca3af;--wy-btn-hover: #f3f4f6;--wy-btn-active: #e0e7ff;--wy-btn-active-fg:#4338ca;--wy-radius: .75rem;--wy-sep: #e5e7eb;display:flex;flex-direction:column;border:1.5px solid var(--wy-border);border-radius:var(--wy-radius);background:var(--wy-bg);overflow:hidden;transition:border-color .15s ease,box-shadow .15s ease;font-family:inherit}.wysiwyg-editor:focus-within{border-color:var(--wy-border-focus);box-shadow:0 0 0 3px #6366f11f}.wysiwyg-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:6px 8px;background:var(--wy-bg-toolbar);border-bottom:1px solid var(--wy-border)}.wysiwyg-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:none;border-radius:6px;background:transparent;color:var(--wy-text);cursor:pointer;transition:background .1s ease,color .1s ease;flex-shrink:0}.wysiwyg-btn:hover{background:var(--wy-btn-hover)}.wysiwyg-btn.is-active{background:var(--wy-btn-active);color:var(--wy-btn-active-fg)}.wysiwyg-btn span{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%}.wysiwyg-separator{width:1px;height:20px;background:var(--wy-sep);margin:0 4px;flex-shrink:0}.wysiwyg-area{flex:1;padding:14px 16px;outline:none;color:var(--wy-text);line-height:1.7;word-break:break-word;overflow-y:auto}.wysiwyg-area:empty:before{content:attr(data-placeholder);color:var(--wy-muted);pointer-events:none}.wysiwyg-area h1,.wysiwyg-area h2,.wysiwyg-area h3{font-weight:700;line-height:1.3;margin-top:1em;margin-bottom:.4em;color:var(--wy-text)}.wysiwyg-area h1{font-size:1.75em}.wysiwyg-area h2{font-size:1.35em}.wysiwyg-area h3{font-size:1.1em}.wysiwyg-area h1:first-child,.wysiwyg-area h2:first-child,.wysiwyg-area h3:first-child{margin-top:0}.wysiwyg-area p{margin:0 0 .75em}.wysiwyg-area p:last-child{margin-bottom:0}.wysiwyg-area strong{font-weight:700}.wysiwyg-area em{font-style:italic}.wysiwyg-area u{text-decoration:underline}.wysiwyg-area s{text-decoration:line-through}.wysiwyg-area ul,.wysiwyg-area ol{padding-left:1.5em;margin:0 0 .75em}.wysiwyg-area ul{list-style-type:disc}.wysiwyg-area ol{list-style-type:decimal}.wysiwyg-area li{margin-bottom:.25em}.wysiwyg-area blockquote{border-left:3px solid var(--wy-border-focus);margin:.75em 0;padding:.5em 1em;background:#6366f10d;border-radius:0 6px 6px 0;color:#4b5563}.wysiwyg-area a{color:var(--wy-btn-active-fg);text-decoration:underline}.wysiwyg-area a:hover{opacity:.8}.wysiwyg-source-area{flex:1;padding:14px 16px;outline:none;color:#a5b4fc;background:#1e1b4b;font-family:Fira Code,Cascadia Code,Consolas,Monaco,monospace;font-size:.8125rem;line-height:1.7;resize:none;border:none;width:100%;box-sizing:border-box;min-height:200px}.wysiwyg-source-area:focus{outline:none}.dark .wysiwyg-source-area{background:#0f0c29}@media(prefers-color-scheme:dark){.wysiwyg-editor{--wy-border: #374151;--wy-bg: #1f2937;--wy-bg-toolbar: #111827;--wy-text: #f9fafb;--wy-muted: #6b7280;--wy-btn-hover: #374151;--wy-btn-active: #312e81;--wy-btn-active-fg:#a5b4fc;--wy-sep: #374151}.wysiwyg-area blockquote{background:#6366f11a;color:#d1d5db}}.dark .wysiwyg-editor{--wy-border: #374151;--wy-bg: #1f2937;--wy-bg-toolbar: #111827;--wy-text: #f9fafb;--wy-muted: #6b7280;--wy-btn-hover: #374151;--wy-btn-active: #312e81;--wy-btn-active-fg:#a5b4fc;--wy-sep: #374151}.dark .wysiwyg-area blockquote{background:#6366f11a;color:#d1d5db}.wysiwyg-area img{max-width:100%;height:auto;border-radius:4px;cursor:pointer;vertical-align:bottom;transition:outline-color .1s ease}.wysiwyg-area img:hover{outline:2px solid rgb(99 102 241 / .35);outline-offset:2px}.wysiwyg-area img.wy-img-align-center{display:block;float:none;margin:.5em auto}.wysiwyg-area img.wy-img-align-left{float:left;margin:.25em 1.25em .5em 0}.wysiwyg-area img.wy-img-align-right{float:right;margin:.25em 0 .5em 1.25em}.wysiwyg-area img.wy-img-align-block-left{display:block;float:none;margin:.5em auto .5em 0}.wysiwyg-area img.wy-img-align-block-right{display:block;float:none;margin:.5em 0 .5em auto}.wy-img-uploading{display:inline-block;padding:4px 10px;border-radius:6px;background:#6366f11a;color:#4338ca;font-size:.8125em;font-style:italic}.wy-img-uploading--error{background:#f43f5e1a;color:#e11d48}.wy-img-toolbar{position:fixed;z-index:10000;display:none;align-items:center;gap:2px;padding:4px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 8px 24px #00000026}.wy-img-toolbar button{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;border:none;border-radius:6px;background:transparent;color:#111827;cursor:pointer}.wy-img-toolbar button:hover{background:#f3f4f6}.wy-img-toolbar button.is-active{background:#e0e7ff;color:#4338ca}.wy-img-toolbar__sep{width:1px;height:18px;background:#e5e7eb;margin:0 4px;flex-shrink:0}.wy-img-resize-box{position:fixed;z-index:9999;display:none;box-sizing:border-box;border:1.5px dashed #6366f1;pointer-events:none}.wy-img-resize-handle{position:absolute;width:10px;height:10px;background:#fff;border:2px solid #6366f1;border-radius:2px;pointer-events:all}.wy-img-resize-handle[data-dir=nw]{top:-6px;left:-6px;cursor:nwse-resize}.wy-img-resize-handle[data-dir=ne]{top:-6px;right:-6px;cursor:nesw-resize}.wy-img-resize-handle[data-dir=sw]{bottom:-6px;left:-6px;cursor:nesw-resize}.wy-img-resize-handle[data-dir=se]{bottom:-6px;right:-6px;cursor:nwse-resize}.wy-img-modal{position:fixed;inset:0;z-index:10050;display:flex;align-items:center;justify-content:center}.wy-img-modal__backdrop{position:absolute;inset:0;background:#0f0f1480;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.wy-img-modal__box{position:relative;width:100%;max-width:440px;max-height:90vh;overflow-y:auto;margin:16px;background:#fff;border-radius:16px;box-shadow:0 20px 60px #0000004d}.wy-img-modal__head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #e5e7eb}.wy-img-modal__title{font-weight:600;font-size:.9375rem;color:#111827}.wy-img-modal__close{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;border-radius:8px;background:transparent;color:#9ca3af;cursor:pointer}.wy-img-modal__close:hover{background:#f3f4f6;color:#111827}.wy-img-modal__tabs{display:flex;gap:6px;padding:14px 20px 0}.wy-img-tab{flex:1;padding:8px 12px;border:none;border-radius:8px;background:#f9fafb;color:#6b7280;font-size:.8125rem;font-weight:500;cursor:pointer;transition:background .1s ease,color .1s ease}.wy-img-tab.is-active{background:#e0e7ff;color:#4338ca}.wy-img-modal__panel{padding:16px 20px 0}.wy-img-dropzone{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:28px 16px;border:2px dashed #e5e7eb;border-radius:12px;cursor:pointer;text-align:center;transition:border-color .15s ease,background .15s ease}.wy-img-dropzone:hover,.wy-img-dropzone.is-dragover{border-color:#6366f1;background:#6366f10d}.wy-img-dropzone__text{margin:0;font-size:.8125rem;color:#111827}.wy-img-dropzone__text span{color:#6366f1;font-weight:600}.wy-img-dropzone__hint{margin:0;font-size:.6875rem;color:#9ca3af}.wy-img-file{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0}.wy-img-preview{position:relative;display:block;width:max-content;max-width:100%;margin:12px auto 0}.wy-img-preview__img{display:block;max-height:180px;max-width:100%;border-radius:8px}.wy-img-preview__remove{position:absolute;top:-8px;right:-8px;width:22px;height:22px;border:none;border-radius:999px;background:#111827;color:#fff;font-size:14px;line-height:1;cursor:pointer}.wy-img-modal__progress{margin-top:12px;height:6px;border-radius:999px;background:#f3f4f6;overflow:hidden}.wy-img-modal__progress-bar{height:100%;width:0%;background:linear-gradient(90deg,#6366f1,#8b5cf6);transition:width .15s ease}.wy-img-url-label{display:block;font-size:.8125rem;font-weight:500;color:#111827}.wy-img-url-input,.wy-img-alt-input,.wy-img-width-input,.wy-img-align-select{display:block;width:100%;margin-top:6px;padding:8px 12px;border:1.5px solid #e5e7eb;border-radius:8px;background:#fff;color:#111827;font-size:.8125rem;font-family:inherit;box-sizing:border-box}.wy-img-url-input:focus,.wy-img-alt-input:focus,.wy-img-width-input:focus,.wy-img-align-select:focus{outline:none;border-color:#6366f1;box-shadow:0 0 0 3px #6366f11f}.wy-img-url-preview img{max-height:120px;max-width:100%;border-radius:8px}.wy-img-modal__options{display:flex;gap:12px;padding:4px 20px 0}.wy-img-opt-label{flex:1;font-size:.8125rem;font-weight:500;color:#111827}.wy-img-modal__error{margin:12px 20px 0;padding:8px 12px;border-radius:8px;background:#f43f5e1a;color:#e11d48;font-size:.75rem}.wy-img-modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 20px 20px}.wy-img-modal__cancel,.wy-img-modal__insert{padding:8px 18px;border:none;border-radius:8px;font-size:.8125rem;font-weight:600;cursor:pointer}.wy-img-modal__cancel{background:#f3f4f6;color:#111827}.wy-img-modal__cancel:hover{background:#e5e7eb}.wy-img-modal__insert{background:linear-gradient(90deg,#4f46e5,#7c3aed);color:#fff}.wy-img-modal__insert:disabled{opacity:.4;cursor:not-allowed}@media(prefers-color-scheme:dark){.wy-img-toolbar{background:#1f2937;border-color:#374151}.wy-img-toolbar button{color:#f9fafb}.wy-img-toolbar button:hover{background:#374151}.wy-img-toolbar button.is-active{background:#312e81;color:#a5b4fc}.wy-img-toolbar__sep{background:#374151}.wy-img-modal__box{background:#1f2937}.wy-img-modal__head{border-color:#374151}.wy-img-modal__title{color:#f9fafb}.wy-img-modal__close{color:#9ca3af}.wy-img-modal__close:hover{background:#374151;color:#f9fafb}.wy-img-tab{background:#111827;color:#9ca3af}.wy-img-tab.is-active{background:#312e81;color:#a5b4fc}.wy-img-dropzone{border-color:#374151}.wy-img-dropzone:hover,.wy-img-dropzone.is-dragover{border-color:#6366f1;background:#6366f11a}.wy-img-dropzone__text,.wy-img-url-label,.wy-img-opt-label{color:#f9fafb}.wy-img-url-input,.wy-img-alt-input,.wy-img-width-input,.wy-img-align-select{background:#111827;border-color:#374151;color:#f9fafb}.wy-img-modal__progress{background:#111827}.wy-img-modal__cancel{background:#374151;color:#f9fafb}.wy-img-modal__cancel:hover{background:#4b5563}}.dark .wy-img-toolbar{background:#1f2937;border-color:#374151}.dark .wy-img-toolbar button{color:#f9fafb}.dark .wy-img-toolbar button:hover{background:#374151}.dark .wy-img-toolbar button.is-active{background:#312e81;color:#a5b4fc}.dark .wy-img-toolbar__sep{background:#374151}.dark .wy-img-modal__box{background:#1f2937}.dark .wy-img-modal__head{border-color:#374151}.dark .wy-img-modal__title{color:#f9fafb}.dark .wy-img-modal__close{color:#9ca3af}.dark .wy-img-modal__close:hover{background:#374151;color:#f9fafb}.dark .wy-img-tab{background:#111827;color:#9ca3af}.dark .wy-img-tab.is-active{background:#312e81;color:#a5b4fc}.dark .wy-img-dropzone{border-color:#374151}.dark .wy-img-dropzone:hover,.dark .wy-img-dropzone.is-dragover{border-color:#6366f1;background:#6366f11a}.dark .wy-img-dropzone__text,.dark .wy-img-url-label,.dark .wy-img-opt-label{color:#f9fafb}.dark .wy-img-url-input,.dark .wy-img-alt-input,.dark .wy-img-width-input,.dark .wy-img-align-select{background:#111827;border-color:#374151;color:#f9fafb}.dark .wy-img-modal__progress{background:#111827}.dark .wy-img-modal__cancel{background:#374151;color:#f9fafb}.dark .wy-img-modal__cancel:hover{background:#4b5563}.wy-img-modal{position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center}.wy-img-modal__backdrop{position:absolute;inset:0;background:#0000008c;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.wy-img-modal__box{position:relative;z-index:1;width:min(540px,calc(100vw - 32px));max-height:calc(100vh - 64px);overflow-y:auto;background:#fff;border-radius:16px;box-shadow:0 24px 64px #00000040;padding:24px;display:flex;flex-direction:column;gap:16px}.wy-img-modal__head{display:flex;align-items:center;justify-content:space-between}.wy-img-modal__title{font-size:1rem;font-weight:700;color:#111827}.wy-img-modal__close{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border:none;background:#f3f4f6;border-radius:8px;cursor:pointer;color:#6b7280;transition:background .15s,color .15s}.wy-img-modal__close:hover{background:#e5e7eb;color:#111827}.wy-img-modal__tabs{display:flex;gap:4px;border-bottom:1px solid #e5e7eb;padding-bottom:0}.wy-img-tab{padding:8px 16px;border:none;background:none;font-size:.875rem;font-weight:500;color:#6b7280;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color .15s,border-color .15s;border-radius:6px 6px 0 0}.wy-img-tab:hover{color:#374151}.wy-img-tab.is-active{color:#6366f1;border-bottom-color:#6366f1}.wy-img-dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:32px 24px;border:2px dashed #d1d5db;border-radius:12px;cursor:pointer;transition:border-color .15s,background .15s;text-align:center}.wy-img-dropzone input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}.wy-img-dropzone:hover,.wy-img-dropzone.is-dragover{border-color:#6366f1;background:#eef2ff}.wy-img-dropzone__icon{color:#9ca3af}.wy-img-dropzone.is-dragover .wy-img-dropzone__icon{color:#6366f1}.wy-img-dropzone__text{font-size:.875rem;color:#6b7280;margin:0}.wy-img-dropzone__text span{color:#6366f1;font-weight:600;text-decoration:underline}.wy-img-dropzone__hint{font-size:.75rem;color:#9ca3af;margin:0}.wy-img-preview{position:relative;display:flex;justify-content:center}.wy-img-preview__img{max-height:200px;max-width:100%;border-radius:10px;object-fit:contain;border:1px solid #e5e7eb}.wy-img-preview__remove{position:absolute;top:-8px;right:-8px;width:24px;height:24px;border-radius:50%;border:none;background:#ef4444;color:#fff;font-size:14px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 6px #0003}.wy-img-preview__remove:hover{background:#dc2626}.wy-img-modal__progress{height:6px;background:#e5e7eb;border-radius:999px;overflow:hidden}.wy-img-modal__progress-bar{height:100%;background:linear-gradient(90deg,#6366f1,#8b5cf6);border-radius:999px;width:0%;transition:width .2s ease}.wy-img-url-label{display:flex;flex-direction:column;gap:6px;font-size:.8125rem;font-weight:500;color:#374151}.wy-img-url-input,.wy-img-alt-input{padding:9px 12px;border:1.5px solid #d1d5db;border-radius:8px;font-size:.875rem;color:#111827;outline:none;transition:border-color .15s,box-shadow .15s;width:100%;box-sizing:border-box}.wy-img-url-input:focus,.wy-img-alt-input:focus{border-color:#6366f1;box-shadow:0 0 0 3px #6366f11f}.wy-img-modal__options{display:grid;grid-template-columns:1fr 1fr;gap:12px}.wy-img-opt-label{display:flex;flex-direction:column;gap:6px;font-size:.8125rem;font-weight:500;color:#374151}.wy-img-width-input,.wy-img-align-select{padding:9px 12px;border:1.5px solid #d1d5db;border-radius:8px;font-size:.875rem;color:#111827;outline:none;background:#fff;transition:border-color .15s;width:100%;box-sizing:border-box}.wy-img-width-input:focus,.wy-img-align-select:focus{border-color:#6366f1;box-shadow:0 0 0 3px #6366f11f}.wy-img-modal__error{padding:10px 14px;background:#fef2f2;border:1px solid #fecaca;border-radius:8px;color:#dc2626;font-size:.8125rem;margin:0}.wy-img-modal__footer{display:flex;justify-content:flex-end;gap:10px;padding-top:4px}.wy-img-modal__cancel{padding:9px 20px;border:1.5px solid #d1d5db;border-radius:10px;background:#fff;color:#374151;font-size:.875rem;font-weight:500;cursor:pointer;transition:background .15s,border-color .15s}.wy-img-modal__cancel:hover{background:#f9fafb;border-color:#9ca3af}.wy-img-modal__insert{padding:9px 20px;border:none;border-radius:10px;background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;font-size:.875rem;font-weight:600;cursor:pointer;transition:opacity .15s,box-shadow .15s;box-shadow:0 2px 8px #6366f159}.wy-img-modal__insert:hover:not(:disabled){opacity:.9;box-shadow:0 4px 12px #6366f166}.wy-img-modal__insert:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}.wy-img-toolbar{position:fixed;z-index:9998;display:none;align-items:center;gap:2px;padding:4px 6px;background:#1f2937;border-radius:8px;box-shadow:0 4px 16px #0000004d}.wy-img-toolbar button{width:26px;height:26px;border:none;background:transparent;color:#d1d5db;border-radius:5px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .12s,color .12s}.wy-img-toolbar button:hover{background:#ffffff1f;color:#fff}.wy-img-toolbar button.is-active{background:#6366f1;color:#fff}.wy-img-toolbar button[data-action=delete]:hover{background:#ef4444;color:#fff}.wy-img-toolbar__sep{width:1px;height:16px;background:#ffffff26;margin:0 2px}.wy-img-resize-box{position:fixed;z-index:9997;pointer-events:none;box-sizing:border-box;outline:2px solid #6366f1;outline-offset:1px}.wy-img-resize-handle{position:absolute;width:10px;height:10px;background:#fff;border:2px solid #6366f1;border-radius:2px;pointer-events:all;cursor:nwse-resize}.wy-img-resize-handle[data-dir=nw]{top:-5px;left:-5px;cursor:nwse-resize}.wy-img-resize-handle[data-dir=ne]{top:-5px;right:-5px;cursor:nesw-resize}.wy-img-resize-handle[data-dir=sw]{bottom:-5px;left:-5px;cursor:nesw-resize}.wy-img-resize-handle[data-dir=se]{bottom:-5px;right:-5px;cursor:nwse-resize}.wysiwyg-area img{max-width:100%;height:auto;cursor:pointer;border-radius:4px;transition:outline .1s}.wysiwyg-area img.wy-img-selected{outline:2px solid #6366f1;outline-offset:2px}.wysiwyg-area img.wy-img-align-center{display:block;margin:0 auto}.wysiwyg-area img.wy-img-align-left{float:left;margin:0 1em .5em 0}.wysiwyg-area img.wy-img-align-right{float:right;margin:0 0 .5em 1em}.wysiwyg-area img.wy-img-align-block-left{display:block;margin:0 auto 0 0}.wysiwyg-area img.wy-img-align-block-right{display:block;margin:0 0 0 auto}.wy-img-uploading{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:#eef2ff;color:#6366f1;border-radius:6px;font-size:.8125rem;font-weight:500;border:1px dashed #a5b4fc}.wy-img-uploading--error{background:#fef2f2;color:#ef4444;border-color:#fca5a5}@media(prefers-color-scheme:dark){.wy-img-modal__box{background:#1f2937;color:#f9fafb}.wy-img-modal__title{color:#f9fafb}.wy-img-modal__close{background:#374151;color:#9ca3af}.wy-img-modal__close:hover{background:#4b5563;color:#f9fafb}.wy-img-tab{color:#9ca3af}.wy-img-tab:hover{color:#e5e7eb}.wy-img-modal__tabs{border-color:#374151}.wy-img-dropzone{border-color:#4b5563}.wy-img-dropzone:hover,.wy-img-dropzone.is-dragover{background:#6366f11a}.wy-img-url-label,.wy-img-opt-label{color:#d1d5db}.wy-img-url-input,.wy-img-alt-input,.wy-img-width-input,.wy-img-align-select{background:#111827;border-color:#4b5563;color:#f9fafb}.wy-img-modal__cancel{background:#374151;border-color:#4b5563;color:#d1d5db}.wy-img-modal__cancel:hover{background:#4b5563}}
package/wysiwyg.min.js ADDED
@@ -0,0 +1,194 @@
1
+ var WysiwygEditor=(function(w,k){"use strict";const E={placeholder:"Tulis sesuatu...",minHeight:200,plugins:[],onChange:null,syncInput:null,uploadUrl:"",uploadFn:null};class m{static#p=new Map;static registerPlugin(e){if(!e.name)throw new Error("[wysiwyg] Plugin harus punya nama.");m.#p.set(e.name,e)}static getPlugins(){return Array.from(m.#p.values())}#l;#e;#n;#u=!1;#s;#k=null;#t;#h;#a=null;#o=null;#i=null;#v=null;#m=null;#c=null;constructor(e,t={}){if(this.#l=typeof e=="string"?document.querySelector(e):e,!this.#l)throw new Error(`[wysiwyg] Element tidak ditemukan: ${e}`);this.#t={...E,...t},this.#h=new AbortController,this.#T(),this.#j(),this.#f()}#T(){this.#l.classList.add("wysiwyg-editor"),this.#l.innerHTML="",this.#s=document.createElement("div"),this.#s.className="wysiwyg-toolbar",this.#s.setAttribute("role","toolbar"),this.#s.setAttribute("aria-label","Toolbar editor"),this.#M(),this.#l.appendChild(this.#s),this.#e=document.createElement("div"),this.#e.className="wysiwyg-area",this.#e.contentEditable="true",this.#e.setAttribute("role","textbox"),this.#e.setAttribute("aria-multiline","true"),this.#e.setAttribute("aria-label","Area editor"),this.#e.setAttribute("spellcheck","true"),this.#e.style.minHeight=this.#t.minHeight+"px",this.#e.dataset.placeholder=this.#t.placeholder,this.#l.appendChild(this.#e),this.#n=document.createElement("textarea"),this.#n.className="wysiwyg-source-area",this.#n.setAttribute("aria-label","HTML source editor"),this.#n.setAttribute("spellcheck","false"),this.#n.setAttribute("autocorrect","off"),this.#n.setAttribute("autocapitalize","off"),this.#n.style.minHeight=this.#t.minHeight+"px",this.#n.style.display="none",this.#l.appendChild(this.#n),this.#t.syncInput&&(this.#k=typeof this.#t.syncInput=="string"?document.querySelector(this.#t.syncInput):this.#t.syncInput),this.#_(),this.#q(),this.#U()}#M(){const e=this.#t.plugins.length?this.#t.plugins.map(i=>typeof i=="string"?m.#p.get(i):i).filter(Boolean):m.getPlugins();let t=null;e.forEach(i=>{if(i.group&&i.group!==t){if(t!==null){const o=document.createElement("div");o.className="wysiwyg-separator",o.setAttribute("aria-hidden","true"),this.#s.appendChild(o)}t=i.group}const s=document.createElement("button");s.type="button",s.className="wysiwyg-btn",s.dataset.plugin=i.name,s.title=i.title||i.name,s.setAttribute("aria-label",i.title||i.name),s.innerHTML=i.icon,this.#s.appendChild(s)})}#_(){const e=document.createElement("div");e.className="wy-img-modal",e.style.display="none",e.setAttribute("role","dialog"),e.setAttribute("aria-modal","true"),e.setAttribute("aria-label","Sisipkan gambar"),e.innerHTML=`
2
+ <div class="wy-img-modal__backdrop"></div>
3
+ <div class="wy-img-modal__box">
4
+ <div class="wy-img-modal__head">
5
+ <span class="wy-img-modal__title">Sisipkan Gambar</span>
6
+ <button type="button" class="wy-img-modal__close" aria-label="Tutup">
7
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
8
+ </button>
9
+ </div>
10
+
11
+ <!-- Tab switcher -->
12
+ <div class="wy-img-modal__tabs">
13
+ <button type="button" class="wy-img-tab is-active" data-tab="upload">Upload File</button>
14
+ <button type="button" class="wy-img-tab" data-tab="url">URL Gambar</button>
15
+ </div>
16
+
17
+ <!-- Tab: Upload -->
18
+ <div class="wy-img-modal__panel" data-panel="upload">
19
+ <label class="wy-img-dropzone" tabindex="0" aria-label="Pilih atau seret file gambar">
20
+ <input type="file" accept="image/*" class="wy-img-file" aria-hidden="true" />
21
+ <div class="wy-img-dropzone__icon">
22
+ <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>
23
+ </div>
24
+ <p class="wy-img-dropzone__text">Seret gambar ke sini atau <span>klik untuk pilih</span></p>
25
+ <p class="wy-img-dropzone__hint">PNG, JPG, GIF, WebP โ€” maks 5MB</p>
26
+ </label>
27
+ <div class="wy-img-preview" style="display:none">
28
+ <img class="wy-img-preview__img" src="" alt="" />
29
+ <button type="button" class="wy-img-preview__remove" aria-label="Hapus pilihan">ร—</button>
30
+ </div>
31
+ <div class="wy-img-modal__progress" style="display:none">
32
+ <div class="wy-img-modal__progress-bar"></div>
33
+ </div>
34
+ </div>
35
+
36
+ <!-- Tab: URL -->
37
+ <div class="wy-img-modal__panel" data-panel="url" style="display:none">
38
+ <label class="wy-img-url-label">
39
+ URL Gambar
40
+ <input type="url" class="wy-img-url-input" placeholder="https://contoh.com/gambar.jpg" />
41
+ </label>
42
+ <label class="wy-img-url-label" style="margin-top:12px">
43
+ Alt Text (deskripsi gambar)
44
+ <input type="text" class="wy-img-alt-input" placeholder="Deskripsi gambar..." />
45
+ </label>
46
+ <div class="wy-img-url-preview" style="display:none; margin-top:12px; text-align:center">
47
+ <img class="wy-img-url-preview__img" src="" alt="" style="max-height:120px; max-width:100%; border-radius:6px;" />
48
+ </div>
49
+ </div>
50
+
51
+ <!-- Options bersama -->
52
+ <div class="wy-img-modal__options">
53
+ <label class="wy-img-opt-label">
54
+ Lebar
55
+ <input type="text" class="wy-img-width-input" placeholder="otomatis" />
56
+ </label>
57
+ <label class="wy-img-opt-label">
58
+ Alignment
59
+ <select class="wy-img-align-select">
60
+ <option value="">Default</option>
61
+ <option value="center">Center</option>
62
+ <option value="left">Float Kiri</option>
63
+ <option value="right">Float Kanan</option>
64
+ <option value="block-left">Blok Kiri</option>
65
+ <option value="block-right">Blok Kanan</option>
66
+ </select>
67
+ </label>
68
+ </div>
69
+
70
+ <p class="wy-img-modal__error" style="display:none"></p>
71
+
72
+ <div class="wy-img-modal__footer">
73
+ <button type="button" class="wy-img-modal__cancel">Batal</button>
74
+ <button type="button" class="wy-img-modal__insert" disabled>Sisipkan</button>
75
+ </div>
76
+ </div>
77
+ `,document.body.appendChild(e),this.#a=e,this.#R()}#q(){const e=document.createElement("div");e.className="wy-img-toolbar",e.style.display="none",e.innerHTML=`
78
+ <button type="button" data-align="" title="Default">
79
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="1"/></svg>
80
+ </button>
81
+ <button type="button" data-align="center" title="Center">
82
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="6" y1="12" x2="18" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
83
+ </button>
84
+ <button type="button" data-align="left" title="Float Kiri">
85
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="8" height="8" rx="1"/><line x1="12" y1="6" x2="21" y2="6"/><line x1="12" y1="10" x2="21" y2="10"/><line x1="2" y1="16" x2="21" y2="16"/><line x1="2" y1="20" x2="21" y2="20"/></svg>
86
+ </button>
87
+ <button type="button" data-align="right" title="Float Kanan">
88
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="14" y="4" width="8" height="8" rx="1"/><line x1="3" y1="6" x2="12" y2="6"/><line x1="3" y1="10" x2="12" y2="10"/><line x1="3" y1="16" x2="21" y2="16"/><line x1="3" y1="20" x2="21" y2="20"/></svg>
89
+ </button>
90
+ <div class="wy-img-toolbar__sep"></div>
91
+ <button type="button" data-action="delete" title="Hapus gambar">
92
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/></svg>
93
+ </button>
94
+ `,document.body.appendChild(e),this.#o=e,this.#N()}openImageDialog(){const e=window.getSelection();this.#v=e.rangeCount?e.getRangeAt(0).cloneRange():null,this.#H(),this.#a.style.display="flex"}#H(){const e=this.#a;e.querySelectorAll(".wy-img-tab").forEach(t=>t.classList.toggle("is-active",t.dataset.tab==="upload")),e.querySelectorAll(".wy-img-modal__panel").forEach(t=>{t.style.display=t.dataset.panel==="upload"?"":"none"}),e.querySelector(".wy-img-file").value="",e.querySelector(".wy-img-preview").style.display="none",e.querySelector(".wy-img-preview__img").src="",e.querySelector(".wy-img-url-input").value="",e.querySelector(".wy-img-alt-input").value="",e.querySelector(".wy-img-url-preview").style.display="none",e.querySelector(".wy-img-url-preview__img").src="",e.querySelector(".wy-img-width-input").value="",e.querySelector(".wy-img-align-select").value="",e.querySelector(".wy-img-modal__progress").style.display="none",e.querySelector(".wy-img-modal__progress-bar").style.width="0%",e.querySelector(".wy-img-modal__insert").disabled=!0,e.querySelector(".wy-img-dropzone").classList.remove("is-dragover"),this.#d(""),this.#m=null}#b(){this.#a.style.display="none",this.#m=null,this.#v=null}#d(e){const t=this.#a.querySelector(".wy-img-modal__error");t.textContent=e||"",t.style.display=e?"":"none"}#R(){const e=this.#a,t={signal:this.#h.signal};e.querySelectorAll(".wy-img-modal__close, .wy-img-modal__cancel, .wy-img-modal__backdrop").forEach(a=>a.addEventListener("click",()=>this.#b(),t)),e.querySelector(".wy-img-modal__box").addEventListener("click",a=>a.stopPropagation(),t),e.querySelectorAll(".wy-img-tab").forEach(a=>{a.addEventListener("click",()=>{e.querySelectorAll(".wy-img-tab").forEach(d=>d.classList.toggle("is-active",d===a)),e.querySelectorAll(".wy-img-modal__panel").forEach(d=>{d.style.display=d.dataset.panel===a.dataset.tab?"":"none"}),this.#d(""),this.#x()},t)});const i=e.querySelector(".wy-img-file"),s=e.querySelector(".wy-img-dropzone");i.addEventListener("change",()=>{const a=i.files?.[0];a&&this.#E(a)},t),["dragover","dragenter"].forEach(a=>{s.addEventListener(a,d=>{d.preventDefault(),s.classList.add("is-dragover")},t)}),["dragleave","dragend"].forEach(a=>{s.addEventListener(a,d=>{d.preventDefault(),s.classList.remove("is-dragover")},t)}),s.addEventListener("drop",a=>{a.preventDefault(),s.classList.remove("is-dragover");const d=a.dataTransfer.files?.[0];d&&this.#E(d)},t),e.querySelector(".wy-img-preview__remove").addEventListener("click",a=>{a.preventDefault(),this.#m=null,i.value="",e.querySelector(".wy-img-preview").style.display="none",this.#x()},t);const o=e.querySelector(".wy-img-url-input"),r=e.querySelector(".wy-img-url-preview"),l=e.querySelector(".wy-img-url-preview__img");let c;o.addEventListener("input",()=>{this.#d(""),this.#x(),clearTimeout(c);const a=o.value.trim();if(!a){r.style.display="none";return}c=setTimeout(()=>{l.onload=()=>{r.style.display=""},l.onerror=()=>{r.style.display="none"},l.src=a},300)},t),e.querySelector(".wy-img-modal__insert").addEventListener("click",()=>this.#B(),t),e.addEventListener("keydown",a=>{a.key==="Escape"&&this.#b()},t)}#x(){const e=this.#a,t=e.querySelector(".wy-img-tab.is-active")?.dataset.tab,i=e.querySelector(".wy-img-modal__insert");t==="upload"?i.disabled=!this.#m:i.disabled=e.querySelector(".wy-img-url-input").value.trim()===""}#E(e){if(this.#d(""),!e.type.startsWith("image/")){this.#d("File harus berupa gambar.");return}if(e.size>5242880){this.#d("Ukuran gambar maksimal 5MB.");return}this.#m=e;const t=this.#a,i=t.querySelector(".wy-img-preview"),s=t.querySelector(".wy-img-preview__img"),o=new FileReader;o.onload=()=>{s.src=o.result,i.style.display=""},o.readAsDataURL(e),this.#x()}async#B(){const e=this.#a,t=e.querySelector(".wy-img-tab.is-active")?.dataset.tab,i=e.querySelector(".wy-img-alt-input").value.trim(),s=e.querySelector(".wy-img-width-input").value.trim(),o=e.querySelector(".wy-img-align-select").value,r=e.querySelector(".wy-img-modal__insert");if(this.#d(""),t==="upload"){if(!this.#m)return;const l=e.querySelector(".wy-img-modal__progress"),c=e.querySelector(".wy-img-modal__progress-bar");r.disabled=!0,l.style.display="",c.style.width="0%";try{const a=await this.#S(this.#m,d=>{c.style.width=d+"%"});this.#A({src:a,alt:i,width:s,align:o}),this.#b()}catch(a){const d=a?.response?.data?.errors?.image?.[0]||a?.response?.data?.message;this.#d(d||"Upload gambar gagal. Silakan coba lagi."),r.disabled=!1}finally{l.style.display="none"}}else{const l=e.querySelector(".wy-img-url-input").value.trim();if(!l)return;this.#A({src:l,alt:i,width:s,align:o}),this.#b()}}#A(e){this.#e.focus();const t=window.getSelection();t.removeAllRanges(),this.#v&&t.addRange(this.#v);const i=document.createElement("img");if(i.src=e.src,i.alt=e.alt||"",this.#z(i,e),t.rangeCount){const s=t.getRangeAt(0);s.deleteContents(),s.insertNode(i),s.setStartAfter(i),s.collapse(!0),t.removeAllRanges(),t.addRange(s)}else this.#e.appendChild(i);this.#r()}async#S(e,t){const i=this.#t.uploadUrl||window.location.origin+"/wysiwyg/upload-image";if(typeof this.#t.uploadFn=="function")return this.#t.uploadFn(e,i,t);const s=new FormData;s.append("image",e);const o=document.querySelector('meta[name="csrf-token"]')?.content,{data:r}=await k.post(i,s,{headers:o?{"X-CSRF-TOKEN":o}:{},onUploadProgress:l=>{!t||!l.total||t(Math.round(l.loaded/l.total*100))}});if(!r?.ok||!r?.url)throw new Error("Respon server tidak valid.");return r.url}async#I(e){if(!e.type.startsWith("image/"))return;if(e.size>5242880){window.alert("Ukuran gambar maksimal 5MB.");return}this.#e.focus();const t=window.getSelection(),i=t.rangeCount?t.getRangeAt(0):null,s=document.createElement("span");s.className="wy-img-uploading",s.contentEditable="false",s.textContent="Mengunggah gambarโ€ฆ",i?(i.deleteContents(),i.insertNode(s),i.setStartAfter(s),i.collapse(!0),t.removeAllRanges(),t.addRange(i)):this.#e.appendChild(s);try{const o=await this.#S(e,l=>{s.textContent=`Mengunggah gambarโ€ฆ ${l}%`}),r=document.createElement("img");r.src=o,r.alt="",s.replaceWith(r)}catch{s.textContent="Gagal mengunggah gambar.",s.classList.add("wy-img-uploading--error"),setTimeout(()=>{s.remove(),this.#r()},3e3)}finally{this.#r()}}#z(e,{width:t,align:i}={}){if(t){const s=String(t).trim();e.style.width=/^\d+$/.test(s)?`${s}px`:s,e.style.height="auto"}this.#L(e,i||"")}#L(e,t){e.classList.remove("wy-img-align-center","wy-img-align-left","wy-img-align-right","wy-img-align-block-left","wy-img-align-block-right"),t&&e.classList.add(`wy-img-align-${t}`)}#N(){const e={signal:this.#h.signal};this.#o.addEventListener("mousedown",t=>t.preventDefault(),e),this.#o.addEventListener("click",t=>{const i=t.target.closest("button");if(!(!i||!this.#i)){if(i.dataset.action==="delete"){const s=this.#i;this.#y(),s.remove(),this.#r();return}i.dataset.align!==void 0&&(this.#L(this.#i,i.dataset.align),this.#C(),this.#g(this.#i),this.#r())}},e)}#C(){if(!this.#i)return;const e=["center","left","right","block-left","block-right"].find(t=>this.#i.classList.contains(`wy-img-align-${t}`))||"";this.#o.querySelectorAll("[data-align]").forEach(t=>{t.classList.toggle("is-active",t.dataset.align===e)})}#D(e){this.#i=e,e.classList.add("wy-img-selected"),this.#o.style.display="flex",this.#c.style.display="block",this.#C(),this.#g(e)}#y(){this.#i&&this.#i.classList.remove("wy-img-selected"),this.#i=null,this.#o.style.display="none",this.#c.style.display="none"}#g(e){const t=e.getBoundingClientRect();this.#c.style.top=`${t.top}px`,this.#c.style.left=`${t.left}px`,this.#c.style.width=`${t.width}px`,this.#c.style.height=`${t.height}px`;const i=this.#o.getBoundingClientRect(),o=t.top>i.height+12?t.top-i.height-8:t.bottom+8,r=Math.max(8,Math.min(t.left,window.innerWidth-i.width-8));this.#o.style.top=`${o}px`,this.#o.style.left=`${r}px`}#U(){const e=document.createElement("div");e.className="wy-img-resize-box",e.style.display="none",e.innerHTML=`
95
+ <div class="wy-img-resize-handle" data-dir="nw"></div>
96
+ <div class="wy-img-resize-handle" data-dir="ne"></div>
97
+ <div class="wy-img-resize-handle" data-dir="sw"></div>
98
+ <div class="wy-img-resize-handle" data-dir="se"></div>
99
+ `,document.body.appendChild(e),this.#c=e;const t={signal:this.#h.signal};e.querySelectorAll(".wy-img-resize-handle").forEach(i=>{i.addEventListener("mousedown",s=>this.#F(s,i.dataset.dir),t)}),window.addEventListener("scroll",()=>{this.#i&&this.#g(this.#i)},{signal:this.#h.signal,capture:!0}),window.addEventListener("resize",()=>{this.#i&&this.#g(this.#i)},t)}#F(e,t){e.preventDefault(),e.stopPropagation();const i=this.#i;if(!i)return;const s=e.clientX,o=i.getBoundingClientRect().width,r=t==="ne"||t==="se"?1:-1,l=30,c=this.#e.getBoundingClientRect().width,a=h=>{const y=(h.clientX-s)*r,p=Math.min(c,Math.max(l,o+y));i.style.width=`${p}px`,i.style.height="auto",this.#g(i)},d=()=>{document.removeEventListener("mousemove",a),document.removeEventListener("mouseup",d),this.#r()};document.addEventListener("mousemove",a),document.addEventListener("mouseup",d)}#j(){const e={signal:this.#h.signal};this.#s.addEventListener("click",t=>{const i=t.target.closest(".wysiwyg-btn");if(!i)return;t.preventDefault(),this.#e.focus();const s=i.dataset.plugin,o=m.#p.get(s)??this.#t.plugins.find(r=>r?.name===s);o?.action&&(o.action(this,i),this.#r())},e),this.#s.addEventListener("mousedown",t=>{t.preventDefault()},e),this.#e.addEventListener("keyup",()=>this.#w(),e),this.#e.addEventListener("mouseup",()=>this.#w(),e),this.#e.addEventListener("selectionchange",()=>this.#w(),e),this.#e.addEventListener("input",()=>this.#r(),e),this.#n.addEventListener("input",()=>{this.#f(),this.#t.onChange?.(this.getHTML())},e),this.#e.addEventListener("paste",t=>{t.preventDefault();const i=t.clipboardData.getData("text/html"),s=t.clipboardData.getData("text/plain"),o=Array.from(t.clipboardData.files||[]);if(i){const r=this.#O(i);document.execCommand("insertHTML",!1,r)}else if(o.length&&o[0].type.startsWith("image/"))this.#I(o[0]);else{const r=s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\n/g,"<br>");document.execCommand("insertHTML",!1,r)}},e),this.#e.addEventListener("keydown",t=>{t.key==="Enter"&&this.#P(t)},e),this.#e.addEventListener("click",t=>{const i=t.target.closest("img");i?(t.preventDefault(),this.#D(i)):this.#y()},e),document.addEventListener("click",t=>{!this.#l.contains(t.target)&&!this.#o?.contains(t.target)&&!this.#a?.contains(t.target)&&!this.#c?.contains(t.target)&&this.#y()},e),this.#e.addEventListener("scroll",()=>{this.#i&&this.#g(this.#i)},e)}#P(e){const t=window.getSelection();if(!t.rangeCount)return;const i=t.getRangeAt(0).startContainer,s=i.nodeType===3?i.parentElement.closest("blockquote"):i.closest?.("blockquote");if(s&&(i.nodeType===3?i.parentElement:i).textContent.trim()===""){e.preventDefault(),s.insertAdjacentElement("afterend",Object.assign(document.createElement("p"),{innerHTML:"<br>"}));const r=s.nextElementSibling,l=document.createRange();l.setStart(r,0),l.collapse(!0),t.removeAllRanges(),t.addRange(l)}}#r(){this.#f(),this.#w(),this.#t.onChange?.(this.getHTML())}#w(){const e=this.#t.plugins.length?this.#t.plugins.map(t=>typeof t=="string"?m.#p.get(t):t).filter(Boolean):m.getPlugins();this.#s.querySelectorAll(".wysiwyg-btn").forEach(t=>{const i=e.find(s=>s?.name===t.dataset.plugin);i?.isActive&&(t.classList.toggle("is-active",i.isActive(this)),t.setAttribute("aria-pressed",i.isActive(this)))})}#f(){this.#k&&(this.#k.value=this.getHTML())}getHTML(){if(this.#u)return this.#n.value.trim();const e=this.#e.cloneNode(!0);return e.querySelectorAll("p:empty, div:empty").forEach(t=>{(t.innerHTML===""||t.innerHTML==="<br>")&&t.remove()}),e.querySelectorAll("img.wy-img-selected").forEach(t=>{t.classList.remove("wy-img-selected"),t.className.trim()||t.removeAttribute("class")}),e.innerHTML.trim()}setHTML(e){this.#y(),this.#e.innerHTML=e||"",this.#u&&(this.#n.value=e||""),this.#f()}toggleSource(){if(this.#y(),this.#u){const e=this.#n.value.trim();this.#u=!1,this.#e.innerHTML=e,this.#e.style.display="",this.#n.style.display="none",this.#e.focus(),this.#s.querySelectorAll(".wysiwyg-btn").forEach(t=>{t.disabled=!1,t.style.opacity=""})}else{const e=this.getHTML();this.#u=!0,this.#n.value=this.#$(e),this.#e.style.display="none",this.#n.style.display="",this.#n.focus(),this.#s.querySelectorAll('.wysiwyg-btn:not([data-plugin="source"])').forEach(t=>{t.disabled=!0,t.style.opacity="0.35"})}this.#f(),this.#w()}#$(e){const t=["p","div","h1","h2","h3","h4","h5","h6","ul","ol","li","blockquote","pre","table","thead","tbody","tfoot","tr","th","td","figure","figcaption","section","article","header","footer","nav","br","hr"],s=e.replace(new RegExp(`<(${t.join("|")})(\\s[^>]*)?>`,"gi"),`
100
+ <$1$2>`).replace(new RegExp(`</(${t.join("|")})>`,"gi"),`</$1>
101
+ `).split(`
102
+ `);let o=0;const r=" ";return s.map(c=>{const a=c.trim();if(!a)return null;const d=(a.match(/^<\/[a-z]/i)||[]).length;d&&(o=Math.max(0,o-1));const h=r.repeat(o)+a,y=(a.match(/<[a-zA-Z][^>]*[^/]>/g)||[]).length,p=(a.match(/<\/[a-zA-Z]/g)||[]).length,g=(a.match(/<[a-zA-Z][^>]*\/>/g)||[]).length;return o=Math.max(0,o+y-p-g+(d?1:0)),h}).filter(c=>c!==null).join(`
103
+ `).trim()}isSourceMode(){return this.#u}focus(){this.#e.focus()}getArea(){return this.#e}exec(e,t=null){this.#e.focus(),document.execCommand(e,!1,t),this.#r()}wrapBlock(e){document.execCommand("formatBlock",!1,e),this.#r()}#O(e){const t=new DOMParser().parseFromString(e,"text/html");["script","style","meta","link","head","xml","o\\:p","w\\:sdt","w\\:sdtContent"].forEach(l=>{t.querySelectorAll(l).forEach(c=>c.remove())}),["span","font","div > font","ins"].forEach(l=>{t.querySelectorAll(l).forEach(c=>{c.replaceWith(...c.childNodes)})});const o={a:["href","target","rel"],img:["src","alt","width","height"],td:["colspan","rowspan"],th:["colspan","rowspan","scope"],"*":[]};return t.body.querySelectorAll("*").forEach(l=>{const c=l.tagName.toLowerCase(),a=o[c]??o["*"];if(Array.from(l.attributes).map(h=>h.name).forEach(h=>{a.includes(h)||l.removeAttribute(h)}),c==="a"){const h=l.getAttribute("href")||"";/^javascript:/i.test(h.trim())&&l.removeAttribute("href"),l.setAttribute("target","_blank"),l.setAttribute("rel","noopener noreferrer")}}),Object.entries({b:"strong",i:"em",strike:"s",del:"s"}).forEach(([l,c])=>{t.querySelectorAll(l).forEach(a=>{const d=t.createElement(c);d.append(...a.childNodes),a.replaceWith(d)})}),t.querySelectorAll("div:empty, p:empty").forEach(l=>l.remove()),t.body.innerHTML.trim()}destroy(){this.#h.abort(),this.#a?.remove(),this.#o?.remove(),this.#c?.remove(),this.#i=null,this.#l.innerHTML="",this.#l.classList.remove("wysiwyg-editor"),this.#u=!1}queryCommand(e){try{return document.queryCommandState(e)}catch{return!1}}getActiveBlock(){const e=window.getSelection();if(!e.rangeCount)return null;let t=e.getRangeAt(0).startContainer;t.nodeType===3&&(t=t.parentElement);const i=t.closest("h1,h2,h3,h4,h5,h6,p,blockquote,pre,li");return i?i.tagName.toLowerCase():null}}const A={smileys:{title:"๐Ÿ˜Š Smileys",emoji:["๐Ÿ˜€","๐Ÿ˜ƒ","๐Ÿ˜„","๐Ÿ˜","๐Ÿ˜†","๐Ÿ˜…","๐Ÿ˜‚","๐Ÿคฃ","๐Ÿ˜Š","๐Ÿ˜‡","๐Ÿ™‚","๐Ÿค—","๐Ÿ˜‰","๐Ÿ˜Œ","๐Ÿ˜","๐Ÿฅฐ","๐Ÿ˜˜","๐Ÿ˜—","๐Ÿ˜š","๐Ÿ˜™","๐Ÿ˜‹","๐Ÿ˜›","๐Ÿ˜œ","๐Ÿคช","๐Ÿ˜","๐Ÿค‘","๐Ÿค“","๐Ÿ˜Ž"]},hand:{title:"๐Ÿ‘‹ Hand Gestures",emoji:["๐Ÿ‘‹","๐Ÿคš","๐Ÿ–","โœ‹","๐Ÿ––","๐Ÿ‘Œ","๐ŸคŒ","๐Ÿค","โœŒ","๐Ÿคž","๐Ÿซฐ","๐ŸคŸ","๐Ÿค˜","๐Ÿค™","๐Ÿ‘","๐Ÿ‘Ž","โœŠ","๐Ÿ‘Š","๐Ÿค›","๐Ÿคœ","๐Ÿ‘","๐Ÿ™Œ","๐Ÿ‘","๐Ÿคฒ","๐Ÿค","๐Ÿคœ","๐Ÿซฑ","๐Ÿซฒ","๐Ÿซถ"]},hearts:{title:"โค๏ธ Hearts & Love",emoji:["โค๏ธ","๐Ÿงก","๐Ÿ’›","๐Ÿ’š","๐Ÿ’™","๐Ÿ’œ","๐Ÿ–ค","๐Ÿค","๐ŸคŽ","๐Ÿ’”","๐Ÿ’•","๐Ÿ’ž","๐Ÿ’“","๐Ÿ’—","๐Ÿ’–","๐Ÿ’˜","๐Ÿ’","๐Ÿ’Ÿ","๐Ÿ’Œ"]},animals:{title:"๐Ÿถ Animals & Nature",emoji:["๐Ÿถ","๐Ÿฑ","๐Ÿญ","๐Ÿน","๐Ÿฐ","๐ŸฆŠ","๐Ÿป","๐Ÿผ","๐Ÿจ","๐Ÿฏ","๐Ÿฆ","๐Ÿฎ","๐Ÿท","๐Ÿธ","๐Ÿต","๐Ÿ™ˆ","๐Ÿ™‰","๐Ÿ™Š","๐Ÿ’","๐Ÿ”","๐Ÿง","๐Ÿฆ","๐Ÿค","๐Ÿฆ†","๐Ÿฆ…","๐Ÿฆ‰","๐Ÿฆ‡","๐Ÿบ"]},food:{title:"๐Ÿ• Food & Drink",emoji:["๐Ÿ","๐ŸŽ","๐Ÿ","๐ŸŠ","๐Ÿ‹","๐ŸŒ","๐Ÿ‰","๐Ÿ‡","๐Ÿ“","๐Ÿˆ","๐Ÿ’","๐Ÿ‘","๐Ÿฅญ","๐Ÿ","๐Ÿฅฅ","๐Ÿฅ‘","๐Ÿ…","๐Ÿ†","๐Ÿฅ‘","๐Ÿฅฆ","๐Ÿฅฌ","๐Ÿฅ’","๐ŸŒถ","๐ŸŒฝ","๐Ÿฅ”","๐Ÿ ","๐Ÿฅ","๐Ÿž","๐Ÿฅ–","๐Ÿฅจ"]},activities:{title:"โšฝ Activities & Sports",emoji:["โšฝ","๐Ÿ€","๐Ÿˆ","โšพ","๐ŸฅŽ","๐ŸŽพ","๐Ÿ","๐Ÿ‰","๐Ÿฅ","๐ŸŽณ","๐Ÿ“","๐Ÿธ","๐Ÿ’","๐Ÿ‘","๐ŸฅŠ","๐Ÿฅ‹","๐ŸŽฃ","๐ŸŽฝ","๐ŸŽฟ","โ›ท","๐Ÿ‚","๐Ÿช‚","๐Ÿ›น","๐Ÿ›ผ","๐Ÿ›ท","๐ŸฅŒ","๐ŸŽฏ","๐Ÿช€","๐Ÿชƒ"]},travel:{title:"โœˆ๏ธ Travel & Places",emoji:["โœˆ๏ธ","๐Ÿš€","๐Ÿ›ธ","๐Ÿš","๐Ÿš‚","๐Ÿšƒ","๐Ÿš„","๐Ÿš…","๐Ÿš†","๐Ÿš‡","๐Ÿšˆ","๐Ÿš‰","๐ŸšŠ","๐Ÿš","๐Ÿšž","๐Ÿš‹","๐ŸšŒ","๐Ÿš","๐ŸšŽ","๐Ÿš","๐Ÿš‘","๐Ÿš’","๐Ÿš“","๐Ÿš”","๐Ÿš•","๐Ÿš–","๐Ÿš—","๐Ÿš˜","๐Ÿš™"]},symbols:{title:"โญ Symbols & Objects",emoji:["โญ","๐ŸŒŸ","โœจ","โšก","๐Ÿ”ฅ","๐Ÿ’ง","โ„๏ธ","๐ŸŒˆ","๐ŸŽ‰","๐ŸŽŠ","๐ŸŽˆ","๐ŸŽ","๐ŸŽ€","๐ŸŽ‚","๐Ÿฐ","๐ŸŽ†","๐ŸŽ‡","๐Ÿ’ฅ","๐ŸŒธ","๐ŸŒบ","๐ŸŒป","๐ŸŒท","๐ŸŒน","๐Ÿฅ€","๐ŸŒผ"]}};function S(n){const e=document.createElement("div");e.style.cssText=`
104
+ position: fixed;
105
+ top: 0;
106
+ left: 0;
107
+ width: 100%;
108
+ height: 100%;
109
+ background: rgba(0, 0, 0, 0.5);
110
+ z-index: 9998;
111
+ `;const t=document.createElement("div");t.style.cssText=`
112
+ position: fixed;
113
+ top: 50%;
114
+ left: 50%;
115
+ transform: translate(-50%, -50%);
116
+ background: white;
117
+ border-radius: 12px;
118
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
119
+ width: 90%;
120
+ max-width: 500px;
121
+ max-height: 600px;
122
+ z-index: 9999;
123
+ display: flex;
124
+ flex-direction: column;
125
+ overflow: hidden;
126
+ `;const i=document.createElement("div");i.style.cssText=`
127
+ padding: 16px;
128
+ border-bottom: 1px solid #eee;
129
+ display: flex;
130
+ justify-content: space-between;
131
+ align-items: center;
132
+ `,i.innerHTML='<h3 style="margin: 0; font-size: 16px;">Select Emoji</h3>';const s=document.createElement("button");s.innerHTML="โœ•",s.style.cssText=`
133
+ background: none;
134
+ border: none;
135
+ font-size: 20px;
136
+ cursor: pointer;
137
+ color: #666;
138
+ `,s.onclick=()=>{e.remove(),t.remove()},i.appendChild(s);const o=document.createElement("div");o.style.cssText=`
139
+ display: flex;
140
+ gap: 4px;
141
+ padding: 8px;
142
+ border-bottom: 1px solid #eee;
143
+ overflow-x: auto;
144
+ `;const r=document.createElement("div");return r.style.cssText=`
145
+ flex: 1;
146
+ overflow-y: auto;
147
+ padding: 12px;
148
+ `,Object.entries(A).forEach(([l,c],a)=>{const d=document.createElement("button");d.innerHTML=c.emoji[0],d.style.cssText=`
149
+ background: ${a===0?"#e0e7ff":"#f3f4f6"};
150
+ border: none;
151
+ width: 36px;
152
+ height: 36px;
153
+ border-radius: 6px;
154
+ cursor: pointer;
155
+ font-size: 18px;
156
+ flex-shrink: 0;
157
+ `,d.onclick=()=>{o.querySelectorAll("button").forEach(p=>{p.style.background="#f3f4f6"}),d.style.background="#e0e7ff",r.innerHTML="";const h=document.createElement("div");h.style.cssText=`
158
+ font-size: 12px;
159
+ font-weight: 600;
160
+ color: #666;
161
+ margin-bottom: 8px;
162
+ text-transform: uppercase;
163
+ `,h.textContent=c.title,r.appendChild(h);const y=document.createElement("div");y.style.cssText=`
164
+ display: grid;
165
+ grid-template-columns: repeat(auto-fill, 36px);
166
+ gap: 4px;
167
+ `,c.emoji.forEach(p=>{const g=document.createElement("button");g.textContent=p,g.style.cssText=`
168
+ width: 36px;
169
+ height: 36px;
170
+ border: 1px solid #ddd;
171
+ background: white;
172
+ border-radius: 6px;
173
+ cursor: pointer;
174
+ font-size: 20px;
175
+ transition: all 0.15s;
176
+ `,g.onmouseover=()=>{g.style.background="#f3f4f6",g.style.transform="scale(1.2)"},g.onmouseout=()=>{g.style.background="white",g.style.transform="scale(1)"},g.onclick=()=>{n(p),e.remove(),t.remove()},y.appendChild(g)}),r.appendChild(y)},o.appendChild(d)}),o.querySelector("button").click(),t.appendChild(i),t.appendChild(o),t.appendChild(r),document.body.appendChild(e),document.body.appendChild(t),e.onclick=()=>{e.remove(),t.remove()},t}const u=(n,e="")=>`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" ${e}>${n}</svg>`,f=(n,e="")=>window.prompt(n,e),v=[{name:"bold",group:"format",title:"Bold (Ctrl+B)",icon:u('<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>'),action:n=>n.exec("bold"),isActive:n=>n.queryCommand("bold")},{name:"italic",group:"format",title:"Italic (Ctrl+I)",icon:u('<line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/>'),action:n=>n.exec("italic"),isActive:n=>n.queryCommand("italic")},{name:"underline",group:"format",title:"Underline (Ctrl+U)",icon:u('<path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3"/><line x1="4" y1="21" x2="20" y2="21"/>'),action:n=>n.exec("underline"),isActive:n=>n.queryCommand("underline")},{name:"strikethrough",group:"format",title:"Strikethrough",icon:u('<path d="M17.3 12a4.6 4.6 0 0 1-1.3 3A5 5 0 0 1 12 17a5.7 5.7 0 0 1-4-1.5"/><path d="M6.6 8.8A4.1 4.1 0 0 1 8 6a5 5 0 0 1 4-1.5 5.7 5.7 0 0 1 3.5 1c1 .7 1.6 1.5 1.8 2.5"/><line x1="4" y1="12" x2="20" y2="12"/>'),action:n=>n.exec("strikeThrough"),isActive:n=>n.queryCommand("strikeThrough")},{name:"h1",group:"heading",title:"Heading 1",icon:'<span style="font-weight:700;font-size:12px;line-height:1">H1</span>',action:n=>{const e=n.getActiveBlock()==="h1";n.wrapBlock(e?"p":"h1")},isActive:n=>n.getActiveBlock()==="h1"},{name:"h2",group:"heading",title:"Heading 2",icon:'<span style="font-weight:700;font-size:12px;line-height:1">H2</span>',action:n=>{const e=n.getActiveBlock()==="h2";n.wrapBlock(e?"p":"h2")},isActive:n=>n.getActiveBlock()==="h2"},{name:"h3",group:"heading",title:"Heading 3",icon:'<span style="font-weight:700;font-size:12px;line-height:1">H3</span>',action:n=>{const e=n.getActiveBlock()==="h3";n.wrapBlock(e?"p":"h3")},isActive:n=>n.getActiveBlock()==="h3"},{name:"ul",group:"list",title:"Bullet List",icon:u('<line x1="9" y1="6" x2="20" y2="6"/><line x1="9" y1="12" x2="20" y2="12"/><line x1="9" y1="18" x2="20" y2="18"/><circle cx="4" cy="6" r="1.5" fill="currentColor" stroke="none"/><circle cx="4" cy="12" r="1.5" fill="currentColor" stroke="none"/><circle cx="4" cy="18" r="1.5" fill="currentColor" stroke="none"/>'),action:n=>n.exec("insertUnorderedList"),isActive:n=>n.queryCommand("insertUnorderedList")},{name:"ol",group:"list",title:"Numbered List",icon:u('<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'),action:n=>n.exec("insertOrderedList"),isActive:n=>n.queryCommand("insertOrderedList")},{name:"blockquote",group:"block",title:"Blockquote",icon:u('<path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"/>'),action:n=>{const e=n.getActiveBlock()==="blockquote";n.wrapBlock(e?"p":"blockquote")},isActive:n=>n.getActiveBlock()==="blockquote"},{name:"link",group:"link",title:"Insert Link",icon:u('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>'),action:n=>{const e=document.queryCommandValue("createLink"),t=f("Masukkan URL:",e||"https://");t!==null&&(t.trim()===""?n.exec("unlink"):(n.exec("createLink",t.trim()),n.getArea().querySelectorAll("a").forEach(i=>{i.target="_blank",i.rel="noopener noreferrer"})))},isActive:n=>n.queryCommand("createLink")},{name:"unlink",group:"link",title:"Remove Link",icon:u('<path d="M18.84 12.25l1.72-1.71h-.02a5.004 5.004 0 0 0-.12-7.07 5.006 5.006 0 0 0-6.95 0l-1.72 1.71"/><path d="M5.17 11.75l-1.71 1.71a5.004 5.004 0 0 0 .12 7.07 5.006 5.006 0 0 0 6.95 0l1.71-1.71"/><line x1="8" y1="2" x2="8" y2="5"/><line x1="2" y1="8" x2="5" y2="8"/><line x1="16" y1="19" x2="16" y2="22"/><line x1="19" y1="16" x2="22" y2="16"/>'),action:n=>n.exec("unlink")},{name:"undo",group:"history",title:"Undo (Ctrl+Z)",icon:u('<polyline points="9 14 4 9 9 4"/><path d="M20 20v-7a4 4 0 0 0-4-4H4"/>'),action:n=>n.exec("undo")},{name:"redo",group:"history",title:"Redo (Ctrl+Y)",icon:u('<polyline points="15 14 20 9 15 4"/><path d="M4 20v-7a4 4 0 0 0 4-4h12"/>'),action:n=>n.exec("redo")},{name:"removeFormat",group:"clear",title:"Hapus Formatting",icon:u('<path d="M4 7V4h16v3"/><path d="M5 20h6"/><path d="M13 4 8 20"/><line x1="17" y1="13" x2="22" y2="18"/><line x1="22" y1="13" x2="17" y2="18"/>'),action:n=>n.exec("removeFormat")},{name:"source",group:"source",title:"Edit HTML Source",icon:u('<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>'),action:n=>n.toggleSource(),isActive:n=>n.isSourceMode()},{name:"image",group:"image",title:"Sisipkan Gambar",icon:u('<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/>'),action:n=>n.openImageDialog()},{name:"table",group:"table",title:"Insert Table (2x2)",icon:u('<rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/>'),action:n=>{const e=L(2,2),t=window.getSelection(),i=t.getRangeAt(0);i.insertNode(e),i.collapse(!1),t.removeAllRanges(),t.addRange(i),n.focus()}},{name:"table-add-row",group:"table",title:"Add Row Below",icon:u('<rect x="2" y="2" width="20" height="20"/><line x1="2" y1="12" x2="22" y2="12"/><line x1="12" y1="2" x2="12" y2="22"/><line x1="17" y1="15" x2="23" y2="15"/><line x1="20" y1="12" x2="20" y2="18"/>'),action:n=>C(n)},{name:"table-delete",group:"table",title:"Delete Table",icon:u('<rect x="3" y="3" width="18" height="18" rx="1" stroke-dasharray="3,3"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="9" y1="9" x2="15" y2="15"/><line x1="15" y1="9" x2="9" y2="15"/>'),action:n=>T(n)}];function L(n,e){const t=document.createElement("table");t.style.cssText=`
177
+ border-collapse: collapse;
178
+ width: 100%;
179
+ margin: 12px 0;
180
+ `;for(let i=0;i<n;i++){const s=document.createElement("tr");for(let o=0;o<e;o++){const r=document.createElement(i===0?"th":"td");r.style.cssText=`
181
+ border: 1px solid #ccc;
182
+ padding: 8px;
183
+ text-align: left;
184
+ min-width: 80px;
185
+ `,r.innerHTML=i===0?`<strong>Header ${o+1}</strong>`:"<br>",s.appendChild(r)}t.appendChild(s)}return t}function C(n){n.getArea();const e=window.getSelection();if(e.rangeCount===0)return;const t=e.getRangeAt(0),i=t.commonAncestorContainer.closest("td")||t.commonAncestorContainer.closest("th");if(!i){alert("Posisikan kursor di dalam tabel terlebih dahulu");return}const s=i.closest("tr"),o=s.closest("table"),r=document.createElement("tr");Array.from(s.children).forEach(()=>{const l=document.createElement("td");l.style.cssText=i.style.cssText,l.innerHTML="<br>",r.appendChild(l)}),o.appendChild(r)}function T(n){n.getArea();const e=window.getSelection();if(e.rangeCount===0)return;const i=e.getRangeAt(0).commonAncestorContainer.closest("table");if(!i){alert("Posisikan kursor di dalam tabel terlebih dahulu");return}confirm("Hapus tabel ini?")&&(i.remove(),n.focus())}v.push({name:"mention",group:"insert",title:"Mention (@username)",icon:u('<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 11h-6m-2-2 2 2-2 2" stroke-width="1.5"/>'),action:n=>{const e=f("Username:","@");if(e&&e.trim()){const t=document.createElement("span");t.className="mention",t.contentEditable=!1,t.style.cssText=`
186
+ background: #e0e7ff;
187
+ color: #4f46e5;
188
+ padding: 2px 6px;
189
+ border-radius: 4px;
190
+ margin: 0 2px;
191
+ font-weight: 500;
192
+ cursor: default;
193
+ display: inline-block;
194
+ `,t.textContent=e,t.dataset.mention=e.replace("@","");const i=window.getSelection().getRangeAt(0);i.insertNode(t),i.setStartAfter(t),i.collapse(!0);const s=window.getSelection();s.removeAllRanges(),s.addRange(i),n.focus()}}},{name:"emoji",group:"insert",title:"Insert Emoji",icon:u('<circle cx="12" cy="12" r="9"/><circle cx="9" cy="9" r="1.5" fill="currentColor"/><circle cx="15" cy="9" r="1.5" fill="currentColor"/><path d="M8 15a4 4 0 0 0 8 0"/>'),action:n=>{S(e=>{const t=window.getSelection().getRangeAt(0),i=document.createTextNode(e);t.insertNode(i),t.setStartAfter(i),t.collapse(!0);const s=window.getSelection();s.removeAllRanges(),s.addRange(t),n.focus()})}});async function b(n,e,t){const i=new FormData;i.append("image",n);const s=document.querySelector('meta[name="csrf-token"]')?.content,o={};return s&&(o["X-CSRF-TOKEN"]=s,o["X-Requested-With"]="XMLHttpRequest"),new Promise((r,l)=>{const c=new XMLHttpRequest;c.open("POST",e),Object.entries(o).forEach(([a,d])=>c.setRequestHeader(a,d)),c.upload.addEventListener("progress",a=>{a.lengthComputable&&t&&t(Math.round(a.loaded/a.total*100))}),c.addEventListener("load",()=>{try{const a=JSON.parse(c.responseText);if(c.status>=200&&c.status<300&&a?.ok&&a?.url)r(a.url);else{const d=a?.message||a?.errors?.image?.[0]||`Server error ${c.status}`;l(new Error(d))}}catch{l(new Error("Respon server tidak valid"))}}),c.addEventListener("error",()=>l(new Error("Koneksi gagal"))),c.addEventListener("abort",()=>l(new Error("Upload dibatalkan"))),c.send(i)})}v.forEach(n=>m.registerPlugin(n));function x(){document.querySelectorAll("[data-wysiwyg]").forEach(n=>{if(n._wysiwygEditor)return;const e=new m(n,{placeholder:n.dataset.wysiwygPlaceholder||"Tulis sesuatu...",minHeight:parseInt(n.dataset.wysiwygHeight||"200",10),syncInput:n.dataset.wysiwygInput||null,uploadUrl:n.dataset.wysiwygUploadUrl||"",uploadFn:b}),t=n.dataset.wysiwygInitial||"";t&&e.setHTML(t),n._wysiwygEditor=e})}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",x):x(),typeof window<"u"&&(window.WysiwygEditor=m,window.WysiwygEditor.create=(n,e={})=>new m(n,{uploadFn:b,...e})),w.WysiwygEditor=m,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"}),w})({},axios);