# Acadenice Patches Liste des patches custom appliques sur le fork Acadenice de Docmost. Ce document est maintenu manuellement pour faciliter le rebase upstream. Repo upstream : `github.com/docmost/docmost` Branche fork : `acadenice/main` ## Conventions - Chaque patch est commit isole avec scope `feat(rebrand)` / `feat(custom)` / etc. - Les modifications in-line de fichiers upstream sont documentees ici avec rationale. - Les nouveaux fichiers (extensions Tiptap custom, hooks, etc.) vont dans des emplacements dedies pour minimiser les conflits de rebase. --- ## Patch 001 — Rebrand minimal "Docmost" -> "DocAdenice" **Date** : 2026-05-07 **Scope** : strings UI visibles utilisateur uniquement **Rationale** : nom temporaire pour les beta-testeurs en attendant le vrai rebranding (logo SVG + design system + manifest PWA). Conserve les identifiants techniques pour ne rien casser et faciliter le rebase upstream. ### Fichiers modifies | Fichier | Avant | Apres | |---------|-------|-------| | `apps/client/index.html` | `
| ` : ajout `data-testid={`cell-${row.id}-${field.name}`}` — permet de cibler une cellule specifique par row ID et nom de champ. #### `apps/client/src/features/acadenice/database-view/components/inline-editor.tsx` - Branche `!canWrite` : ajout `data-testid="inline-editor-readonly"` sur le `` — permet aux tests RBAC de verifier que l'editeur est bien en lecture seule. - Branche `default` (`TextInput`) : ajout `data-testid="inline-editor-input"` — permet de cibler l'input dans les tests d'edition inline. #### `apps/client/src/features/acadenice/database-view/renderers/kanban-renderer.tsx` - `KanbanCard` wrapper div : ajout `data-testid={`kanban-card-${row.id}`}` — ciblage par row ID pour les tests de drag. - `KanbanColumn` wrapper div : ajout `data-testid={`kanban-column-${column.label}`}` — ciblage par label de colonne. - Board div (DndContext child) : ajout `data-testid="kanban-board"` — detection de la presence du kanban dans la page. #### `apps/client/src/features/acadenice/database-view/renderers/calendar-renderer.tsx` - Root div du composant : ajout `data-testid="calendar-renderer"` — detection de la presence du calendrier. #### `apps/client/Dockerfile.e2e` (nouveau) - Cree pour docker-compose.e2e.yml : build Vite du client + serve via `serve@14` sur le port 5173. - Utilise uniquement pour les e2e — pas de changement sur le Dockerfile de production. ### Raison des choix - `data-testid` sur les elements conteneurs plutot que sur les elements internes — plus stable face aux refactors de structure interne. - Pas de `data-testid` sur les elements Mantine (Button, Select, etc.) : ces composants ont leurs propres selectors d'accessibilite (role, aria-label) que Playwright prefere nativement. - Aucun `data-testid` ajoute dans les hooks, services, ou extension Tiptap — non necessaire pour les assertions UI e2e. ### Tests impactes 7 scenarios e2e dans `e2e/tests/` utilisent ces testids : - `database-view-insert.spec.ts` : `table-renderer` - `database-view-edit-inline.spec.ts` : `cell-{rowId}-{fieldName}`, `inline-editor-input` - `database-view-realtime-sse.spec.ts` : `table-renderer` - `database-view-rbac-denied.spec.ts` : `inline-editor-readonly` - `database-view-kanban-drag.spec.ts` : `kanban-board`, `kanban-column-{label}`, `kanban-card-{rowId}` - `database-view-calendar-reschedule.spec.ts` : `calendar-renderer` --- ## Patch 009 — R3.2 : Backlinks bidirectionnels + extension Tiptap wikilinks **Date** : 2026-05-08 **Commit** : `2fc310a` **Scope** : Schema DB + module backend backlinks + extension Tiptap wikilinks + panel UI "Linked references" **Rationale** : R3.2 permet a chaque page DocAdenice de savoir quelles autres pages y font reference. Deux vecteurs : wikilinks `[[Page Title]]` / `[[Page Title|alias]]` (nouveau) et mentions `@page` existantes (deja dans Docmost natif). L'indexation est async (fire-and-forget apres chaque save collaboratif) et idempotente (delete-then-insert par page source). ### Table SQL `acadenice_backlink` (prefixe acadenice_, zero conflit upstream) : - `id` UUID PK, `source_page_id` -> `pages.id` CASCADE, `target_page_id` -> `pages.id` CASCADE - `link_type` VARCHAR(20) CHECK IN ('wikilink', 'mention', 'database_embed') - `context_excerpt` TEXT (200 chars autour du lien pour preview UI) - `workspace_id` -> `workspaces.id` CASCADE (scope guard) - UNIQUE(source_page_id, target_page_id, link_type) - 3 index: idx_backlink_target, idx_backlink_source, idx_backlink_workspace - Migration up + down idempotente (ifNotExists) ### Fichiers crees (backend) | Fichier | Role | |---------|------| | `apps/server/src/database/migrations/20260508T100000-create-acadenice-backlink.ts` | Migration Kysely up+down | | `apps/server/src/core/acadenice/backlinks/backlinks.module.ts` | Module NestJS | | `apps/server/src/core/acadenice/backlinks/services/backlink-parser.service.ts` | Walke Tiptap JSON, extrait wikilinks/mentions/databaseView, resoud titres via LIKE workspace-scoped | | `apps/server/src/core/acadenice/backlinks/services/backlink-indexer.service.ts` | Idempotent reindex (delete source -> insert), skip self-refs + null targets | | `apps/server/src/core/acadenice/backlinks/services/backlink.service.ts` | Lecture permission-aware (space_members / public visibility), groupe par link_type | | `apps/server/src/core/acadenice/backlinks/controllers/backlinks.controller.ts` | GET /api/acadenice/pages/:pageId/backlinks (JwtAuthGuard) | | `apps/server/src/core/acadenice/backlinks/events/page-content-updated.listener.ts` | @OnEvent listener -> reindex async, exporte ACADENICE_PAGE_CONTENT_UPDATED_EVENT | | `apps/server/src/core/acadenice/backlinks/spec/backlink-parser.service.spec.ts` | 10 tests Vitest | | `apps/server/src/core/acadenice/backlinks/spec/backlink-indexer.service.spec.ts` | 5 tests Vitest | | `apps/server/src/core/acadenice/backlinks/spec/backlink.service.spec.ts` | 4 tests Vitest | | `apps/server/src/core/acadenice/backlinks/spec/backlinks.controller.spec.ts` | 4 tests Vitest | ### Fichiers crees (frontend) | Fichier | Role | |---------|------| | `apps/client/src/features/acadenice/wikilinks/extension/wikilink-extension.ts` | Tiptap Node inline+atom, attrs pageId/title/alias, Suggestion plugin [[, insertWikilink command, ReactNodeView (broken-link state) | | `apps/client/src/features/acadenice/wikilinks/extension/wikilink-suggestion.ts` | Render callbacks floating-ui (pattern mention) | | `apps/client/src/features/acadenice/wikilinks/extension/wikilink-list.tsx` | Popup suggestions pages via useSearchSuggestionsQuery | | `apps/client/src/features/acadenice/wikilinks/extension/wikilink-list.module.css` | Styles popup | | `apps/client/src/features/acadenice/wikilinks/__tests__/wikilink-extension.test.ts` | 9 tests Vitest (schema/attrs/commands/HTML) | | `apps/client/src/features/acadenice/backlinks/queries/backlinks-query.ts` | useBacklinks(pageId) React Query (staleTime 30s) | | `apps/client/src/features/acadenice/backlinks/components/linked-references-panel.tsx` | Panel accordeon groupe par link_type, excerpt, navigate source, empty/loading/error states | | `apps/client/src/features/acadenice/backlinks/components/linked-references-panel.module.css` | Styles panel | | `apps/client/src/features/acadenice/backlinks/__tests__/linked-references-panel.test.tsx` | 7 tests Vitest (RTL) | ### Fichiers modifies (patches upstream minimaux) | Fichier | Modification | |---------|--------------| | `apps/server/src/collaboration/extensions/persistence.extension.ts` | +import EventEmitter2 + ACADENICE_PAGE_CONTENT_UPDATED_EVENT, +inject eventEmitter, +emit apres enqueuePageHistory | | `apps/server/src/core/core.module.ts` | +import AcadeniceBacklinksModule, +1 entree imports[] | | `apps/client/src/features/editor/extensions/extensions.ts` | +import WikilinkExtension, +1 entree mainExtensions[] | | `apps/client/src/features/editor/full-editor.tsx` | +import LinkedReferencesPanel, +render Divider + panel apres MemoizedPageEditor | | `apps/client/public/locales/en-US/translation.json` | +11 cles (backlinks.* + wikilink.*) | | `apps/client/public/locales/fr-FR/translation.json` | +11 cles FR | ### Choix techniques tranches | Choix | Decision | |-------|----------| | Wikilink syntax | [[Title]] et [[Title\|alias]] supportes (Obsidian style) | | Resolution wikilink | LIKE case-insensitive exact sur `pages.title` dans le workspace. Ambiguite -> null (broken link). Pas de fuzzy. | | Reindex strategy | Full reindex par page (delete-then-insert), idempotent. OK jusqu'a 10k pages. | | Excerpt | 100 chars avant + 100 chars apres l'occurrence du titre. | | Event debounce | Pas de debounce cote listener — Hocuspocus debounce deja la persistance en amont. Fire-and-forget. | | Placement panel | Sticky bottom de la page (apres l'editeur dans full-editor.tsx), Divider de separation. | ### Tests count - Backend: +23 tests Vitest (4 suites spec/) - Frontend: +16 tests Vitest (wikilink-extension + linked-references-panel) - Total cumule fork: 96 (R3.1.d) + 39 nouveaux = 135 tests ### Nouvelles dependances a installer (PAS installee — convention fork) Aucune nouvelle dep pour R3.2. Le Tiptap Suggestion est fourni par `@tiptap/suggestion` (deja en dependance de Docmost pour le systeme mention). floating-ui deja present. ### Verifications skipped (convention fork — Corentin run) - `pnpm install` : non execute - `pnpm typecheck` : non execute - `pnpm test` : non execute - `pnpm migration:up` : non execute (migration lisible et reversible, a tester) - Lint : non execute ### Points a debattre avec Corentin 1. **Placement panel backlinks** : actuellement ajoute en bas de `full-editor.tsx` (dans le conteneur principal de la page). Alternatives : sidebar dedicee (panneau right) ou drawer Mantine. Deplacer sans changer la logique — LinkedReferencesPanel prend juste `pageId`. 2. **Resolution wikilink par pageId** : si l'utilisateur tape `[[Mon titre]]` et que le titre change plus tard, le lien devient broken. L'indexer re-resoud au prochain save de la page source. Pour stocker l'ID resolus des la saisie, il faudrait que la suggestion popup injecte `pageId` directement dans l'attr — c'est deja le cas (la selection dans WikilinkList passe `pageId`). Seuls les wikilinks saisis a la main sans passer par la popup auront `pageId=null`. 3. **Wikilink navigation** : le click navigue vers `/page/${pageId}`. DocAdenice utilise des URLs `/${spaceSlug}/page/${slugId}`. Il faudra un lookup ou un redirect dans App.tsx pour les pageIds sans slugId. Alternative : stocker slugId dans l'attr wikilink au moment de la suggestion. 4. **Index initial** : les pages existantes n'ont pas de backlinks indices. Un script one-shot `BacklinkIndexerService.reindexPage(pageId)` sur toutes les pages peut etre declenche manuellement ou via un endpoint admin `POST /api/acadenice/admin/backlinks/reindex`. ### TODO non bloquants - Script/endpoint de reindex massif initial (pages pre-R3.2) - data-testid sur LinkedReferencesPanel pour e2e Playwright (R3.1.e pattern) - CSS global pour `.wikilink` et `.wikilink--broken` (actuellement inline dans NodeView) - Pagination des backlinks si > 100 (rare mais possible) - Endpoint `DELETE /api/acadenice/admin/backlinks/reindex` pour repartir de zero |