@silexlabs/silex 3.5.1-1 → 3.5.1-2

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@silexlabs/silex",
3
3
  "description": "Free and easy website builder for everyone.",
4
- "version": "3.5.1-1",
4
+ "version": "3.5.1-2",
5
5
  "version:backwardcompat": "3.0.0",
6
6
  "author": "Alex Hoyau (https://lexoyo.me/)",
7
7
  "main": "dist/server/server/index.js",
@@ -99,13 +99,13 @@
99
99
  "dependencies": {
100
100
  "@fontsource/ubuntu": "^5.0.13",
101
101
  "@fortawesome/fontawesome-free": "6.6.0",
102
- "@silexlabs/grapesjs-advanced-selector": "1.3.0",
103
- "@silexlabs/grapesjs-data-source": "0.1.1-5",
102
+ "@silexlabs/grapesjs-advanced-selector": "1.3.1-0",
103
+ "@silexlabs/grapesjs-data-source": "0.1.1-6",
104
104
  "@silexlabs/grapesjs-filter-styles": "1.2.0",
105
105
  "@silexlabs/grapesjs-fonts": "1.2.0",
106
106
  "@silexlabs/grapesjs-keymaps-dialog": "1.2.0",
107
107
  "@silexlabs/grapesjs-loading": "1.2.0",
108
- "@silexlabs/grapesjs-notifications": "0.2.0",
108
+ "@silexlabs/grapesjs-notifications": "0.2.1-0",
109
109
  "@silexlabs/grapesjs-storage-rate-limit": "1.2.0",
110
110
  "@silexlabs/grapesjs-symbols": "1.3.0",
111
111
  "@silexlabs/grapesjs-ui-suggest-classes": "1.2.0",
@@ -233,6 +233,11 @@
233
233
  .gjs-sm-sector .gjs-sm-sector-label {
234
234
  margin-right: 0.3em
235
235
  }
236
+
237
+ .fa-grip-vertical:before {
238
+ content: "\f58e";
239
+ font-weight: 900;
240
+ }
236
241
  .gjs-sm-sector .gjs-sm-sector-title::before,
237
242
  .gjs-sm-sector .gjs-sm-sector-title::after {
238
243
  content: 'o';
@@ -19,13 +19,21 @@
19
19
  align-items: center;
20
20
  background-color: $darkerPrimaryColor;
21
21
  border-bottom: 1px solid rgba(0, 0, 0, 0.25);
22
- cursor: pointer;
22
+ cursor: cursor;
23
+ transition: opacity 0.2s ease, background-color 0.2s ease;
23
24
  &:hover {
24
25
  background-color: $primaryColor;
25
26
  .pages__page-name {
26
27
  color: $quaternaryColor;
27
28
  }
28
29
  }
30
+ &:active {
31
+ cursor: grabbing;
32
+ }
33
+ }
34
+ .pages__page-dragging {
35
+ opacity: 0.4;
36
+ cursor: grabbing !important;
29
37
  }
30
38
  .pages__page-selected {
31
39
  background-color: $lighterPrimaryColor;
@@ -41,20 +49,31 @@
41
49
  white-space: nowrap;
42
50
  overflow: hidden;
43
51
  text-overflow: ellipsis;
44
- height: 13px;
45
- font-size: initial;
52
+ font-size: 13px;
46
53
  text-transform: capitalize;
47
- padding: 10px 6px;
54
+ padding: 6px;
48
55
  flex: 1;
49
56
  text-align: left;
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 4px;
50
60
  }
51
61
  .pages__icon {
52
- padding: 10px 6px;
62
+ padding: 6px;
53
63
  cursor: pointer;
64
+ font-size: 12px;
54
65
  }
55
66
  .pages__icon:hover {
56
67
  color: $quaternaryColor;
57
68
  }
69
+ .pages__drag-handle {
70
+ cursor: grab;
71
+ opacity: 0.6;
72
+ flex-shrink: 0;
73
+ &:hover {
74
+ opacity: 1;
75
+ }
76
+ }
58
77
  }
59
78
  .#{$pn-prefix}buttons {
60
79
  flex-direction: column;
