@silexlabs/silex 3.0.0-alpha.97 → 3.0.0

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 (206) hide show
  1. package/.env.default +2 -1
  2. package/README.md +31 -9
  3. package/dist/client/client/api.d.ts +5 -0
  4. package/dist/client/client/assetUrl.d.ts +3 -0
  5. package/dist/client/client/config.d.ts +31 -0
  6. package/dist/client/client/events.d.ts +7 -1
  7. package/dist/client/client/grapesjs/PublicationManager.d.ts +2 -1
  8. package/dist/client/client/grapesjs/css-props.d.ts +6 -0
  9. package/dist/client/client/grapesjs/img.d.ts +6 -0
  10. package/dist/client/client/grapesjs/index.d.ts +2 -2
  11. package/dist/client/client/grapesjs/new-page-dialog.d.ts +1 -1
  12. package/dist/client/client/grapesjs/page-panel.d.ts +1 -1
  13. package/dist/client/client/grapesjs/project-bar.d.ts +16 -0
  14. package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
  15. package/dist/client/client/grapesjs/rich-text.d.ts +4 -1
  16. package/dist/client/client/grapesjs/semantic.d.ts +1 -1
  17. package/dist/client/client/grapesjs/settings-sections.d.ts +15 -0
  18. package/dist/client/client/grapesjs/settings.d.ts +3 -1
  19. package/dist/client/client/grapesjs/symbolDialogs.d.ts +1 -1
  20. package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
  21. package/dist/client/client/index.d.ts +10 -0
  22. package/dist/client/client/publication-transformers.d.ts +4 -3
  23. package/dist/client/client/publish-custom-code-block.d.ts +4 -0
  24. package/dist/client/client/publish-fonts.d.ts +4 -0
  25. package/dist/client/client/utils.d.ts +4 -0
  26. package/dist/client/constants.d.ts +1 -0
  27. package/dist/client/css/admin.css +273 -32
  28. package/dist/client/index.html +2 -1
  29. package/dist/client/js/main.js +164 -123
  30. package/dist/client/js/main.js.LICENSE.txt +15 -1
  31. package/dist/client/types.d.ts +12 -7
  32. package/dist/plugins/client/client/events.d.ts +7 -1
  33. package/dist/plugins/client/client/events.js +7 -1
  34. package/dist/plugins/client/client/events.js.map +1 -1
  35. package/dist/plugins/client/plugins/client/template.js +108 -58
  36. package/dist/plugins/client/plugins/client/template.js.map +1 -1
  37. package/dist/plugins/client/types.d.ts +12 -7
  38. package/dist/plugins/client/types.js +6 -0
  39. package/dist/plugins/client/types.js.map +1 -1
  40. package/dist/plugins/server/constants.d.ts +1 -0
  41. package/dist/plugins/server/constants.js +2 -1
  42. package/dist/plugins/server/constants.js.map +1 -1
  43. package/dist/plugins/server/plugins/server/FtpConnector.d.ts +1 -0
  44. package/dist/plugins/server/plugins/server/FtpConnector.js +12 -1
  45. package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
  46. package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +3 -0
  47. package/dist/plugins/server/plugins/server/GitlabConnector.js +72 -35
  48. package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
  49. package/dist/plugins/server/server/config.js +3 -3
  50. package/dist/plugins/server/server/config.js.map +1 -1
  51. package/dist/plugins/server/server/connectors/FsHosting.d.ts +1 -0
  52. package/dist/plugins/server/server/connectors/FsHosting.js +13 -1
  53. package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -1
  54. package/dist/plugins/server/server/connectors/FsStorage.d.ts +2 -1
  55. package/dist/plugins/server/server/connectors/FsStorage.js +29 -25
  56. package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
  57. package/dist/plugins/server/server/connectors/connectors.d.ts +1 -0
  58. package/dist/plugins/server/server/jobs.js +4 -2
  59. package/dist/plugins/server/server/jobs.js.map +1 -1
  60. package/dist/plugins/server/types.d.ts +12 -7
  61. package/dist/plugins/server/types.js +7 -1
  62. package/dist/plugins/server/types.js.map +1 -1
  63. package/dist/server/constants.js +2 -1
  64. package/dist/server/server/api/publicationApi.js +3 -5
  65. package/dist/server/server/api/websiteApi.js +22 -3
  66. package/dist/server/server/cli.js +9 -6
  67. package/dist/server/server/config.js +3 -3
  68. package/dist/server/server/connectors/FsHosting.js +13 -1
  69. package/dist/server/server/connectors/FsStorage.js +28 -25
  70. package/dist/server/server/jobs.js +4 -2
  71. package/dist/server/types.js +7 -1
  72. package/package.json +34 -30
  73. package/public/assets/favicon.ico +0 -0
  74. package/public/assets/favicon.png +0 -0
  75. package/public/assets/logo-silex-small.png +0 -0
  76. package/src/css/_variables.scss +5 -3
  77. package/src/css/dialog.scss +22 -1
  78. package/src/css/fonts.scss +31 -0
  79. package/src/css/footer.scss +1 -1
  80. package/src/css/grapesjs-plugins.scss +34 -0
  81. package/src/css/grapesjs.scss +26 -11
  82. package/src/css/index.scss +1 -0
  83. package/src/css/project-bar.scss +11 -7
  84. package/src/css/silex.scss +58 -5
  85. package/src/html/index.pug +2 -2
  86. package/src/ts/client/api.ts +14 -9
  87. package/src/ts/client/assetUrl.test.ts +39 -0
  88. package/src/ts/client/assetUrl.ts +24 -5
  89. package/src/ts/client/config.ts +85 -4
  90. package/src/ts/client/events.ts +15 -9
  91. package/src/ts/client/grapesjs/PublicationManager.ts +71 -38
  92. package/src/ts/client/grapesjs/PublicationUi.ts +13 -11
  93. package/src/ts/client/grapesjs/css-props.ts +544 -0
  94. package/src/ts/client/grapesjs/eleventy.ts +2 -5
  95. package/src/ts/client/grapesjs/footer.ts +24 -9
  96. package/src/ts/client/grapesjs/img.ts +147 -0
  97. package/src/ts/client/grapesjs/index.ts +63 -25
  98. package/src/ts/client/grapesjs/internal-links.test.ts +2 -1
  99. package/src/ts/client/grapesjs/internal-links.ts +0 -1
  100. package/src/ts/client/grapesjs/new-page-dialog.ts +2 -3
  101. package/src/ts/client/grapesjs/page-panel.ts +28 -3
  102. package/src/ts/client/grapesjs/project-bar.ts +83 -64
  103. package/src/ts/client/grapesjs/resize-panel.ts +20 -0
  104. package/src/ts/client/grapesjs/rich-text.ts +21 -13
  105. package/src/ts/client/grapesjs/semantic.ts +3 -3
  106. package/src/ts/client/grapesjs/settings-sections.ts +117 -0
  107. package/src/ts/client/grapesjs/settings.ts +120 -77
  108. package/src/ts/client/grapesjs/symbolDialogs.ts +2 -3
  109. package/src/ts/client/grapesjs/view-buttons.ts +42 -0
  110. package/src/ts/client/index.ts +13 -6
  111. package/src/ts/client/publication-transformers.test.ts +13 -5
  112. package/src/ts/client/publication-transformers.ts +60 -47
  113. package/{dist/plugins/client/client/utils.js → src/ts/client/publish-custom-code-block.ts} +14 -8
  114. package/{dist/plugins/client/page.js → src/ts/client/publish-fonts.ts} +19 -14
  115. package/src/ts/client/utils.ts +18 -0
  116. package/src/ts/constants.ts +1 -0
  117. package/src/ts/plugins/client/template.ts +112 -60
  118. package/src/ts/plugins/server/FtpConnector.ts +14 -2
  119. package/src/ts/plugins/server/GitlabConnector.ts +71 -32
  120. package/src/ts/server/api/publicationApi.ts +5 -5
  121. package/src/ts/server/api/websiteApi.ts +39 -9
  122. package/src/ts/server/cli.ts +7 -5
  123. package/src/ts/server/config.ts +4 -4
  124. package/src/ts/server/connectors/FsHosting.ts +11 -5
  125. package/src/ts/server/connectors/FsStorage.ts +33 -4
  126. package/src/ts/server/connectors/connectors.ts +1 -1
  127. package/src/ts/server/jobs.ts +4 -2
  128. package/src/ts/types.ts +10 -7
  129. package/dist/plugins/client/client/api.d.ts +0 -85
  130. package/dist/plugins/client/client/api.js +0 -114
  131. package/dist/plugins/client/client/api.js.map +0 -1
  132. package/dist/plugins/client/client/assetUrl.d.ts +0 -51
  133. package/dist/plugins/client/client/assetUrl.js +0 -224
  134. package/dist/plugins/client/client/assetUrl.js.map +0 -1
  135. package/dist/plugins/client/client/config.d.ts +0 -58
  136. package/dist/plugins/client/client/config.js +0 -90
  137. package/dist/plugins/client/client/config.js.map +0 -1
  138. package/dist/plugins/client/client/grapesjs/LoginDialog.d.ts +0 -15
  139. package/dist/plugins/client/client/grapesjs/LoginDialog.js +0 -195
  140. package/dist/plugins/client/client/grapesjs/LoginDialog.js.map +0 -1
  141. package/dist/plugins/client/client/grapesjs/PublicationManager.d.ts +0 -72
  142. package/dist/plugins/client/client/grapesjs/PublicationManager.js +0 -356
  143. package/dist/plugins/client/client/grapesjs/PublicationManager.js.map +0 -1
  144. package/dist/plugins/client/client/grapesjs/PublicationUi.d.ts +0 -62
  145. package/dist/plugins/client/client/grapesjs/PublicationUi.js +0 -336
  146. package/dist/plugins/client/client/grapesjs/PublicationUi.js.map +0 -1
  147. package/dist/plugins/client/client/grapesjs/blocks.d.ts +0 -1
  148. package/dist/plugins/client/client/grapesjs/blocks.js +0 -37
  149. package/dist/plugins/client/client/grapesjs/blocks.js.map +0 -1
  150. package/dist/plugins/client/client/grapesjs/breadcrumbs.d.ts +0 -1
  151. package/dist/plugins/client/client/grapesjs/breadcrumbs.js +0 -75
  152. package/dist/plugins/client/client/grapesjs/breadcrumbs.js.map +0 -1
  153. package/dist/plugins/client/client/grapesjs/footer.d.ts +0 -2
  154. package/dist/plugins/client/client/grapesjs/footer.js +0 -28
  155. package/dist/plugins/client/client/grapesjs/footer.js.map +0 -1
  156. package/dist/plugins/client/client/grapesjs/index.d.ts +0 -5
  157. package/dist/plugins/client/client/grapesjs/index.js +0 -313
  158. package/dist/plugins/client/client/grapesjs/index.js.map +0 -1
  159. package/dist/plugins/client/client/grapesjs/internal-links.d.ts +0 -1
  160. package/dist/plugins/client/client/grapesjs/internal-links.js +0 -219
  161. package/dist/plugins/client/client/grapesjs/internal-links.js.map +0 -1
  162. package/dist/plugins/client/client/grapesjs/new-page-dialog.d.ts +0 -2
  163. package/dist/plugins/client/client/grapesjs/new-page-dialog.js +0 -83
  164. package/dist/plugins/client/client/grapesjs/new-page-dialog.js.map +0 -1
  165. package/dist/plugins/client/client/grapesjs/openImport.d.ts +0 -9
  166. package/dist/plugins/client/client/grapesjs/openImport.js +0 -74
  167. package/dist/plugins/client/client/grapesjs/openImport.js.map +0 -1
  168. package/dist/plugins/client/client/grapesjs/page-panel.d.ts +0 -3
  169. package/dist/plugins/client/client/grapesjs/page-panel.js +0 -113
  170. package/dist/plugins/client/client/grapesjs/page-panel.js.map +0 -1
  171. package/dist/plugins/client/client/grapesjs/project-bar.d.ts +0 -1
  172. package/dist/plugins/client/client/grapesjs/project-bar.js +0 -104
  173. package/dist/plugins/client/client/grapesjs/project-bar.js.map +0 -1
  174. package/dist/plugins/client/client/grapesjs/rich-text.d.ts +0 -1
  175. package/dist/plugins/client/client/grapesjs/rich-text.js +0 -31
  176. package/dist/plugins/client/client/grapesjs/rich-text.js.map +0 -1
  177. package/dist/plugins/client/client/grapesjs/semantic.d.ts +0 -1
  178. package/dist/plugins/client/client/grapesjs/semantic.js +0 -110
  179. package/dist/plugins/client/client/grapesjs/semantic.js.map +0 -1
  180. package/dist/plugins/client/client/grapesjs/settings.d.ts +0 -2
  181. package/dist/plugins/client/client/grapesjs/settings.js +0 -198
  182. package/dist/plugins/client/client/grapesjs/settings.js.map +0 -1
  183. package/dist/plugins/client/client/grapesjs/storage.d.ts +0 -1
  184. package/dist/plugins/client/client/grapesjs/storage.js +0 -126
  185. package/dist/plugins/client/client/grapesjs/storage.js.map +0 -1
  186. package/dist/plugins/client/client/grapesjs/symbolDialogs.d.ts +0 -3
  187. package/dist/plugins/client/client/grapesjs/symbolDialogs.js +0 -36
  188. package/dist/plugins/client/client/grapesjs/symbolDialogs.js.map +0 -1
  189. package/dist/plugins/client/client/publication-transformers.d.ts +0 -41
  190. package/dist/plugins/client/client/publication-transformers.js +0 -238
  191. package/dist/plugins/client/client/publication-transformers.js.map +0 -1
  192. package/dist/plugins/client/client/utils.d.ts +0 -2
  193. package/dist/plugins/client/client/utils.js.map +0 -1
  194. package/dist/plugins/client/constants.d.ts +0 -29
  195. package/dist/plugins/client/constants.js +0 -46
  196. package/dist/plugins/client/constants.js.map +0 -1
  197. package/dist/plugins/client/page.d.ts +0 -2
  198. package/dist/plugins/client/page.js.map +0 -1
  199. package/dist/plugins/client/plugins/client/11ty.d.ts +0 -17
  200. package/dist/plugins/client/plugins/client/11ty.js +0 -86
  201. package/dist/plugins/client/plugins/client/11ty.js.map +0 -1
  202. package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +0 -2
  203. package/dist/plugins/client/plugins/client/publicationRenderer.js +0 -170
  204. package/dist/plugins/client/plugins/client/publicationRenderer.js.map +0 -1
  205. package/src/ts/plugins/client/11ty.ts +0 -104
  206. package/src/ts/plugins/client/publicationRenderer.ts +0 -174
