geoproject-app/ARCHITECTURE.md
sarahgarcin1 ebe6bb6401
All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 22s
ajout de la page formulaire
2026-07-14 19:32:39 +02:00

11 KiB
Raw Permalink Blame History

ARCHITECTURE MAP (Claude-oriented, dense)

Maintained by Claude for Claude. Goal: reconstruct the mental model of /src fast. Keep terse. Rule: update this file in the SAME commit as any structural change (file added/removed/renamed/moved, or a responsibility shift). A Stop hook (.claude/hooks/check-architecture-note.sh) reminds Claude Code sessions. No line counts here — they drift; describe structure & responsibility only. Legend: calls/depends-on · Pinia store · mutates customCss · flag/gotcha · dead code (no live importer) Status: composables/stores/utils mapped. components/ = partial (only flags + tree); fill on demand.

DATA FLOW (the whole app in 8 lines)

  1. Kirby (PHP templates) renders narrative HTML into a hidden #content-source div + injects the Vue SPA.
  2. App.vue boots: loads narrative (⊙narrative) + stylesheet (⊙stylesheet), mounts double-buffered preview iframes.
  3. usePreviewRenderer builds each iframe srcdoc = fontFaceCss + base css + customCss + PagedJS polyfill + #content-source innerHTML.
  4. User hovers/clicks inside iframe → useIframeInteractions (listeners attached on iframe load) routes target → the right popup.
  5. Popups (Element/Image/Carte) + Settings panels edit CSS via ⊙stylesheet write API → customCss changes.
  6. watch(stylesheet.content) in usePreviewRenderer re-renders the hidden frame + crossfades. Scroll % preserved.
  7. Settings↔Popup share page-level defaults via ⊙textDefaults/⊙imageDefaults/⊙carteDefaults (read when a toggle is OFF).
  8. Save: ⊙stylesheet.saveCustomCss() / ⊙narrative content save → Kirby REST (CSRF via utils/kirby-auth).

Central hub = ⊙stylesheet (everything CSS funnels through it). Selection/visual state lives in useIframeInteractions.

stores/ (all shared global state; Pinia setup-stores)

  • stylesheet.js ⊙ — THE CSS hub. State: baseCss, customCss, fontFaceCss, isEditing, isDirty/isSaving, narrativeId. content=computed(fontFace+base+custom). Read API: extractBlock(sel) [custom→base], extractCustomBlock, extractValue(sel,prop,includeCommented=true)[numeric|string|commented], extractRawValue(sel,prop)[string], extractNumericValue(sel,prop,units), extractSpacing(sel,prop,units)[{simple}|{detailed}]. Write API ✎: updateProperty(sel,prop,val,unit), updateStyle(sel,prop,preformattedVal), removeProperty(sel,prop), replaceBlock(old,new), replaceInCustomCss(s,r), setCustomCss, comment/uncommentCssBlock, getBlockState. createRule = internal helper of updateStyle. Also: applyDefaultsCss() seeds defaults from utils/defaults; loadStylesheet/loadBaseCss; initializeFromNarrative; saveCustomCss→Kirby. ⚠ extractValue vs extractRawValue: DIFFERENT semantics, same-ish name. updateProperty(val,unit separate) vs updateStyle(pre-formatted) — both exist.
  • narrative.js ⊙ — narrative data (content tree + nav), content-visibility dirty state, save content to Kirby.
  • textDefaults.js / imageDefaults.js / carteDefaults.js ⊙ — page-level defaults bridge. Each: refs (textDefaults: fontFamily/fontSize/lineHeight/color; image|carte: width/height) + _initialized. Settings write, Popup reads when toggle OFF. (Were singleton composables → converted to Pinia.)

