feat: Introduce the Shaarli Professional theme with new templates, styles, scripts, and updated documentation.
This commit is contained in:
parent
5d486d3d97
commit
70ebda3079
317
README.md
317
README.md
@ -1,55 +1,204 @@
|
||||
# Shaarli BC Theme (Professional)
|
||||
# Shaarli Professional Theme
|
||||
|
||||
> Thème moderne pour [Shaarli](https://github.com/shaarli/Shaarli), pensé pour une expérience professionnelle, responsive et compatible modes clair/sombre.
|
||||
> Thème moderne et professionnel pour [Shaarli](https://github.com/shaarli/Shaarli), pensé pour une expérience utilisateur premium, responsive et performante.
|
||||
|
||||
## Présentation
|
||||
|
||||
- **Nom interne :** Professional
|
||||
- **Version :** 1.0.0
|
||||
- **Auteur :** Antigravity
|
||||
- **Dossier :** `tpl/shaarli-pro`
|
||||
| | |
|
||||
|---|---|
|
||||
| **Nom interne** | Professional (`shaarli-pro`) |
|
||||
| **Version** | 1.0.0 |
|
||||
| **Compatibilité** | Shaarli ≥ 0.9, PHP ≥ 7.4 |
|
||||
| **Licence** | MIT |
|
||||
| **Dossier** | `tpl/shaarli-pro` |
|
||||
|
||||
Le thème fournit un layout type application avec barre latérale fixe, navigation fluide, focus sur l’accessibilité et une couche JavaScript dédiée aux interactions.
|
||||
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é.
|
||||
|
||||
## Fonctionnalités principales
|
||||
---
|
||||
|
||||
1. **Mode clair/sombre natif** avec mémorisation du choix utilisateur.
|
||||
2. **Sidebar intelligente** : navigation compacte sur desktop, menu escamotable sur mobile.
|
||||
3. **Recherche “Spotlight”** en surimpression : raccourci `S`, navigation au clavier, surlignage des résultats.
|
||||
4. **Filtres rapides** pour isoler liens publics, privés ou non tagués.
|
||||
5. **Variables CSS centralisées** pour personnaliser palette, ombres, typographies.
|
||||
## Fonctionnalités
|
||||
|
||||
## Compatibilité et prérequis
|
||||
### 🎨 Design & Interface
|
||||
|
||||
- Shaarli ≥ 0.9 (sélection du thème depuis *Tools > Configure your Shaarli*).
|
||||
- Accès en écriture au répertoire `tpl/`.
|
||||
- Possibilité de modifier `data/config.json.php` pour les installations plus anciennes.
|
||||
- **Mode clair / sombre** avec bascule instantanée, mémorisation dans `localStorage`, et support de `prefers-color-scheme` du système.
|
||||
- **Anti-FOUC** : un script inline dans `<head>` applique le thème *avant* le chargement du CSS pour éviter le flash de contenu non stylé.
|
||||
- **Variables CSS centralisées** (`--primary`, `--bg-body`, `--text-main`, etc.) pour une personnalisation aisée de la palette, des ombres et de la typographie.
|
||||
- **Police Inter** (Google Fonts) pour une typographie moderne et lisible.
|
||||
- **Icônes Material Design Icons** (MDI Webfont v7.2) pour une iconographie cohérente et riche.
|
||||
- **Animations fluides** : transitions sur les cartes, menus, modales, filtres, et effets de hover.
|
||||
|
||||
### 📐 Layout Sidebar + Header
|
||||
|
||||
- **Sidebar fixe** sur desktop (230px) avec :
|
||||
- Logo et titre de l'instance
|
||||
- Navigation : Bookmarks, Tag Cloud, Picture Wall, Daily
|
||||
- Section Admin (si connecté) : Tools, Settings
|
||||
- Bouton « New Bookmark » (si connecté)
|
||||
- Toggle clair/sombre intégré
|
||||
- **Header sticky** avec :
|
||||
- Navigation principale (HOME, TAG CLOUD, PICTURE WALL, DAILY, SEARCH)
|
||||
- Actions rapides : filtres, multi-sélection, RSS, outils, login/logout
|
||||
- Support des boutons de plugins (`plugins_header.buttons_toolbar`)
|
||||
- **Responsive** : la sidebar se transforme en menu escamotable (hamburger) sous 1024px.
|
||||
|
||||
### 🔍 Recherche « Spotlight »
|
||||
|
||||
- Modale de recherche en surimpression inspirée de macOS Spotlight.
|
||||
- **Raccourci clavier** : touche `S` pour ouvrir, `ESC` pour fermer.
|
||||
- **Deux modes** : recherche par texte libre ou filtrage par tags.
|
||||
- **Résultats en temps réel** avec défilement au clavier (↑ / ↓) et sélection via `Enter`.
|
||||
- **Surlignage** (highlight) des termes correspondants dans les résultats.
|
||||
- **Debounce** (150ms) pour optimiser les performances lors de la saisie.
|
||||
|
||||
### 🎛️ Filtres Rapides
|
||||
|
||||
- **Panneau de filtres** en dropdown depuis le header :
|
||||
- Nombre de liens par page (20, 50, 100, ou valeur personnalisée)
|
||||
- Filtrage par visibilité : liens privés, publics, ou tous (si connecté)
|
||||
- Filtrage par tags : liens non tagués uniquement
|
||||
- **Bannière d'information** dynamique indiquant les filtres actifs avec compteur de résultats.
|
||||
- **Indicateur visuel** (badge pulsant) sur le bouton filtre lorsque des filtres sont actifs.
|
||||
- **Bouton "Clear"** pour réinitialiser tous les filtres en un clic.
|
||||
|
||||
### 📄 Trois Vues de Liens
|
||||
|
||||
| Vue | Description |
|
||||
|-----|-------------|
|
||||
| **Grille** | Cartes avec miniatures, description tronquée (3 lignes), tags et actions. Idéal pour le parcours visuel. |
|
||||
| **Liste** | Disposition horizontale avec miniature latérale en fondu, description (2 lignes), footer avec tags et actions. |
|
||||
| **Compact** | Mode dense style tableau : titre + URL sur une ligne, tags et actions alignés à droite. Parfait pour les grandes collections. |
|
||||
|
||||
- Le choix de vue est persisté dans `localStorage`.
|
||||
- Chaque vue a son propre positionnement pour : badges de visibilité, cases de sélection, boutons d'action.
|
||||
|
||||
### ✅ Multi-Sélection & Actions Groupées
|
||||
|
||||
- **Mode sélection** activable via le bouton dans le header.
|
||||
- Cases à cocher sur chaque bookmark (apparaissent au survol ou en mode sélection).
|
||||
- **Barre d'actions groupées** fixe en bas avec :
|
||||
- Compteur de sélection
|
||||
- « Select all » pour sélectionner tous les liens visibles
|
||||
- Actions : DELETE, SET PUBLIC, SET PRIVATE
|
||||
- Bouton CANCEL pour quitter le mode
|
||||
|
||||
### 🖼️ Picture Wall (Mur d'Images)
|
||||
|
||||
- **Grille responsive** avec taille d'image configurable via un slider (120px–400px).
|
||||
- **Boutons +/−** pour ajuster rapidement la taille.
|
||||
- **Persistance** de la taille choisie dans `localStorage`.
|
||||
- **Overlay au survol** avec titre et URL du bookmark.
|
||||
- **Animations** : zoom léger de l'image au survol, apparition de l'overlay en slide.
|
||||
- **Indicateur de lien externe** (icône en haut à droite au survol).
|
||||
|
||||
### 📅 Page Daily
|
||||
|
||||
- Navigation jour / semaine / mois avec boutons de sélection.
|
||||
- Affichage chronologique des bookmarks avec :
|
||||
- Titre cliquable vers l'URL originale
|
||||
- Miniature (si disponible)
|
||||
- Description formatée
|
||||
- Horodatage et tags
|
||||
- Navigation précédent / suivant entre les périodes.
|
||||
- Support des zones de plugins.
|
||||
|
||||
### 🎵 Lecteur Multimédia Persistant
|
||||
|
||||
- **Détection automatique** des URLs audio/streaming dans les bookmarks.
|
||||
- Bouton ▶ injecté dans la barre d'actions des bookmarks contenant des médias.
|
||||
- **Barre de lecture fixe** en bas de page avec :
|
||||
- Lecture / Pause
|
||||
- Barre de progression interactive
|
||||
- Affichage du temps (ou « LIVE » pour les flux en continu)
|
||||
- Contrôle de volume avec bouton mute
|
||||
- Bouton de fermeture
|
||||
- **Persistance entre pages** : le lecteur reprend la lecture au même point lors de la navigation.
|
||||
- Formats supportés : MP3, OGG, FLAC, WAV, AAC, M4A, OPUS, WMA, WEBM, M3U8, M3U, PLS.
|
||||
|
||||
### 🔌 Intégration des Plugins
|
||||
|
||||
- **QR Code** : remplacement de l'image inline par une icône MDI + modale élégante avec animation.
|
||||
- **ReadItLater** :
|
||||
- Icônes MDI (œil ouvert/fermé) au lieu du texte brut
|
||||
- Badge « To Read » positionné sur les bookmarks non lus
|
||||
- Bordure rouge sur les bookmarks à lire
|
||||
- Masquage du bouton « Mark as Read » dans la zone de pagination
|
||||
- **Zones de plugins** : toutes les zones Shaarli standard sont supportées (`plugin_start_zone`, `plugin_end_zone`, `link_plugin`, `edit_link_plugin`, `buttons_toolbar`, etc.).
|
||||
|
||||
### 📄 Pages d'Administration
|
||||
|
||||
- **Outils** : liste moderne avec icônes, labels, sous-labels et chevrons de navigation.
|
||||
- **Configuration** : formulaires stylés avec le design system du thème.
|
||||
- **Gestion des tags** : renommage et suppression avec interface claire.
|
||||
- **Import / Export** : pages de gestion des bookmarks Netscape.
|
||||
- **Plugins Admin** : configuration et activation/désactivation des plugins.
|
||||
- **Statistiques** : affichage du nombre total de liens et liens privés.
|
||||
- **Bookmarklets** : boutons « Shaare link » et « Add Note » prêts à glisser.
|
||||
- **Apps tierces** : liens rapides vers les extensions Firefox, Chrome, et apps Android/iOS.
|
||||
|
||||
### ♿ Accessibilité & Performance
|
||||
|
||||
- `:focus-visible` pour les éléments interactifs (clavier uniquement).
|
||||
- `content-visibility: auto` sur les cartes de bookmarks pour un rendu optimisé.
|
||||
- `loading="lazy"` sur les images et miniatures.
|
||||
- Support `@media print` : masquage automatique de la sidebar, header et actions.
|
||||
- Markup sémantique HTML5.
|
||||
- Attributs `title` sur tous les boutons et liens d'action.
|
||||
|
||||
### 📱 Responsive Design
|
||||
|
||||
| Breakpoint | Adaptations |
|
||||
|------------|-------------|
|
||||
| `> 1024px` | Layout complet sidebar + contenu |
|
||||
| `≤ 1024px` | Sidebar escamotable, menu mobile, textes de nav masqués |
|
||||
| `≤ 768px` | Grille en colonne unique, toolbar empilé, header nav masqué |
|
||||
| `≤ 480px` | Paddings réduits, vue liste en colonne, miniatures pleine largeur |
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Télécharger** ce dépôt ou récupérer uniquement `shaarli-pro/` via la page des releases.
|
||||
### 1. Téléchargement
|
||||
|
||||
```bash
|
||||
git clone https://git.dracodev.net/Projets/Shaarli_bm_theme.git
|
||||
git clone https://github.com/votre-utilisateur/shaarli_bc_theme.git
|
||||
```
|
||||
2. **Copier** le dossier `shaarli-pro/` dans `tpl/` à côté du dossier `default/`.
|
||||
|
||||
### 2. Copie dans Shaarli
|
||||
|
||||
Copier le dossier `shaarli-pro/` dans le répertoire `tpl/` de votre instance Shaarli, à côté du dossier `default/` :
|
||||
|
||||
```bash
|
||||
docker cp "./shaarli-pro" myshaarli:/var/www/shaarli/tpl/
|
||||
# Via Docker
|
||||
docker cp "./shaarli-pro" shaarli_bookmarks:/var/www/shaarli/tpl/
|
||||
|
||||
# Ou manuellement
|
||||
cp -r shaarli-pro/ /path/to/shaarli/tpl/
|
||||
```
|
||||
3. **Vérifier les droits** (lecture par l’utilisateur du serveur web).
|
||||
|
||||
### 3. Permissions
|
||||
|
||||
```bash
|
||||
docker exec -it myshaarli chown -R www-data:www-data /var/www/shaarli/tpl/
|
||||
# Docker
|
||||
docker exec -it shaarli_bookmarks chown -R nginx:nginx /var/www/shaarli/tpl/shaarli-pro
|
||||
|
||||
# Manuel
|
||||
chown -R www-data:www-data /path/to/shaarli/tpl/shaarli-pro/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Activation
|
||||
|
||||
### Via l’interface (recommandé)
|
||||
1. Connectez-vous à Shaarli en tant qu’administrateur.
|
||||
### Via l'interface (recommandé)
|
||||
|
||||
1. Connectez-vous à Shaarli en tant qu'administrateur.
|
||||
2. Ouvrez **Tools > Configure your Shaarli**.
|
||||
3. Choisissez **Professional (shaarli-pro)** dans la liste des thèmes.
|
||||
4. Sauvegardez, puis rafraîchissez la page d’accueil.
|
||||
4. Sauvegardez, puis rafraîchissez la page d'accueil.
|
||||
|
||||
### Activation manuelle (Shaarli < 0.9)
|
||||
Ajoutez ou mettez à jour `data/config.json.php` :
|
||||
|
||||
Modifiez `data/config.json.php` :
|
||||
|
||||
```json
|
||||
{
|
||||
@ -58,35 +207,115 @@ Ajoutez ou mettez à jour `data/config.json.php` :
|
||||
}
|
||||
}
|
||||
```
|
||||
ou utiliser cette commande docker
|
||||
|
||||
Ou via Docker :
|
||||
|
||||
```bash
|
||||
docker exec -it myshaarli sed -i 's/"theme": "default"/"theme": "shaarli-pro"/' /var/www/shaarli/data/config.json.php
|
||||
```
|
||||
|
||||
Redémarrez votre service PHP si nécessaire et videz le cache navigateur.
|
||||
Redémarrez PHP si nécessaire et videz le cache navigateur.
|
||||
|
||||
## Configuration & personnalisation
|
||||
---
|
||||
|
||||
- **Palette et typographies** : modifiez les variables dans `shaarli-pro/css/style.css` ou surchargez-les proprement via `data/user.css`.
|
||||
- **Comportements front** : adaptez `shaarli-pro/js/script.js` (toggle thème, recherche, filtres, sidebar).
|
||||
- **CSS/JS personnalisés** : placez vos ajouts dans `data/user.css` ou créez un template additionnel (`extra.html`) pour de futurs scripts.
|
||||
## Configuration & Personnalisation
|
||||
|
||||
## Structure utile
|
||||
### Palette et Typographies
|
||||
|
||||
- `shaarli-pro/css/style.css` : styles principaux et variables.
|
||||
- `shaarli-pro/js/script.js` : interactions (mode sombre, recherche, filtres, sidebar mobile).
|
||||
- `shaarli-pro/*.html` : templates RainTPL couvrant toutes les vues Shaarli.
|
||||
- `example_Shaarli-Material/` : thème hérité servant d’exemple/sandbox.
|
||||
Modifiez les variables CSS dans `shaarli-pro/css/style.css` section `:root` et `[data-theme="dark"]`, ou surchargez-les proprement via `data/user.css` :
|
||||
|
||||
## Mise à jour
|
||||
```css
|
||||
/* data/user.css */
|
||||
:root {
|
||||
--primary: #8b5cf6; /* Violet au lieu de bleu */
|
||||
--primary-hover: #7c3aed;
|
||||
--bg-body: #fafaf9;
|
||||
}
|
||||
```
|
||||
|
||||
1. Sauvegardez vos données (`data/`) et vos surcharges.
|
||||
### Comportements JavaScript
|
||||
|
||||
Adaptez `shaarli-pro/js/script.js` pour personnaliser :
|
||||
- Le mode de recherche par défaut (tags vs texte)
|
||||
- Les extensions audio reconnues par le lecteur
|
||||
- Le comportement des filtres
|
||||
|
||||
### Templates additionnels
|
||||
|
||||
Créez un fichier `tpl/shaarli-pro/extra.html` pour injecter du CSS/JS supplémentaire — il sera automatiquement inclus s'il existe.
|
||||
|
||||
---
|
||||
|
||||
## Structure du Projet
|
||||
|
||||
```
|
||||
shaarli-pro/
|
||||
├── css/
|
||||
│ └── style.css # Styles principaux + variables CSS (light/dark)
|
||||
├── js/
|
||||
│ └── script.js # Interactions (thème, recherche, filtres, sélection, lecteur)
|
||||
├── theme_info.php # Métadonnées du thème
|
||||
├── 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
|
||||
├── editlink.html # Formulaire d'ajout/édition de bookmark
|
||||
├── 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
|
||||
├── loginform.html # Formulaire de connexion
|
||||
├── configure.html # Page de configuration
|
||||
├── changepassword.html # Changement de mot de passe
|
||||
├── changetag.html # Gestion des tags
|
||||
├── pluginsadmin.html # Administration des plugins
|
||||
├── server.html # Informations serveur
|
||||
├── server.requirements.html # Prérequis serveur
|
||||
├── import.html # Import de bookmarks
|
||||
├── 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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Raccourcis Clavier
|
||||
|
||||
| Touche | Action |
|
||||
|--------|--------|
|
||||
| `S` | Ouvrir la recherche Spotlight |
|
||||
| `ESC` | Fermer la recherche / les filtres / les modales |
|
||||
| `↑` / `↓` | Naviguer dans les résultats de recherche |
|
||||
| `Enter` | Sélectionner le résultat courant |
|
||||
|
||||
---
|
||||
|
||||
## Mise à Jour
|
||||
|
||||
1. Sauvegardez vos personnalisations (`data/user.css`, `extra.html`).
|
||||
2. Remplacez le dossier `tpl/shaarli-pro/` par la nouvelle version.
|
||||
3. Purgez les caches navigateur/OPcache.
|
||||
3. Purgez les caches navigateur et OPcache.
|
||||
4. Vérifiez le bon fonctionnement de vos plugins.
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
- Ouvrez une issue GitHub en joignant captures et logs.
|
||||
- Indiquez vos versions (Shaarli, thème, plugins activés) pour faciliter le diagnostic.
|
||||
- Ouvrez une issue en joignant captures d'écran et logs.
|
||||
- Indiquez vos versions : Shaarli, PHP, thème et plugins activés.
|
||||
|
||||
Bon partage de liens !
|
||||
---
|
||||
|
||||
Bon partage de liens ! 🔖
|
||||
|
||||
@ -2387,9 +2387,6 @@ select:focus {
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.picwall-pictureframe {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@ -2450,7 +2447,6 @@ select:focus {
|
||||
}
|
||||
|
||||
.picwall-title {
|
||||
display: block;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
@ -3668,4 +3664,83 @@ select:focus {
|
||||
.media-player-time {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Footer ===== */
|
||||
.footer-main {
|
||||
margin-top: 3rem;
|
||||
padding: 2rem 0;
|
||||
border-top: 1px solid var(--border);
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.footer-main a {
|
||||
color: var(--text-main);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.footer-main a:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.footer-feeds {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.footer-separator {
|
||||
margin: 0 0.5rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.plugin-footer-text {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* ===== Tools Page ===== */
|
||||
.page-tools .list-sortable-handle {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin-right: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-tools .list-sortable-handle i {
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.page-tools .list-group-item>i {
|
||||
color: var(--text-muted);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bookmarklet-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.third-party-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.third-party-links .btn i {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
/* Utility */
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
@ -77,12 +77,12 @@
|
||||
{if="!$hide_timestamps || $is_logged_in"}
|
||||
<a href="{$base_path}/shaare/{$value.shorturl}" class="daily-item-footer-subtitle" title="{'Permalink'|t}">
|
||||
{if="$type === 'week'"}
|
||||
{function="strftime('%a %e %b, %H:%M', $link.timestamp)"}
|
||||
{function="date('D j M, H:i', $link.timestamp)"}
|
||||
{else}
|
||||
{if="$type === 'month'"}
|
||||
{function="strftime('%a %e %b %Y, %H:%M', $link.timestamp)"}
|
||||
{function="date('D j M Y, H:i', $link.timestamp)"}
|
||||
{else}
|
||||
{function="strftime('%H:%M', $link.timestamp)"}
|
||||
{function="date('H:i', $link.timestamp)"}
|
||||
{/if}
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
@ -40,13 +40,13 @@
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="lf_title{$index}">{'Title'|t}</label>
|
||||
<div class="{$asyncLoadClass}">
|
||||
<input type="text" class="form-control" name="lf_title" id="lf_title{$index}" {if="empty($batch_mode) && $link.title==''"}class="autofocus form-control"{else}class="form-control"{/if} value="{$link.title}" placeholder="Page title">
|
||||
<input type="text" class="form-control{if="empty($batch_mode) && $link.title==''"} autofocus{/if}" name="lf_title" id="lf_title{$index}" value="{$link.title}" placeholder="Page title">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="lf_description{$index}">{'Description'|t}</label>
|
||||
<div class="{$asyncLoadClass}">
|
||||
<textarea class="form-control" name="lf_description" id="lf_description{$index}" {if="empty($batch_mode) && $link.description==''"}class="autofocus form-control"{/if} placeholder="Add a description..." rows="5">{$link.description}</textarea>
|
||||
<textarea class="form-control{if="empty($batch_mode) && $link.description==''"} autofocus{/if}" name="lf_description" id="lf_description{$index}" placeholder="Add a description..." rows="5">{$link.description}</textarea>
|
||||
</div>
|
||||
{if="$formatter==='markdown'"}
|
||||
<div class="sublabel">
|
||||
|
||||
@ -1140,30 +1140,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
'.mp3', '.mp4', '.ogg', '.webm', '.m3u8', '.flac', '.wav', '.aac',
|
||||
'.m4a', '.opus', '.wma', '.oga', '.m3u', '.pls'
|
||||
];
|
||||
const STREAM_PATTERNS = [
|
||||
'icecast', 'shoutcast', 'stream', 'radio', 'listen', 'audio',
|
||||
'/live', '.stream'
|
||||
];
|
||||
|
||||
function isMediaUrl(url) {
|
||||
if (!url) return false;
|
||||
const lower = url.toLowerCase();
|
||||
// Check file extensions
|
||||
// Strip query params and fragment for cleaner extension matching
|
||||
const pathname = lower.split('?')[0].split('#')[0];
|
||||
// Check file extensions at end of path
|
||||
for (const ext of MEDIA_EXTENSIONS) {
|
||||
if (lower.includes(ext)) return true;
|
||||
if (pathname.endsWith(ext)) return true;
|
||||
}
|
||||
// Check streaming patterns
|
||||
for (const pattern of STREAM_PATTERNS) {
|
||||
if (lower.includes(pattern) && (lower.includes('http') || lower.includes('//'))) {
|
||||
// Must also look like an audio/stream URL (not just any page about audio)
|
||||
if (lower.endsWith('.mp3') || lower.endsWith('.ogg') || lower.endsWith('.m3u8') ||
|
||||
lower.endsWith('.aac') || lower.endsWith('.flac') || lower.endsWith('.m4a') ||
|
||||
lower.endsWith('.wav') || lower.endsWith('.opus') || lower.endsWith('.pls') ||
|
||||
lower.endsWith('.m3u') || lower.includes('.mp3') || lower.includes('.ogg') ||
|
||||
lower.includes('stream') || lower.includes('icecast') || lower.includes('listen')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Check streaming URL patterns (path-segment matching)
|
||||
if (/\/(stream|listen|live|icecast|shoutcast)(\/|$|\?)/i.test(url)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
</a>
|
||||
<span class="link-url">{$value.url}</span>
|
||||
<div class="link-meta">
|
||||
<span class="link-date">{function="strftime('%c', $value.timestamp)"}</span>
|
||||
<span class="link-date">{$value.created|format_date}</span>
|
||||
<a href="{$base_path}/shaare/{$value.shorturl}" class="link-permalink" title="Permalink"><i class="mdi mdi-link-variant"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html{if="$language !== 'auto'"} lang="{$language}"{/if}>
|
||||
|
||||
<head>
|
||||
{$pageName="linklist"}
|
||||
{include="includes"}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{include="page.header"}
|
||||
|
||||
<div id="linklist">
|
||||
{loop="$plugin_start_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
|
||||
<!-- Toolbar with View Toggle -->
|
||||
<div class="content-toolbar">
|
||||
<div class="toolbar-left">
|
||||
{include="linklist.paging"}
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<div class="view-toggle">
|
||||
<button class="view-toggle-btn active" id="view-grid-btn" title="Grid View">
|
||||
<i class="mdi mdi-view-grid"></i>
|
||||
</button>
|
||||
<button class="view-toggle-btn" id="view-list-btn" title="List View">
|
||||
<i class="mdi mdi-view-list"></i>
|
||||
</button>
|
||||
<button class="view-toggle-btn" id="view-compact-btn" title="Compact View">
|
||||
<i class="mdi mdi-view-headline"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if="count($links)==0"}
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">
|
||||
<i class="mdi mdi-bookmark-off-outline"></i>
|
||||
</div>
|
||||
<h2 class="empty-state-title">No bookmarks found</h2>
|
||||
<p class="empty-state-text">
|
||||
{if="!empty($search_term)"}
|
||||
No results for: <strong>{$search_term}</strong>
|
||||
{else}
|
||||
Start adding bookmarks to see them here.
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
{else}
|
||||
|
||||
<div class="links-list view-grid" id="links-list">
|
||||
{loop="$links"}
|
||||
<div id="{$value.id}" class="link-outer{if="isset($value.sticky) && $value.sticky"} is-sticky{/if}{if="$value.class"} {$value.class}{/if}{if="$value.private"} private{else} public{/if}" data-id="{$value.id}">
|
||||
@ -1,15 +1,15 @@
|
||||
<footer class="footer-main" style="margin-top: 3rem; padding: 2rem 0; border-top: 1px solid var(--border); text-align: center; color: var(--text-secondary); font-size: 0.9rem;">
|
||||
<footer class="footer-main">
|
||||
<p>
|
||||
Powered by <a href="https://github.com/shaarli/Shaarli" target="_blank" rel="noopener" style="color:var(--text-main);">Shaarli</a>.
|
||||
Powered by <a href="https://github.com/shaarli/Shaarli" target="_blank" rel="noopener">Shaarli</a>.
|
||||
{if="$is_logged_in"}
|
||||
<span class="footer-divider">|</span> <a href="{$base_path}/admin/tools" style="color:var(--text-main);">Administration</a>
|
||||
<span class="footer-separator">|</span> <a href="{$base_path}/admin/tools">Administration</a>
|
||||
{/if}
|
||||
</p>
|
||||
<div class="footer-feeds" style="margin-top: 0.5rem;">
|
||||
<div class="footer-feeds">
|
||||
<a href="{$base_path}/feed/rss" title="RSS Feed"><i class="mdi mdi-rss"></i> RSS</a>
|
||||
<span style="margin: 0 0.5rem;">•</span>
|
||||
<span class="footer-separator">•</span>
|
||||
<a href="{$base_path}/feed/atom" title="Atom Feed">Atom</a>
|
||||
<span style="margin: 0 0.5rem;">•</span>
|
||||
<span class="footer-separator">•</span>
|
||||
<a href="{$base_path}/daily" title="Daily">Daily</a>
|
||||
</div>
|
||||
|
||||
|
||||
@ -15,76 +15,76 @@
|
||||
<div class="card-header">{'Settings'|t}</div>
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/configure">
|
||||
<div class="list-sortable-handle" style="cursor: pointer; padding: 0; margin-right: 1rem;">
|
||||
<i class="mdi mdi-cog" style="font-size: 1.5rem; color: var(--primary);"></i>
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-cog"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Configure your Shaarli'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Change Title, timezone...'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right" style="color: var(--text-muted);"></i>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
{if="!$openshaarli"}
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/password">
|
||||
<div class="list-sortable-handle" style="cursor: pointer; padding: 0; margin-right: 1rem;">
|
||||
<i class="mdi mdi-lock" style="font-size: 1.5rem; color: var(--primary);"></i>
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-lock"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Change password'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Change your password'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right" style="color: var(--text-muted);"></i>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
{/if}
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/plugins">
|
||||
<div class="list-sortable-handle" style="cursor: pointer; padding: 0; margin-right: 1rem;">
|
||||
<i class="mdi mdi-puzzle" style="font-size: 1.5rem; color: var(--primary);"></i>
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-puzzle"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Plugin administration'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Enable, disable and configure plugins'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right" style="color: var(--text-muted);"></i>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/server">
|
||||
<div class="list-sortable-handle" style="cursor: pointer; padding: 0; margin-right: 1rem;">
|
||||
<i class="mdi mdi-server" style="font-size: 1.5rem; color: var(--primary);"></i>
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-server"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Server administration'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Check instance\'s server configuration'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right" style="color: var(--text-muted);"></i>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/tags">
|
||||
<div class="list-sortable-handle" style="cursor: pointer; padding: 0; margin-right: 1rem;">
|
||||
<i class="mdi mdi-tag-multiple" style="font-size: 1.5rem; color: var(--primary);"></i>
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-tag-multiple"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Manage Tags'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Rename or delete a tag in all links'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right" style="color: var(--text-muted);"></i>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/import">
|
||||
<div class="list-sortable-handle" style="cursor: pointer; padding: 0; margin-right: 1rem;">
|
||||
<i class="mdi mdi-file-import" style="font-size: 1.5rem; color: var(--primary);"></i>
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-file-import"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Import'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Import Netscape html bookmarks'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right" style="color: var(--text-muted);"></i>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action ripple" href="{$base_path}/admin/export">
|
||||
<div class="list-sortable-handle" style="cursor: pointer; padding: 0; margin-right: 1rem;">
|
||||
<i class="mdi mdi-file-export" style="font-size: 1.5rem; color: var(--primary);"></i>
|
||||
<div class="list-sortable-handle">
|
||||
<i class="mdi mdi-file-export"></i>
|
||||
</div>
|
||||
<div class="list-group-item-content">
|
||||
<div class="list-group-item-label">{'Export'|t}</div>
|
||||
<div class="list-group-item-sublabel">{'Export Netscape html bookmarks'|t}</div>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-right" style="color: var(--text-muted);"></i>
|
||||
<i class="mdi mdi-chevron-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -136,17 +136,13 @@
|
||||
<p class="text-muted" style="margin-bottom: 1.5rem;">
|
||||
Drag these buttons to your bookmarks bar to quickly add links from any page.
|
||||
</p>
|
||||
<div class="row" style="display: flex; gap: 1rem; justify-content: center;">
|
||||
<div class="col-xs-6 text-center">
|
||||
<a class="btn btn-primary" href="javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;var%20desc=document.getSelection().toString();if(desc.length>4000){desc=desc.substr(0,4000)+'...';alert('{function="str_replace(' ', '%20', t('The selected text is too long, it will be truncated.'))"}');}window.open('{$pageabsaddr}admin/shaare?post='%20+%20encodeURIComponent(url)+'&title='%20+%20encodeURIComponent(title)+'&description='%20+%20encodeURIComponent(desc)+'&source=bookmarklet','_blank','menubar=no,height=800,width=600,toolbar=no,scrollbars=yes,status=no,dialog=1');})();">
|
||||
<i class="mdi mdi-bookmark-plus"></i> {'Shaare link'|t}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-6 text-center">
|
||||
<a class="btn btn-secondary" href="javascript:(function(){var desc=document.getSelection().toString();if(desc.length>4000){desc=desc.substr(0,4000)+'...';alert('{function="str_replace(' ', '%20', t('The selected text is too long, it will be truncated.'))"}');}window.open('{$pageabsaddr}?private=1&post='+'&description='%20+%20encodeURIComponent(desc)+'&source=bookmarklet','_blank','menubar=no,height=800,width=600,toolbar=no,scrollbars=yes,status=no,dialog=1');})();">
|
||||
<i class="mdi mdi-note-plus"></i> {'Add Note'|t}
|
||||
</a>
|
||||
</div>
|
||||
<div class="bookmarklet-actions">
|
||||
<a class="btn btn-primary" href="javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;var%20desc=document.getSelection().toString();if(desc.length>4000){desc=desc.substr(0,4000)+'...';alert('{function="str_replace(' ', '%20', t('The selected text is too long, it will be truncated.'))"}');}window.open('{$pageabsaddr}admin/shaare?post='%20+%20encodeURIComponent(url)+'&title='%20+%20encodeURIComponent(title)+'&description='%20+%20encodeURIComponent(desc)+'&source=bookmarklet','_blank','menubar=no,height=800,width=600,toolbar=no,scrollbars=yes,status=no,dialog=1');})();">
|
||||
<i class="mdi mdi-bookmark-plus"></i> {'Shaare link'|t}
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="javascript:(function(){var desc=document.getSelection().toString();if(desc.length>4000){desc=desc.substr(0,4000)+'...';alert('{function="str_replace(' ', '%20', t('The selected text is too long, it will be truncated.'))"}');}window.open('{$pageabsaddr}?private=1&post='+'&description='%20+%20encodeURIComponent(desc)+'&source=bookmarklet','_blank','menubar=no,height=800,width=600,toolbar=no,scrollbars=yes,status=no,dialog=1');})();">
|
||||
<i class="mdi mdi-note-plus"></i> {'Add Note'|t}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -158,21 +154,21 @@
|
||||
<div class="card">
|
||||
<div class="card-header">{'3rd party'|t}</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group" style="flex-direction: row; flex-wrap: wrap; gap: 1rem; justify-content: center;">
|
||||
<div class="third-party-links">
|
||||
<a href="https://addons.mozilla.org/fr/firefox/addon/shaarli/" target="_blank" class="btn btn-secondary">
|
||||
<i class="mdi mdi-firefox" style="font-size: 1.2rem;"></i> Firefox
|
||||
<i class="mdi mdi-firefox"></i> Firefox
|
||||
</a>
|
||||
<a href="https://chromewebstore.google.com/detail/add-to-shaarli/jhfblapoehcfajokolimghdfmeeakbee" target="_blank" class="btn btn-secondary">
|
||||
<i class="mdi mdi-google-chrome" style="font-size: 1.2rem;"></i> Chrome
|
||||
<i class="mdi mdi-google-chrome"></i> Chrome
|
||||
</a>
|
||||
<a href="https://f-droid.org/fr/packages/com.dimtion.shaarlier/" target="_blank" class="btn btn-secondary">
|
||||
<i class="mdi mdi-android" style="font-size: 1.2rem;"></i> Shaarlier
|
||||
<i class="mdi mdi-android"></i> Shaarlier
|
||||
</a>
|
||||
<a href="https://stakali.toneiv.eu/" target="_blank" class="btn btn-secondary">
|
||||
<i class="mdi mdi-android" style="font-size: 1.2rem;"></i> Stakali
|
||||
<i class="mdi mdi-android"></i> Stakali
|
||||
</a>
|
||||
<a href="https://github.com/lockcp/ShaarliOS" target="_blank" class="btn btn-secondary">
|
||||
<i class="mdi mdi-apple-ios" style="font-size: 1.2rem;"></i> iOS
|
||||
<i class="mdi mdi-apple-ios"></i> iOS
|
||||
</a>
|
||||
</div>
|
||||
<p class="text-center text-muted" style="margin-top: 1.5rem;">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user