@@ -1,4 +1,5 @@
1
1
  @use "variables" as *;
2
+ @use "grapesjs-plugins";
2
3
 
3
4
  #gjs {
4
5
  flex: 1 1 auto;
@@ -35,7 +36,7 @@
35
36
  left: auto;
36
37
  }
37
38
  .#{$cv-prefix}canvas {
38
- width: calc(100% - $viewsPanelWidth - $projectBarWidth);
39
+ width: calc(100% - var(--viewsPanelWidth) - $projectBarWidth);
39
40
  }
40
41
  .#{$pn-prefix}devices-c {
41
42
  display: flex;
@@ -44,7 +45,6 @@
44
45
  }
45
46
  }
46
47
  .#{$pn-prefix}views-container {
47
- width: $viewsPanelWidth;
48
48
  }
49
49
 
50
50
  .gjs-editor {
@@ -52,6 +52,17 @@
52
52
  font-size: 0.75rem;
53
53
  }
54
54
 
55
+ // Dialogs
56
+ .gjs-mdl-header {
57
+ background-color: $primaryColor;
58
+ color: $secondaryColor;
59
+ padding: 20px;
60
+ border-bottom: 1px solid $lighterPrimaryColor;
61
+ .gjs-mdl-btn-close {
62
+ top: 15px;
63
+ }
64
+ }
65
+
55
66
  // Inside the canvas
