fix: éditeur Forge — espace checkbox/texte aperçu, flash du preview, menus table/langue coupés #BUG-115
CI / lint (push) Successful in 3m11s
CI / security (push) Successful in 2m0s
CI / test (push) Successful in 5m16s
CI / build (push) Successful in 1m49s
CI / e2e (push) Successful in 17m47s

This commit is contained in:
2026-10-14 21:56:37 -04:00
parent cbe3d9bc75
commit 1892062d88
11 changed files with 58 additions and 24 deletions
+24 -9
View File
@@ -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);