tinymce-plugin-multipreview 1.0.1 → 1.0.2

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.
@@ -0,0 +1 @@
1
+ "use strict";const e=(t,o)=>{const c=t.__vccOpts||t;for(const[r,s]of o)c[r]=s;return c};exports._export_sfc=e;
@@ -0,0 +1,9 @@
1
+ const s = (t, r) => {
2
+ const o = t.__vccOpts || t;
3
+ for (const [c, e] of r)
4
+ o[c] = e;
5
+ return o;
6
+ };
7
+ export {
8
+ s as _
9
+ };
@@ -0,0 +1,420 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c="multipreview",n={mobile:{label:"手机",icon:`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2
+ <rect x="5" y="2" width="14" height="20" rx="2" ry="2"/>
3
+ <line x1="12" y1="18" x2="12.01" y2="18"/>
4
+ </svg>`,width:375,height:812,scale:.75},tablet:{label:"平板",icon:`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5
+ <rect x="4" y="2" width="16" height="20" rx="2" ry="2"/>
6
+ <line x1="12" y1="18" x2="12.01" y2="18"/>
7
+ </svg>`,width:768,height:1024,scale:.65},desktop:{label:"电脑",icon:`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
8
+ <rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
9
+ <line x1="8" y1="21" x2="16" y2="21"/>
10
+ <line x1="12" y1="17" x2="12" y2="21"/>
11
+ </svg>`,width:1280,height:800,scale:.55}};function d(i,e){const t=i.getContent();return`<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <title>预览</title>${`
14
+ <style>
15
+ * { box-sizing: border-box; }
16
+ body {
17
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
18
+ font-size: 16px;
19
+ line-height: 1.7;
20
+ color: #333;
21
+ margin: 0;
22
+ padding: 20px;
23
+ word-wrap: break-word;
24
+ overflow-wrap: break-word;
25
+ }
26
+ img { max-width: 100%; height: auto; }
27
+ table { border-collapse: collapse; width: 100%; }
28
+ td, th { border: 1px solid #ddd; padding: 8px; }
29
+ pre { overflow-x: auto; background: #f5f5f5; padding: 12px; border-radius: 4px; }
30
+ code { background: #f5f5f5; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }
31
+ blockquote {
32
+ border-left: 4px solid #ddd;
33
+ margin: 0;
34
+ padding-left: 16px;
35
+ color: #666;
36
+ }
37
+ h1,h2,h3,h4,h5,h6 { line-height: 1.3; margin-top: 1.5em; margin-bottom: 0.5em; }
38
+ p { margin: 0.8em 0; }
39
+ a { color: #0066cc; }
40
+ ul, ol { padding-left: 1.5em; }
41
+ ${e||""}
42
+ </style>
43
+ `}</head><body>${t}</body></html>`}function h(){return`
44
+ <div class="tpm-overlay" id="tpm-overlay">
45
+ <div class="tpm-modal">
46
+ <!-- 顶部工具栏 -->
47
+ <div class="tpm-toolbar">
48
+ <div class="tpm-toolbar-left">
49
+ <div class="tpm-logo">
50
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
51
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
52
+ <circle cx="12" cy="12" r="3"/>
53
+ </svg>
54
+ <span>内容预览</span>
55
+ </div>
56
+ </div>
57
+ <div class="tpm-device-tabs">
58
+ ${Object.entries(n).map(([e,t])=>`
59
+ <button class="tpm-device-btn ${e==="mobile"?"active":""}" data-device="${e}" title="${t.label}">
60
+ ${t.icon}
61
+ <span>${t.label}</span>
62
+ </button>
63
+ `).join("")}
64
+ </div>
65
+ <div class="tpm-toolbar-right">
66
+ <button class="tpm-btn tpm-btn-rotate" id="tpm-rotate" title="旋转屏幕">
67
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
68
+ <polyline points="23 4 23 10 17 10"/>
69
+ <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
70
+ </svg>
71
+ </button>
72
+ <button class="tpm-btn tpm-btn-close" id="tpm-close" title="关闭">
73
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
74
+ <line x1="18" y1="6" x2="6" y2="18"/>
75
+ <line x1="6" y1="6" x2="18" y2="18"/>
76
+ </svg>
77
+ </button>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- 预览区域 -->
82
+ <div class="tpm-stage" id="tpm-stage">
83
+ <div class="tpm-device-wrapper" id="tpm-device-wrapper">
84
+ <div class="tpm-device-frame" id="tpm-device-frame">
85
+ <div class="tpm-device-notch" id="tpm-device-notch">
86
+ <div class="tpm-notch-inner"></div>
87
+ </div>
88
+ <div class="tpm-device-screen">
89
+ <iframe class="tpm-iframe" id="tpm-iframe" sandbox="allow-same-origin allow-scripts"></iframe>
90
+ </div>
91
+ <div class="tpm-device-home" id="tpm-device-home">
92
+ <div class="tpm-home-bar"></div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+
98
+ <!-- 底部信息栏 -->
99
+ <div class="tpm-footer">
100
+ <span class="tpm-device-info" id="tpm-device-info">375 × 812</span>
101
+ <span class="tpm-separator">·</span>
102
+ <span class="tpm-device-label" id="tpm-device-label">手机预览</span>
103
+ <span class="tpm-hint">横屏模式下旋转设备以获得更好体验</span>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ `}const g=`
108
+ .tpm-overlay {
109
+ position: fixed;
110
+ inset: 0;
111
+ background: rgba(8, 8, 20, 0.85);
112
+ backdrop-filter: blur(12px);
113
+ -webkit-backdrop-filter: blur(12px);
114
+ z-index: 999999;
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ animation: tpm-fade-in 0.25s ease;
119
+ }
120
+ @keyframes tpm-fade-in {
121
+ from { opacity: 0; }
122
+ to { opacity: 1; }
123
+ }
124
+ .tpm-modal {
125
+ width: 100%;
126
+ height: 100%;
127
+ display: flex;
128
+ flex-direction: column;
129
+ overflow: hidden;
130
+ }
131
+
132
+ /* 工具栏 */
133
+ .tpm-toolbar {
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: space-between;
137
+ padding: 0 24px;
138
+ height: 60px;
139
+ background: rgba(255,255,255,0.04);
140
+ border-bottom: 1px solid rgba(255,255,255,0.08);
141
+ flex-shrink: 0;
142
+ gap: 16px;
143
+ }
144
+ .tpm-toolbar-left, .tpm-toolbar-right {
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 8px;
148
+ min-width: 120px;
149
+ }
150
+ .tpm-toolbar-right { justify-content: flex-end; }
151
+ .tpm-logo {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 8px;
155
+ color: rgba(255,255,255,0.9);
156
+ font-size: 15px;
157
+ font-weight: 600;
158
+ letter-spacing: 0.02em;
159
+ }
160
+ .tpm-logo svg {
161
+ width: 20px;
162
+ height: 20px;
163
+ color: #6ee7f7;
164
+ }
165
+ .tpm-device-tabs {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 4px;
169
+ background: rgba(255,255,255,0.06);
170
+ border-radius: 10px;
171
+ padding: 4px;
172
+ }
173
+ .tpm-device-btn {
174
+ display: flex;
175
+ align-items: center;
176
+ gap: 6px;
177
+ padding: 6px 14px;
178
+ border: none;
179
+ background: transparent;
180
+ color: rgba(255,255,255,0.5);
181
+ border-radius: 7px;
182
+ cursor: pointer;
183
+ font-size: 13px;
184
+ font-weight: 500;
185
+ transition: all 0.2s ease;
186
+ white-space: nowrap;
187
+ }
188
+ .tpm-device-btn svg {
189
+ width: 16px;
190
+ height: 16px;
191
+ flex-shrink: 0;
192
+ }
193
+ .tpm-device-btn:hover {
194
+ color: rgba(255,255,255,0.8);
195
+ background: rgba(255,255,255,0.08);
196
+ }
197
+ .tpm-device-btn.active {
198
+ background: rgba(110, 231, 247, 0.15);
199
+ color: #6ee7f7;
200
+ }
201
+ .tpm-btn {
202
+ display: flex;
203
+ align-items: center;
204
+ justify-content: center;
205
+ width: 36px;
206
+ height: 36px;
207
+ border: none;
208
+ background: rgba(255,255,255,0.06);
209
+ color: rgba(255,255,255,0.6);
210
+ border-radius: 8px;
211
+ cursor: pointer;
212
+ transition: all 0.2s ease;
213
+ }
214
+ .tpm-btn svg { width: 18px; height: 18px; }
215
+ .tpm-btn:hover {
216
+ background: rgba(255,255,255,0.12);
217
+ color: rgba(255,255,255,0.9);
218
+ }
219
+ .tpm-btn-close:hover { background: rgba(255, 80, 80, 0.2); color: #ff6b6b; }
220
+
221
+ /* 预览舞台 */
222
+ .tpm-stage {
223
+ flex: 1;
224
+ display: flex;
225
+ align-items: center;
226
+ justify-content: center;
227
+ overflow: hidden;
228
+ padding: 30px 20px;
229
+ position: relative;
230
+ }
231
+ .tpm-stage::before {
232
+ content: '';
233
+ position: absolute;
234
+ inset: 0;
235
+ background: radial-gradient(ellipse at 50% 50%, rgba(110, 231, 247, 0.03) 0%, transparent 70%);
236
+ pointer-events: none;
237
+ }
238
+ .tpm-device-wrapper {
239
+ display: flex;
240
+ align-items: center;
241
+ justify-content: center;
242
+ transition: all 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
243
+ transform-origin: center center;
244
+ }
245
+
246
+ /* 设备外壳 */
247
+ .tpm-device-frame {
248
+ position: relative;
249
+ border-radius: 40px;
250
+ background: linear-gradient(160deg, #2a2a3e 0%, #1a1a2e 50%, #12121f 100%);
251
+ box-shadow:
252
+ 0 0 0 1px rgba(255,255,255,0.12),
253
+ 0 0 0 2px rgba(0,0,0,0.8),
254
+ 0 30px 80px rgba(0,0,0,0.6),
255
+ inset 0 1px 0 rgba(255,255,255,0.1),
256
+ 0 0 60px rgba(110, 231, 247, 0.06);
257
+ transition: all 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
258
+ overflow: hidden;
259
+ }
260
+ .tpm-device-frame::before {
261
+ content: '';
262
+ position: absolute;
263
+ inset: 0;
264
+ border-radius: inherit;
265
+ background: linear-gradient(135deg, rgba(255,255,255,0.06) 0%, transparent 50%);
266
+ pointer-events: none;
267
+ z-index: 2;
268
+ }
269
+
270
+ /* 刘海 */
271
+ .tpm-device-notch {
272
+ position: absolute;
273
+ top: 0;
274
+ left: 50%;
275
+ transform: translateX(-50%);
276
+ width: 120px;
277
+ height: 30px;
278
+ background: #0a0a14;
279
+ border-radius: 0 0 20px 20px;
280
+ z-index: 10;
281
+ display: flex;
282
+ align-items: center;
283
+ justify-content: center;
284
+ transition: all 0.3s ease;
285
+ }
286
+ .tpm-notch-inner {
287
+ width: 60px;
288
+ height: 6px;
289
+ background: #1a1a2e;
290
+ border-radius: 3px;
291
+ }
292
+
293
+ /* 屏幕 */
294
+ .tpm-device-screen {
295
+ position: absolute;
296
+ inset: 12px;
297
+ border-radius: 28px;
298
+ overflow: hidden;
299
+ background: #fff;
300
+ }
301
+ .tpm-iframe {
302
+ width: 100%;
303
+ height: 100%;
304
+ border: none;
305
+ display: block;
306
+ }
307
+
308
+ /* Home 条 */
309
+ .tpm-device-home {
310
+ position: absolute;
311
+ bottom: 8px;
312
+ left: 50%;
313
+ transform: translateX(-50%);
314
+ z-index: 10;
315
+ display: flex;
316
+ align-items: center;
317
+ justify-content: center;
318
+ transition: all 0.3s ease;
319
+ }
320
+ .tpm-home-bar {
321
+ width: 100px;
322
+ height: 4px;
323
+ background: rgba(255,255,255,0.35);
324
+ border-radius: 2px;
325
+ }
326
+
327
+ /* 平板样式 */
328
+ .tpm-device-frame.tablet {
329
+ border-radius: 24px;
330
+ }
331
+ .tpm-device-frame.tablet .tpm-device-notch {
332
+ width: 80px;
333
+ height: 20px;
334
+ border-radius: 0 0 12px 12px;
335
+ }
336
+ .tpm-device-frame.tablet .tpm-notch-inner {
337
+ width: 40px;
338
+ height: 5px;
339
+ }
340
+
341
+ /* PC 样式 */
342
+ .tpm-device-frame.desktop {
343
+ border-radius: 12px 12px 0 0;
344
+ }
345
+ .tpm-device-frame.desktop .tpm-device-notch { display: none; }
346
+ .tpm-device-frame.desktop .tpm-device-home { display: none; }
347
+ .tpm-device-frame.desktop .tpm-device-screen { inset: 32px 8px 8px; border-radius: 4px; }
348
+ .tpm-device-frame.desktop::after {
349
+ content: '';
350
+ position: absolute;
351
+ top: 0;
352
+ left: 0;
353
+ right: 0;
354
+ height: 32px;
355
+ background: linear-gradient(to bottom, #252538, #1e1e30);
356
+ display: flex;
357
+ align-items: center;
358
+ padding-left: 16px;
359
+ }
360
+ .tpm-desktop-bar {
361
+ position: absolute;
362
+ top: 0;
363
+ left: 0;
364
+ right: 0;
365
+ height: 32px;
366
+ background: linear-gradient(to bottom, #2a2a42, #1e1e32);
367
+ border-radius: 12px 12px 0 0;
368
+ display: flex;
369
+ align-items: center;
370
+ padding: 0 12px;
371
+ gap: 6px;
372
+ z-index: 5;
373
+ }
374
+ .tpm-desktop-dot {
375
+ width: 10px;
376
+ height: 10px;
377
+ border-radius: 50%;
378
+ }
379
+
380
+ /* 底部信息 */
381
+ .tpm-footer {
382
+ display: flex;
383
+ align-items: center;
384
+ justify-content: center;
385
+ gap: 8px;
386
+ height: 40px;
387
+ background: rgba(255,255,255,0.03);
388
+ border-top: 1px solid rgba(255,255,255,0.06);
389
+ flex-shrink: 0;
390
+ font-size: 12px;
391
+ color: rgba(255,255,255,0.3);
392
+ }
393
+ .tpm-device-info { color: rgba(110, 231, 247, 0.7); font-weight: 600; letter-spacing: 0.05em; }
394
+ .tpm-separator { color: rgba(255,255,255,0.15); }
395
+ .tpm-device-label { color: rgba(255,255,255,0.5); }
396
+ .tpm-hint { margin-left: 16px; font-size: 11px; opacity: 0.6; }
397
+
398
+ /* 旋转动画 */
399
+ .tpm-device-wrapper.rotating .tpm-device-frame {
400
+ animation: tpm-rotate-pulse 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
401
+ }
402
+ @keyframes tpm-rotate-pulse {
403
+ 0% { transform: scale(1); }
404
+ 50% { transform: scale(0.9) rotate(5deg); }
405
+ 100% { transform: scale(1); }
406
+ }
407
+
408
+ /* 设备切换动画 */
409
+ @keyframes tpm-device-in {
410
+ from { opacity: 0; transform: scale(0.85) translateY(20px); }
411
+ to { opacity: 1; transform: scale(1) translateY(0); }
412
+ }
413
+ .tpm-device-frame.entering {
414
+ animation: tpm-device-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
415
+ }
416
+ `;function b(){if(document.getElementById("tpm-styles"))return;const i=document.createElement("style");i.id="tpm-styles",i.textContent=g,document.head.appendChild(i)}class l{constructor(e,t={}){this.editor=e,this.options=t,this.currentDevice="mobile",this.isLandscape=!1,this.modal=null}open(){b();const e=document.createElement("div");e.innerHTML=h(),this.modal=e.firstElementChild,document.body.appendChild(this.modal),this._setupEvents(),this._switchDevice("mobile",!1),this._loadContent()}close(){this.modal&&(this.modal.style.animation="tpm-fade-in 0.2s ease reverse",setTimeout(()=>{var e;(e=this.modal)==null||e.remove(),this.modal=null},200))}_loadContent(){const e=this.modal.querySelector("#tpm-iframe"),t=d(this.editor,this.options.customStyles),r=new Blob([t],{type:"text/html;charset=utf-8"}),o=URL.createObjectURL(r);e.src=o,e.onload=()=>URL.revokeObjectURL(o)}_switchDevice(e,t=!0){var m;this.currentDevice=e,this.isLandscape=!1;const r=n[e],o=this.modal.querySelector("#tpm-device-frame");if(this.modal.querySelector("#tpm-device-wrapper"),o.className=`tpm-device-frame ${e}`,t&&(o.classList.add("entering"),setTimeout(()=>o.classList.remove("entering"),400)),e==="desktop"){if(!o.querySelector(".tpm-desktop-bar")){const a=document.createElement("div");a.className="tpm-desktop-bar",a.innerHTML=`
417
+ <div class="tpm-desktop-dot" style="background:#ff5f57"></div>
418
+ <div class="tpm-desktop-dot" style="background:#febc2e"></div>
419
+ <div class="tpm-desktop-dot" style="background:#28c840"></div>
420
+ `,o.prepend(a)}}else(m=o.querySelector(".tpm-desktop-bar"))==null||m.remove();this._applyDimensions(r.width,r.height,r.scale),this._updateInfo(r),this.modal.querySelectorAll(".tpm-device-btn").forEach(a=>{a.classList.toggle("active",a.dataset.device===e)});const p=this.modal.querySelector("#tpm-rotate");p.style.display=e==="desktop"?"none":"flex"}_applyDimensions(e,t,r){const o=this.modal.querySelector("#tpm-device-frame"),p=this.modal.querySelector("#tpm-device-wrapper");o.style.width=e+"px",o.style.height=t+"px",p.style.transform=`scale(${r})`}_rotate(){const e=n[this.currentDevice];this.isLandscape=!this.isLandscape;const t=this.modal.querySelector("#tpm-device-wrapper");t.classList.add("rotating"),setTimeout(()=>t.classList.remove("rotating"),500),this.isLandscape?(this._applyDimensions(e.height,e.width,e.scale*.85),this._updateInfo({...e,width:e.height,height:e.width,label:e.label+" (横屏)"})):(this._applyDimensions(e.width,e.height,e.scale),this._updateInfo(e))}_updateInfo(e){const t=this.modal.querySelector("#tpm-device-frame"),r=parseInt(t.style.width),o=parseInt(t.style.height);this.modal.querySelector("#tpm-device-info").textContent=`${r} × ${o}`,this.modal.querySelector("#tpm-device-label").textContent=e.label+" 预览"}_setupEvents(){this.modal.querySelector("#tpm-close").addEventListener("click",()=>this.close()),this.modal.addEventListener("click",e=>{e.target===this.modal&&this.close()}),this._keyHandler=e=>{e.key==="Escape"&&this.close()},document.addEventListener("keydown",this._keyHandler),this.modal.querySelectorAll(".tpm-device-btn").forEach(e=>{e.addEventListener("click",()=>this._switchDevice(e.dataset.device))}),this.modal.querySelector("#tpm-rotate").addEventListener("click",()=>this._rotate())}destroy(){document.removeEventListener("keydown",this._keyHandler),this.close()}}function s(i,e={}){return i.addCommand("mceMultiPreview",function(){new l(i,e).open()}),i.ui.registry.addButton("multipreview",{tooltip:"多端预览",icon:"preview",onAction:()=>i.execCommand("mceMultiPreview")}),i.ui.registry.addMenuItem("multipreview",{text:"多端预览",icon:"preview",onAction:()=>i.execCommand("mceMultiPreview")}),{getMetadata:()=>({name:"Multi-Device Preview Plugin",url:"https://github.com/your-org/tinymce-plugin-multipreview"})}}typeof window<"u"&&(typeof window.tinymce<"u"&&window.tinymce.PluginManager.add(c,function(i){const e=i.getParam("multipreview",{});return s(i,e)}),window.TinyMCEPreviewPlugin={PreviewController:l,DEVICES:n,getFullHtml:d,initPlugin:s});typeof module<"u"&&module.exports&&(module.exports={PreviewController:l,DEVICES:n,getFullHtml:d,initPlugin:s,PLUGIN_NAME:c});exports.DEVICES=n;exports.PLUGIN_NAME=c;exports.PreviewController=l;exports.getFullHtml=d;exports.initPlugin=s;
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .tinymce-preview-wrapper[data-v-a0160936]{width:100%}.editor-container[data-v-a0160936]{border-radius:8px;overflow:hidden;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.1);box-shadow:0 1px 3px #0000001a}.preview-trigger[data-v-a0160936]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin-top:12px}.preview-btn[data-v-a0160936]{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:8px;padding:8px 20px;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;-webkit-transition:all .2s ease;transition:all .2s ease;-webkit-box-shadow:0 2px 8px rgba(102,126,234,.4);box-shadow:0 2px 8px #667eea66}.preview-btn svg[data-v-a0160936]{width:16px;height:16px}.preview-btn[data-v-a0160936]:hover{-webkit-transform:translateY(-1px);transform:translateY(-1px);-webkit-box-shadow:0 4px 16px rgba(102,126,234,.5);box-shadow:0 4px 16px #667eea80}.preview-btn[data-v-a0160936]:active{-webkit-transform:translateY(0);transform:translateY(0)}.tinymce-preview-wrapper[data-v-fda05aaa]{width:100%}.editor-container[data-v-fda05aaa]{border-radius:8px;overflow:hidden;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.1);box-shadow:0 1px 3px #0000001a}.preview-trigger[data-v-fda05aaa]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin-top:12px}.preview-btn[data-v-fda05aaa]{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:8px;padding:8px 20px;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;-webkit-transition:all .2s ease;transition:all .2s ease;-webkit-box-shadow:0 2px 8px rgba(102,126,234,.4);box-shadow:0 2px 8px #667eea66}.preview-btn svg[data-v-fda05aaa]{width:16px;height:16px}.preview-btn[data-v-fda05aaa]:hover{-webkit-transform:translateY(-1px);transform:translateY(-1px);-webkit-box-shadow:0 4px 16px rgba(102,126,234,.5);box-shadow:0 4px 16px #667eea80}
@@ -0,0 +1,11 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const d=require("@tinymce/tinymce-vue"),t=require("vue"),c=require("../_plugin-vue_export-helper-BHFhmbuH.cjs"),u={name:"TinymcePreviewEditor",components:{Editor:d},props:{modelValue:{type:String,default:""},disabled:{type:Boolean,default:!1},apiKey:{type:String,default:"no-api-key"},pluginPath:{type:String,default:"/tinymce/plugins/multipreview/plugin.js"},extraToolbar:{type:String,default:""},customPreviewStyles:{type:String,default:""},showExternalTrigger:{type:Boolean,default:!1},height:{type:[Number,String],default:500}},emits:["update:modelValue","change","editor-init"],data(){return{editorInstance:null,content:this.modelValue}},computed:{editorConfig(){return{height:this.height,menubar:"file edit view insert format tools table help",plugins:["advlist","autolink","lists","link","image","charmap","anchor","searchreplace","visualblocks","code","fullscreen","insertdatetime","media","table","wordcount","multipreview"],toolbar:["undo redo | blocks | bold italic underline strikethrough | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | link image media table | code fullscreen | multipreview",this.extraToolbar].filter(Boolean).join(" | "),multipreview:{customStyles:this.customPreviewStyles},external_plugins:{multipreview:this.pluginPath},content_style:`
2
+ body {
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4
+ font-size: 16px;
5
+ line-height: 1.6;
6
+ color: #374151;
7
+ max-width: 800px;
8
+ margin: 0 auto;
9
+ padding: 20px;
10
+ }
11
+ `,branding:!1,promotion:!1,setup:e=>{this.editorInstance=e}}}},watch:{modelValue(e){e!==this.content&&(this.content=e)},content(e){this.$emit("update:modelValue",e)}},methods:{onEditorInit(e,i){this.editorInstance=i,this.$emit("editor-init",i)},onContentChange(){this.$emit("change",this.content)},openPreview(){this.editorInstance&&this.editorInstance.execCommand("mceMultiPreview")},getContent(){var e;return((e=this.editorInstance)==null?void 0:e.getContent())??this.content},setContent(e){this.editorInstance&&this.editorInstance.setContent(e),this.content=e}}},m={class:"tinymce-preview-wrapper"},p={class:"editor-container"},g={key:0,class:"preview-trigger"};function h(e,i,r,f,l,n){const s=t.resolveComponent("editor");return t.openBlock(),t.createElementBlock("div",m,[t.createElementVNode("div",p,[t.createVNode(s,{modelValue:l.content,"onUpdate:modelValue":i[0]||(i[0]=o=>l.content=o),init:n.editorConfig,disabled:r.disabled,onInit:n.onEditorInit},null,8,["modelValue","init","disabled","onInit"])]),r.showExternalTrigger?(t.openBlock(),t.createElementBlock("div",g,[t.createElementVNode("button",{class:"preview-btn",onClick:i[1]||(i[1]=(...o)=>n.openPreview&&n.openPreview(...o))},[...i[2]||(i[2]=[t.createElementVNode("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t.createElementVNode("path",{d:"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"}),t.createElementVNode("circle",{cx:"12",cy:"12",r:"3"})],-1),t.createTextVNode(" 多端预览 ",-1)])])])):t.createCommentVNode("",!0)])}const a=c._export_sfc(u,[["render",h],["__scopeId","data-v-a0160936"]]);exports.TinymcePreviewEditor=a;exports.default=a;
@@ -0,0 +1,180 @@
1
+ import c from "@tinymce/tinymce-vue";
2
+ import { resolveComponent as u, openBlock as s, createElementBlock as a, createElementVNode as i, createVNode as m, createTextVNode as p, createCommentVNode as h } from "vue";
3
+ import { _ as g } from "../_plugin-vue_export-helper-CHgC5LLL.js";
4
+ const f = {
5
+ name: "TinymcePreviewEditor",
6
+ components: { Editor: c },
7
+ props: {
8
+ /** 编辑器内容 (v-model) */
9
+ modelValue: {
10
+ type: String,
11
+ default: ""
12
+ },
13
+ /** 是否禁用 */
14
+ disabled: {
15
+ type: Boolean,
16
+ default: !1
17
+ },
18
+ /** TinyMCE API Key(cloud 版需要) */
19
+ apiKey: {
20
+ type: String,
21
+ default: "no-api-key"
22
+ },
23
+ /** 插件脚本路径 */
24
+ pluginPath: {
25
+ type: String,
26
+ default: "/tinymce/plugins/multipreview/plugin.js"
27
+ },
28
+ /** 工具栏配置(额外) */
29
+ extraToolbar: {
30
+ type: String,
31
+ default: ""
32
+ },
33
+ /** 自定义预览样式 */
34
+ customPreviewStyles: {
35
+ type: String,
36
+ default: ""
37
+ },
38
+ /** 是否显示外部预览按钮 */
39
+ showExternalTrigger: {
40
+ type: Boolean,
41
+ default: !1
42
+ },
43
+ /** 编辑器高度 */
44
+ height: {
45
+ type: [Number, String],
46
+ default: 500
47
+ }
48
+ },
49
+ emits: ["update:modelValue", "change", "editor-init"],
50
+ data() {
51
+ return {
52
+ editorInstance: null,
53
+ content: this.modelValue
54
+ };
55
+ },
56
+ computed: {
57
+ editorConfig() {
58
+ return {
59
+ height: this.height,
60
+ menubar: "file edit view insert format tools table help",
61
+ plugins: [
62
+ "advlist",
63
+ "autolink",
64
+ "lists",
65
+ "link",
66
+ "image",
67
+ "charmap",
68
+ "anchor",
69
+ "searchreplace",
70
+ "visualblocks",
71
+ "code",
72
+ "fullscreen",
73
+ "insertdatetime",
74
+ "media",
75
+ "table",
76
+ "wordcount",
77
+ "multipreview"
78
+ ],
79
+ toolbar: [
80
+ "undo redo | blocks | bold italic underline strikethrough | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | link image media table | code fullscreen | multipreview",
81
+ this.extraToolbar
82
+ ].filter(Boolean).join(" | "),
83
+ multipreview: {
84
+ customStyles: this.customPreviewStyles
85
+ },
86
+ external_plugins: {
87
+ multipreview: this.pluginPath
88
+ },
89
+ content_style: `
90
+ body {
91
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
92
+ font-size: 16px;
93
+ line-height: 1.6;
94
+ color: #374151;
95
+ max-width: 800px;
96
+ margin: 0 auto;
97
+ padding: 20px;
98
+ }
99
+ `,
100
+ branding: !1,
101
+ promotion: !1,
102
+ setup: (e) => {
103
+ this.editorInstance = e;
104
+ }
105
+ };
106
+ }
107
+ },
108
+ watch: {
109
+ modelValue(e) {
110
+ e !== this.content && (this.content = e);
111
+ },
112
+ content(e) {
113
+ this.$emit("update:modelValue", e);
114
+ }
115
+ },
116
+ methods: {
117
+ onEditorInit(e, t) {
118
+ this.editorInstance = t, this.$emit("editor-init", t);
119
+ },
120
+ onContentChange() {
121
+ this.$emit("change", this.content);
122
+ },
123
+ /** 编程式打开预览 */
124
+ openPreview() {
125
+ this.editorInstance && this.editorInstance.execCommand("mceMultiPreview");
126
+ },
127
+ /** 获取编辑器内容 */
128
+ getContent() {
129
+ var e;
130
+ return ((e = this.editorInstance) == null ? void 0 : e.getContent()) ?? this.content;
131
+ },
132
+ /** 设置编辑器内容 */
133
+ setContent(e) {
134
+ this.editorInstance && this.editorInstance.setContent(e), this.content = e;
135
+ }
136
+ }
137
+ }, v = { class: "tinymce-preview-wrapper" }, w = { class: "editor-container" }, y = {
138
+ key: 0,
139
+ class: "preview-trigger"
140
+ };
141
+ function b(e, t, r, x, l, n) {
142
+ const d = u("editor");
143
+ return s(), a("div", v, [
144
+ i("div", w, [
145
+ m(d, {
146
+ modelValue: l.content,
147
+ "onUpdate:modelValue": t[0] || (t[0] = (o) => l.content = o),
148
+ init: n.editorConfig,
149
+ disabled: r.disabled,
150
+ onInit: n.onEditorInit
151
+ }, null, 8, ["modelValue", "init", "disabled", "onInit"])
152
+ ]),
153
+ r.showExternalTrigger ? (s(), a("div", y, [
154
+ i("button", {
155
+ class: "preview-btn",
156
+ onClick: t[1] || (t[1] = (...o) => n.openPreview && n.openPreview(...o))
157
+ }, [...t[2] || (t[2] = [
158
+ i("svg", {
159
+ viewBox: "0 0 24 24",
160
+ fill: "none",
161
+ stroke: "currentColor",
162
+ "stroke-width": "2"
163
+ }, [
164
+ i("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
165
+ i("circle", {
166
+ cx: "12",
167
+ cy: "12",
168
+ r: "3"
169
+ })
170
+ ], -1),
171
+ p(" 多端预览 ", -1)
172
+ ])])
173
+ ])) : h("", !0)
174
+ ]);
175
+ }
176
+ const C = /* @__PURE__ */ g(f, [["render", b], ["__scopeId", "data-v-a0160936"]]);
177
+ export {
178
+ C as TinymcePreviewEditor,
179
+ C as default
180
+ };
@@ -0,0 +1,7 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const t=require("vue"),v=require("@tinymce/tinymce-vue"),f=require("../_plugin-vue_export-helper-BHFhmbuH.cjs"),w={class:"tinymce-preview-wrapper"},h={class:"editor-container"},b={key:0,class:"preview-trigger"},y={__name:"TinymcePreviewEditor.vue3",props:{modelValue:{type:String,default:""},disabled:{type:Boolean,default:!1},pluginPath:{type:String,default:"/tinymce/plugins/multipreview/plugin.js"},customPreviewStyles:{type:String,default:""},showExternalTrigger:{type:Boolean,default:!1},height:{type:[Number,String],default:500}},emits:["update:modelValue","change","editor-init"],setup(u,{expose:c,emit:d}){const l=u,a=d,n=t.ref(null),i=t.ref(l.modelValue);t.watch(()=>l.modelValue,e=>{e!==i.value&&(i.value=e)}),t.watch(i,e=>{a("update:modelValue",e),a("change",e)});const m=t.computed(()=>({height:l.height,menubar:"file edit view insert format tools table help",plugins:["advlist","autolink","lists","link","image","charmap","anchor","searchreplace","visualblocks","code","fullscreen","insertdatetime","media","table","wordcount","multipreview"],toolbar:"undo redo | blocks | bold italic underline | forecolor backcolor | alignleft aligncenter alignright | bullist numlist | link image media table | code fullscreen | multipreview",multipreview:{customStyles:l.customPreviewStyles},external_plugins:{multipreview:l.pluginPath},content_style:`
2
+ body {
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4
+ font-size: 16px; line-height: 1.6; color: #374151;
5
+ max-width: 800px; margin: 0 auto; padding: 20px;
6
+ }
7
+ `,branding:!1,promotion:!1,setup:e=>{n.value=e}})),p=(e,o)=>{n.value=o,a("editor-init",o)},r=()=>{var e;return(e=n.value)==null?void 0:e.execCommand("mceMultiPreview")};return c({openPreview:r,getContent:()=>{var e;return((e=n.value)==null?void 0:e.getContent())??i.value},setContent:e=>{var o;(o=n.value)==null||o.setContent(e),i.value=e}}),(e,o)=>(t.openBlock(),t.createElementBlock("div",w,[t.createElementVNode("div",h,[t.createVNode(t.unref(v),{modelValue:i.value,"onUpdate:modelValue":o[0]||(o[0]=g=>i.value=g),init:m.value,disabled:l.disabled,onInit:p},null,8,["modelValue","init","disabled"])]),l.showExternalTrigger?(t.openBlock(),t.createElementBlock("div",b,[t.createElementVNode("button",{class:"preview-btn",onClick:r},[...o[1]||(o[1]=[t.createElementVNode("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t.createElementVNode("path",{d:"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"}),t.createElementVNode("circle",{cx:"12",cy:"12",r:"3"})],-1),t.createTextVNode(" 多端预览 ",-1)])])])):t.createCommentVNode("",!0)]))}},s=f._export_sfc(y,[["__scopeId","data-v-fda05aaa"]]);exports.TinymcePreviewEditor=s;exports.default=s;
@@ -0,0 +1,110 @@
1
+ import { ref as s, watch as d, computed as h, openBlock as u, createElementBlock as c, createElementVNode as n, createVNode as b, unref as y, createTextVNode as x, createCommentVNode as _ } from "vue";
2
+ import k from "@tinymce/tinymce-vue";
3
+ import { _ as V } from "../_plugin-vue_export-helper-CHgC5LLL.js";
4
+ const C = { class: "tinymce-preview-wrapper" }, S = { class: "editor-container" }, E = {
5
+ key: 0,
6
+ class: "preview-trigger"
7
+ }, P = {
8
+ __name: "TinymcePreviewEditor.vue3",
9
+ props: {
10
+ modelValue: { type: String, default: "" },
11
+ disabled: { type: Boolean, default: !1 },
12
+ pluginPath: { type: String, default: "/tinymce/plugins/multipreview/plugin.js" },
13
+ customPreviewStyles: { type: String, default: "" },
14
+ showExternalTrigger: { type: Boolean, default: !1 },
15
+ height: { type: [Number, String], default: 500 }
16
+ },
17
+ emits: ["update:modelValue", "change", "editor-init"],
18
+ setup(m, { expose: p, emit: g }) {
19
+ const o = m, a = g, l = s(null), i = s(o.modelValue);
20
+ d(() => o.modelValue, (e) => {
21
+ e !== i.value && (i.value = e);
22
+ }), d(i, (e) => {
23
+ a("update:modelValue", e), a("change", e);
24
+ });
25
+ const v = h(() => ({
26
+ height: o.height,
27
+ menubar: "file edit view insert format tools table help",
28
+ plugins: [
29
+ "advlist",
30
+ "autolink",
31
+ "lists",
32
+ "link",
33
+ "image",
34
+ "charmap",
35
+ "anchor",
36
+ "searchreplace",
37
+ "visualblocks",
38
+ "code",
39
+ "fullscreen",
40
+ "insertdatetime",
41
+ "media",
42
+ "table",
43
+ "wordcount",
44
+ "multipreview"
45
+ ],
46
+ toolbar: "undo redo | blocks | bold italic underline | forecolor backcolor | alignleft aligncenter alignright | bullist numlist | link image media table | code fullscreen | multipreview",
47
+ multipreview: { customStyles: o.customPreviewStyles },
48
+ external_plugins: { multipreview: o.pluginPath },
49
+ content_style: `
50
+ body {
51
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
52
+ font-size: 16px; line-height: 1.6; color: #374151;
53
+ max-width: 800px; margin: 0 auto; padding: 20px;
54
+ }
55
+ `,
56
+ branding: !1,
57
+ promotion: !1,
58
+ setup: (e) => {
59
+ l.value = e;
60
+ }
61
+ })), f = (e, t) => {
62
+ l.value = t, a("editor-init", t);
63
+ }, r = () => {
64
+ var e;
65
+ return (e = l.value) == null ? void 0 : e.execCommand("mceMultiPreview");
66
+ };
67
+ return p({ openPreview: r, getContent: () => {
68
+ var e;
69
+ return ((e = l.value) == null ? void 0 : e.getContent()) ?? i.value;
70
+ }, setContent: (e) => {
71
+ var t;
72
+ (t = l.value) == null || t.setContent(e), i.value = e;
73
+ } }), (e, t) => (u(), c("div", C, [
74
+ n("div", S, [
75
+ b(y(k), {
76
+ modelValue: i.value,
77
+ "onUpdate:modelValue": t[0] || (t[0] = (w) => i.value = w),
78
+ init: v.value,
79
+ disabled: o.disabled,
80
+ onInit: f
81
+ }, null, 8, ["modelValue", "init", "disabled"])
82
+ ]),
83
+ o.showExternalTrigger ? (u(), c("div", E, [
84
+ n("button", {
85
+ class: "preview-btn",
86
+ onClick: r
87
+ }, [...t[1] || (t[1] = [
88
+ n("svg", {
89
+ viewBox: "0 0 24 24",
90
+ fill: "none",
91
+ stroke: "currentColor",
92
+ "stroke-width": "2"
93
+ }, [
94
+ n("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
95
+ n("circle", {
96
+ cx: "12",
97
+ cy: "12",
98
+ r: "3"
99
+ })
100
+ ], -1),
101
+ x(" 多端预览 ", -1)
102
+ ])])
103
+ ])) : _("", !0)
104
+ ]));
105
+ }
106
+ }, z = /* @__PURE__ */ V(P, [["__scopeId", "data-v-fda05aaa"]]);
107
+ export {
108
+ z as TinymcePreviewEditor,
109
+ z as default
110
+ };
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "tinymce-plugin-multipreview",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "TinyMCE 多端预览插件,支持手机、平板、PC 三种视口预览",
5
+ "type": "module",
5
6
  "main": "dist/plugin.cjs.js",
6
7
  "module": "dist/plugin.esm.js",
7
8
  "unpkg": "dist/plugin.umd.js",
8
- "types": "dist/plugin.d.ts",
9
9
  "files": [
10
10
  "dist",
11
11
  "src",
@@ -13,10 +13,11 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "dev": "vite",
16
- "build": "vite build",
17
- "build:vue": "vue-tsc --noEmit && vite build --config vite.vue.config.js",
16
+ "build": "vite build && vite build --config vite.vue.config.js",
17
+ "build:plugin": "vite build",
18
+ "build:vue": "vite build --config vite.vue.config.js",
18
19
  "preview": "vite preview",
19
- "release": "npm run build && npm run build:vue && npm publish"
20
+ "release": "npm run build && npm publish"
20
21
  },
21
22
  "keywords": [
22
23
  "tinymce",
@@ -39,11 +40,10 @@
39
40
  "@tinymce/tinymce-vue": "^3 || ^4 || ^5"
40
41
  },
41
42
  "devDependencies": {
43
+ "@tinymce/tinymce-vue": "^5.0.0",
42
44
  "@vitejs/plugin-vue": "^4.0.0",
43
45
  "tinymce": "^6.0.0",
44
- "typescript": "^5.0.0",
45
46
  "vite": "^5.0.0",
46
- "vue": "^3.3.0",
47
- "vue-tsc": "^1.8.0"
47
+ "vue": "^3.3.0"
48
48
  }
49
49
  }
package/src/plugin.d.ts DELETED
@@ -1,46 +0,0 @@
1
- // TypeScript 类型定义
2
- export declare const PLUGIN_NAME: string;
3
-
4
- export interface DeviceConfig {
5
- label: string;
6
- icon: string;
7
- width: number;
8
- height: number;
9
- scale: number;
10
- }
11
-
12
- export interface Devices {
13
- mobile: DeviceConfig;
14
- tablet: DeviceConfig;
15
- desktop: DeviceConfig;
16
- }
17
-
18
- export interface PreviewControllerOptions {
19
- customStyles?: string;
20
- }
21
-
22
- export declare class PreviewController {
23
- constructor(editor: any, options?: PreviewControllerOptions);
24
- open(): void;
25
- close(): void;
26
- destroy(): void;
27
- }
28
-
29
- export declare function getFullHtml(editor: any, customStyles?: string): string;
30
-
31
- export declare function initPlugin(editor: any, options?: PreviewControllerOptions): {
32
- getMetadata: () => {
33
- name: string;
34
- url: string;
35
- };
36
- };
37
-
38
- export declare const DEVICES: Devices;
39
-
40
- export default {
41
- PreviewController,
42
- DEVICES,
43
- getFullHtml,
44
- initPlugin,
45
- PLUGIN_NAME
46
- };
File without changes