@@ -20,7 +20,7 @@ import { ApiConnectorLoggedInPostMessage, ApiConnectorLoginQuery, ApiPublication
20
20
  import { Editor } from 'grapesjs'
21
21
  import { PublicationUi } from './PublicationUi'
22
22
  import { getUser, logout, publicationStatus, publish } from '../api'
23
- import { API_CONNECTOR_LOGIN, API_CONNECTOR_PATH, API_PATH } from '../../constants'
23
+ import { API_CONNECTOR_LOGIN, API_CONNECTOR_PATH, API_PATH, SILEX_VERSION } from '../../constants'
24
24
  import { ClientEvent } from '../events'
25
25
  import { resetRenderComponents, resetRenderCssRules, transformPermalink, transformFiles, transformPath, renderComponents, renderCssRules } from '../publication-transformers'
26
26
  import { hashString } from '../utils'
@@ -288,6 +288,8 @@ export class PublicationManager {
288
288
  // Get the data to publish, clone the objects because plugins can change it
289
289
  const projectData = { ...this.editor.getProjectData() as WebsiteData }
290
290
  const siteSettings = { ...this.editor.getModel().get('settings') as WebsiteSettings }
291
+ // Check for missing SEO tags and warn user
292
+ this.checkSeoTags(siteSettings)
291
293
  let preventDefaultStart = false
292
294
  this.editor.trigger(ClientEvent.PUBLISH_START, {projectData, siteSettings, preventDefault: () => preventDefaultStart = true, publicationManager: this })
293
295
  if(preventDefaultStart) {
@@ -354,6 +356,158 @@ export class PublicationManager {
354
356
  return files
355
357
  }
356
358
 
359
+ /**
360
+ * Helper function to check if a viewport meta tag exists in the head content
361
+ * @param headContent - HTML content from head settings
362
+ * @returns true if viewport meta tag is found
363
+ */
364
+ private hasViewportMeta(headContent: string): boolean {
365
+ if (!headContent) return false
366
+ // Check for viewport meta tag with various formats
367
+ const viewportPattern = /<meta[^>]*name\s*=\s*["']viewport["'][^>]*>/i
368
+ return viewportPattern.test(headContent)
369
+ }
370
+
371
+ /**
372
+ * Get the default viewport meta tag if no viewport is present
373
+ * @param siteHead - Head content from site settings
374
+ * @param pageHead - Head content from page settings
375
+ * @returns viewport meta tag or empty string if already present
376
+ */
377
+ private ensureViewportMeta(siteHead: string, pageHead: string): string {
378
+ if (this.hasViewportMeta(siteHead) || this.hasViewportMeta(pageHead)) {
379
+ return ''
380
+ }
381
+ return '<meta name="viewport" content="width=device-width, initial-scale=1.0">'
382
+ }
383
+
384
+ /**
385
+ * Helper function to check if a generator meta tag exists in the head content
386
+ * @param headContent - HTML content from head settings
387
+ * @returns true if generator meta tag is found
388
+ */
389
+ private hasGeneratorMeta(headContent: string): boolean {
390
+ if (!headContent) return false
391
+ // Check for generator meta tag with various formats
392
+ const generatorPattern = /<meta[^>]*name\s*=\s*["']generator["'][^>]*>/i
393
+ return generatorPattern.test(headContent)
394
+ }
395
+
396
+ /**
397
+ * Get the default generator meta tag if no generator is present
398
+ * @param siteHead - Head content from site settings
399
+ * @param pageHead - Head content from page settings
400
+ * @returns generator meta tag or empty string if already present
401
+ */
402
+ private ensureGeneratorMeta(siteHead: string, pageHead: string): string {
403
+ if (this.hasGeneratorMeta(siteHead) || this.hasGeneratorMeta(pageHead)) {
404
+ return ''
405
+ }
406
+ return `<meta name="generator" content="Silex v${ SILEX_VERSION }">`
407
+ }
408
+
409
+ /**
410
+ * Helper function to check if a canonical link exists in the head content
411
+ * @param headContent - HTML content from head settings
412
+ * @returns true if canonical link is found
413
+ */
414
+ private hasCanonicalLink(headContent: string): boolean {
415
+ if (!headContent) return false
416
+ const canonicalPattern = /<link[^>]*rel\s*=\s*["']canonical["'][^>]*>/i
417
+ return canonicalPattern.test(headContent)
418
+ }
419
+
420
+ /**
421
+ * Helper function to check if alternate links exist in the head content
422
+ * @param headContent - HTML content from head settings
423
+ * @returns true if alternate links are found
424
+ */
425
+ private hasAlternateLinks(headContent: string): boolean {
426
+ if (!headContent) return false
427
+ const alternatePattern = /<link[^>]*rel\s*=\s*["']alternate["'][^>]*>/i
428
+ return alternatePattern.test(headContent)
429
+ }
430
+
431
+ /**
432
+ * Check for missing SEO tags and emit warnings
433
+ * @param siteSettings - Site settings
434
+ */
435
+ private checkSeoTags(siteSettings: WebsiteSettings): void {
436
+ const pages = this.editor.Pages.getAll()
437
+
438
+ // Utility function to get a setting from the page or the site settings
439
+ const getSetting = (pageSettings: WebsiteSettings, name: string) =>
440
+ (pageSettings || {})[name] || (siteSettings || {})[name] || ''
441
+
442
+ for (const page of pages) {
443
+ const pageSettings = page.get('settings') as WebsiteSettings || {}
444
+ const siteHead = siteSettings?.head || ''
445
+ const pageHead = pageSettings?.head || ''
446
+ const pageName = page.get('name') || 'Unnamed page'
447
+ const bodyComponent = page.getMainComponent()
448
+ const componentId = bodyComponent?.getId()
449
+ const pageId = page.getId()
450
+
451
+ // Check for lang attribute
452
+ const lang = getSetting(pageSettings, 'lang')
453
+ if (!lang || lang.trim() === '') {
454
+ this.editor.runCommand('notifications:add', {
455
+ id: `seo-validation-lang-${pageId}`,
456
+ type: 'warning',
457
+ message: `Page "${pageName}": Missing lang attribute on &lt;html&gt; tag. This is important for accessibility and SEO.`,
458
+ componentId,
459
+ group: 'seo-validation'
460
+ })
461
+ }
462
+
463
+ // Check for title
464
+ const title = getSetting(pageSettings, 'title')
465
+ if (!title || title.trim() === '') {
466
+ this.editor.runCommand('notifications:add', {
467
+ id: `seo-validation-title-${pageId}`,
468
+ type: 'warning',
469
+ message: `Page "${pageName}": Missing &lt;title&gt; tag. This is critical for SEO.`,
470
+ componentId,
471
+ group: 'seo-validation'
472
+ })
473
+ }
474
+
475
+ // Check for description
476
+ const description = getSetting(pageSettings, 'description')
477
+ if (!description || description.trim() === '') {
478
+ this.editor.runCommand('notifications:add', {
479
+ id: `seo-validation-description-${pageId}`,
480
+ type: 'warning',
481
+ message: `Page "${pageName}": Missing meta description. This is important for SEO and search results display.`,
482
+ componentId,
483
+ group: 'seo-validation'
484
+ })
485
+ }
486
+
487
+ // Check for canonical link
488
+ if (!this.hasCanonicalLink(siteHead) && !this.hasCanonicalLink(pageHead)) {
489
+ this.editor.runCommand('notifications:add', {
490
+ id: `seo-validation-canonical-${pageId}`,
491
+ type: 'warning',
492
+ message: `Page "${pageName}": Missing canonical link. This helps prevent duplicate content issues.`,
493
+ componentId,
494
+ group: 'seo-validation'
495
+ })
496
+ }
497
+
498
+ // Check for alternate links (hreflang for multilingual sites)
499
+ if (!this.hasAlternateLinks(siteHead) && !this.hasAlternateLinks(pageHead)) {
500
+ this.editor.runCommand('notifications:add', {
501
+ id: `seo-validation-alternate-${pageId}`,
502
+ type: 'warning',
503
+ message: `Page "${pageName}": Missing alternate links (hreflang). Consider adding these for multilingual sites.`,
504
+ componentId,
505
+ group: 'seo-validation'
506
+ })
507
+ }
508
+ }
509
+ }
510
+
357
511
  async *getHtmlFilesYield(siteSettings: WebsiteSettings, preventDefault): AsyncGenerator<WebsiteFile | undefined> {
358
512
  for (const page of this.editor.Pages.getAll()) {
359
513
  // Clone the settings because plugins can change them
@@ -395,6 +549,8 @@ export class PublicationManager {
395
549
  // Useful data for HTML result
396
550
  const title = getSetting('title')
397
551
  const favicon = getSetting('favicon')
552
+ const viewportMeta = this.ensureViewportMeta(clonedSiteSettings?.head || '', pageSettings?.head || '')
553
+ const generatorMeta = this.ensureGeneratorMeta(clonedSiteSettings?.head || '', pageSettings?.head || '')
398
554
 
399
555
  // Return the HTML file
400
556
  yield {
@@ -402,6 +558,8 @@ export class PublicationManager {
402
558
  <html lang="${getSetting('lang')}">
403
559
  <head>
404
560
  <meta charset="UTF-8">
561
+ ${viewportMeta}
562
+ ${generatorMeta}
405
563
  <link rel="stylesheet" href="${cssPermalink}" />
406
564
  ${clonedSiteSettings?.head || ''}
407
565
  ${pageSettings?.head || ''}
@@ -58,76 +58,102 @@ export default function(editor: Editor/*, opts: EleventyPluginOptions*/): void {
58
58
  panelButtons.add([{
59
59
  id: 'refresh-data-sources',
60
60
  className: `${REFRESH_BUTTON_BASE_CLASS} fa-refresh`,
61
- command: () => {
62
- // Refresh all data sources
63
- editor.runCommand(COMMAND_REFRESH)
64
- },
61
+ command: COMMAND_REFRESH,
65
62
  togglable: false,
66
- attributes: { title: 'Refresh data sources (reload dynamic content) - Ctrl+Alt+R' },
63
+ attributes: { title: 'Refresh data sources (reload dynamic content) - Ctrl+Alt+R' }
67
64
  }, {
68
65
  id: 'toggle-data-source-preview',
69
- className: 'fa fa-fw data-source-preview-toggle fa-table',
70
- command: {
71
- run: () => editor.runCommand(COMMAND_PREVIEW_ACTIVATE),
72
- stop: () => editor.runCommand(COMMAND_PREVIEW_DEACTIVATE),
73
- },
74
- togglable: true,
75
- active: true,
76
- attributes: { title: 'Enable data source preview (Ctrl+Alt+V)' },
66
+ className: 'fa fa-fw data-source-preview-toggle fa-table gjs-pn-active gjs-four-color',
67
+ command: COMMAND_PREVIEW_TOGGLE,
68
+ togglable: false,
69
+ attributes: { title: 'Enable/Disable data source preview (Ctrl+Alt+V)' }
77
70
  }, {
78
- // Visual separator after data source buttons
71
+ // Separator for visual grouping of data source buttons in the panel
79
72
  id: 'data-source-separator',
80
73
  className: 'data-source-separator',
81
- command: () => {}, // No action on click
74
+ command: () => {},
82
75
  togglable: false,
83
- attributes: { title: '' },
76
+ attributes: { title: '' }
84
77
  }], { at: 0 })
85
78
 
86
- // Listen for preview activation/deactivation events to update the button icon and tooltip
87
- editor.on(PREVIEW_ACTIVATED, () => {
79
+ // Update the preview toggle button's tooltip and visual state based on activation
80
+ let isActive = true
81
+ function updatePreviewActiveButton(editor) {
88
82
  const button = editor.Panels.getButton('options', 'toggle-data-source-preview')
89
83
  if (button) {
90
- button.set('active', true)
91
- button.set('attributes', { title: 'Disable data source preview (Ctrl+Alt+V)' })
84
+ button.set('attributes', {
85
+ title: isActive
86
+ ? 'Disable data source preview (Ctrl+Alt+V)'
87
+ : 'Enable data source preview (Ctrl+Alt+V)'
88
+ })
89
+ }
90
+ const el = getPanelButtonEl(editor, 'options', '.data-source-preview-toggle')
91
+ if (el) {
92
+ if (isActive) {
93
+ el.classList.add('gjs-pn-active', 'gjs-four-color')
94
+ } else {
95
+ el.classList.remove('gjs-pn-active', 'gjs-four-color')
96
+ }
92
97
  }
98
+ }
99
+
100
+ // FIXME: This should not be needed as the state is alreay set in the button definition
101
+ setTimeout(() => updatePreviewActiveButton(editor), 1000)
102
+
103
+ editor.on(PREVIEW_ACTIVATED, () => {
104
+ isActive = true
105
+ setTimeout(() => updatePreviewActiveButton(editor))
93
106
  })
94
107
  editor.on(PREVIEW_DEACTIVATED, () => {
95
- const button = editor.Panels.getButton('options', 'toggle-data-source-preview')
96
- if (button) {
97
- button.set('active', false)
98
- button.set('attributes', { title: 'Enable data source preview (Ctrl+Alt+V)' })
99
- }
108
+ isActive = false
109
+ updatePreviewActiveButton(editor)
100
110
  })
101
111
 
102
- // Listen for preview activation/deactivation events to update the button tooltip and style
103
- // The button now handles its own state, so these listeners are not needed.
104
- editor.on(DATA_SOURCE_DATA_LOAD_START, () => {
112
+ // Returns the DOM element for a button in a panel using a selector
113
+ function getPanelButtonEl(editor, panelId, selector) {
114
+ const panel = editor.Panels.getPanel(panelId)
115
+ if (!panel) return null
116
+ console.log({panel, selector}, panel.view?.el, panel.view?.el?.querySelector(selector))
117
+ return panel.view
118
+ ?.el
119
+ ?.querySelector(selector)
120
+ }
121
+
122
+ // Update the refresh button's visual state and tooltip based on data source loading status
123
+ function setRefreshButtonState(editor, status) {
105
124
  const button = editor.Panels.getButton('options', 'refresh-data-sources')
106
- if (button) {
125
+ if (!button) return
126
+ switch (status) {
127
+ case 'loading':
107
128
  button.set('className', `${REFRESH_BUTTON_BASE_CLASS} fa-spinner`)
108
129
  button.set('title', 'Refreshing data sources (loading dynamic content)...')
130
+ break
131
+ case 'error':
132
+ button.set('className', `${REFRESH_BUTTON_BASE_CLASS} fa-exclamation-triangle`)
133
+ button.set('title', 'Error refreshing data sources, check the notifications')
134
+ break
135
+ default:
136
+ button.set('className', `${REFRESH_BUTTON_BASE_CLASS} fa-refresh`)
137
+ button.set('title', 'Refresh data sources (reload dynamic content) - Ctrl+Alt+R')
109
138
  }
110
- })
139
+ }
140
+
141
+ // Update refresh button state on data source events
111
142
  editor.on(`
112
143
  ${ DATA_SOURCE_DATA_LOAD_END }
113
144
  ${ DATA_SOURCE_DATA_LOAD_CANCEL }
114
145
  `, () => {
115
- const button = editor.Panels.getButton('options', 'refresh-data-sources')
116
- if (button) {
117
- button.set('className', `${REFRESH_BUTTON_BASE_CLASS} fa-refresh`)
118
- button.set('title', 'Refresh data sources (reload dynamic content) - Ctrl+Alt+R')
119
- }
146
+ setRefreshButtonState(editor, 'ready')
120
147
  })
121
148
  editor.on(DATA_SOURCE_ERROR, (error: Error) => {
122
149
  console.error('Data source error', error)
123
- const button = editor.Panels.getButton('options', 'refresh-data-sources')
124
- if (button) {
125
- button.set('className', `${REFRESH_BUTTON_BASE_CLASS} fa-exclamation-triangle`)
126
- button.set('title', 'Error refreshing data sources, check the notifications')
127
- }
150
+ setRefreshButtonState(editor, 'error')
151
+ })
152
+ editor.on(DATA_SOURCE_DATA_LOAD_START, () => {
153
+ setRefreshButtonState(editor, 'loading')
128
154
  })
129
155
 
130
- // Add keyboard shortcuts
156
+ // Register keyboard shortcuts for data source actions
131
157
  editor.Keymaps.add('data-source:refresh', 'ctrl+alt+r', COMMAND_REFRESH, {
132
158
  prevent: true,
133
159
  })
@@ -172,24 +172,33 @@ function getItemDisplayName(editor: Editor, index: number): string {
172
172
  const settings = page?.get('settings') as Silex11tyPluginWebsiteSettings | undefined
173
173
 
174
174
  const body = page?.getMainComponent()
175
- if (!body) return
176
-
177
- if (settings?.eleventySeoTitle || settings?.eleventyPermalink) {
178
- // Parse the permalink expression
179
- const nameExpression = toExpression(settings.eleventySeoTitle || settings.eleventyPermalink)
175
+ if (!body) return `Page ${index + 1}`
176
+
177
+ // Try SEO title first
178
+ if (settings?.eleventySeoTitle) {
179
+ const titleExpression = toExpression(settings.eleventySeoTitle)
180
+ if (Array.isArray(titleExpression) && titleExpression.length > 0) {
181
+ const title = getValue(titleExpression, body, false)
182
+ if (typeof title === 'string' && title.trim()) return title
183
+ }
184
+ }
180
185
 
181
- if (Array.isArray(nameExpression) && nameExpression.length > 0) {
182
- const name = getValue(nameExpression, body, false)
183
- if (typeof name === 'string') return name
186
+ // Try permalink second
187
+ if (settings?.eleventyPermalink) {
188
+ const permalinkExpression = toExpression(settings.eleventyPermalink)
189
+ if (Array.isArray(permalinkExpression) && permalinkExpression.length > 0) {
190
+ const permalink = getValue(permalinkExpression, body, false)
191
+ if (typeof permalink === 'string' && permalink.trim()) return permalink
184
192
  }
185
193
  }
186
- return `Page ${index}`
194
+
195
+ // Final fallback to index-based name (1-indexed)
196
+ return `Page ${index + 1}`
187
197
  } catch (e) {
188
- console.error('Error generating permalink for item:', e)
198
+ console.error('Error generating display name for item:', e)
199
+ // Final fallback to index-based name (1-indexed)
200
+ return `Page ${index + 1}`
189
201
  }
190
-
191
- // Final fallback to index-based name
192
- return `Page ${index + 1}`
193
202
  }
194
203
 
195
204
  function handleItemClick(editor: Editor, index: number) {
@@ -243,19 +243,6 @@ export default (editor: Editor, opts) => {
243
243
  info: 'Controls content overflow in a block.',
244
244
  }, { at: 6 })
245
245
 
246
- editor.StyleManager.addProperty('dimension', {
247
- name: 'Scroll Behavior',
248
- property: 'scroll-behavior',
249
- type: 'select',
250
- defaults: '',
251
- options: [
252
- { id: '', value: '', name: '' },
253
- { id: 'auto', value: 'auto', name: 'auto' },
254
- { id: 'smooth', value: 'smooth', name: 'smooth' }
255
- ],
256
- info: 'Sets smooth or auto scroll behavior.',
257
- }, { at: 7 })
258
-
259
246
  editor.StyleManager.removeProperty('dimension', 'margin')
260
247
  editor.StyleManager.addProperty('dimension', {
261
248
  name: 'Margin',
@@ -794,6 +781,18 @@ export default (editor: Editor, opts) => {
794
781
  ],
795
782
  info: 'The column-span CSS property makes it possible for an element to span across all columns when its value is set to all.',
796
783
  }, { at: 9 })
784
+ editor.StyleManager.addProperty('extra', {
785
+ name: 'Scroll Behavior',
786
+ property: 'scroll-behavior',
787
+ type: 'select',
788
+ defaults: '',
789
+ options: [
790
+ { id: '', value: '', name: '' },
791
+ { id: 'auto', value: 'auto', name: 'auto' },
792
+ { id: 'smooth', value: 'smooth', name: 'smooth' }
793
+ ],
794
+ info: 'Sets smooth or auto scroll behavior.',
795
+ }, { at: 10 })
797
796
  editor.StyleManager.addProperty('extra', {
798
797
  name: 'Scroll Snap Type',
799
798
  property: 'scroll-snap-type',
@@ -824,7 +823,7 @@ export default (editor: Editor, opts) => {
824
823
  ],
825
824
  }],
826
825
  info: 'The scroll-snap-type CSS property sets the direction and mode of enforced snap points on the scroll container.',
827
- }, { at: 10 })
826
+ }, { at: 11 })
828
827
  editor.StyleManager.addProperty('extra', {
829
828
  name: 'Scroll Padding',
830
829
  property: 'scroll-padding',
@@ -832,7 +831,7 @@ export default (editor: Editor, opts) => {
832
831
  defaults: '',
833
832
  units: ['px', 'em', 'rem', '%'],
834
833
  info: 'Defines offsets for scroll snapping.',
835
- }, { at: 11 })
834
+ }, { at: 12 })
836
835
  editor.StyleManager.addProperty('extra', {
837
836
  name: 'Scroll Snap Align',
838
837
  property: 'scroll-snap-align',
@@ -846,7 +845,7 @@ export default (editor: Editor, opts) => {
846
845
  { id: 'center', value: 'center', name: 'center' },
847
846
  ],
848
847
  info: 'Aligns elements to scroll snaps.',
849
- }, { at: 11 })
848
+ }, { at: 13 })
850
849
 
851
850
  editor.SelectorManager.states.add({name: 'before', label: 'Before'})
852
851
  editor.SelectorManager.states.add({name: 'after', label: 'After'})