@squeditor/squeditor-framework 1.0.2 → 1.0.4

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.
Files changed (33) hide show
  1. package/README.md +22 -31
  2. package/package.json +1 -1
  3. package/php/functions.php +45 -0
  4. package/project-template/package.json +3 -1
  5. package/project-template/squeditor.config.js +21 -9
  6. package/project-template/src/assets/js/_slider_dynamic.js +2 -0
  7. package/project-template/src/assets/js/gsap-init.js +28 -1
  8. package/project-template/src/assets/js/main.js +1 -0
  9. package/project-template/src/assets/js/modules/splide-init.js +207 -0
  10. package/project-template/src/assets/js/modules/swiper-init.js +216 -0
  11. package/project-template/src/assets/js/uikit-components.js +27 -21
  12. package/project-template/src/assets/scss/_base.scss +0 -9
  13. package/project-template/src/assets/scss/_components.scss +107 -2
  14. package/project-template/src/assets/scss/_swiper.scss +30 -0
  15. package/project-template/src/assets/scss/main.scss +2 -1
  16. package/project-template/src/assets/scss/themes/_two.scss +95 -0
  17. package/project-template/src/assets/static/images/og-default.png +0 -0
  18. package/project-template/src/assets/static/images/placeholder.png +0 -0
  19. package/project-template/src/index.php +5 -5
  20. package/project-template/src/init.php +38 -2
  21. package/project-template/src/page-templates/head.php +9 -1
  22. package/project-template/src/slider-test.php +87 -0
  23. package/project-template/src/template-parts/header.php +11 -11
  24. package/project-template/tailwind.config.js +29 -1
  25. package/scripts/build-components.js +78 -1
  26. package/scripts/copy-static.js +26 -0
  27. package/scripts/dev-router.php +10 -1
  28. package/scripts/dev.js +30 -1
  29. package/scripts/package-customer.js +52 -21
  30. package/scripts/package-dist.js +1 -1
  31. package/scripts/scaffold.js +4 -3
  32. package/scripts/snapshot.js +134 -33
  33. package/scripts/utils/resolve-pages.js +47 -0
package/README.md CHANGED
@@ -1,37 +1,28 @@
1
- # ⚡️ Squeditor Framework
2
-
3
1
  **Squeditor Framework** is a high-performance, developer-first framework for building lightning-fast, static websites. It combines the power of **PHP-style templating** with the modern performance of **Tailwind CSS**, the interactivity of **UIKit 3**, and the speed of **Vite**.
4
2
 
5
- > [!IMPORTANT]
6
- > Squeditor Framework is NOT a WordPress theme framework or a CMS. It is a build-time framework that uses PHP for development and outputs 100% flat, portable HTML/CSS/JS for production and compatible with Squeditor, Elementor, Gutenberg, and other page builders.
7
-
8
- ## 🚀 Key Features
9
-
10
- ### 🛠 Zero-Runtime PHP Templating
11
- Develop using familiar WordPress-style functions like `get_template_part()` and `get_section()`, use PHP arrays as your data layer, and leverage loops to build complex layouts. The production build "snaps" everything into pure HTML, requiring zero server-side processing.
3
+ We implement AI Agents skills to help you build your website easily in minutes. You can use AI Agents to generate well organized and clean code, components, sections, and pages and ship landing pages and saas and agency websites 10x faster.
12
4
 
13
- ### 🧩 Selective UIKit 3 Integration
14
- Stop loading massive component libraries. Squeditor Framework uses a **Component Manifest System** (via `uikit-manifest.json`) to tree-shake UIKit. Use only what you need (like a Slider or Modal) and Squeditor Framework will automatically bundle just the necessary SCSS and JS.
5
+ ### Showcase and Demo
15
6
 