56
67
  .gjs-toolbar {
57
68
  background-color: $tertiaryColor;
@@ -66,6 +77,19 @@
66
77
  outline: 1px solid $tertiaryColor;
67
78
  }
68
79
 
80
+ // Property panels
81
+ .gjs-pn-buttons {
82
+ justify-content: unset;
83
+ }
84
+
85
+ .gjs-pn-views-container, .gjs-pn-views {
86
+ width: var(--viewsPanelWidth);
87
+ }
88
+
89
+ .gjs-pn-options {
90
+ right: var(--viewsPanelWidth);
91
+ }
92
+
69
93
  /*
70
94
  */
71
95
  // Font awesome 5 fix
@@ -141,12 +165,3 @@
141
165
  .fa-diamond:before {
142
166
  content: "\f3a5";
143
167
  }
144
- /*
145
- .fa-plus:before {
146
- content: "+";
147
- font-size: 35px;
148
- }
149
- // .fa-plus {
150
- // background-image: svg(<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32V224H48c-17.7 0-32 14.3-32 32s14.3 32 32 32H192V432c0 17.7 14.3 32 32 32s32-14.3 32-32V288H400c17.7 0 32-14.3 32-32s-14.3-32-32-32H256V80z"/></svg>);
151
- // }
152
- */
@@ -10,6 +10,7 @@
10
10
  @use "loader";
11
11
  @use "footer";
12
12
  @use "dialog";
13
+ @use "fonts";
13
14
  @use "project-bar";
14
15
  @use "page-panel";
15
16
  @use "website-info";
@@ -1,14 +1,17 @@
1
1
  @use "variables" as *;
2
2
 
3
3
  // squeeze the stage iframe when panel is open
4
- iframe.enable-squeeze.gjs-frame {
5
- transition: width $transitionSpeedMedium, height $transitionSpeedMedium, left $transitionSpeedMedium;
6
- }
7
- iframe.enable-squeeze.silex-squeeze-left {
8
- --project-panel-width: max(150px, 15.7%); /* Why 16? Also sass vars do not work here */
9
- left: var(--project-panel-width);
10
- width: calc(100% - var(--project-panel-width));
4
+ //.silex-squeeze-left .gjs-frame-wrapper {
5
+ // margin-left: 18%;
6
+ // width: auto;
7
+ //}
8
+ .silex-squeeze-left .gjs-cv-canvas {
9
+ left: calc(13% + 35px);
10
+ width: auto;
11
+ right: var(--viewsPanelWidth);
11
12
  }
13
+
14
+
12
15
  // button bar
13
16
  .#{$pn-prefix}project-bar-panel {
14
17
  width: $projectBarWidth;
@@ -35,6 +38,7 @@ iframe.enable-squeeze.silex-squeeze-left {
35
38
  background-repeat: no-repeat;
36
39
  background-position: center center;
37
40
  background-color: transparent;
41
+ background-size: contain;
38
42
  }
39
43
  }
