| @@ -16,9 +16,9 @@ | ||
| 16 | 16 | <div class="metasync-editor-header"> |
| 17 | 17 | <div class="metasync-editor-header-left"> |
| 18 | 18 | <a href="<?php echo esc_url(admin_url('edit.php?post_type=page')); ?>" class="metasync-back-button"> |
| 19 | 19 | <span class="dashicons dashicons-arrow-left-alt2"></span> |
| 20 | - <?php _e('Back to Pages', 'metasync'); ?> | |
| 20 | + <?php esc_html_e('Back to Pages', 'metasync'); ?> | |
| 21 | 21 | </a> |
| 22 | 22 | <div class="metasync-page-title"> |
| 23 | 23 | <strong><?php echo esc_html($post->post_title); ?></strong> |
| 24 | 24 | <span class="metasync-editor-badge">⚡ <?php echo esc_html($label); ?></span> |
| @@ -27,9 +27,9 @@ | ||
| 27 | 27 | |
| 28 | 28 | <div class="metasync-editor-header-center"> |
| 29 | 29 | <div class="metasync-editor-status"> |
| 30 | 30 | <span class="metasync-status-indicator"></span> |
| 31 | - <span class="metasync-status-text"><?php _e('Ready', 'metasync'); ?></span> | |
| 31 | + <span class="metasync-status-text"><?php esc_html_e('Ready', 'metasync'); ?></span> | |
| 32 | 32 | </div> |
| 33 | 33 | </div> |
| 34 | 34 | |
| 35 | 35 | <div class="metasync-editor-header-right"> |
| @@ -34,26 +34,122 @@ | ||
| 34 | 34 | |
| 35 | 35 | <div class="metasync-editor-header-right"> |
| 36 | 36 | <button type="button" class="button metasync-preview-button" title="<?php esc_attr_e('Preview', 'metasync'); ?>"> |
| 37 | 37 | <span class="dashicons dashicons-visibility"></span> |
| 38 | - <?php _e('Preview', 'metasync'); ?> | |
| 38 | + <?php esc_html_e('Preview', 'metasync'); ?> | |
| 39 | 39 | </button> |
| 40 | 40 | <button type="button" class="button button-primary metasync-save-button" title="<?php esc_attr_e('Save Changes', 'metasync'); ?>"> |
| 41 | 41 | <span class="dashicons dashicons-saved"></span> |
| 42 | - <?php _e('Save', 'metasync'); ?> | |
| 42 | + <?php esc_html_e('Save', 'metasync'); ?> | |
| 43 | 43 | </button> |
| 44 | 44 | </div> |
| 45 | 45 | </div> |
| 46 | 46 | |
| 47 | 47 | <!-- Editor Container --> |
| 48 | - <div class="metasync-editor-container" id="metasync-gjs-editor"> | |
| 49 | - <!-- GrapesJS will initialize here --> | |
| 48 | + <div class="metasync-editor-workspace"> | |
| 49 | + <div class="metasync-editor-container" id="metasync-gjs-editor"> | |
| 50 | + <!-- GrapesJS will initialize here --> | |
| 51 | + </div> | |
| 52 | + | |
| 53 | + <!-- | |
| 54 | + Sidebar for the editor's own managers. GrapesJS empties its | |
| 55 | + container element when it initializes, so these mount points live | |
| 56 | + outside it and are handed to the editor as `appendTo` targets. Each | |
| 57 | + manager renders itself into its own mount, which keeps the panels in | |
| 58 | + the DOM instead of relying on timed manual re-parenting. | |
| 59 | + --> | |
| 60 | + <div class="metasync-editor-sidebar" id="metasync-editor-sidebar"> | |
| 61 | + <div class="metasync-selected-element" id="metasync-selected-element" hidden> | |
| 62 | + <div class="metasync-selected-element-label"><?php esc_html_e('Editing Element', 'metasync'); ?></div> | |
| 63 | + <div class="metasync-selected-element-name" id="metasync-element-name"></div> | |
| 64 | + </div> | |
| 65 | + | |
| 66 | + <div class="metasync-editor-panel-switcher" id="metasync-editor-panel-switcher"></div> | |
| 67 | + | |
| 68 | + <div class="metasync-editor-panel" id="metasync-panel-styles" data-metasync-panel="styles"> | |
| 69 | + <div class="metasync-editor-selectors" id="metasync-editor-selectors"></div> | |
| 70 | + <div class="metasync-editor-styles" id="metasync-editor-styles"></div> | |
| 71 | + </div> | |
| 72 | + <div class="metasync-editor-panel" id="metasync-panel-traits" data-metasync-panel="traits" hidden> | |
| 73 | + <div class="metasync-editor-traits" id="metasync-editor-traits"></div> | |
| 74 | + </div> | |
| 75 | + <div class="metasync-editor-panel" id="metasync-panel-layers" data-metasync-panel="layers" hidden> | |
| 76 | + <div class="metasync-editor-layers" id="metasync-editor-layers"></div> | |
| 77 | + </div> | |
| 78 | + <div class="metasync-editor-panel" id="metasync-panel-blocks" data-metasync-panel="blocks" hidden> | |
| 79 | + <div class="metasync-editor-blocks" id="metasync-editor-blocks"></div> | |
| 80 | + </div> | |
| 81 | + </div> | |
| 50 | 82 | </div> |
| 51 | 83 | |
| 84 | + <!-- | |
| 85 | + Transient action feedback (upload/save failures, expired session). | |
| 86 | + Written by the editor script; the live region lets assistive | |
| 87 | + technology announce it without blocking interaction the way | |
| 88 | + alert() does. | |
| 89 | + --> | |
| 90 | + <div class="metasync-editor-notice" id="metasync-editor-notice" role="alert" aria-live="assertive" hidden></div> | |
| 91 | + | |
| 92 | + <!-- | |
| 93 | + Shown only when an editor dependency fails to load or the canvas fails | |
| 94 | + to start, so the editor explains itself instead of rendering blank. | |
| 95 | + --> | |
| 96 | + <div class="metasync-load-failure" id="metasync-editor-load-failure" role="alert" aria-live="assertive" hidden> | |
| 97 | + <div class="metasync-load-failure-inner"> | |
| 98 | + <span class="dashicons dashicons-warning metasync-load-failure-icon" aria-hidden="true"></span> | |
| 99 | + <h2 class="metasync-load-failure-title"></h2> | |
| 100 | + <p class="metasync-load-failure-message"></p> | |
| 101 | + <p class="metasync-load-failure-detail"></p> | |
| 102 | + <div class="metasync-load-failure-actions"> | |
| 103 | + <button type="button" class="button button-primary metasync-load-failure-reload"></button> | |
| 104 | + <button type="button" class="button metasync-load-failure-dismiss" style="display:none;"></button> | |
| 105 | + <a class="button" href="<?php echo esc_url(admin_url('edit.php?post_type=page')); ?>"><?php esc_html_e('Back to Pages', 'metasync'); ?></a> | |
| 106 | + </div> | |
| 107 | + </div> | |
| 108 | + </div> | |
| 109 | + | |
| 52 | 110 | <!-- Hidden data --> |
| 53 | 111 | <textarea id="metasync-html-content" style="display:none;"><?php echo esc_textarea($html_content); ?></textarea> |
| 54 | 112 | </div> |
| 55 | 113 | |
| 114 | +<script> | |
| 115 | +/* | |
| 116 | + * Record editor assets that fail to load. Runs before the footer scripts so a | |
| 117 | + * blocked or missing dependency can be named in the on-page diagnostic rather | |
| 118 | + * than surfacing as an empty canvas. Only records names; shows nothing itself. | |
| 119 | + */ | |
| 120 | +(function() { | |
| 121 | + 'use strict'; | |
| 122 | + | |
| 123 | + window.metasyncEditorAssets = window.metasyncEditorAssets || { failed: [] }; | |
| 124 | + | |
| 125 | + document.addEventListener('error', function(event) { | |
| 126 | + var el = event.target; | |
| 127 | + if (!el || !el.tagName) { | |
| 128 | + return; | |
| 129 | + } | |
| 130 | + | |
| 131 | + var src = el.tagName === 'SCRIPT' ? el.src : (el.tagName === 'LINK' ? el.href : ''); | |
| 132 | + if (!src) { | |
| 133 | + return; | |
| 134 | + } | |
| 135 | + | |
| 136 | + var name = ''; | |
| 137 | + if (src.indexOf('grapesjs-blocks-basic') !== -1) { | |
| 138 | + name = 'grapesjs-blocks-basic'; | |
| 139 | + } else if (src.indexOf('grapes.min') !== -1) { | |
| 140 | + name = 'grapesjs'; | |
| 141 | + } else { | |
| 142 | + return; | |
| 143 | + } | |
| 144 | + | |
| 145 | + if (window.metasyncEditorAssets.failed.indexOf(name) === -1) { | |
| 146 | + window.metasyncEditorAssets.failed.push(name); | |
| 147 | + } | |
| 148 | + }, true); | |
| 149 | +})(); | |
| 150 | +</script> | |
| 151 | + | |
| 56 | 152 | <style> |
| 57 | 153 | /* Inline critical styles to prevent flash */ |
| 58 | 154 | .metasync-html-editor-wrapper { |
| 59 | 155 | position: fixed; |
| @@ -72,6 +168,72 @@ | ||
| 72 | 168 | } |
| 73 | 169 | |
| 74 | 170 | #wpadminbar { |
| 75 | 171 | z-index: 100000; |
| 172 | +} | |
| 173 | + | |
| 174 | +/* Dependency / start-up failure notice. Hidden until the editor reports one. */ | |
| 175 | +.metasync-load-failure[hidden] { | |
| 176 | + display: none; | |
| 177 | +} | |
| 178 | + | |
| 179 | +.metasync-load-failure { | |
| 180 | + position: absolute; | |
| 181 | + top: 56px; | |
| 182 | + left: 0; | |
| 183 | + right: 0; | |
| 184 | + display: flex; | |
| 185 | + justify-content: center; | |
| 186 | + padding: 24px; | |
| 187 | + z-index: 100001; | |
| 188 | +} | |
| 189 | + | |
| 190 | +.metasync-load-failure-fatal { | |
| 191 | + top: 0; | |
| 192 | + bottom: 0; | |
| 193 | + align-items: center; | |
| 194 | + background: #1e1e1e; | |
| 195 | +} | |
| 196 | + | |
| 197 | +.metasync-load-failure-inner { | |
| 198 | + max-width: 560px; | |
| 199 | + padding: 24px; | |
| 200 | + border: 1px solid #3c434a; | |
| 201 | + border-radius: 6px; | |
| 202 | + background: #23282d; | |
| 203 | + color: #f0f0f1; | |
| 204 | + text-align: center; | |
| 205 | +} | |
| 206 | + | |
| 207 | +.metasync-load-failure-icon { | |
| 208 | + font-size: 32px; | |
| 209 | + width: 32px; | |
| 210 | + height: 32px; | |
| 211 | + color: #dba617; | |
| 212 | +} | |
| 213 | + | |
| 214 | +.metasync-load-failure-title { | |
| 215 | + margin: 12px 0 8px; | |
| 216 | + font-size: 18px; | |
| 217 | + color: #f0f0f1; | |
| 218 | +} | |
| 219 | + | |
| 220 | +.metasync-load-failure-message { | |
| 221 | + margin: 0 0 8px; | |
| 222 | + line-height: 1.6; | |
| 223 | + color: #c3c4c7; | |
| 224 | +} | |
| 225 | + | |
| 226 | +.metasync-load-failure-detail { | |
| 227 | + margin: 0 0 16px; | |
| 228 | + font-family: Menlo, Consolas, monospace; | |
| 229 | + font-size: 12px; | |
| 230 | + color: #a7aaad; | |
| 231 | +} | |
| 232 | + | |
| 233 | +.metasync-load-failure-actions { | |
| 234 | + display: flex; | |
| 235 | + gap: 8px; | |
| 236 | + justify-content: center; | |
| 237 | + flex-wrap: wrap; | |
| 76 | 238 | } |
| 77 | 239 | </style> |