utils/ (pure, no reactivity unless noted)

  • defaults.js — frozen constants: PAGE/TEXT/ELEMENT/HEADING/PARAGRAPH_CLASS/IMAGE/CARTE/INLINE_DEFAULTS. Single source of default values.
  • css-parsing.js — extractCssBlock, extractCssValue, updateCssValue, parseValueWithUnit (default export obj) + named parseCssProperties(block)→{prop:val}.
  • css-comments.js — comment/uncomment/extract commented CSS blocks (inheritance toggling). Used by ⊙stylesheet.
  • unit-conversion.js — convertUnit(v,from,to) for px/mm/rem/em.
  • element-settings-descriptors.js — SETTING_GROUPS, SPECIAL_GROUPS(['font','fontSize','lineHeight','color']), INDEPENDENT_TAGS(set), createStyleProps(refs), createUnitProps(refs). Feeds the element settings engine.
  • debounce.js — createDebouncer(delay)→{debouncedUpdate(cb)}.
  • popup-position.js — createPopupPositioner(w,h)→{calculatePosition(event)} (viewport-clamped).
  • coloris.js — initColoris(overrides) — color-picker (@melloware/coloris) global init/config.
  • kirby-auth.js — getCsrfToken().

composables/ (real Vue composables only)

Element settings (3-file unit, refactored — orchestrator/engine/model)

  • useElementSettings.js — ORCHESTRATOR for ElementPopup. Owns selector/currentTag refs, per-selector persistence (elementStates Map), toggle cache, open/close lifecycle (open→initTogglesAndCache/applyTagDefaults/applyStoredValues), all watchers. guard={active,deactivateNextTick()} suppresses watcher feedback during self-writes. Exposes flat API consumed by ElementPopup. → useElementStyleState, useElementCss, ⊙stylesheet, ⊙textDefaults, useProjectFonts, createDebouncer.
  • useElementCss.js ✎ — ENGINE: the only store↔refs↔preview boundary. writer(updateProp/removeProps/applyGroup/removeSpecialGroupProps), display computeds(displayedCss/editableFullCss/elementCss + hasInCss/isInline/isIndependent), sync(syncRefsFromCss→applyParsedGroupToRefs/enableGroupFromParsed/resetSpecialGroupToInherited, handleCssInput→writeCssBlock), loader(loadValuesFromStylesheet). helpers: parseLength/cssString.
  • useElementStyleState.js — reactive MODEL: one ref per CSS prop + settingEnabled + settingCache + styleProps/unitProps (from descriptors). No logic.

Preview / iframe / interaction

  • useIframeInteractions.js — hover labels + click routing inside iframe → element/image/carte popups. getSelectorFromElement, getContentElement, getImageFigure, getCarteFigure, handleIframeMouseMove/Click, popup-close handlers. (Page-template dead code removed → archive/page-template-styling/.)
  • usePreviewRenderer.js — double-buffered iframe render w/ crossfade + scroll-% persistence. Builds srcdoc. watches ⊙stylesheet.content & ⊙narrative.data → renderPreview. setKeyboardShortcutHandler. Forwards Space key from iframe to parent for pan mode. → Coloris.close on iframe click.
  • usePreviewControls.js — zoom (scale + --preview-h CSS var) + horizontal pan (Space+drag, overlay fixe, ±50% limit) + auto-centering. Wheel zoom proportional (deltaY×0.001). Replaces former ZoomControls logic and @panzoom/panzoom (removed).
  • usePrintPreview.js — collect iframe styles, swap into document, wait for images, inject print background CSS (computed values from iframe :root), window.print(), reload.
  • useKeyboardShortcuts.js — Cmd/Ctrl+S(save)/+P(print), Esc(close popup), \(toggle panel). Listens on document + attachToIframe.

Fonts / spacing

  • useProjectFonts.js — SINGLETON. PROJECT_FONTS list, @font-face gen, loadFont(name)→⊙stylesheet.setFontFaceCss, loadAllFontPreviews (select preview), loadFontsFromCss. loadedFontsCss computed.
  • useSpacingEditor.js — margin/padding reactive (4 sides) + lock + unit convert. Used by ElementPopup (margin/padding passed into useElementSettings).
  • usePageSettings.js — all @page state + CSS read/write for PageSettings.vue (format/dimensions, margins, background, page numbers/running title via updatePageFooters CSS surgery). PageSettings.vue is now a thin view (Coloris wiring only).