16
- ### 🎨 Token-Based Styling Architecture
17
- Fully themeable via a centralized Design Token system (`_tokens.scss`). Every component and layout utility consumes CSS Custom Properties (`--sq-*`), allowing you to swap entire visual identities or color schemas (Light/Dark) instantly across the site.
7
+ [Main Demo](https://squeditor.com/showcase/main-demo/)
8
+ [All Components](https://squeditor.com/showcase/all-components/)
9
+ [Style Guide](https://squeditor.com/showcase/style-guide/)
10
+ [GSAP Animations](https://squeditor.com/showcase/gsap-animations/)
18
11
 
19
- ### 🌓 Built-in Light/Dark Mode
20
- Native support for `sq-theme-dark` classes and Tailwind's `dark:` utilities. The framework detects schema settings from the environment (or URL) and applies them globally to the body, ensuring a seamless and premium dark mode experience.
12
+ [Full Documentation](https://docs.squeditor.com)
21
13
 
22
- ### 📚 Section-Based Workflow
23
- Build pages in minutes using a library of pre-built, token-aware section variants:
24
- - **Hero**: Video, Split, Centered
25
- - **Cards**: Grids, Horizontal, Feature lists
26
- - **CTA**: Split, Banner, In-line
27
- - **Global**: Responsive Header, Deep-linked Footer
14
+ ### Key Features
28
15
 
29
- ### 📖 Automatic Style Guide
30
- Every project includes an automatically generated `style-guide.php` page that renders every active component and its variants, serving as your project's living documentation.
16
+ - Zero-Runtime PHP Templating
17
+ - Selective UIKit 3 Integration
18
+ - Token-Based Styling Architecture
19
+ - Built-in Light/Dark Mode
20
+ - Section-Based Workflow
21
+ - Automatic Style Guide
31
22
 
32
23
  ---
33
24
 
34
- ## 🏗 Project Architecture
25
+ ### Project Architecture
35
26
 
36
27
  ```bash
37
28
  [workspace-root]/
@@ -43,9 +34,9 @@ Every project includes an automatically generated `style-guide.php` page that re
43
34
 
44
35
  ---
45
36
 
46
- ## 🏁 Getting Started
37
+ ### Getting Started
47
38
 
48
- ### 1. Scaffold a New Project
39
+ #### 1. Scaffold a New Project
49
40
  Squeditor includes a robust CLI scaffolding tool. It generates a completely clean, minimalist project instance fully pre-configured to utilize the advanced GSAP and UIKit engines without demo bloat.
50
41
 
51
42
  To scaffold your new project, simply run the following command in your terminal:
@@ -54,7 +45,7 @@ npx @squeditor/squeditor-framework your-project-name
54
45
  ```
55
46
  *(Replace `your-project-name` with your desired folder name).*
56
47
 
57
- ### 2. Development
48
+ #### 2. Development
58
49
  Navigate into your newly generated project folder, install its dependencies, and start the development server:
59
50
  ```bash
60
51
  cd your-project-name
@@ -67,7 +58,7 @@ npm run dev
67
58
  > Vite will show a link to `http://127.0.0.1:5173/` in your terminal. **Do not use that link.**
68
59
  > Instead, always visit the PHP Server address: **`http://127.0.0.1:3001`**. This is because Squeditor Framework uses PHP for template rendering, and Vite only serves the static assets.
69
60
 
70
- ### 4. Build for Production
61
+ #### 3. Build for Production
71
62
  Generate the static `dist/` folder and a distributable ZIP archive:
72
63
  ```bash
73
64
  npm run build
@@ -75,7 +66,7 @@ npm run build
75
66
 
76
67
  ---
77
68
 
78
- ## 🛠 Technology Stack
69
+ ### Technology Stack
79
70
 
80
71
  - **Dev Engine**: PHP 8.2+ (as a templating pre-processor)
81
72
  - **CSS Architecture**: Tailwind CSS + SCSS (Layered Overrides)
@@ -86,13 +77,13 @@ npm run build
86
77
 
87
78
  ---
88
79
 
89
- ## 🤝 Contributing
80
+ ### Contributing
90
81
 
91
82
  We welcome contributions! Whether it's adding new section variants, improving the build pipeline, or refining the token system, feel free to open a PR.
92
83
 
93
84
  ---
94
85
 
95
- ## 📄 License
86
+ ### License
96
87
 
97
88
  Squeditor Framework is released under the [MIT License](LICENSE).
98
89
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squeditor/squeditor-framework",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Squeditor Framework is a high-performance, developer-first framework for building lightning-fast, static websites. It combines the power of PHP-style templating with the modern performance of Tailwind CSS, the interactivity of UIKit 3, and the speed of Vite.",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
package/php/functions.php CHANGED
@@ -90,3 +90,48 @@ function parse_frontmatter(string $file): array {
90
90
  }
91
91
  return [];
92
92
  }
93
+
94
+ /**
95
+ * Load an asset file's contents directly (e.g., for inline SVG).
96
+ *
97
+ * @param string $path Path relative to src/assets/, e.g., 'static/icons/vite.svg'
98
+ * @return string File contents or empty string if not found
99
+ */
100
+ function get_asset(string $path): string {
101
+ $full_path = SRC_PATH . '/assets/' . ltrim($path, '/');
102
+ if (!file_exists($full_path)) {
103
+ trigger_error("Asset not found: {$full_path}", E_USER_WARNING);
104
+ return '';
105
+ }
106
+ return file_get_contents($full_path);
107
+ }
108
+
109
+ /**
110
+ * Load an image file's contents directly (e.g., for inline SVG images) or return an <img> tag.
111
+ *
112
+ * @param string $filename Filename inside src/assets/static/images/, e.g., 'logo.svg'
113
+ * @param string $alt Alt text for the image (if returning an <img> tag)
114
+ * @param string $class Custom CSS class(es) to add to the <img> tag
115
+ * @param bool $inline Whether to return raw file contents (e.g. inline SVG). Default false.
116
+ * @return string Image tag or file contents, or empty string if not found
117
+ */
118
+ function get_image(string $filename, string $alt = '', string $class = '', bool $inline = false): string {
119
+ $full_path = SRC_PATH . '/assets/static/images/' . ltrim($filename, '/');
120
+ if (!file_exists($full_path)) {
121
+ trigger_error("Image not found: {$full_path}", E_USER_WARNING);
122
+ return '';
123
+ }
124
+
125
+ if ($inline) {
126
+ return file_get_contents($full_path);
127
+ }
128
+
129
+ // We assume images are served from /assets/static/images/ relative to document root
130
+ // This path might need to be adjusted based on the server setup, but for now
131
+ // it returns a relative web path.
132
+ $web_path = '/assets/static/images/' . ltrim($filename, '/');
133
+
134
+ $class_attr = $class !== '' ? sprintf(' class="%s"', htmlspecialchars($class)) : '';
135
+
136
+ return sprintf('<img src="%s" alt="%s"%s>', htmlspecialchars($web_path), htmlspecialchars($alt), $class_attr);
137
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "my-project-name",
2
+ "name": "new-project-name",
3
3
  "private": true,
4
4
  "scripts": {
5
5
  "dev": "node ../squeditor-framework/scripts/dev.js",
@@ -11,7 +11,9 @@
11
11
  "preview:vercel": "vercel dist --yes"
12
12
  },
13
13
  "dependencies": {
14
+ "@splidejs/splide": "^4.1.4",
14
15
  "gsap": "^3.13.0",
16
+ "swiper": "^12.1.2",
15
17
  "uikit": "^3.21.0"
16
18
  },
17
19
  "devDependencies": {
@@ -1,8 +1,25 @@
1
- // showcase/squeditor.config.js
1
+ // squeditor.config.js
2
2
  module.exports = {
3
3
  framework: '../squeditor-framework',
4
- name: 'my-project-name',
4
+ name: 'customer-bundle',
5
5
  version: '1.0.0',
6
+ themes: {
7
+ 'two': {
8
+ label: 'Two Theme',
9
+ bodyClass: 'theme-two',
10
+ scss: 'src/assets/scss/themes/_two.scss',
11
+ pages: ['404.php', 'slider-test.php'],
12
+ distSubfolder: '',
13
+ }
14
+ },
15
+ gsap: {
16
+ plugins: ['ScrollTrigger', 'SplitText', 'Flip', 'Observer'],
17
+ initScript: 'src/assets/js/gsap-init.js',
18
+ advancedScript: 'src/assets/js/gsap-advanced.js',
19
+ },
20
+ slider: {
21
+ library: 'splide', // 'swiper', 'splide', or false
22
+ },
6
23
  components: [
7
24
  'sticky',
8
25
  'utility',
@@ -43,11 +60,6 @@ module.exports = {
43
60
  'video',
44
61
  'iframe',
45
62
  ],
46
- gsap: {
47
- plugins: ['ScrollTrigger', 'SplitText', 'Flip', 'Observer'],
48
- initScript: 'src/assets/js/gsap-init.js',
49
- advancedScript: 'src/assets/js/gsap-advanced.js',
50
- },
51
63
  output: {
52
64
  css: 'src/assets/css',
53
65
  js: 'src/assets/js',
@@ -55,7 +67,7 @@ module.exports = {
55
67
  devServer: { port: 3001, root: 'src' },
56
68
  snapshot: {
57
69
  baseUrl: 'http://127.0.0.1:3001',
58
- pages: ['/', '/404.php'],
70
+ pages: ['*'],
59
71
  outputDir: 'dist',
60
72
  rewriteExtension: true,
61
73
  },
@@ -75,7 +87,7 @@ module.exports = {
75
87
  }
76
88
  },
77
89
  dist: {
78
- zipName: 'my-project-name.zip',
90
+ zipName: 'squeditor-build.zip',
79
91
  previewPlatform: 'netlify',
80
92
  },
81
93
  };
@@ -0,0 +1,2 @@
1
+ // Auto-generated by build-components.js — DO NOT EDIT
2
+ import './modules/splide-init.js';
@@ -478,6 +478,19 @@ function initTimelines() {
478
478
  }
479
479
 
480
480
  // Initialize timeline with data-gsap-timeline parameters (e.g. {delay: 1, repeat: -1, yoyo: true})
481
+ // OPT-IN FOUC protection: if the developer set style="visibility: hidden" on the element,
482
+ // GSAP will manage show/hide. We only use onReverseComplete (reliable) and handle
483
+ // the "show" part directly in control handlers for bulletproof timing.
484
+ const isInitiallyHidden = timelineEl.style.visibility === 'hidden';
485
+ if (isInitiallyHidden) {
486
+ timelineEl._sqHidden = true; // Flag for control handlers
487
+ const originalOnReverseComplete = tlConfig.onReverseComplete;
488
+ tlConfig.onReverseComplete = function() {
489
+ timelineEl.style.visibility = 'hidden';
490
+ if (originalOnReverseComplete) originalOnReverseComplete.call(this);
491
+ };
492
+ }
493
+
481
494
  const tl = gsap.timeline(tlConfig);
482
495
  timelineEl._sqTimeline = tl; // Bind to DOM node so external buttons can trigger it.
483
496
 
@@ -548,7 +561,21 @@ function initTimelineControls() {
548
561
  const tl = t._sqTimeline;
549
562
  if (tl) {
550
563
  if (action === 'toggle') {
551
- tl.reversed() ? tl.play() : tl.reverse();
564
+ if (t._sqOpen) {
565
+ tl.reverse();
566
+ t._sqOpen = false;
567
+ } else {
568
+ if (t._sqHidden) t.style.visibility = 'visible';
569
+ tl.play();
570
+ t._sqOpen = true;
571
+ }
572
+ } else if (action === 'play') {
573
+ if (t._sqHidden) t.style.visibility = 'visible';
574
+ t._sqOpen = true;
575
+ tl.play();
576
+ } else if (action === 'reverse') {
577
+ t._sqOpen = false;
578
+ tl.reverse();
552
579
  } else {
553
580
  tl[action]();
554
581
  }
@@ -1,6 +1,7 @@
1
1
  // src/assets/js/main.js
2
2
  import './gsap-init.js';
3
3
  import './gsap-advanced.js';
4
+ import './_slider_dynamic.js';
4
5
  // SCSS is imported directly in base.php for development (to prevent FOUC) and loaded via <link> in production.
5
6
 
6
7
  // Remove the dev server FOUC shield
@@ -0,0 +1,207 @@
1
+ import Splide, { EventInterface } from '@splidejs/splide';
2
+
3
+ // Splide CSS is handled separately by build-components.js → slider.min.css
4
+
5
+ /**
6
+ * Creates a generic Slide Effect Transition component for Splide,
7
+ * layering slides while preserving the physical list dimensions for native drag support,
8
+ * and transitioning specific CSS properties (scale, blur, clip-path).
9
+ */
10
+ function createEffectTransition(duration, easing, applyStylesFn) {
11
+ return function CustomTransition(SplideInstance, Components) {
12
+ const { bind } = EventInterface(SplideInstance);
13
+ const { list } = Components.Elements;
14
+ let endCallback;
15
+ let activeIndex = SplideInstance.index;
16
+
17
+ function getBaseTransform(slideEl) {
18
+ const slides = Components.Elements.slides;
19
+ const idx = slides.indexOf(slideEl);
20
+ // Overlaps the slide dynamically without destroying the container's physical width flow math
21
+ const isVertical = SplideInstance.options.direction === 'ttb';
22
+ return isVertical ? `translateY(-${100 * idx}%)` : `translateX(-${100 * idx}%)`;
23
+ }
24
+
25
+ function mount() {
26
+ bind(list, 'transitionend', e => {
27
+ const slides = Components.Elements.slides;
28
+ if (endCallback && slides.includes(e.target)) {
29
+ endCallback();
30
+ endCallback = null;
31
+ }
32
+ });
33
+
34
+ const setupSlides = () => {
35
+ const slides = Components.Elements.slides;
36
+ activeIndex = SplideInstance.index; // Reset on setup/refresh
37
+
38
+ slides.forEach((slide, index) => {
39
+ slide.style.transition = 'none';
40
+ // We must NOT use absolute/grid because it destroys Splide's drag threshold math!
41
+ // Instead we shift the visual representation only, keeping the phantom DOM bounds intact:
42
+ const baseTransform = getBaseTransform(slide);
43
+
44
+ slide.style.width = '100%';
45
+ slide.style.margin = '0'; // override any Splide inline margins
46
+ slide.style.transformOrigin = 'center';
47
+
48
+ if (index === activeIndex) {
49
+ slide.style.opacity = '1';
50
+ slide.style.zIndex = '1';
51
+ slide.style.pointerEvents = 'auto'; // Active slide gets clicks
52
+ applyStylesFn(slide, 'in', baseTransform);
53
+ } else {
54
+ slide.style.opacity = '0';
55
+ slide.style.zIndex = '0';
56
+ slide.style.pointerEvents = 'none'; // Prevent invisible slides from absorbing clicks
57
+ applyStylesFn(slide, 'out', baseTransform);
58
+ }
59
+ });
60
+ };
61
+
62
+ // Run immediately upon component mount and on every refresh
63
+ setupSlides();
64
+ SplideInstance.on('refresh', setupSlides);
65
+ }
66
+
67
+ function start(index, done) {
68
+ const slides = Components.Elements.slides;
69
+ const prevSlide = slides[activeIndex];
70
+ const nextSlide = slides[index];
71
+
72
+ // Update local tracker to the new index immediately
73
+ activeIndex = index;
74
+
75
+ if (!nextSlide || prevSlide === nextSlide) {
76
+ done();
77
+ return;
78
+ }
79
+
80
+ // Clean up unneeded slides right away to avoid visual glitches
81
+ slides.forEach(slide => {
82
+ if (slide !== prevSlide && slide !== nextSlide) {
83
+ const base = getBaseTransform(slide);
84
+ slide.style.transition = 'none';
85
+ slide.style.opacity = '0';
86
+ slide.style.zIndex = '0';
87
+ slide.style.pointerEvents = 'none';
88
+ applyStylesFn(slide, 'out', base);
89
+ }
90
+ });
91
+
92
+ // Outgoing slide animation
93
+ if (prevSlide) {
94
+ const prevBase = getBaseTransform(prevSlide);
95
+ prevSlide.style.transition = `all ${duration}ms ${easing}`;
96
+ prevSlide.style.zIndex = '1';
97
+ prevSlide.style.opacity = '0';
98
+ prevSlide.style.pointerEvents = 'none';
99
+ applyStylesFn(prevSlide, 'out', prevBase);
100
+ }
101
+
102
+ // Reset incoming slide to 'out' state before animating 'in'
103
+ const nextBase = getBaseTransform(nextSlide);
104
+ nextSlide.style.transition = 'none';
105
+ nextSlide.style.zIndex = '2';
106
+ nextSlide.style.opacity = '0';
107
+ nextSlide.style.pointerEvents = 'none';
108
+ applyStylesFn(nextSlide, 'out', nextBase);
109
+
110
+ // Force browser flow flush so the 'out' state is registered visually
111
+ void nextSlide.offsetWidth;
112
+
113
+ // Incoming slide animation
114
+ nextSlide.style.transition = `all ${duration}ms ${easing}`;
115
+ nextSlide.style.opacity = '1';
116
+ nextSlide.style.pointerEvents = 'auto'; // allow interaction
117
+ applyStylesFn(nextSlide, 'in', nextBase);
118
+
119
+ endCallback = done;
120
+
121
+ // Failsafe timeout in case transitionend event gets skipped/missed
122
+ setTimeout(() => {
123
+ if (endCallback) {
124
+ endCallback();
125
+ endCallback = null;
126
+ }
127
+ }, duration + 50);
128
+ }
129
+
130
+ function cancel() {
131
+ endCallback = null;
132
+ }
133
+
134
+ return { mount, start, cancel };
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Pre-defined custom transitions
140
+ * Developers can extend these following the Splide Docs.
141
+ * Transition properties manipulate opacity automatically; use `applyStylesFn` for transforms.
142
+ */
143
+ const CustomTransitions = {
144
+ 'scaleup': createEffectTransition(800, 'cubic-bezier(0.16, 1, 0.3, 1)', (slide, state, base) => {
145
+ const trans = state === 'in' ? 'scale(1)' : 'scale(0.85)';
146
+ slide.style.transform = `${base} ${trans}`;
147
+ }),
148
+ 'scaledown': createEffectTransition(800, 'cubic-bezier(0.16, 1, 0.3, 1)', (slide, state, base) => {
149
+ const trans = state === 'in' ? 'scale(1)' : 'scale(1.15)';
150
+ slide.style.transform = `${base} ${trans}`;
151
+ }),
152
+ 'blurfade': createEffectTransition(800, 'ease-in-out', (slide, state, base) => {
153
+ slide.style.filter = state === 'in' ? 'blur(0px)' : 'blur(10px)';
154
+ slide.style.transform = `${base} scale(1)`; // scale(1) required to prevent blur rendering bugs
155
+ }),
156
+ 'reveal': createEffectTransition(1000, 'cubic-bezier(0.85, 0, 0.15, 1)', (slide, state, base) => {
157
+ slide.style.clipPath = state === 'in' ? 'inset(0 0 0 0)' : 'inset(100% 0 0 0)';
158
+ const trans = state === 'in' ? 'translateY(0%)' : 'translateY(15%)';
159
+ slide.style.transform = `${base} ${trans}`;
160
+ }),
161
+ };
162
+
163
+ /**
164
+ * Initializes all Splide sliders on the page
165
+ * Configuration is inherently read by Splide directly from the valid JSON `data-splide` attribute
166
+ */
167
+ export function initSplide() {
168
+ const splides = document.querySelectorAll('.splide');
169
+ splides.forEach(el => {
170
+ try {
171
+ let optionsStr = el.getAttribute('data-splide');
172
+ let requestedType = null;
173
+ let overrideOptions = {};
174
+
175
+ if (optionsStr) {
176
+ const options = JSON.parse(optionsStr);
177
+
178
+ if (options.type && CustomTransitions[options.type]) {
179
+ requestedType = options.type;
180
+
181
+ // Force the slider into 'fade' mode so the core engine expects stacked slides natively,
182
+ // apply 'rewind' to emulate the infinite looping of our custom transitions.
183
+ overrideOptions = { type: 'fade', rewind: true };
184
+
185
+ // CRITICAL: Splide natively parses the DOM attribute directly inside its constructor!
186
+ // We must alter the original DOM data string before init so it doesn't overwrite 'fade' back
187
+ options.type = 'fade';
188
+ el.setAttribute('data-splide', JSON.stringify(options));
189
+ }
190
+ }
191
+
192
+ const splide = new Splide(el, overrideOptions);
193
+
194
+ if (requestedType && CustomTransitions[requestedType]) {
195
+ splide.mount({}, CustomTransitions[requestedType]);
196
+ } else {
197
+ splide.mount();
198
+ }
199
+ } catch (e) {
200
+ console.error('[Squeditor] Error initializing Splide slider', el, e);
201
+ }
202
+ });
203
+ }
204
+
205
+ document.addEventListener('DOMContentLoaded', () => {
206
+ initSplide();
207
+ });