40
44
 
@@ -29,11 +29,34 @@
29
29
  width: 100%;
30
30
  }
31
31
 
32
+ // Resize options panel button
33
+ .silex-workspace {
34
+ .gjs-pn-viewsOptions {
35
+ z-index: $z-views-options-buttons;
36
+ right: 0;
37
+ left: auto;
38
+
39
+ .viewsOptions__size-btn::before {
40
+ content: '←';
41
+ }
42
+ }
43
+ }
44
+
45
+ .silex-hidden {
46
+ /* do not really hide, because it breaks the code editor init in the site settings + for animation */
47
+ visibility: hidden;
48
+ position: absolute;
49
+ opacity: 0;
50
+ height: 10px;
51
+ width: 10px;
52
+ overflow: hidden;
53
+ }
54
+
32
55
  // full screen settings
33
56
  .settings-dialog > div {
34
57
  max-width: none;
35
58
  width: 100%;
36
- max-height: 100vh;
59
+ height: 100vh;
37
60
  overflow: auto;
38
61
  form.silex-form {
39
62
  padding-bottom: 100px; // leave space for fixed footer
@@ -63,6 +86,10 @@
63
86
  font-size: x-small;
64
87
  }
65
88
  }
89
+ .CodeMirror {
90
+ min-height: 400px;
91
+ height: auto;
92
+ }
66
93
  }
67
94
 
68
95
  button.silex-button,
@@ -83,6 +110,7 @@ input.silex-button[type="submit"] {
83
110
  padding: 5px;
84
111
  }
85
112
 