components/ (TODO: full map on demand)

Tree: App.vue → PagedJsWrapper(content-source), EditorPanel→{PageSettings,TextSettings,FigureDefaultsSettings×2(kind=image|carte),StylesheetViewer,ContentPanel→ContentTreeItem}, ElementPopup + FigureSettingsPopup×2 (kind=image|carte) (+BasePopup), PreviewLoader, SaveButton, PrintButton, ZoomControls. ui/: BasePopup, NumberInput, InputWithUnit, UnitToggle, ZoomControls, CssFileImport. blocks/: per content-block render (Text/Heading/Image/Video/Audio/Map/Quote/List) via blocks/index.js. Popups expose imperatively (defineExpose: open/close/visible/handleClick); App wires refs into useIframeInteractions.

  • ui/SettingSection.vue — reusable toggleable section (checkbox toggle + label header, body slot, optional #footer + #header-extra slots). Used by ElementPopup & FigureSettingsPopup. emits toggle/section-click. (Settings panels use raw non-toggle setting__section markup.) ⚠ Popup close re-entry: BasePopup.close() emits 'close', wired back to the host's handleClose → its close logic. Host close MUST guard if (basePopup.value?.visible) basePopup.value.close() or it infinite-recurses.
  • FigureSettingsPopup.vue (thin view) + useFigureSettings.js (composable) — per-figure width/height/margin/position editor for block-image & block-carte. prop kind('image'|'carte') selects baseClass + selector derivation + defaults store. Composable logic in named helpers: read{Width,Height,Margins,Position}FromCss (store→data), apply*, loadFromCss/restoreFromStored, parse*FromText/persistStateAsCss (manual edits). exposes handleClick(figure,event). (Merged from former ImagePopup/CartePopup.)
  • editor/FigureDefaultsSettings.vue — default width/height panel for images & cartes. prop kind; image targets [.block-image,.geoformat-cover-image], carte targets [.block-carte]; writes ⊙image/carteDefaults. (Merged from former ImageSettings/CarteSettings.)

PHP templates (public/site/templates/) — notable additions

  • form.php — Page avec deux formulaires de contact (autonome / partenaire). POST handling natif Kirby : CSRF (csrf()), honeypot, kirby()->email(), Post/Redirect/Get (go()). Blueprint : blueprints/pages/form.yml (writer text + email_to text). CSS : assets/css/form.css (chargé conditionnellement dans header-web.php). Pas de plugin tiers.

PHP snippets (public/site/snippets/)

  • cover-img.php — snippet réutilisable pour les images de couverture responsives.
  • blocks/gallery.php — rendu du bloc galerie : grille CSS avec --gallery-cols (14 colonnes), srcset 2 tailles, liens class="glightbox". Blueprint : blueprints/blocks/gallery.yml. Preview panel : composant natif Kirby (preview: gallery dans le blueprint — ne pas créer de plugin k-gallery-block-preview car Kirby en fournit un). Génère un <img> avec 3 breakpoints w srcset, crop Kirby (ratio configurable via $imgW/$imgH, défaut 900×600 = 3:2), point focus panel → position nommée (crop) + object-position CSS. Variables : $file, $alt, $lazy (bool, défaut true), $sizes (string), $imgW, $imgH. Utilisé par home.php, projects.php, actus.php, actu.php.

REFACTOR BACKLOG

  • (low priority) StylesheetViewer.vue & ContentTreeItem.vue are long but mostly <style> CSS with short, simple scripts — not worth splitting.

ARCHIVED (removed from live tree, restorable)

  • archive/page-template-styling/ — "page template styling" feature (PagePopup + its wiring). See its REINSTALL.md. Removed orphans (no importer): useLinkedSpacing.js, ui/MarginEditor.vue, SidePanel.vue (git history only).