core-maugli 1.2.34 → 1.2.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "core-maugli",
3
3
  "description": "Astro & Tailwind CSS blog theme for Maugli.",
4
4
  "type": "module",
5
- "version": "1.2.34",
5
+ "version": "1.2.36",
6
6
  "license": "GPL-3.0-or-later OR Commercial",
7
7
  "repository": {
8
8
  "type": "git",
@@ -22,7 +22,7 @@
22
22
  "dev": "node resize-all.cjs && node scripts/generate-previews.js && astro dev",
23
23
  "prestart": "node resize-all.cjs && node scripts/generate-previews.js",
24
24
  "start": "astro dev",
25
- "build": "node scripts/optimize-images.cjs && node typograf-batch.js && node scripts/verify-assets.js && node scripts/generate-previews.js && astro build",
25
+ "build": "node scripts/flatten-images.cjs && node scripts/optimize-images.cjs && node typograf-batch.js && node scripts/verify-assets.js && node scripts/generate-previews.js && astro build",
26
26
  "build:fast": "node resize-all.cjs && node typograf-batch.js && node scripts/verify-assets.js && node scripts/generate-previews.js && astro build",
27
27
  "optimize": "node scripts/optimize-images.cjs",
28
28
  "optimize:squoosh": "node scripts/squoosh-optimize.js",
Binary file
Binary file
Binary file
@@ -0,0 +1,66 @@
1
+ // copy-content-images.cjs - выносим все изображения из подпапок public/img в корень public/img
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const sourceDir = './public/img';
6
+ const targetDir = './public/img';
7
+
8
+ // Функция для рекурсивного поиска и копирования изображений из подпапок
9
+ async function flattenImages(sourceDir, targetDir) {
10
+ if (!fs.existsSync(sourceDir)) {
11
+ console.log(`📁 Папка ${sourceDir} не существует, пропускаем`);
12
+ return 0;
13
+ }
14
+
15
+ const items = fs.readdirSync(sourceDir);
16
+ let copiedCount = 0;
17
+
18
+ for (const item of items) {
19
+ const sourcePath = path.join(sourceDir, item);
20
+ const stat = fs.statSync(sourcePath);
21
+
22
+ if (stat.isDirectory()) {
23
+ // Рекурсивно обрабатываем подпапки
24
+ const copied = await flattenImages(sourcePath, targetDir);
25
+ copiedCount += copied;
26
+ } else if (stat.isFile()) {
27
+ const ext = path.extname(item).toLowerCase();
28
+
29
+ // Проверяем, что это изображение и что оно в подпапке (не в корне)
30
+ if (['.jpg', '.jpeg', '.png', '.webp', '.gif', '.svg'].includes(ext)) {
31
+ const relativePath = path.relative(sourceDir, sourcePath);
32
+
33
+ // Если файл не в корне public/img, то копируем его в корень
34
+ if (relativePath.includes(path.sep)) {
35
+ const targetPath = path.join(targetDir, item);
36
+
37
+ // Проверяем, что файл с таким именем не существует в корне
38
+ if (!fs.existsSync(targetPath)) {
39
+ fs.copyFileSync(sourcePath, targetPath);
40
+ console.log(`📋 Вынесено: ${relativePath} → ${item}`);
41
+ copiedCount++;
42
+ } else {
43
+ console.log(`⚠️ Пропущено (уже существует): ${item}`);
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ return copiedCount;
51
+ }
52
+
53
+ async function main() {
54
+ console.log('🚀 Начинаем вынос изображений из подпапок public/img в корень public/img...');
55
+
56
+ // Выносим все изображения из подпапок в корень
57
+ const totalCopied = await flattenImages(sourceDir, targetDir);
58
+
59
+ console.log('');
60
+ console.log(`✅ Вынос завершен! Скопировано ${totalCopied} изображений в корень public/img/`);
61
+ console.log('🔄 Netlify Image Optimization теперь сможет их легче обрабатывать');
62
+ console.log('⚡ Sharp оптимизация также будет применена к файлам в корне');
63
+ console.log('📁 Все изображения теперь доступны напрямую из /img/имя_файла.webp');
64
+ }
65
+
66
+ main().catch(console.error);
@@ -0,0 +1,78 @@
1
+ // copy-content-images.cjs - выносим все изображения из подпапок public/img в корень public/img
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const sourceDir = './public/img';
6
+ const targetDir = './public/img';
7
+
8
+ // Функция для рекурсивного поиска и копирования изображений из подпапок
9
+ async function flattenImages(currentDir) {
10
+ const items = fs.readdirSync(currentDir);
11
+ let copiedCount = 0;
12
+
13
+ for (const item of items) {
14
+ const itemPath = path.join(currentDir, item);
15
+ const stat = fs.statSync(itemPath);
16
+
17
+ if (stat.isDirectory()) {
18
+ // Рекурсивно обрабатываем подпапки
19
+ const copied = await flattenImages(itemPath);
20
+ copiedCount += copied;
21
+ } else if (stat.isFile()) {
22
+ const ext = path.extname(item).toLowerCase();
23
+
24
+ // Проверяем, что это изображение и что оно НЕ в корне public/img
25
+ if (['.jpg', '.jpeg', '.png', '.webp', '.gif', '.svg'].includes(ext)) {
26
+ const isInSubfolder = currentDir !== sourceDir;
27
+
28
+ if (isInSubfolder) {
29
+ const targetPath = path.join(sourceDir, item);
30
+
31
+ // Проверяем, что файл с таким именем не существует в корне
32
+ if (!fs.existsSync(targetPath)) {
33
+ fs.copyFileSync(itemPath, targetPath);
34
+ console.log(`📋 Вынесено: ${path.relative('./public', itemPath)} → img/${item}`);
35
+ copiedCount++;
36
+ } else {
37
+ // Если файл уже существует, добавляем префикс папки
38
+ const folderName = path.basename(currentDir);
39
+ const nameWithoutExt = path.parse(item).name;
40
+ const extension = path.parse(item).ext;
41
+ const newName = `${folderName}_${nameWithoutExt}${extension}`;
42
+ const targetPathWithPrefix = path.join(sourceDir, newName);
43
+
44
+ if (!fs.existsSync(targetPathWithPrefix)) {
45
+ fs.copyFileSync(itemPath, targetPathWithPrefix);
46
+ console.log(`📋 Вынесено с префиксом: ${path.relative('./public', itemPath)} → img/${newName}`);
47
+ copiedCount++;
48
+ } else {
49
+ console.log(`⚠️ Пропущено (уже существует): ${item}`);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ return copiedCount;
58
+ }
59
+
60
+ async function main() {
61
+ console.log('🚀 Начинаем вынос изображений из подпапок public/img в корень public/img...');
62
+
63
+ if (!fs.existsSync(sourceDir)) {
64
+ console.log(`📁 Папка ${sourceDir} не существует!`);
65
+ return;
66
+ }
67
+
68
+ // Выносим все изображения из подпапок в корень
69
+ const totalCopied = await flattenImages(sourceDir);
70
+
71
+ console.log('');
72
+ console.log(`✅ Вынос завершен! Скопировано ${totalCopied} изображений в корень public/img/`);
73
+ console.log('🔄 Netlify Image Optimization теперь сможет их легче обрабатывать');
74
+ console.log('⚡ Sharp оптимизация также будет применена к файлам в корне');
75
+ console.log('📁 Все изображения теперь доступны напрямую из /img/имя_файла.webp');
76
+ }
77
+
78
+ main().catch(console.error);
@@ -1,5 +1,5 @@
1
1
  // MAUGLI_CONFIG_VERSION — config version for CLI/automation compatibility
2
- export const MAUGLI_CONFIG_VERSION = '0.3';
2
+ export const MAUGLI_CONFIG_VERSION = '0.4';
3
3
  // Main configuration interface for the Maugli project
4
4
  export interface MaugliConfig {
5
5
  // Show example/demo content (for CLI/empty blog setup)
@@ -63,6 +63,7 @@ export interface MaugliConfig {
63
63
  // Control display of tags/rubrics
64
64
  // Theme switcher
65
65
  enableThemeSwitcher?: boolean; // Enable theme switcher (true by default)
66
+ defaultTheme?: 'light' | 'dark' | 'auto'; // Default theme (light, dark, or auto based on system preference)
66
67
  // Social and contact links (displayed in the footer)
67
68
  links?: Record<string, string>; // Social/contact links for footer
68
69
  navLinks?: Array<{ key: string; label: string; href: string }>; // Navigation links
@@ -93,7 +94,7 @@ export interface MaugliConfig {
93
94
  }
94
95
  // Main exported configuration object for the Maugli project
95
96
  export const maugliConfig: MaugliConfig = {
96
- configVersion: MAUGLI_CONFIG_VERSION, // Config version for CLI/automation compatibility
97
+ configVersion: MAUGLI_CONFIG_VERSION, // Config version for CLI/automation compatibility (0.4)
97
98
  showExamples: true, // Show example/demo content (set false to hide all demo content)
98
99
  brand: {
99
100
  name: 'Maugli', // Brand name
@@ -122,6 +123,7 @@ export const maugliConfig: MaugliConfig = {
122
123
  netlifyEnabled: true, // Enable Netlify deployment button (default: true)
123
124
  },
124
125
  enableThemeSwitcher: true, // Enable theme switcher (true by default)
126
+ defaultTheme: 'dark', // Default theme (dark by default)
125
127
  seo: {
126
128
  titleSuffix: ' — Maugli', // Suffix for page titles
127
129
  defaultImage: '/default-image.webp', // Default image for SEO
@@ -5,6 +5,7 @@ import Footer from '../components/Footer.astro';
5
5
  import MaugliFloatingLabel from '../components/MaugliFloatingLabel.astro';
6
6
  import Header from '../components/Header.astro';
7
7
  import Nav from '../components/Nav.astro';
8
+ import { maugliConfig } from '../config/maugli.config';
8
9
 
9
10
  export type Props = HeadProps & { showHeader?: boolean; fullWidth?: boolean };
10
11
 
@@ -15,11 +16,27 @@ const { showHeader = true, fullWidth = false, ...head } = Astro.props;
15
16
  <html lang="ru" class="antialiased break-words bg-main">
16
17
  <head>
17
18
  <BaseHead {...head} />
18
- <script>
19
+ <script define:vars={{ defaultTheme: maugliConfig.defaultTheme || 'auto' }}>
19
20
  // Инициализация темы до загрузки DOM
20
21
  function initTheme() {
21
- const theme = localStorage.getItem('theme');
22
- if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
22
+ const savedTheme = localStorage.getItem('theme');
23
+ let appliedTheme;
24
+
25
+ if (savedTheme) {
26
+ // Если есть сохранённая тема, используем её
27
+ appliedTheme = savedTheme;
28
+ } else {
29
+ // Если нет сохранённой темы, используем значение из конфигурации
30
+ if (defaultTheme === 'auto') {
31
+ // Автоматический выбор на основе системных настроек
32
+ appliedTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
33
+ } else {
34
+ // Используем тему из конфигурации
35
+ appliedTheme = defaultTheme;
36
+ }
37
+ }
38
+
39
+ if (appliedTheme === 'dark') {
23
40
  document.documentElement.classList.add('dark');
24
41
  localStorage.setItem('theme', 'dark');
25
42
  } else {
@@ -43,12 +60,25 @@ const { showHeader = true, fullWidth = false, ...head } = Astro.props;
43
60
  <MaugliFloatingLabel />
44
61
  </div>
45
62
 
46
- <script>
63
+ <script define:vars={{ defaultTheme: maugliConfig.defaultTheme || 'auto' }}>
47
64
  // Сохранение темы при переходах между страницами
48
65
  document.addEventListener('astro:page-load', () => {
49
66
  // Переинициализируем тему на каждой новой странице
50
- const theme = localStorage.getItem('theme');
51
- if (theme === 'dark') {
67
+ const savedTheme = localStorage.getItem('theme');
68
+ let appliedTheme;
69
+
70
+ if (savedTheme) {
71
+ appliedTheme = savedTheme;
72
+ } else {
73
+ // Если нет сохранённой темы, используем значение из конфигурации
74
+ if (defaultTheme === 'auto') {
75
+ appliedTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
76
+ } else {
77
+ appliedTheme = defaultTheme;
78
+ }
79
+ }
80
+
81
+ if (appliedTheme === 'dark') {
52
82
  document.documentElement.classList.add('dark');
53
83
  } else {
54
84
  document.documentElement.classList.remove('dark');
@@ -18,8 +18,8 @@
18
18
 
19
19
  /* Light Theme */
20
20
  --text-main: #111c2d;
21
- --text-heading: #2d3e5b;
22
- --text-muted: #585e6a;
21
+ --text-heading: #1c273b;
22
+ --text-muted: #464b56;
23
23
  --bg-main: #ffffff;
24
24
  --bg-main-40: rgba(255, 255, 255, 0.3);
25
25
  --bg-muted: rgba(245, 247, 249, 0.94);
@@ -51,8 +51,8 @@
51
51
 
52
52
  /* Dark Theme */
53
53
  --text-main: #ffffff;
54
- --text-heading: rgba(202, 252, 254, 0.7);
55
- --text-muted: #9ca3af;
54
+ --text-heading: rgba(202, 252, 254, 0.85);
55
+ --text-muted: #a6afbe;
56
56
  --bg-main: #0b131e;
57
57
  --bg-main-40: rgba(11, 19, 30, 0.4);
58
58
  --bg-muted: #131d2cde;