fix: éditeur Forge — espace checkbox/texte aperçu, flash du preview, menus table/langue coupés #BUG-115
This commit is contained in:
@@ -88,6 +88,8 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
.preview-content p { margin: 6px 0; }
|
||||
.preview-content ul, .preview-content ol { margin: 6px 0; padding-left: 24px; }
|
||||
.preview-content li { margin: 2px 0; }
|
||||
.preview-content .task-list-item { list-style: none; margin-left: -1.2em; }
|
||||
.preview-content .task-list-item input[type="checkbox"] { margin-right: 6px; }
|
||||
.preview-content code { background: var(--bg3); padding: 1px 5px; border-radius: 3px; font-family: var(--mono); font-size: 12px; color: var(--ai); }
|
||||
.preview-content pre { background: var(--bg3); padding: 12px 16px; border-radius: var(--R); overflow-x: auto; margin: 10px 0; border: 1px solid var(--border); }
|
||||
.preview-content pre code { background: none; padding: 0; color: var(--text); }
|
||||
@@ -134,7 +136,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
.qi-item:hover { background: var(--bg-hover); color: var(--text); }
|
||||
|
||||
/* Inline form (table dims, code lang) */
|
||||
.inline-form { position: absolute; z-index: 195; background: var(--bg4); border: 1px solid var(--border2); border-radius: var(--R); box-shadow: var(--shadow); display: none; padding: 8px 10px; min-width: 200px; }
|
||||
.inline-form { position: fixed; z-index: 195; background: var(--bg4); border: 1px solid var(--border2); border-radius: var(--R); box-shadow: var(--shadow); display: none; padding: 8px 10px; min-width: 200px; }
|
||||
.inline-form.on { display: block; animation: fadeIn 100ms; }
|
||||
.inline-form-label { font-size: 10px; font-weight: 600; color: var(--text3); text-transform: uppercase; letter-spacing: 0.4px; margin-bottom: 6px; }
|
||||
.inline-form-row { display: flex; gap: 6px; align-items: center; margin-bottom: 5px; }
|
||||
@@ -148,7 +150,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
.inline-form-actions button.ok:hover { background: #5a8fc5; }
|
||||
|
||||
/* Lang picker (in QI menu style) */
|
||||
.lang-picker { position: absolute; z-index: 195; width: 200px; max-height: 320px; overflow-y: auto; background: var(--bg4); border: 1px solid var(--border2); border-radius: var(--R); box-shadow: var(--shadow); display: none; padding: 3px; }
|
||||
.lang-picker { position: fixed; z-index: 195; width: 200px; max-height: 320px; overflow-y: auto; background: var(--bg4); border: 1px solid var(--border2); border-radius: var(--R); box-shadow: var(--shadow); display: none; padding: 3px; }
|
||||
.lang-picker.on { display: block; animation: fadeIn 100ms; }
|
||||
.lang-item { display: flex; align-items: center; gap: 7px; padding: 6px 9px; font-size: 12px; color: var(--text2); cursor: pointer; border-radius: var(--r); font-family: var(--mono); }
|
||||
.lang-item:hover, .lang-item.sel { background: var(--bg-hover); color: var(--text); }
|
||||
@@ -764,6 +766,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
// ---- Markdown Preview (server-side rendering via ObsiGate API) ----
|
||||
var previewTimer = null;
|
||||
var _previewLoading = false;
|
||||
var _previewDirty = false;
|
||||
function schedulePreview() {
|
||||
if (!previewVisible) return;
|
||||
clearTimeout(previewTimer);
|
||||
@@ -772,7 +775,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
|
||||
function renderPreview() {
|
||||
if (!previewVisible) return;
|
||||
if (_previewLoading) return;
|
||||
if (_previewLoading) { _previewDirty = true; return; }
|
||||
// Demo mode: fallback to client-side marked
|
||||
if (!fileVault || !filePath) {
|
||||
try {
|
||||
@@ -784,7 +787,11 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
}
|
||||
// Save first, then fetch server-rendered HTML
|
||||
_previewLoading = true;
|
||||
previewContent.innerHTML = '<div class="preview-loading"><span class="spin"></span> Rendering...</div>';
|
||||
// Ne jamais effacer le rendu affiché : un update garde l'ancien contenu
|
||||
// jusqu'à l'arrivée du nouveau (sinon l'aperçu flashe à chaque frappe).
|
||||
if (!previewContent.firstChild) {
|
||||
previewContent.innerHTML = '<div class="preview-loading"><span class="spin"></span> Rendering...</div>';
|
||||
}
|
||||
var content = val();
|
||||
fetch('/api/file/' + encodeURIComponent(fileVault) + '/save?path=' + encodeURIComponent(filePath) + '&backup=false', {
|
||||
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
||||
@@ -799,6 +806,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
previewContent.innerHTML = data.html || '<div class="preview-loading">Empty document</div>';
|
||||
processPreviewBlocks();
|
||||
_previewLoading = false;
|
||||
if (_previewDirty) { _previewDirty = false; schedulePreview(); }
|
||||
}).catch(function(e) {
|
||||
previewContent.innerHTML = '<div class="preview-loading">Erreur de rendu</div>';
|
||||
_previewLoading = false;
|
||||
@@ -1082,8 +1090,11 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
ifCols.value = '3'; ifRows.value = '3';
|
||||
var pt = caretToPixel(null);
|
||||
var wr = editorWrap.getBoundingClientRect();
|
||||
inlineForm.style.left = (wr.left + pt.x) + 'px';
|
||||
inlineForm.style.top = (wr.top + pt.y + 8) + 'px';
|
||||
var _ftop = wr.top + pt.y + 8, _fleft = wr.left + pt.x;
|
||||
if (_ftop + 130 > window.innerHeight) _ftop = window.innerHeight - 140;
|
||||
if (_fleft + 210 > window.innerWidth) _fleft = window.innerWidth - 220;
|
||||
inlineForm.style.left = _fleft + 'px';
|
||||
inlineForm.style.top = _ftop + 'px';
|
||||
inlineForm.classList.add('on');
|
||||
ifCols.focus(); ifCols.select();
|
||||
}
|
||||
@@ -1130,8 +1141,11 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
langPicker.innerHTML = html;
|
||||
var pt = caretToPixel(null);
|
||||
var wr = editorWrap.getBoundingClientRect();
|
||||
langPicker.style.left = (wr.left + pt.x) + 'px';
|
||||
langPicker.style.top = (wr.top + pt.y + 8) + 'px';
|
||||
var _ltop = wr.top + pt.y + 8, _lleft = wr.left + pt.x;
|
||||
if (_ltop + 320 > window.innerHeight) _ltop = window.innerHeight - 330;
|
||||
if (_lleft + 200 > window.innerWidth) _lleft = window.innerWidth - 210;
|
||||
langPicker.style.left = _lleft + 'px';
|
||||
langPicker.style.top = _ltop + 'px';
|
||||
langPicker.classList.add('on');
|
||||
// Click handler
|
||||
langPicker.querySelectorAll('.lang-item').forEach(function(item) {
|
||||
@@ -1428,7 +1442,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
slashSearch.addEventListener('input', function() { filterSlash(slashSearch.value); });
|
||||
slashMenu.addEventListener('click', function(e) {
|
||||
var item = e.target.closest('.slash-item');
|
||||
if (item) selectSlashAction(item.getAttribute('data-action'));
|
||||
if (item) { e.stopPropagation(); selectSlashAction(item.getAttribute('data-action')); }
|
||||
});
|
||||
function navSlash(dir) {
|
||||
var vis = getVisibleSlash(); if (vis.length === 0) return;
|
||||
@@ -1452,6 +1466,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
|
||||
document.getElementById('qi-btn').addEventListener('click', function(e) { e.stopPropagation(); toggleQI(); });
|
||||
qiMenu.addEventListener('click', function(e) {
|
||||
var item = e.target.closest('.qi-item'); if (!item) return;
|
||||
e.stopPropagation();
|
||||
var action = item.getAttribute('data-qi');
|
||||
qiMenu.classList.remove('on');
|
||||
selectSlashAction(action);
|
||||
|
||||
Reference in New Issue
Block a user