feat: A20 phase 3 LOT 1 — shell + library migres, harnais csp_preview vert (v7.38.0)
FlowDeck CI / lint (push) Successful in 1m56s
FlowDeck CI / test (push) Successful in 15m32s
FlowDeck CI / docker (push) Successful in 1m51s

Ajout :
- e2e/csp_preview.spec.js — apercu CSP strict SANS deployer : le build
  officiel @alpinejs/csp (e2e/fixtures/alpine.csp.js, 0 eval) est servi a
  la place de alpine.min.js par interception Playwright ; toute expression
  que le parseur maison ne digere pas = pageerror (filet). Premiere
  surface VERTE : library (composant lie, icones SVG via Alpine.effect,
  recherche ouverte + focalisee, 0 erreur).

Changed :
- 16 composants x-data="fn()" enregistres via Alpine.data (registre =
  seule resolution du build CSP, probe « Undefined variable » ;
  scripts classiques executes pendant le parsing => alpine:init toujours
  joint) : appState, libraryPage, workspacesPage, editorState, board x4,
  settings/import/table_view/team_load/trash/workspace/welcome/accounts/
  card_detail.
- base.html (shell) migre : x-effect document.* -> syncSidebarClass(),
  $nextTick(arrow) -> initSidebarSort(), window.FlowDeck.* ->
  fdCreatePage/fdCreateFolder/fdGwRefresh, Object.keys/Math.min/
  window.innerWidth dans x-for et :style -> sidebarSections()/
  sectionMenuPos() — tout = simple appel de methode.
- x-html restants du shell -> x-init + Alpine.effect : icone agent,
  carte projet, library x3 ; recherche library -> toggleSearch()
  (Alpine.nextTick) ; openMoveSelected() pour Object.keys en expression.
- eslint : 70 warnings -> 0/0 (globals getCsrf depuis A38 ph1,
  /* exported openCardDetail */ + /* global owner, repo */, 3 ;; residuels).

suite **1093/1093** · ruff OK · eslint 0/0 · E2E 3/3 (csp_preview + smoke x2)
· docs a jour (ROADMAP ph3 LOT 1, CHANGELOG, WORKLOAD, VERSION)
This commit is contained in:
2026-10-02 12:08:18 -04:00
parent 840d2b2615
commit 6ff88237fc
26 changed files with 365 additions and 30 deletions
+28 -2
View File
@@ -1,5 +1,11 @@
/* exported libraryPage -- appeles depuis les attributs HTML des templates */
// A20 phase 3 : enregistrement Alpine.data — le build CSP ne résout que le
// registre (probe : globale window → « Undefined variable: libraryPage »).
document.addEventListener('alpine:init', function () {
Alpine.data('libraryPage', libraryPage);
});
function libraryPage() {
return {
tab: 'recents',
@@ -160,7 +166,27 @@ function libraryPage() {
_escHtml(s) { var d=document.createElement('div'); d.textContent = s||''; return d.innerHTML; },
_escAttr(s) { return String(s||'').replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); },
_renderIcon(item) {
openMoveSelected() {
this.openMovePicker(Object.keys(this.selected).map(Number));
},
// ── A20 phase 3 (surface library) : formes compatibles build CSP ──
// $nextTick / x-html ne passent pas par l'évaluateur maison du build CSP
// (arrow inline = parse error ; x-html = interdit) → méthodes JS réelles,
// réactivité via Alpine.effect — valable sous les deux builds.
toggleSearch() {
this.searchOpen = !this.searchOpen;
if (this.searchOpen) Alpine.nextTick(() => {
var el = document.getElementById('lib-search-input');
if (el) el.focus();
});
},
bindHtmlIcon(el) {
Alpine.effect(() => { el.innerHTML = getSvgIcon(this.emptyIcon, 48); });
},
bindHtmlItem(el, item) {
Alpine.effect(() => { el.innerHTML = this._renderIcon(item); });
},
_renderIcon(item) {
if (!item) return getSvgIcon('file', 14);
// Custom emoji (image URL), icon name, or unicode emoji
if (item.page_icon) return '<span style="font-size:14px;line-height:1;display:inline-flex;align-items:center;">' + (window.fdIconHtml ? window.fdIconHtml(item.page_icon, 14) : this._escHtml(item.page_icon)) + '</span>';
@@ -363,7 +389,7 @@ function libraryPage() {
},
_getCsrf() {
var m = getCsrf();;
var m = getCsrf();
return m;
},