Add asset pipeline and professionalize theme
CI / build (push) Failing after 6m2s

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.
This commit is contained in:
2026-08-09 09:25:03 -04:00
parent 10af62dd6c
commit f753b27542
68 changed files with 4743 additions and 1989 deletions
+39 -15
View File
@@ -3,6 +3,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="referrer" content="same-origin">
<link rel="icon" type="image/png" href="{$base_path}/{function="ltrim($asset_path, '/')"}/img/favicon.png">
<link rel="manifest" href="{$base_path}/{function="ltrim($asset_path, '/')"}/manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Shaarli Pro">
<link rel="apple-touch-icon" href="{$base_path}/{function="ltrim($asset_path, '/')"}/img/icons/icon-192x192.png">
<link rel="alternate" type="application/rss+xml" href="{$feedurl}?do=rss{$searchcrits}#" title="RSS Feed" />
<link rel="alternate" type="application/atom+xml" href="{$feedurl}?do=atom{$searchcrits}#" title="ATOM Feed" />
<link rel="search" type="application/opensearchdescription+xml" href="{$base_path}/open-search#" title="Shaarli search - {$shaarlititle}" />
@@ -10,17 +15,16 @@
<!-- Prevent dark mode flash (FOUC) - must run before CSS loads -->
<script>
(function() {
// Try to load theme config from bookmark first, fallback to localStorage
// Load theme preferences from localStorage to prevent FOUC
var themeMode = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
var themeId = localStorage.getItem('shaarit_theme_id') || 'DEFAULT';
// Try to fetch theme config from bookmark (async, but we'll use localStorage as fallback)
// This is a simplified check - the full sync happens in script.js
var bookmarkConfig = sessionStorage.getItem('themes_config');
if (bookmarkConfig) {
// Apply persisted theme config (saved by the theme picker)
var storedConfig = localStorage.getItem('shaarit_themes_config');
if (storedConfig) {
try {
var config = JSON.parse(bookmarkConfig);
var config = JSON.parse(storedConfig);
if (config.default) themeId = config.default;
if (config.mode) themeMode = config.mode;
} catch(e) {}
@@ -48,13 +52,13 @@
})();
</script>
<!-- Professional Theme CSS -->
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/css/style.css?v=1.0.4" />
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/css/themes.css?v=1.0.0" />
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/css/custom_views.css?v=1.0.5" />
<!-- Professional Theme CSS (minified build) -->
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/style.min.css?v={$version_hash}" />
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/themes.min.css?v={$version_hash}" />
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/custom_views.min.css?v={$version_hash}" />
{if="$pageName=='editlink' || $pageName=='addlink' || $pageName=='editlinkbatch'"}
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/css/awesomplete.css?v=1.0.4" />
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/js/awesomplete.min.js?v=1.0.4" defer></script>
<link type="text/css" rel="stylesheet" href="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/awesomplete.min.css?v={$version_hash}" />
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/awesomplete.min.js?v={$version_hash}" defer></script>
{/if}
{if="$pageName=='editlink' || $pageName=='editlinkbatch' || ($pageName=='linklist' && isset($search_tags) && preg_match('/(^|[\s,])note([\s,]|$)/i', (string) $search_tags))"}
@@ -68,6 +72,9 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
<!-- Google Fonts (loaded here instead of @import in CSS to avoid render block) -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Icons (Material Design Icons) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/MaterialDesign-Webfont/7.2.96/css/materialdesignicons.min.css" crossorigin="anonymous">
@@ -95,9 +102,26 @@ visibility: '{$visibility}',
untaggedonly: (function(){/*{if="$untaggedonly"}*/return true;/*{else}*/return false;/*{/if}*/})()
};
</script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/js/shaarit-rules.js?v=1.0.0" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/js/script.js?v=1.0.5" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/js/custom_views.js?v=1.0.6" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/i18n.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/a11y.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/shaarit-rules.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/theme.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/navigation.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/search.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/bulk-actions.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/script.min.js?v={$version_hash}" defer></script>
<script src="{$base_path}/{function="ltrim($asset_path, '/')"}/dist/custom_views.min.js?v={$version_hash}" defer></script>
<!-- Register PWA service worker -->
<script>
(function() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('{$base_path}/{function="ltrim($asset_path, '/')"}/service-worker.js')
.then(function() { /* service worker registered */ })
.catch(function() { /* registration failed, continue normally */ });
}
})();
</script>
{if="file_exists('tpl/shaarli-pro/extra.html')"}
{include="extra"}