From f753b27542f9bd0ae2c16ad3ae22915c203d5796 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Sun, 9 Aug 2026 09:25:03 -0400 Subject: [PATCH] Add asset pipeline and professionalize theme Introduce a Node build system for minified CSS/JS with content hashes, add PWA support, i18n helpers, accessibility improvements, and more translatable strings. Move theme configuration from bookmarks to localStorage, add Playwright tests, and set up CI workflows. --- .github/workflows/ci.yml | 42 + .github/workflows/e2e.yml | 43 + .gitignore | 22 + CHANGELOG.md | 41 + PLAN.md | 233 ++++ README.md | 107 +- shaarli-pro/build.js | 235 ++++ shaarli-pro/changetag.html | 10 +- shaarli-pro/configure.html | 4 +- shaarli-pro/css/custom_views.css | 13 +- shaarli-pro/css/style.css | 72 +- shaarli-pro/daily.html | 24 +- shaarli-pro/dailyrss.html | 2 +- shaarli-pro/dist/a11y.min.js | 1 + shaarli-pro/dist/awesomplete.min.css | 1 + shaarli-pro/dist/awesomplete.min.js | 2 + shaarli-pro/dist/bulk-actions.min.js | 1 + shaarli-pro/dist/custom_views.min.css | 1 + shaarli-pro/dist/custom_views.min.js | 1 + shaarli-pro/dist/i18n.min.js | 1 + shaarli-pro/dist/manifest.json | 80 ++ shaarli-pro/dist/metadata.min.js | 62 ++ shaarli-pro/dist/navigation.min.js | 1 + shaarli-pro/dist/script.min.js | 1 + shaarli-pro/dist/search.min.js | 1 + shaarli-pro/dist/shaarit-rules.min.js | 1 + shaarli-pro/dist/style.min.css | 1 + shaarli-pro/dist/theme.min.js | 1 + shaarli-pro/dist/themes.min.css | 1 + shaarli-pro/editlink.html | 4 +- shaarli-pro/error.html | 2 +- shaarli-pro/export.bookmarks.html | 2 +- shaarli-pro/export.html | 8 +- shaarli-pro/feed.atom.html | 4 +- shaarli-pro/feed.rss.html | 4 +- shaarli-pro/i18n-keys.md | 193 ++++ shaarli-pro/img/icons/icon-192x192.png | Bin 0 -> 7992 bytes shaarli-pro/img/icons/icon-512x512.png | Bin 0 -> 31306 bytes shaarli-pro/import.html | 14 +- shaarli-pro/includes.html | 54 +- shaarli-pro/install.html | 2 +- shaarli-pro/js/custom_views.js | 450 ++++---- shaarli-pro/js/i18n.js | 190 ++++ shaarli-pro/js/modules/a11y.js | 66 ++ shaarli-pro/js/modules/bulk-actions.js | 275 +++++ shaarli-pro/js/modules/navigation.js | 95 ++ shaarli-pro/js/modules/search.js | 766 +++++++++++++ shaarli-pro/js/modules/theme.js | 122 ++ shaarli-pro/js/script.js | 1416 ++---------------------- shaarli-pro/linklist.html | 68 +- shaarli-pro/linklist.paging.html | 10 +- shaarli-pro/manifest.json | 25 + shaarli-pro/migrate-todos.php | 104 -- shaarli-pro/opensearch.html | 6 +- shaarli-pro/package-lock.json | 1254 +++++++++++++++++++++ shaarli-pro/package.json | 23 + shaarli-pro/page.footer.html | 32 +- shaarli-pro/page.header.html | 194 ++-- shaarli-pro/player.html | 10 +- shaarli-pro/playwright.config.js | 33 + shaarli-pro/pluginsadmin.html | 2 +- shaarli-pro/pluginscontent.html | 3 + shaarli-pro/server.requirements.html | 2 +- shaarli-pro/service-worker.js | 99 ++ shaarli-pro/tag.cloud.html | 4 +- shaarli-pro/tests/smoke.spec.js | 57 + shaarli-pro/theme_info.php | 2 +- shaarli-pro/tools.html | 132 ++- 68 files changed, 4743 insertions(+), 1989 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/e2e.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 PLAN.md create mode 100644 shaarli-pro/build.js create mode 100644 shaarli-pro/dist/a11y.min.js create mode 100644 shaarli-pro/dist/awesomplete.min.css create mode 100644 shaarli-pro/dist/awesomplete.min.js create mode 100644 shaarli-pro/dist/bulk-actions.min.js create mode 100644 shaarli-pro/dist/custom_views.min.css create mode 100644 shaarli-pro/dist/custom_views.min.js create mode 100644 shaarli-pro/dist/i18n.min.js create mode 100644 shaarli-pro/dist/manifest.json create mode 100644 shaarli-pro/dist/metadata.min.js create mode 100644 shaarli-pro/dist/navigation.min.js create mode 100644 shaarli-pro/dist/script.min.js create mode 100644 shaarli-pro/dist/search.min.js create mode 100644 shaarli-pro/dist/shaarit-rules.min.js create mode 100644 shaarli-pro/dist/style.min.css create mode 100644 shaarli-pro/dist/theme.min.js create mode 100644 shaarli-pro/dist/themes.min.css create mode 100644 shaarli-pro/i18n-keys.md create mode 100644 shaarli-pro/img/icons/icon-192x192.png create mode 100644 shaarli-pro/img/icons/icon-512x512.png create mode 100644 shaarli-pro/js/i18n.js create mode 100644 shaarli-pro/js/modules/a11y.js create mode 100644 shaarli-pro/js/modules/bulk-actions.js create mode 100644 shaarli-pro/js/modules/navigation.js create mode 100644 shaarli-pro/js/modules/search.js create mode 100644 shaarli-pro/js/modules/theme.js create mode 100644 shaarli-pro/manifest.json delete mode 100644 shaarli-pro/migrate-todos.php create mode 100644 shaarli-pro/package-lock.json create mode 100644 shaarli-pro/package.json create mode 100644 shaarli-pro/playwright.config.js create mode 100644 shaarli-pro/pluginscontent.html create mode 100644 shaarli-pro/service-worker.js create mode 100644 shaarli-pro/tests/smoke.spec.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9c4b32a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: shaarli-pro + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: shaarli-pro/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build theme assets + run: npm run build + + - name: Verify JavaScript syntax + run: | + for f in js/*.js js/modules/*.js dist/*.js; do + node --check "$f" + done + + - name: Ensure dist/ is up-to-date + run: | + git diff --exit-code dist/ || ( + echo "dist/ is out of date. Run 'npm run build' and commit the changes." && exit 1 + ) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..1fa5287 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,43 @@ +name: E2E Tests + +on: + workflow_dispatch: + schedule: + # Run every Sunday at 03:00 UTC. + - cron: '0 3 * * 0' + +jobs: + e2e: + runs-on: ubuntu-latest + defaults: + run: + working-directory: shaarli-pro + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: shaarli-pro/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run E2E tests + env: + SHAARLI_URL: ${{ secrets.SHAARLI_URL }} + run: npm run test:e2e + + - name: Upload Playwright report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: shaarli-pro/playwright-report/ + retention-days: 7 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7b4430 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Dependencies +node_modules/ + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# OS files +.DS_Store +Thumbs.db + +# Editor files +.vscode/ +.idea/ +*.swp +*.swo + +# The dist/ directory contains generated minified assets. +# It is intentionally committed so the theme works without running npm install. +# If you prefer to generate it in CI, uncomment the next line. +# dist/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8f228ab --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +Toutes les modifications notables de ce projet seront documentées ici. + +Le format est basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/), +et ce projet adhère à [Semantic Versioning](https://semver.org/lang/fr/). + +## [1.1.0] - 2026-08-09 + +### Ajouté + +- Pipeline de build frontend (`npm run build`) minifiant CSS/JS avec PostCSS/cssnano et Terser. +- Génération d'assets dans `shaarli-pro/dist/` avec hash de contenu dans le manifeste. +- Cache-busting des CSS/JS via `{$version_hash}` de Shaarli. +- Chargement de la police Inter via `` au lieu d'un `@import` CSS (meilleures performances). +- Modules JavaScript thématiques dans `js/modules/` : `theme.js`, `navigation.js`, `search.js`, `bulk-actions.js`, `a11y.js`. +- Système d'internationalisation côté client (`js/i18n.js`) et clés documentées dans `i18n-keys.md`. +- Support PWA : `manifest.json`, icônes 192×192 et 512×512, et service worker de cache des assets statiques. +- Workflow GitHub Actions CI vérifiant le build et la syntaxe JS. +- Socle de tests E2E avec Playwright (`tests/smoke.spec.js`) et workflow dédié. + +### Modifié + +- Mise à jour de `theme_info.php` en version `1.1.0`. +- Nettoyage des `console.log` de debug. +- Suppression de `migrate-todos.php` du dossier thème. +- Amélioration de la gestion des erreurs sur les appels `fetch`. + +### Corrigé + +- Failles XSS potentielles liées à l'utilisation d'`innerHTML` avec du contenu utilisateur. +- Template `pluginscontent.html` manquant, requis par certains plugins officiels. +- Duplication de la zone `tools_plugin` dans `tools.html`. +- Boutons icônes sans `aria-label` et contrastes insuffisants. +- Support `prefers-reduced-motion` et `prefers-contrast: more`. + +## [1.0.0] - 2026 + +### Ajouté + +- Version initiale du thème Shaarli Pro avec sidebar, header, modes clair/sombre, 16 palettes, recherche Spotlight, vues notes/todos/archive, lecteur multimédia et multi-sélection. diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..b78e471 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,233 @@ +# Plan d'amélioration – Shaarli Professional Theme (`shaarli-pro`) + +> Objectif : transformer le thème actuel, riche en fonctionnalités mais encore très artisanal, en une solution **professionnelle**, **sécurisée**, **maintenable**, **performante** et **accessible**. + +Ce plan repose sur une analyse statique complète du code (README, rapport d'analyse existant, templates, CSS, JS, comparaison avec le thème `default` officiel de Shaarli). + +--- + +## 1. Synthèse de l'état actuel + +### 1.1 Métriques + +| Fichier / catégorie | Taille | Remarque | +|---|---|---| +| `js/script.js` | ~3 620 lignes | Thème, navigation, recherche, lecteur, multi-sélection, etc. | +| `js/custom_views.js` | ~6 160 lignes | Notes, todos, archive, color picker, background studio | +| `js/shaarit-rules.js` | 377 lignes | Règles métier ShaarIt (tags, détection, URLs) | +| `css/style.css` | ~4 700 lignes | Design system principal | +| `css/custom_views.css` | ~1 780 lignes | Styles des vues notes/todos | +| `css/themes.css` | ~1 680 lignes | 16 palettes de couleurs | +| Templates HTML | ~40 fichiers | Dont plusieurs non documentés | +| Tests / CI / lint | 0 | Aucun outil d'assurance qualité | + +### 1.2 Forces à préserver + +- UI moderne, cohérente, avec sidebar + header. +- Mode clair/sombre et système de thèmes multiples. +- Fonctionnalités avancées : notes, todos, archive, lecteur média, multi-sélection, recherche Spotlight. +- Variables CSS et design system en place. +- Utilisation de `localStorage` pour la persistance locale. + +### 1.3 Appréciation globale + +Le thème vaut **~6,5 / 10** en termes de professionnalisation. Il est fonctionnellement abouti, mais accumule de la dette technique : failles XSS potentielles, code monolithique, duplication, dépendances externes non maîtrisées, absence de tests et documentation obsolète. + +--- + +## 2. Problèmes critiques – à traiter en priorité (Phase 1) + +Ces points bloquent une mise en production professionnelle. + +### 2.1 Sécurité + +| Problème | Fichier(s) | Risque | Action | +|---|---|---|---| +| **XSS via `innerHTML`** sur descriptions, previews, lecteur popup, tags | `js/script.js` (l. 1547, 1603, 2151…), `js/custom_views.js` (passim) | Exécution de code si un bookmark contient du HTML/JS malveillant | Tout contenu dynamique doit passer par `textContent` ou être échappé avec `escapeHtml`. Remplacer `innerHTML` par `innerText` / création DOM sécurisée. | +| **Génération de code JavaScript en chaîne** pour la popup lecteur | `js/script.js` `buildPlayerHTML()` (l. 2523–3053) | Injection possible si titre/URL contiennent des quotes ou du code | Supprimer `buildPlayerHTML()` ; utiliser le template `player.html` existant ou un composant DOM réel. | +| **Persistance de la configuration via un bookmark privé** (`shaarit_config`) | `js/script.js` `ShaaritThemeConfig` (l. 7–154) | Fuite de config, pollution de la base, risque si un attaquant crée un bookmark avec ce tag | Remplacer par `localStorage` uniquement, ou par un vrai plugin backend dédié. | +| **URLs internes fixes vers `shaarit.app`** | `js/shaarit-rules.js` (l. 142–148), `js/script.js`, `migrate-todos.php` | Dépendance externe, fuite de données, rupture si le domaine change | Rendre les schémas d'URLs internes configurables (`note://`, `todo://`, ou URLs relatives). | +| **Script PHP de migration dans le thème** | `migrate-todos.php` | Modification directe de la base, pas de contrôle d'accès, exécutable via web | Supprimer du thème ; en faire un plugin admin ou un script CLI documenté hors `tpl/`. | +| **`descEl.innerHTML` utilisé comme source de Markdown** | `js/custom_views.js` (l. 2422, 2492, 4239) | Récupère le HTML rendu par Shaarli, potentiellement exécutable | Utiliser `textContent` comme source unique. | + +### 2.2 Bugs et instabilités + +| Problème | Fichier(s) | Action | +|---|---|---| +| **`player.html` n'est jamais utilisé** mais dupliqué en JS | `player.html`, `js/script.js` | Supprimer `player.html` si inutile, ou l'utiliser comme cible de la popup. | +| **Template officiel `pluginscontent.html` absent** | `tpl/default/pluginscontent.html` | Ajouter `pluginscontent.html` pour ne pas casser les plugins qui utilisent `plugin_content`. | +| **Gestion d'erreurs `fetch` insuffisante** | `js/script.js`, `js/custom_views.js` | Ajouter des `try/catch`, vérifier `response.ok`, informer l'utilisateur en cas d'échec. | +| **Écouteurs d'événements ajoutés mais jamais supprimés** | `js/script.js` (102 `addEventListener`), `js/custom_views.js` (104) | Nettoyer les listeners dans les modales, popups, sidebars mobile. | +| **`tools_plugin` inclus deux fois** | `tools.html` (l. 217 et 283) | Ne garder qu'une seule zone. | +| **Hidden tags stockés uniquement dans `localStorage`** | `hidden-tags.html`, `tools.html` | Per-browser, non synchronisés. Persister côté serveur (plugin) ou au minimum documenter la limitation. | +| **Sélecteurs DOM très longs et fragiles** | `css/style.css`, JS | Raccourcir et utiliser des classes sémantiques (BEM ou équivalent). | + +### 2.3 Compatibilité avec Shaarli officiel + +- Le thème `default` utilise `{$version_hash}` pour le cache-busting ; `shaarli-pro` utilise des versions codées en dur (`v=1.0.5`, etc.) qui ne correspondent pas à `theme_info.php` (`1.0.0`). +- OpenGraph et `apple-touch-icon` sont absents du `includes.html` alors que le thème officiel les fournit. +- Les templates officiels utilisent systématiquement `{'key'|t}` pour l'internationalisation ; `shaarli-pro` mélange français, anglais et textes en dur. + +--- + +## 3. Points non finis / fonctionnalités à finaliser + +### 3.1 Vue Archive + +- `initArchiveView()` existe (`js/custom_views.js`) mais l'expérience semble moins aboutie que Notes/Todos (moins de styles dédiés, pas de création d'archive explicite). +- **Action** : finaliser l'UI archive ou fusionner avec une fonction "archiver" depuis la vue Notes. + +### 3.2 Gestion des tags cachés + +- Double implémentation : `hidden-tags.html` et le panneau dans `tools.html`. +- La logique est dupliquée entre `js/script.js` (chargement) et le script inline de `hidden-tags.html`. +- **Action** : centraliser la liste des tags système, définir une source unique (idéalement côté serveur via un plugin). + +### 3.3 Lecteur média + +- Le lecteur inline et la popup partagent un état via `localStorage` et `BroadcastChannel`, ce qui est complexe. +- `player.html` n'est pas utilisé. +- **Action** : choisir une architecture unique (inline OU popup via un vrai template) et la stabiliser. + +### 3.4 Panneau des thèmes + +- `themes.css` charge 16 palettes (37 Ko) même si l'utilisateur n'en utilise qu'une. +- **Action** : charger dynamiquement le CSS du thème actif, ou au minimum splitter `themes.css` par thème. + +### 3.5 README et documentation + +- Le README annonce une "Structure du Projet" incomplète : il omet `custom_views.js/css`, `themes.css`, `player.html`, `hidden-tags.html`, `install.html`, `migrate-todos.php`, `shaarit-rules.js`, `backgrounds-manifest.json`, les fonds d'écran `note-bg-*`. +- **Action** : réécrire le README, ajouter un CHANGELOG et un guide de contribution. + +--- + +## 4. Axes d'amélioration structurants + +### 4.1 Qualité et maintenabilité du code + +1. **Modulariser le JavaScript** + - Découper `script.js` en modules thématiques : `theme.js`, `search.js`, `media-player.js`, `bulk-actions.js`, `editor.js`, `mobile-nav.js`. + - Découper `custom_views.js` en : `notes.js`, `todos.js`, `archive.js`, `palette.js`, `background-studio.js`. +2. **Supprimer les `console.log` de debug** (calendar, pin, theme sync, etc.). +3. **Uniformiser la gestion des erreurs** : toutes les fonctions `async` doivent avoir un `try/catch` et un retour utilisateur. +4. **Réduire la spécificité CSS** : éviter les sélecteurs de plus de 3 niveaux, nettoyer les `!important` (26 dans `custom_views.css`). +5. **Dédupliquer la navigation** sidebar / header, soit via un template partagé, soit via génération JS à partir d'un tableau de liens. + +### 4.2 Performance + +1. **Mettre en place un build frontend** (Vite, Webpack ou Rollup) pour : + - minifier JS/CSS, + - splitter les thèmes, + - ajouter des hashes de cache-busting, + - héberger localement les polices et les icônes si possible. +2. **Charger `custom_views.js` uniquement sur les pages concernées** (actuellement chargé partout via `includes.html`). +3. **Remplacer l'`@import` Google Fonts** par un `` ou des polices locales. +4. **Optimiser les images** de fond (`note-bg-*`) : WebP/AVIF, redimensionnement, lazy-loading. +5. **Supprimer le CSS mort** avec un outil comme PurgeCSS ou un audit manuel des classes. + +### 4.3 Accessibilité (a11y) + +1. **Rendre les modales focus-trap** (recherche, filtres, QR code, notes, todos). +2. **Utiliser `aria-selected`** sur les boutons de mode de recherche et les onglets. +3. **Ajouter `prefers-reduced-motion`** pour désactiver les animations. +4. **Vérifier tous les contrastes** (notamment `--tag-green-text` signalé comme limite). +5. **Ajouter des skip-links** et s'assurer que tous les boutons icônes ont un `aria-label` explicite. +6. **Remplacer les emojis utilisés comme indicateurs** (📌 dans `linklist.html`) par des icônes sémantiques. + +### 4.4 Internationalisation (i18n) + +1. **Extraire toutes les chaînes** des templates vers le format Shaarli : `{'Ma clé'|t}`. +2. Faire de même pour les textes générés en JS (labels de boutons, messages d'erreur, placeholders). +3. Documenter les nouvelles clés de traduction ajoutées. + +### 4.5 Architecture des fonctionnalités ShaarIt + +1. **Rendre les règles ShaarIt configurables** : le serveur Shaarli, pas `shaarit.app`, doit être la source de vérité pour les URLs internes. +2. **Ne plus utiliser des tags comme base de données** pour stocker la config thème ; utiliser `localStorage` ou un plugin backend. +3. **Réfléchir à un backend dédié** pour notes/todos (plugin PHP) afin d'éviter les contournements par bookmarks et `fetch` sur les formulaires d'édition. + +--- + +## 5. Nouvelles idées à intégrer (par ordre de valeur) + +| Idée | Intérêt | Complexité | +|---|---|---| +| **Service Worker / PWA** (offline, manifest, icônes) | Professionnalisation forte, usage mobile | Moyenne | +| **Tests E2E avec Playwright** (recherche, thème, multi-sélection, lecteur) | Garantie de non-régression | Moyenne | +| **CI/CD GitHub Actions** (lint, build, tests, release) | Qualité continue | Faible | +| **ESLint + Stylelint + Prettier** | Maintenabilité, style cohérent | Faible | +| **Mode haut contraste** (`prefers-contrast`) | Accessibilité | Faible | +| **Amélioration du lecteur** : playlist, historique, gestion des erreurs | UX | Moyenne | +| **Recherche serveur** pour les grosses collections (au lieu de parser le DOM) | Performance | Moyenne/forte | +| **API côté serveur** pour notes/todos/tags cachés | Sécurité, fiabilité | Forte | +| **Dashboard de statistiques** plus riche dans Tools | UX | Moyenne | +| **Documentation technique auto-générée** (JSDoc, Storybook-like) | Maintenabilité | Moyenne | + +--- + +## 6. Roadmap recommandée + +### Phase 1 – Sécurité et stabilité (2 à 3 semaines) + +- [ ] Audit XSS : remplacer tous les `innerHTML` à risque par du DOM sécurisé. +- [ ] Supprimer ou sécuriser `buildPlayerHTML()` ; utiliser `player.html` ou un composant DOM. +- [ ] Retirer `migrate-todos.php` du thème (plugin/CLI séparé). +- [ ] Remplacer la persistance "bookmark config" par `localStorage` pur ou un plugin backend. +- [ ] Ajouter le template `pluginscontent.html` manquant. +- [ ] Nettoyer les `console.log` de debug. +- [ ] Gestion d'erreurs robuste sur tous les `fetch`. +- [ ] Tests manuels complets sur une instance Shaarli propre. + +### Phase 2 – Qualité et maintenabilité (2 à 3 semaines) + +- [ ] Découper `script.js` et `custom_views.js` en modules thématiques. +- [ ] Refactoriser la navigation sidebar/header pour supprimer la duplication. +- [ ] Unifier le CSS variables et supprimer les variables mortes. +- [ ] Extraire les textes en dur vers `{'key'|t}`. +- [ ] Mettre en place ESLint + Stylelint + Prettier. +- [ ] Réécrire le README et ajouter un CHANGELOG. + +### Phase 3 – Performance et accessibilité (1 à 2 semaines) + +- [ ] Mettre en place un build (minification, hash, split des thèmes). +- [ ] Charger `custom_views.js` à la demande. +- [ ] Optimiser les polices et icônes (local/swap). +- [ ] Audit a11y complet (modales, contrastes, reduced-motion). +- [ ] Optimiser les images de fond. + +### Phase 4 – Évolutions (selon les priorités utilisateur) + +- [ ] PWA / Service Worker. +- [ ] Tests E2E Playwright. +- [ ] CI/CD GitHub Actions. +- [ ] Backend plugin pour notes/todos/config. +- [ ] Améliorations du lecteur (playlist, historique). + +--- + +## 7. Critères de validation + +Pour considérer le thème comme professionnel, les critères suivants doivent être atteints : + +1. **Sécurité** : aucune alerte critique (XSS, injection, `eval`, `document.write`) ; contenu utilisateur toujours échappé. +2. **Stabilité** : aucune erreur JS en console sur les parcours principaux (login, liste, ajout, édition, recherche, notes, todos, tools). +3. **Compatibilité** : tous les templates officiels sont présents ou justifiés ; les plugins standard fonctionnent. +4. **Performance** : score Lighthouse > 90 sur Performance et Accessibilité (sur une instance de référence). +5. **Maintenabilité** : code modularisé, linté, documenté. +6. **Internationalisation** : toutes les chaînes utilisateur passent par le système de traduction Shaarli. +7. **Tests** : tests E2E critiques passants (recherche, multi-sélection, changement de thème, lecteur). + +--- + +## 8. Premières actions immédiates (si vous voulez démarrer demain) + +1. Corriger les failles XSS dans `js/script.js::renderResults`, `js/custom_views.js` et la popup lecteur. +2. Supprimer `migrate-todos.php` du dossier thème. +3. Décider de l'avenir de la persistance thème : `localStorage` uniquement ou plugin backend ? +4. Créer `pluginscontent.html`. +5. Nettoyer les `console.log` restants. +6. Mettre à jour le README avec la structure réelle. + +--- + +*Plan rédigé le 8 août 2026. Il doit être affiné avec les retours utilisateurs et les contraintes réelles de déploiement (version de Shaarli, hébergement, usage du mobile).* \ No newline at end of file diff --git a/README.md b/README.md index f1e02f3..9898fb5 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,11 @@ | | | |---|---| | **Nom interne** | Professional (`shaarli-pro`) | -| **Version** | 1.0.0 | +| **Version** | 1.1.0 | | **Compatibilité** | Shaarli ≥ 0.9, PHP ≥ 7.4 | | **Licence** | MIT | | **Dossier** | `tpl/shaarli-pro` | +| **Build** | Node.js ≥ 18, `npm run build` | Le thème offre un layout de type application avec barre latérale fixe, navigation fluide, mode clair/sombre natif, et une couche JavaScript riche dédiée à l'interactivité. @@ -155,6 +156,55 @@ Le thème offre un layout de type application avec barre latérale fixe, navigat --- +## Build & Développement + +Le thème inclut un pipeline de build frontend pour minifier les assets et ajouter du cache-busting. + +```bash +cd shaarli-pro +npm install # installer les dépendances +npm run build # minifier CSS/JS et générer dist/ +npm run build:css # CSS uniquement +npm run build:js # JS uniquement +npm run clean # supprimer dist/ +``` + +Les templates chargent les fichiers minifiés depuis `dist/` avec le hash de version Shaarli (`{$version_hash}`). +Les sources (`css/`, `js/`) restent les fichiers de référence : **ne modifiez jamais `dist/` à la main**. + +Après chaque build, pensez à committer `dist/` pour que le thème reste utilisable sans Node.js côté production. + +## Tests + +### Tests E2E (Playwright) + +Un socle de tests E2E est fourni pour valider les parcours critiques : + +```bash +cd shaarli-pro +npm run test:e2e:install # installer les navigateurs Playwright (une fois) +SHAARLI_URL=http://localhost:8080 npm run test:e2e +``` + +Les tests couvrent actuellement : + +- chargement de la page d'accueil, +- ouverture de la recherche au clavier (`S`), +- bascule clair/sombre, +- présence du skip-link, +- présence du bouton de multi-sélection. + +Un workflow GitHub Actions dédié (`.github/workflows/e2e.yml`) permet de les exécuter manuellement ou via cron une fois configuré avec `SHAARLI_URL`. + +## PWA (Progressive Web App) + +Le thème fournit un manifeste et un service worker de base : + +- `manifest.json` : icônes, theme-color, mode standalone. +- `service-worker.js` : cache les assets statiques du thème (CSS/JS/icônes) pour un fonctionnement dégradé offline. + +Le service worker intercepte uniquement les requêtes situées dans son scope (le dossier du thème). Les pages Shaarli dynamiques continuent d'être servies par le réseau. + ## Installation ### 1. Téléchargement @@ -163,7 +213,7 @@ Le thème offre un layout de type application avec barre latérale fixe, navigat git clone https://git.dracodev.net/Projets/Shaarli_bm_theme.git ``` -### 3. Supprimer le thème en place +### 2. Supprimer le thème en place ```bash # Via Docker @@ -173,7 +223,9 @@ docker exec -it shaarli_bookmarks rm -rf /var/www/shaarli/tpl/shaarli-pro rm -rf /path/to/shaarli/tpl/shaarli-pro ``` -### 2. Copie dans Shaarli +### 3. Copie dans Shaarli + +Le dossier `dist/` contient les assets CSS/JS déjà minifiés et est versionné. Vous n'avez **pas besoin de lancer `npm install`** si vous utilisez le thème tel quel. Copier le dossier `shaarli-pro/` dans le répertoire `tpl/` de votre instance Shaarli, à côté du dossier `default/` : @@ -185,7 +237,7 @@ docker cp "./shaarli-pro" shaarli_bookmarks:/var/www/shaarli/tpl/ cp -r shaarli-pro/ /path/to/shaarli/tpl/ ``` -### 3. Permissions +### 4. Permissions ```bash # Docker @@ -226,6 +278,19 @@ docker exec -it myshaarli sed -i 's/"theme": "default"/"theme": "shaarli-pro"/' Redémarrez PHP si nécessaire et videz le cache navigateur. +### Notes importantes + +- Le dossier `dist/` (assets minifiés) est versionné. Si vous clonez/pull le dépôt tel quel, le thème fonctionne immédiatement sans Node.js. +- Si vous modifiez `css/` ou `js/`, vous devez relancer le build et committer le nouveau `dist/` : + + ```bash + cd shaarli-pro + npm install + npm run build + git add dist/ + git commit -m "rebuild assets" + ``` + --- ## Configuration & Personnalisation @@ -261,22 +326,48 @@ Créez un fichier `tpl/shaarli-pro/extra.html` pour injecter du CSS/JS suppléme ``` shaarli-pro/ ├── css/ -│ └── style.css # Styles principaux + variables CSS (light/dark) +│ ├── style.css # Styles principaux + variables CSS (light/dark) +│ ├── themes.css # 16 palettes de couleurs +│ ├── custom_views.css # Styles des vues Notes / Todos / Archive +│ └── awesomplete.css # Autocomplete des tags ├── js/ -│ └── script.js # Interactions (thème, recherche, filtres, sélection, lecteur) +│ ├── script.js # Thème, recherche, filtres, multi-sélection, lecteur média +│ ├── custom_views.js # Vues Notes, Todos, Archive, Background Studio, Color Picker +│ ├── i18n.js # Système d'internationalisation côté client +│ ├── shaarit-rules.js # Détection notes/todos/pins et règles métier ShaarIt +│ ├── modules/ # Modules JS thématiques (theme, search, navigation, bulk-actions, a11y) +│ ├── awesomplete.min.js # Autocomplete des tags +│ ├── metadata.min.js # Récupération asynchrone des métadonnées de lien +│ ├── backgrounds-manifest.js # Manifest des fonds d'écran notes +│ └── backgrounds-manifest.json # Données des fonds d'écran +├── dist/ # Assets minifiés générés par le build (CSS + JS) +├── img/ +│ ├── favicon.png +│ ├── note-bg-light/ # Fonds d'écran clairs pour les notes +│ └── note-bg-dark/ # Fonds d'écran sombres pour les notes ├── theme_info.php # Métadonnées du thème +├── manifest.json # Manifest PWA +├── service-worker.js # Service Worker pour le cache offline +├── package.json # Dépendances et scripts de build +├── build.js # Pipeline de minification CSS/JS ├── includes.html # Head commun (meta, CSS, JS, config Shaarli) ├── page.header.html # Sidebar + Header + Search overlay + Filtres ├── page.footer.html # Footer + Bulk actions bar + Media player ├── linklist.html # Page principale des bookmarks ├── linklist.paging.html # Composant de pagination ├── daily.html # Vue quotidienne / hebdomadaire / mensuelle +├── dailyrss.html # Template RSS quotidien ├── editlink.html # Formulaire d'ajout/édition de bookmark +├── editlink.batch.html # Édition par lot ├── picwall.html # Mur d'images avec contrôle de taille ├── tag.cloud.html # Nuage de tags avec filtre alphabétique ├── tag.list.html # Liste de tags avec recherche dynamique ├── tag.sort.html # Navigation entre vues tag -├── tools.html # Page d'administration +├── tools.html # Page d'administration + gestion des thèmes et tags cachés +├── hidden-tags.html # Gestion des tags système cachés +├── player.html # Lecteur média popup (template autonome) +├── pluginscontent.html # Zone de contenu injectée par les plugins +├── install.html # Page d'installation Shaarli ├── loginform.html # Formulaire de connexion ├── configure.html # Page de configuration ├── changepassword.html # Changement de mot de passe @@ -288,12 +379,10 @@ shaarli-pro/ ├── export.html # Export de bookmarks ├── export.bookmarks.html # Template d'export Netscape ├── addlink.html # Ajout rapide de lien -├── editlink.batch.html # Édition par lot ├── thumbnails.html # Synchronisation des miniatures ├── opensearch.html # Descripteur OpenSearch ├── feed.rss.html # Template de flux RSS ├── feed.atom.html # Template de flux Atom -├── dailyrss.html # Template RSS quotidien ├── 404.html # Page d'erreur 404 ├── error.html # Page d'erreur générique └── page.html # Page wrapper diff --git a/shaarli-pro/build.js b/shaarli-pro/build.js new file mode 100644 index 0000000..6e3d74c --- /dev/null +++ b/shaarli-pro/build.js @@ -0,0 +1,235 @@ +#!/usr/bin/env node +/** + * Build pipeline for the Shaarli Pro theme. + * + * Actions: + * - Minify CSS with PostCSS + cssnano. + * - Minify JavaScript with Terser. + * - Compute content hashes for cache-busting. + * - Emit a manifest.json in dist/. + * + * Usage: + * npm run build # full build + * npm run build:css # CSS only + * npm run build:js # JS only + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); + +const terserAvailable = (function () { + try { + require.resolve('terser'); + return true; + } catch (e) { + return false; + } +})(); + +const postcssAvailable = (function () { + try { + require.resolve('postcss'); + require.resolve('cssnano'); + return true; + } catch (e) { + return false; + } +})(); + +const ROOT = __dirname; +const DIST_DIR = path.join(ROOT, 'dist'); +const MANIFEST_PATH = path.join(DIST_DIR, 'manifest.json'); + +const cssFiles = [ + 'css/style.css', + 'css/themes.css', + 'css/custom_views.css', + 'css/awesomplete.css' +]; + +const jsFiles = [ + 'js/i18n.js', + 'js/shaarit-rules.js', + 'js/modules/a11y.js', + 'js/modules/theme.js', + 'js/modules/navigation.js', + 'js/modules/search.js', + 'js/modules/bulk-actions.js', + 'js/script.js', + 'js/custom_views.js', + // Already minified vendor files – copy them as-is but still hash them. + 'js/awesomplete.min.js', + 'js/metadata.min.js' +]; + +const args = process.argv.slice(2); +const cssOnly = args.includes('--css-only'); +const jsOnly = args.includes('--js-only'); +const buildCss = !jsOnly; +const buildJs = !cssOnly; + +function hash(content) { + return crypto.createHash('sha256').update(content).digest('hex').slice(0, 12); +} + +function ensureDir(dir) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +async function minifyCss(file) { + if (!postcssAvailable) { + throw new Error('PostCSS / cssnano dependencies are missing. Run npm install.'); + } + const postcss = require('postcss'); + const cssnano = require('cssnano'); + + const inputPath = path.join(ROOT, file); + const content = fs.readFileSync(inputPath, 'utf8'); + const result = await postcss([cssnano({ preset: 'default' })]).process(content, { + from: inputPath, + to: inputPath + }); + return result.css; +} + +async function minifyJs(file) { + if (!terserAvailable) { + throw new Error('Terser dependency is missing. Run npm install.'); + } + const { minify } = require('terser'); + + const inputPath = path.join(ROOT, file); + const content = fs.readFileSync(inputPath, 'utf8'); + + const isVendor = file.includes('.min.js'); + if (isVendor) { + // Vendor files are already minified; just validate syntax and copy. + return content; + } + + const result = await minify(content, { + compress: { + drop_console: false, // keep user-facing console warnings + drop_debugger: true, + passes: 1 + }, + mangle: { + reserved: ['shaarli', 'ShaarliProI18n', 'ShaarliProUtils', 'ShaarliProA11y'] + }, + format: { + comments: /^!|@preserve|@license|copyright/i + }, + sourceMap: false + }); + + if (result.error) { + throw new Error(`Terser error in ${file}: ${result.error}`); + } + return result.code; +} + +async function build() { + const start = Date.now(); + if (fs.existsSync(DIST_DIR)) { + fs.rmSync(DIST_DIR, { recursive: true, force: true }); + } + ensureDir(DIST_DIR); + + const manifest = { + generatedAt: new Date().toISOString(), + files: {} + }; + + const processed = []; + const errors = []; + + if (buildCss) { + for (const file of cssFiles) { + const inputPath = path.join(ROOT, file); + if (!fs.existsSync(inputPath)) { + errors.push(`CSS file not found: ${file}`); + continue; + } + try { + const minified = await minifyCss(file); + const fileHash = hash(minified); + const baseName = path.basename(file, '.css'); + const ext = path.extname(file); + const outputName = `${baseName}.min${ext}`; + const outputPath = path.join(DIST_DIR, outputName); + fs.writeFileSync(outputPath, minified, 'utf8'); + manifest.files[file] = { + dist: `dist/${outputName}`, + hash: fileHash, + size: minified.length + }; + processed.push(`css/${outputName}`); + } catch (e) { + errors.push(`CSS ${file}: ${e.message}`); + } + } + } + + if (buildJs) { + for (const file of jsFiles) { + const inputPath = path.join(ROOT, file); + if (!fs.existsSync(inputPath)) { + errors.push(`JS file not found: ${file}`); + continue; + } + try { + const minified = await minifyJs(file); + const fileHash = hash(minified); + const baseName = path.basename(file, '.js'); + const ext = path.extname(file); + // Avoid double .min for vendor files already minified. + const outputName = baseName.endsWith('.min') + ? `${baseName}${ext}` + : `${baseName}.min${ext}`; + const outputPath = path.join(DIST_DIR, outputName); + fs.writeFileSync(outputPath, minified, 'utf8'); + manifest.files[file] = { + dist: `dist/${outputName}`, + hash: fileHash, + size: minified.length + }; + processed.push(`js/${outputName}`); + } catch (e) { + errors.push(`JS ${file}: ${e.message}`); + } + } + } + + fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2), 'utf8'); + + const duration = Date.now() - start; + + if (errors.length > 0) { + console.error('\nBuild completed with errors:'); + for (const err of errors) { + console.error(` ✗ ${err}`); + } + } + + if (processed.length > 0) { + console.log(`\nBuilt ${processed.length} file(s) in ${duration}ms:`); + for (const name of processed) { + console.log(` ✓ ${name}`); + } + } + + console.log(`Manifest written to ${path.relative(ROOT, MANIFEST_PATH)}`); + + if (errors.length > 0) { + process.exit(1); + } +} + +build().catch(err => { + console.error('Build failed:', err); + process.exit(1); +}); diff --git a/shaarli-pro/changetag.html b/shaarli-pro/changetag.html index f339ed8..bda33e3 100644 --- a/shaarli-pro/changetag.html +++ b/shaarli-pro/changetag.html @@ -15,17 +15,17 @@
{"Manage tags"|t}
-

All modifications are case sensitive.

+

{'All modifications are case sensitive.'|t}

- + + data-list="{loop="$tags"}{$key}, {/loop}" placeholder="{'Old tag'|t}"/>
- - + +

{'You can also edit tags in the'|t} {'tag list'|t}. diff --git a/shaarli-pro/configure.html b/shaarli-pro/configure.html index 12c9be5..d636d77 100644 --- a/shaarli-pro/configure.html +++ b/shaarli-pro/configure.html @@ -16,7 +16,7 @@

{'Configuration'|t}
- +
@@ -122,7 +122,7 @@
{'RSS direct links'|t}
-
Enabling it will show a permalink in the description.
+
{'Enabling it will show a permalink in the description.'|t}