113
+ .silex-button.disabled,
86
114
  .silex-button[disabled] {
87
115
  opacity: .8;
88
116
  cursor: unset;
@@ -98,6 +126,7 @@ input.silex-button[type="submit"] {
98
126
 
99
127
  .silex-button--size {
100
128
  max-height: 30px;
129
+ margin: 0 10px;
101
130
  }
102
131
 
103
132
  .silex-button--small {
@@ -113,11 +142,15 @@ input.silex-button[type="submit"] {
113
142
  display: block;
114
143
  }
115
144
 
145
+ .silex-sidebar-dialog,
116
146
  .silex-bar {
117
147
  display: flex;
118
148
  &>* {
119
149
  margin: 5px;
120
150
  }
151
+ main {
152
+ width: 100%;
153
+ }
121
154
  }
122
155
 
123
156
  ol.silex-list,
@@ -125,15 +158,15 @@ ul.silex-list {
125
158
  list-style: none;
126
159
  margin: 0;
127
160
  padding: 0;
128
- background-color: #575757;
161
+ //background-color: #575757;
129
162
  &>li {
130
163
  padding: 5px;
131
- border-bottom: 1px solid #575757;
132
- background-color: #444;
164
+ border-bottom: 1px solid $lighterPrimaryColor;
165
+ //background-color: #444;
133
166
  margin: 5px;
134
167
  }
135
168
  fieldset {
136
- border-color: #575757;
169
+ border-color: $lighterPrimaryColor;
137
170
  }
138
171
  h4 { // badge-like
139
172
  display: inline-block;
@@ -158,6 +191,26 @@ ul.silex-list {
158
191
  }
159
192
  }
160
193
 
194
+ ol.silex-list--menu,
195
+ ul.silex-list--menu {
196
+ &>li {
197
+ padding: 15px;
198
+ margin: 0;
199
+ border: 1px solid transparent;
200
+ border-bottom-color: $lighterPrimaryColor;
201
+ text-align: center;
202
+ cursor: pointer;
203
+ }
204
+ &>li:first-child {
205
+ border-top-color: $lighterPrimaryColor;
206
+ }
207
+ .active,
208
+ &>li:hover {
209
+ color: $tertiaryColor;
210
+ background-color: $darkerPrimaryColor;
211
+ }
212
+ }
213
+
161
214
  form.silex-form {
162
215
  display: flex;
163
216
  flex-direction: column;
@@ -34,6 +34,6 @@ html(lang="en", translate="no")
34
34
  script(src='js/main.js')
35
35
 
36
36
  block silex-start
37
- // Here you can use silex.config to hook into silex options before silex starts
38
37
  script.
39
- window.silex.start()
38
+ silex.start()
39
+ // From here silex.config is available and Silex is getting started
@@ -15,8 +15,8 @@
15
15
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
- import { API_CONNECTOR_LIST, API_CONNECTOR_USER, API_CONNECTOR_LOGOUT, API_CONNECTOR_PATH, API_PATH, API_PUBLICATION_PATH, API_PUBLICATION_PUBLISH, API_PUBLICATION_STATUS, API_WEBSITE_ASSET_READ, API_WEBSITE_ASSETS_WRITE, API_WEBSITE_DELETE, API_WEBSITE_LIST, API_WEBSITE_PATH, API_WEBSITE_READ, API_WEBSITE_WRITE, API_WEBSITE_META_READ, API_WEBSITE_META_WRITE, API_WEBSITE_CREATE } from '../constants'
19
- import { ApiPublicationPublishBody, ApiPublicationPublishQuery, ApiPublicationPublishResponse, ApiPublicationStatusQuery, ApiPublicationStatusResponse, ConnectorId, JobData, JobId, PublicationJobData, WebsiteId, ApiConnectorListResponse, ApiConnectorListQuery, ConnectorData, ConnectorType, ApiWebsiteReadQuery, ApiWebsiteReadResponse, WebsiteData, ApiWebsiteWriteQuery, ApiWebsiteWriteBody, ApiWebsiteWriteResponse, ApiWebsiteDeleteQuery, ApiWebsiteAssetsReadQuery, ApiWebsiteAssetsReadResponse, ApiWebsiteAssetsWriteQuery, ApiWebsiteAssetsWriteBody, ApiWebsiteAssetsWriteResponse, ClientSideFile, PublicationData, ApiConnectorUserResponse, ConnectorUser, WebsiteMeta, ApiConnectorLogoutQuery, ApiConnectorUserQuery, ApiWebsiteListResponse, ApiWebsiteListQuery, WebsiteMetaFileContent, ApiWebsiteCreateQuery, ApiWebsiteCreateBody, ApiWebsiteCreateResponse, ApiWebsiteMetaWriteQuery, ApiWebsiteMetaWriteBody, ApiWebsiteMetaWriteResponse, ApiWebsiteMetaReadQuery, ApiWebsiteMetaReadResponse, ConnectorOptions, ApiError } from '../types'
18
+ import { API_CONNECTOR_LIST, API_CONNECTOR_USER, API_CONNECTOR_LOGOUT, API_CONNECTOR_PATH, API_PATH, API_PUBLICATION_PATH, API_PUBLICATION_PUBLISH, API_PUBLICATION_STATUS, API_WEBSITE_ASSET_READ, API_WEBSITE_ASSETS_WRITE, API_WEBSITE_DELETE, API_WEBSITE_LIST, API_WEBSITE_PATH, API_WEBSITE_READ, API_WEBSITE_WRITE, API_WEBSITE_META_READ, API_WEBSITE_META_WRITE, API_WEBSITE_CREATE, API_WEBSITE_DUPLICATE } from '../constants'
19
+ import { ApiPublicationPublishBody, ApiPublicationPublishQuery, ApiPublicationPublishResponse, ApiPublicationStatusQuery, ApiPublicationStatusResponse, ConnectorId, JobData, JobId, PublicationJobData, WebsiteId, ApiConnectorListResponse, ApiConnectorListQuery, ConnectorData, ConnectorType, ApiWebsiteReadQuery, ApiWebsiteReadResponse, WebsiteData, ApiWebsiteWriteQuery, ApiWebsiteWriteBody, ApiWebsiteWriteResponse, ApiWebsiteDeleteQuery, ApiWebsiteAssetsReadQuery, ApiWebsiteAssetsReadResponse, ApiWebsiteAssetsWriteQuery, ApiWebsiteAssetsWriteBody, ApiWebsiteAssetsWriteResponse, ClientSideFile, PublicationData, ApiConnectorUserResponse, ConnectorUser, WebsiteMeta, ApiConnectorLogoutQuery, ApiConnectorUserQuery, ApiWebsiteListResponse, ApiWebsiteListQuery, WebsiteMetaFileContent, ApiWebsiteCreateQuery, ApiWebsiteCreateBody, ApiWebsiteCreateResponse, ApiWebsiteMetaWriteQuery, ApiWebsiteMetaWriteBody, ApiWebsiteMetaWriteResponse, ApiWebsiteMetaReadQuery, ApiWebsiteMetaReadResponse, ConnectorOptions, ApiError, ApiWebsiteDuplicateQuery } from '../types'
20
20
 
21
21
  export enum ApiRoute {
22
22
  PUBLICATION_PUBLISH = API_PATH + API_PUBLICATION_PATH + API_PUBLICATION_PUBLISH,
@@ -27,6 +27,7 @@ export enum ApiRoute {
27
27
  WEBSITE_READ = API_PATH + API_WEBSITE_PATH + API_WEBSITE_READ,
28
28
  WEBSITE_WRITE = API_PATH + API_WEBSITE_PATH + API_WEBSITE_WRITE,
29
29
  WEBSITE_DELETE = API_PATH + API_WEBSITE_PATH + API_WEBSITE_DELETE,
30
+ WEBSITE_DUPLICATE = API_PATH + API_WEBSITE_PATH + API_WEBSITE_DUPLICATE,
30
31
  WEBSITE_CREATE = API_PATH + API_WEBSITE_PATH + API_WEBSITE_CREATE,
31
32
  WEBSITE_LIST = API_PATH + API_WEBSITE_PATH + API_WEBSITE_LIST,
32
33
  WEBSITE_ASSETS_READ = API_PATH + API_WEBSITE_PATH + API_WEBSITE_ASSET_READ,
@@ -83,23 +84,27 @@ export async function websiteList({connectorId}: {connectorId?: ConnectorId}): P
83
84
  }
84
85
 
85
86
  export async function websiteLoad({websiteId, connectorId}: {websiteId: WebsiteId, connectorId?: ConnectorId}): Promise<WebsiteData> {
86
- return api<ApiWebsiteReadQuery, null, ApiWebsiteReadResponse>(ApiRoute.WEBSITE_READ, 'GET', { websiteId, connectorId: connectorId }) as Promise<WebsiteData>
87
+ return api<ApiWebsiteReadQuery, null, ApiWebsiteReadResponse>(ApiRoute.WEBSITE_READ, 'GET', { websiteId, connectorId }) as Promise<WebsiteData>
87
88
  }
88
89
 
89
90
  export async function websiteSave({websiteId, data, connectorId}: {websiteId: WebsiteId, data: WebsiteData, connectorId?: ConnectorId}): Promise<void> {
90
- const { message } = await api<ApiWebsiteWriteQuery, ApiWebsiteWriteBody, ApiWebsiteWriteResponse>(ApiRoute.WEBSITE_WRITE, 'POST', { websiteId, connectorId: connectorId }, data)
91
+ const { message } = await api<ApiWebsiteWriteQuery, ApiWebsiteWriteBody, ApiWebsiteWriteResponse>(ApiRoute.WEBSITE_WRITE, 'POST', { websiteId, connectorId }, data)
91
92
  }
92
93
 
93
94
  export async function websiteDelete({websiteId, connectorId}: {websiteId: WebsiteId, connectorId?: ConnectorId}): Promise<void> {
94
- await api<ApiWebsiteDeleteQuery, null, null>(ApiRoute.WEBSITE_DELETE, 'DELETE', { websiteId, connectorId: connectorId })
95
+ await api<ApiWebsiteDeleteQuery, null, null>(ApiRoute.WEBSITE_DELETE, 'DELETE', { websiteId, connectorId })
96
+ }
97
+
98
+ export async function websiteDuplicate({websiteId, connectorId}: {websiteId: WebsiteId, connectorId?: ConnectorId}): Promise<void> {
99
+ await api<ApiWebsiteDuplicateQuery, null, null>(ApiRoute.WEBSITE_DUPLICATE, 'POST', { websiteId, connectorId })
95
100
  }
96
101
 
97
102
  export async function websiteCreate({websiteId, data, connectorId}: {websiteId: WebsiteId, data: WebsiteMetaFileContent, connectorId?: ConnectorId}): Promise<void> {
98
- await api<ApiWebsiteCreateQuery, ApiWebsiteCreateBody, ApiWebsiteCreateResponse>(ApiRoute.WEBSITE_CREATE, 'PUT', { connectorId: connectorId }, data)
103
+ await api<ApiWebsiteCreateQuery, ApiWebsiteCreateBody, ApiWebsiteCreateResponse>(ApiRoute.WEBSITE_CREATE, 'PUT', { connectorId }, data)
99
104
  }
100
105
 
101
106
  export async function websiteMetaWrite({websiteId, data, connectorId}: {websiteId: WebsiteId, data: WebsiteMetaFileContent, connectorId?: ConnectorId}): Promise<void> {
102
- await api<ApiWebsiteMetaWriteQuery, ApiWebsiteMetaWriteBody, ApiWebsiteMetaWriteResponse>(ApiRoute.WEBSITE_META_WRITE, 'POST', { websiteId, connectorId: connectorId }, data)
107
+ await api<ApiWebsiteMetaWriteQuery, ApiWebsiteMetaWriteBody, ApiWebsiteMetaWriteResponse>(ApiRoute.WEBSITE_META_WRITE, 'POST', { websiteId, connectorId }, data)
103
108
  }
104
109
 
105
110
  export async function websiteMetaRead({websiteId, connectorId}: {websiteId: WebsiteId, connectorId?: ConnectorId}): Promise<WebsiteMeta> {
@@ -107,7 +112,7 @@ export async function websiteMetaRead({websiteId, connectorId}: {websiteId: Webs
107
112
  }
108
113
 
109
114
  export async function websiteAssetsLoad({path, websiteId, connectorId}: {path: string, websiteId: WebsiteId, connectorId: ConnectorId}): Promise<string> {
110
- return api<ApiWebsiteAssetsReadQuery, null, ApiWebsiteAssetsReadResponse>(`${ApiRoute.WEBSITE_ASSETS_READ}/${path}`, 'GET', { websiteId, connectorId: connectorId })
115
+ return api<ApiWebsiteAssetsReadQuery, null, ApiWebsiteAssetsReadResponse>(`${ApiRoute.WEBSITE_ASSETS_READ}/${path}`, 'GET', { websiteId, connectorId })
111
116
  }
112
117
 
113
118
  /**
@@ -115,7 +120,7 @@ export async function websiteAssetsLoad({path, websiteId, connectorId}: {path: s
115
120
  * @see assetManager in src/ts/client/grapesjs/index.ts
116
121
  */
117
122
  export async function websiteAssetsSave({websiteId, connectorId, files}: {websiteId: WebsiteId, connectorId: ConnectorId, files: ClientSideFile[]}): Promise<string[]> {
118
- const { data } = await api<ApiWebsiteAssetsWriteQuery, ApiWebsiteAssetsWriteBody, ApiWebsiteAssetsWriteResponse>(ApiRoute.WEBSITE_ASSETS_WRITE, 'POST', { websiteId, connectorId: connectorId }, files)
123
+ const { data } = await api<ApiWebsiteAssetsWriteQuery, ApiWebsiteAssetsWriteBody, ApiWebsiteAssetsWriteResponse>(ApiRoute.WEBSITE_ASSETS_WRITE, 'POST', { websiteId, connectorId }, files)
119
124
  return data
120
125
  }
121
126
 
@@ -1,4 +1,5 @@
1
1
  import { jest, expect, describe, it, beforeEach } from '@jest/globals'
2
+ import { removeTempDataFromStyles } from './assetUrl'
2
3
 
3
4
  describe('assetUrl', () => {
4
5
  beforeEach(() => {
@@ -122,4 +123,42 @@ describe('assetUrl', () => {
122
123
  expect(storedToDisplayed(`/api/website/assets/test.webp?websiteId=${websiteIdEncoded}&connectorId=${connectorIdEncoded}`, websiteId, connectorId))
123
124
  .toBe(`/api/website/assets/test.webp?websiteId=${websiteIdEncoded}&connectorId=${connectorIdEncoded}`)
124
125
  })
126
+
127
+ it('multiple backgrounds', async () => {
128
+ // // FIXME: this should mock the functions but it doesnt
129
+ // const mocked = {
130
+ // displayedToStored: jest.fn((url: string) => url),
131
+ // storedToDisplayed: jest.fn((url: string) => url),
132
+ // }
133
+ // jest.mock('./assetUrl', () => (mocked))
134
+ //addTempDataToStyles([toStyle(bgImageDisplayed)], websiteId, connectorId)
135
+ //expect(mocked.displayedToStored).toBeCalledTimes(1)
136
+ const { addTempDataToStyles } = await import('./assetUrl')
137
+
138
+ const websiteId = 'test-id'
139
+ const connectorId = 'test-connector'
140
+ const bgImageDisplayed = `linear-gradient(to right, #1fb101 0%, #df1313 67%, rgba(234, 97, 97, 255) 78%, white 100%), url('/api/website/assets/qIg7JPRc.webp?websiteId=${websiteId}&connectorId=${connectorId}'), linear-gradient(#0ca311 0%, #0ca311 100%)`
141
+ const bgImageStored = 'linear-gradient(to right, #1fb101 0%, #df1313 67%, rgba(234, 97, 97, 255) 78%, white 100%), url(\'/assets/qIg7JPRc.webp\'), linear-gradient(#0ca311 0%, #0ca311 100%)'
142
+ const selectors = ['body']
143
+ function toStyle(bgImage: string) {
144
+ return {
145
+ style: {
146
+ 'background-image': bgImage,
147
+ },
148
+ selectors,
149
+ }
150
+ }
151
+ // From stored to displayed
152
+ expect(addTempDataToStyles([toStyle(bgImageStored)], websiteId, connectorId))
153
+ .toEqual([toStyle(bgImageDisplayed)])
154
+ // From displayed to displayed (error case)
155
+ expect(addTempDataToStyles([toStyle(bgImageDisplayed)], websiteId, connectorId))
156
+ .toEqual([toStyle(bgImageDisplayed)])
157
+ // From displayed to stored
158
+ expect(removeTempDataFromStyles([toStyle(bgImageDisplayed)]))
159
+ .toEqual([toStyle(bgImageStored)])
160
+ //// From stored to stored (error case)
161
+ expect(removeTempDataFromStyles([toStyle(bgImageStored)]))
162
+ .toEqual([toStyle(bgImageStored)])
163
+ })
125
164
  })
@@ -41,6 +41,11 @@ export function storedToDisplayed(path: string, websiteId: WebsiteId, storageId:
41
41
  // .replace(/^\//, '') // remove the first slash if it exists
42
42
  //return decodeURIComponent(encodedPath)
43
43
  } else {
44
+ if(path.startsWith('<svg') || path.startsWith('data:image')) {
45
+ // SVG or data URL
46
+ return path
47
+ }
48
+ // Not a stored path, is there a problem?
44
49
  console.warn('storedToDisplayed: path is not a stored path', path)
45
50
  return path
46
51
  }
@@ -69,6 +74,10 @@ export function displayedToStored(path: string): string {
69
74
  .replace(/^\//, '') // remove the first slash if it exists
70
75
  return decodeURIComponent(encodedPath)
71
76
  } else {
77
+ if(path.startsWith('<svg') || path.startsWith('data:image')) {
78
+ // SVG or data URL
79
+ return path
80
+ }
72
81
  console.warn('displayedToStored: path is not a displayed path', path)
73
82
  return path
74
83
  }
@@ -196,17 +205,22 @@ export function removeTempDataFromPages(pages: Page[]): Page[] {
196
205
 
197
206
  /**
198
207
  * Add data to stylesheets
208
+ * e.g. linear-gradient(to right, #1fb101 0%, #df1313 67%, rgba(234, 97, 97, 255) 78%, white 100%), url('/assets/qIg7JPRc.webp'), linear-gradient(#0ca311 0%, #0ca311 100%)
199
209
  */
200
210
  export function addTempDataToStyles(styles: Style[], websiteId: WebsiteId, storageId: ConnectorId): Style[] {
201
- const pattern = /url\((["']?)([^"']+?)\1\)/g
211
+ const regex = new RegExp(/(.*)(url\((["']?)([^"']+?)\1\))*(.*)/, 'g')
212
+
202
213
  return styles.map((style: Style) => {
203
214
  if (style.style && style.style['background-image']) {
204
- const urls = [...style.style['background-image'].matchAll(pattern)].map(match => storedToDisplayed(match[2], websiteId, storageId))
215
+ const bgImage = style.style['background-image']
216
+ .replace(/url\((['"]?)([^'"]+)\1\)/g, (match, quote, url) => {
217
+ return `url(${quote}${storedToDisplayed(url, websiteId, storageId)}${quote})`
218
+ })
205
219
  return {
206
220
  ...style,
207
221
  style: {
208
222
  ...style.style,
209
- 'background-image': urls.map(url => `url('${url}')`).join(', '),
223
+ 'background-image': bgImage,
210
224
  },
211
225
  }
212
226
  }
@@ -216,17 +230,22 @@ export function addTempDataToStyles(styles: Style[], websiteId: WebsiteId, stora
216
230
 
217
231
  /**
218
232
  * Remove temp data from stylesheets
233
+ * The property `background-image` contains the URLs of the assets and other values,
234
+ * e.g. `linear-gradient(to right, #1fb101 0%, #df1313 67%, rgba(234, 97, 97, 255) 78%, white 100%), url('/api/website/assets/qIg7JPRc.webp?websiteId=default&connectorId=fs-storage'), linear-gradient(#0ca311 0%, #0ca311 100%)`
219
235
  */
220
236
  export function removeTempDataFromStyles(styles: Style[]): Style[] {
221
237
  const pattern = /url\((["']?)([^"']+?)\1\)/g
222
238
  return styles.map((style: Style) => {
223
239
  if (style.style && style.style['background-image']) {
224
- const urls = [...style.style['background-image'].matchAll(pattern)].map(match => displayedToStored(match[2]))
240
+ const bgImage = style.style['background-image']
241
+ .replace(/url\((['"]?)([^'"]+)\1\)/g, (match, quote, url) => {
242
+ return `url(${quote}${displayedToStored(url)}${quote})`
243
+ })
225
244
  return {
226
245
  ...style,
227
246
  style: {
228
247
  ...style.style,
229
- 'background-image': urls.map(url => `url('${url}')`).join(', '),
248
+ 'background-image': bgImage,
230
249
  },
231
250
  }
232
251
  }
@@ -20,8 +20,15 @@ import { getEditor, getEditorConfig } from './grapesjs'
20
20
  import { CLIENT_CONFIG_FILE_NAME, DEFAULT_LANGUAGE, DEFAULT_WEBSITE_ID } from '../constants'
21
21
  import { ConnectorId, WebsiteId } from '../types'
22
22
  import { Editor, EditorConfig, Page } from 'grapesjs'
23
- import { PublicationTransformer, validatePublicationTransformer } from './publication-transformers'
23
+ import { PublicationTransformer, publicationTransformerDefault, validatePublicationTransformer } from './publication-transformers'
24
24
  import * as api from './api'
25
+ import { assetsPublicationTransformer } from './assetUrl'
26
+ import { SettingsSection } from './grapesjs/settings-sections'
27
+ import { addSection, removeSection } from './grapesjs/settings'
28
+
29
+ // Plugins
30
+ import publishCustomCodeBlock from './publish-custom-code-block'
31
+ import publishFonts from './publish-fonts'
25
32
 
26
33
  /**
27
34
  * @fileoverview Silex client side config
@@ -57,10 +64,38 @@ export class ClientConfig extends Config {
57
64
  */
58
65
  debug = false
59
66
 
67
+ /**
68
+ * Add hash in the file name of CSS at the time of publication
69
+ * If true, CSS files of generated pages will look like `page-name.123456.css` instead of `page-name.css`
70
+ * This is useful to avoid caching issues
71
+ * @default true
72
+ */
73
+ addHashInCssFileName = true
74
+
75
+ /**
76
+ * Replaced elements
77
+ * This is a list of elements which support the object-fit and object-position CSS properties
78
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/Replaced_element
79
+ * When selected, Silex will show the object-fit and object-position properties in the style panel
80
+ */
81
+ replacedElements = [
82
+ 'img',
83
+ 'video',
84
+ 'iframe',
85
+ 'embed',
86
+ 'object',
87
+ 'audio',
88
+ 'canvas',
89
+ // 'option',
90
+ ]
91
+
60
92
  /**
61
93
  * Grapesjs config
62
94
  */
63
- grapesJsConfig: EditorConfig
95
+ grapesJsConfig: EditorConfig = {
96
+ plugins: [],
97
+ pluginsOpts: {},
98
+ }
64
99
 
65
100
  /**
66
101
  * Client config url
@@ -72,7 +107,22 @@ export class ClientConfig extends Config {
72
107
  * Init GrapesJS config which depend on the config file properties
73
108
  */
74
109
  initGrapesConfig() {
75
- this.grapesJsConfig = getEditorConfig(this.websiteId, this.storageId, this.rootUrl)
110
+ // Get the initial config
111
+ const config = getEditorConfig(this)
112
+
113
+ // Merge with the config modified by plugins
114
+ this.grapesJsConfig = {
115
+ ...this.grapesJsConfig,
116
+ ...config,
117
+ plugins: [
118
+ ...this.grapesJsConfig.plugins,
119
+ ...config.plugins,
120
+ ],
121
+ pluginsOpts: {
122
+ ...this.grapesJsConfig.pluginsOpts,
123
+ ...config.pluginsOpts,
124
+ },
125
+ }
76
126
  }
77
127
 
78
128
  /**
@@ -85,7 +135,14 @@ export class ClientConfig extends Config {
85
135
  /**
86
136
  * Publication transformers let plugins change files before they are published
87
137
  */
88
- publicationTransformers: Array<PublicationTransformer> = []
138
+ publicationTransformers: Array<PublicationTransformer> = [assetsPublicationTransformer, publicationTransformerDefault]
139
+
140
+ /**
141
+ * Reset publication transformers
142
+ */
143
+ resetPublicationTransformers() {
144
+ this.publicationTransformers = [assetsPublicationTransformer]
145
+ }
89
146
 
90
147
  /**
91
148
  * Add a publication transformer(s)
@@ -102,4 +159,28 @@ export class ClientConfig extends Config {
102
159
  // Add to the list
103
160
  this.publicationTransformers = this.publicationTransformers.concat(transformers)
104
161
  }
162
+
163
+ /**
164
+ * Add a section to the settings dialog
165
+ */
166
+ addSettings(section: SettingsSection, siteOrPage: 'site' | 'page', position: 'first' | 'last' | number = 'last') {
167
+ addSection(section, siteOrPage, position)
168
+ }
169
+
170
+ /**
171
+ * Remove a section from the settings dialog
172
+ */
173
+ removeSettings(id: string, siteOrPage: 'site' | 'page') {
174
+ removeSection(id, siteOrPage)
175
+ }
176
+
177
+ /**
178
+ * Add default plugins
179
+ */
180
+ async addDefaultPlugins() {
181
+ await this.addPlugin([
182
+ publishCustomCodeBlock,
183
+ publishFonts,
184
+ ], {})
185
+ }
105
186
  }
@@ -17,18 +17,24 @@
17
17
 
18
18
  export enum ClientEvent {
19
19
  // Sent on the config object
20
- STARTUP_START = 'silex:startup:start',
21
- STARTUP_END = 'silex:startup:end',
22
- GRAPESJS_START = 'silex:grapesjs:start',
23
- GRAPESJS_END = 'silex:grapesjs:end',
20
+ STARTUP_START = 'silex:startup:start', /* Loading is over and Silex is starting */
21
+ STARTUP_END = 'silex:startup:end', /* Silex is ready to be used */
22
+ GRAPESJS_START = 'silex:grapesjs:start', /* GrapesJS is about to be initialized, it is time to edit config.grapesJsConfig */
23
+ GRAPESJS_END = 'silex:grapesjs:end', /* GrapesJS is ready to be used, `editor` is passed as an argument */
24
24
 
25
- // Sent on the editor object
26
- PUBLISH_START = 'silex:publish:start',
27
- PUBLISH_DATA = 'silex:publish:data',
28
- PUBLISH_END = 'silex:publish:end',
29
- PUBLISH_ERROR = 'silex:publish:error',
25
+ // Sent on GrapesJs editor object (returned by silex.getEditor())
26
+ PUBLISH_START = 'silex:publish:start', /* Publication starts, you can read+write {projectData, siteSettings} */
27
+ PUBLISH_PAGE = 'silex:publish:page', /* Publication of a page, read+write { siteSettings, pageSettings, page } */
28
+ PUBLISH_DATA = 'silex:publish:data', /* Just before we send the published data to the server, read+write all publication data, check PublicationData type in types.ts */
29
+ PUBLISH_END = 'silex:publish:end', /* Publication is over, the argument is the publication result with {success: boolean, message: string} */
30
+ PUBLISH_ERROR = 'silex:publish:error', /* Publication failed, the argument is the publication result with {success: boolean, message: string} */
30
31
  PUBLISH_LOGIN_START = 'silex:publish:login:start',
31
32
  PUBLISH_LOGIN_END = 'silex:publish:login:end',
32
33
  ASSET_WRITE_END = 'silex:asset:write:end',
33
34
  WRITE_END = 'silex:write:end',
35
+ SETTINGS_OPEN = 'silex:settings:open', /* The settings dialog is opened */
36
+ SETTINGS_CLOSE = 'silex:settings:close', /* The settings dialog is closed */
37
+ SETTINGS_SAVE_START = 'silex:settings:save:start', /* The settings dialog is closed and the settings are about to be saved */
38
+ SETTINGS_SAVE_END = 'silex:settings:save:end', /* The settings dialog is closed and the settings are saved */
39
+ SETTINGS_SECTION_CHANGE = 'silex:settings:section-change', /* The user clicked on a section in the settings dialog */
34
40
  }