docs(fork): update ACADENICE_PATCHES.md Patch 009 for R3.2
This commit is contained in:
parent
2fc310a2f2
commit
8cd57f93b3
1 changed files with 116 additions and 0 deletions
|
|
@ -683,3 +683,119 @@ En attendant, le rendu est identique fonctionnellement (HTML table + colonnes de
|
||||||
- `database-view-rbac-denied.spec.ts` : `inline-editor-readonly`
|
- `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-kanban-drag.spec.ts` : `kanban-board`, `kanban-column-{label}`, `kanban-card-{rowId}`
|
||||||
- `database-view-calendar-reschedule.spec.ts` : `calendar-renderer`
|
- `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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue