create-nextblock 0.0.1

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/bin/create-nextblock.js +997 -0
  2. package/package.json +25 -0
  3. package/scripts/sync-template.js +284 -0
  4. package/templates/nextblock-template/.env.example +37 -0
  5. package/templates/nextblock-template/.swcrc +30 -0
  6. package/templates/nextblock-template/README.md +194 -0
  7. package/templates/nextblock-template/app/(auth-pages)/forgot-password/page.tsx +57 -0
  8. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -0
  9. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +28 -0
  10. package/templates/nextblock-template/app/(auth-pages)/sign-in/page.tsx +67 -0
  11. package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +70 -0
  12. package/templates/nextblock-template/app/ToasterProvider.tsx +17 -0
  13. package/templates/nextblock-template/app/[slug]/PageClientContent.tsx +147 -0
  14. package/templates/nextblock-template/app/[slug]/page.tsx +145 -0
  15. package/templates/nextblock-template/app/[slug]/page.utils.ts +183 -0
  16. package/templates/nextblock-template/app/actions/email.ts +31 -0
  17. package/templates/nextblock-template/app/actions/formActions.ts +65 -0
  18. package/templates/nextblock-template/app/actions/languageActions.ts +130 -0
  19. package/templates/nextblock-template/app/actions/postActions.ts +80 -0
  20. package/templates/nextblock-template/app/actions.ts +146 -0
  21. package/templates/nextblock-template/app/api/process-image/route.ts +210 -0
  22. package/templates/nextblock-template/app/api/revalidate/route.ts +86 -0
  23. package/templates/nextblock-template/app/api/revalidate-log/route.ts +23 -0
  24. package/templates/nextblock-template/app/api/upload/presigned-url/route.ts +106 -0
  25. package/templates/nextblock-template/app/api/upload/proxy/route.ts +84 -0
  26. package/templates/nextblock-template/app/auth/callback/route.ts +58 -0
  27. package/templates/nextblock-template/app/blog/[slug]/PostClientContent.tsx +169 -0
  28. package/templates/nextblock-template/app/blog/[slug]/page.tsx +177 -0
  29. package/templates/nextblock-template/app/blog/[slug]/page.utils.ts +136 -0
  30. package/templates/nextblock-template/app/blog/page.tsx +77 -0
  31. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +321 -0
  32. package/templates/nextblock-template/app/cms/blocks/actions.ts +434 -0
  33. package/templates/nextblock-template/app/cms/blocks/components/BackgroundSelector.tsx +348 -0
  34. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorArea.tsx +567 -0
  35. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +98 -0
  36. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeCard.tsx +58 -0
  37. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeSelector.tsx +62 -0
  38. package/templates/nextblock-template/app/cms/blocks/components/ColumnEditor.tsx +276 -0
  39. package/templates/nextblock-template/app/cms/blocks/components/DeleteBlockButtonClient.tsx +47 -0
  40. package/templates/nextblock-template/app/cms/blocks/components/EditableBlock.tsx +182 -0
  41. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +120 -0
  42. package/templates/nextblock-template/app/cms/blocks/components/SectionConfigPanel.tsx +133 -0
  43. package/templates/nextblock-template/app/cms/blocks/components/SortableBlockItem.tsx +46 -0
  44. package/templates/nextblock-template/app/cms/blocks/editors/ButtonBlockEditor.tsx +85 -0
  45. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +182 -0
  46. package/templates/nextblock-template/app/cms/blocks/editors/HeadingBlockEditor.tsx +111 -0
  47. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +150 -0
  48. package/templates/nextblock-template/app/cms/blocks/editors/PostsGridBlockEditor.tsx +79 -0
  49. package/templates/nextblock-template/app/cms/blocks/editors/SectionBlockEditor.tsx +337 -0
  50. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +81 -0
  51. package/templates/nextblock-template/app/cms/blocks/editors/VideoEmbedBlockEditor.tsx +64 -0
  52. package/templates/nextblock-template/app/cms/components/ConfirmationModal.tsx +51 -0
  53. package/templates/nextblock-template/app/cms/components/ContentLanguageSwitcher.tsx +145 -0
  54. package/templates/nextblock-template/app/cms/components/CopyContentFromLanguage.tsx +203 -0
  55. package/templates/nextblock-template/app/cms/components/LanguageFilterSelect.tsx +69 -0
  56. package/templates/nextblock-template/app/cms/dashboard/page.tsx +247 -0
  57. package/templates/nextblock-template/app/cms/layout.tsx +10 -0
  58. package/templates/nextblock-template/app/cms/media/UploadFolderContext.tsx +22 -0
  59. package/templates/nextblock-template/app/cms/media/[id]/edit/page.tsx +80 -0
  60. package/templates/nextblock-template/app/cms/media/actions.ts +577 -0
  61. package/templates/nextblock-template/app/cms/media/components/DeleteMediaButtonClient.tsx +53 -0
  62. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -0
  63. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -0
  64. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +157 -0
  65. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +275 -0
  66. package/templates/nextblock-template/app/cms/media/components/MediaImage.tsx +70 -0
  67. package/templates/nextblock-template/app/cms/media/components/MediaPickerDialog.tsx +195 -0
  68. package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +362 -0
  69. package/templates/nextblock-template/app/cms/media/page.tsx +120 -0
  70. package/templates/nextblock-template/app/cms/navigation/[id]/edit/page.tsx +101 -0
  71. package/templates/nextblock-template/app/cms/navigation/actions.ts +358 -0
  72. package/templates/nextblock-template/app/cms/navigation/components/DeleteNavItemButton.tsx +52 -0
  73. package/templates/nextblock-template/app/cms/navigation/components/NavigationItemForm.tsx +248 -0
  74. package/templates/nextblock-template/app/cms/navigation/components/NavigationLanguageSwitcher.tsx +132 -0
  75. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +701 -0
  76. package/templates/nextblock-template/app/cms/navigation/components/SortableNavItem.tsx +98 -0
  77. package/templates/nextblock-template/app/cms/navigation/new/page.tsx +26 -0
  78. package/templates/nextblock-template/app/cms/navigation/page.tsx +102 -0
  79. package/templates/nextblock-template/app/cms/navigation/utils.ts +51 -0
  80. package/templates/nextblock-template/app/cms/pages/[id]/edit/EditPageClient.tsx +121 -0
  81. package/templates/nextblock-template/app/cms/pages/[id]/edit/page.tsx +79 -0
  82. package/templates/nextblock-template/app/cms/pages/actions.ts +241 -0
  83. package/templates/nextblock-template/app/cms/pages/components/DeletePageButtonClient.tsx +47 -0
  84. package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +253 -0
  85. package/templates/nextblock-template/app/cms/pages/new/page.tsx +52 -0
  86. package/templates/nextblock-template/app/cms/pages/page.tsx +232 -0
  87. package/templates/nextblock-template/app/cms/posts/[id]/edit/page.tsx +183 -0
  88. package/templates/nextblock-template/app/cms/posts/actions.ts +309 -0
  89. package/templates/nextblock-template/app/cms/posts/components/DeletePostButtonClient.tsx +55 -0
  90. package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +419 -0
  91. package/templates/nextblock-template/app/cms/posts/new/page.tsx +21 -0
  92. package/templates/nextblock-template/app/cms/posts/page.tsx +192 -0
  93. package/templates/nextblock-template/app/cms/revisions/JsonDiffView.tsx +86 -0
  94. package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +201 -0
  95. package/templates/nextblock-template/app/cms/revisions/actions.ts +84 -0
  96. package/templates/nextblock-template/app/cms/revisions/service.ts +344 -0
  97. package/templates/nextblock-template/app/cms/revisions/utils.ts +127 -0
  98. package/templates/nextblock-template/app/cms/settings/copyright/actions.ts +68 -0
  99. package/templates/nextblock-template/app/cms/settings/copyright/components/CopyrightForm.tsx +78 -0
  100. package/templates/nextblock-template/app/cms/settings/copyright/page.tsx +32 -0
  101. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +117 -0
  102. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +216 -0
  103. package/templates/nextblock-template/app/cms/settings/languages/[id]/edit/page.tsx +77 -0
  104. package/templates/nextblock-template/app/cms/settings/languages/actions.ts +261 -0
  105. package/templates/nextblock-template/app/cms/settings/languages/components/DeleteLanguageButton.tsx +76 -0
  106. package/templates/nextblock-template/app/cms/settings/languages/components/LanguageForm.tsx +167 -0
  107. package/templates/nextblock-template/app/cms/settings/languages/new/page.tsx +34 -0
  108. package/templates/nextblock-template/app/cms/settings/languages/page.tsx +156 -0
  109. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +19 -0
  110. package/templates/nextblock-template/app/cms/settings/logos/actions.ts +114 -0
  111. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +177 -0
  112. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +11 -0
  113. package/templates/nextblock-template/app/cms/settings/logos/page.tsx +118 -0
  114. package/templates/nextblock-template/app/cms/settings/logos/types.ts +8 -0
  115. package/templates/nextblock-template/app/cms/users/[id]/edit/page.tsx +91 -0
  116. package/templates/nextblock-template/app/cms/users/actions.ts +156 -0
  117. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +71 -0
  118. package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +138 -0
  119. package/templates/nextblock-template/app/cms/users/page.tsx +183 -0
  120. package/templates/nextblock-template/app/favicon.ico +0 -0
  121. package/templates/nextblock-template/app/globals.css +401 -0
  122. package/templates/nextblock-template/app/layout.tsx +191 -0
  123. package/templates/nextblock-template/app/lib/sitemap-utils.ts +68 -0
  124. package/templates/nextblock-template/app/page.tsx +109 -0
  125. package/templates/nextblock-template/app/providers.tsx +43 -0
  126. package/templates/nextblock-template/app/robots.txt/route.ts +19 -0
  127. package/templates/nextblock-template/app/sitemap.xml/route.ts +63 -0
  128. package/templates/nextblock-template/app/unauthorized/page.tsx +27 -0
  129. package/templates/nextblock-template/backup/backup_2025-06-19.sql +8057 -0
  130. package/templates/nextblock-template/backup/backup_2025-06-20.sql +8159 -0
  131. package/templates/nextblock-template/backup/backup_2025-07-08.sql +8411 -0
  132. package/templates/nextblock-template/backup/backup_2025-07-09.sql +8442 -0
  133. package/templates/nextblock-template/backup/backup_2025-07-10.sql +8442 -0
  134. package/templates/nextblock-template/backup/backup_2025-10-01.sql +8803 -0
  135. package/templates/nextblock-template/backup/backup_2025-10-02.sql +9749 -0
  136. package/templates/nextblock-template/components/BlockRenderer.tsx +119 -0
  137. package/templates/nextblock-template/components/FooterNavigation.tsx +33 -0
  138. package/templates/nextblock-template/components/Header.tsx +42 -0
  139. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -0
  140. package/templates/nextblock-template/components/LanguageSwitcher.tsx +103 -0
  141. package/templates/nextblock-template/components/ResponsiveNav.tsx +372 -0
  142. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +17 -0
  143. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +93 -0
  144. package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +180 -0
  145. package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -0
  146. package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +69 -0
  147. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +98 -0
  148. package/templates/nextblock-template/components/blocks/renderers/HeadingBlockRenderer.tsx +41 -0
  149. package/templates/nextblock-template/components/blocks/renderers/HeroBlockRenderer.tsx +240 -0
  150. package/templates/nextblock-template/components/blocks/renderers/ImageBlockRenderer.tsx +79 -0
  151. package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +33 -0
  152. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +189 -0
  153. package/templates/nextblock-template/components/blocks/renderers/TextBlockRenderer.tsx +31 -0
  154. package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +59 -0
  155. package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +51 -0
  156. package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +40 -0
  157. package/templates/nextblock-template/components/blocks/types.ts +8 -0
  158. package/templates/nextblock-template/components/env-var-warning.tsx +33 -0
  159. package/templates/nextblock-template/components/form-message.tsx +26 -0
  160. package/templates/nextblock-template/components/header-auth.tsx +71 -0
  161. package/templates/nextblock-template/components/submit-button.tsx +23 -0
  162. package/templates/nextblock-template/components/theme-switcher.tsx +78 -0
  163. package/templates/nextblock-template/context/AuthContext.tsx +138 -0
  164. package/templates/nextblock-template/context/CurrentContentContext.tsx +42 -0
  165. package/templates/nextblock-template/context/LanguageContext.tsx +206 -0
  166. package/templates/nextblock-template/docs/cms-application-overview.md +56 -0
  167. package/templates/nextblock-template/docs/cms-architecture-overview.md +73 -0
  168. package/templates/nextblock-template/docs/files-structure.md +426 -0
  169. package/templates/nextblock-template/docs/tiptap-bundle-optimization-summary.md +174 -0
  170. package/templates/nextblock-template/eslint.config.mjs +28 -0
  171. package/templates/nextblock-template/index.d.ts +5 -0
  172. package/templates/nextblock-template/lib/blocks/README.md +670 -0
  173. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +1001 -0
  174. package/templates/nextblock-template/lib/ui/ColorPicker.ts +1 -0
  175. package/templates/nextblock-template/lib/ui/ConfirmationDialog.ts +1 -0
  176. package/templates/nextblock-template/lib/ui/CustomSelectWithInput.ts +1 -0
  177. package/templates/nextblock-template/lib/ui/Skeleton.ts +1 -0
  178. package/templates/nextblock-template/lib/ui/avatar.ts +1 -0
  179. package/templates/nextblock-template/lib/ui/badge.ts +1 -0
  180. package/templates/nextblock-template/lib/ui/button.ts +1 -0
  181. package/templates/nextblock-template/lib/ui/card.ts +1 -0
  182. package/templates/nextblock-template/lib/ui/checkbox.ts +1 -0
  183. package/templates/nextblock-template/lib/ui/dialog.ts +1 -0
  184. package/templates/nextblock-template/lib/ui/dropdown-menu.ts +1 -0
  185. package/templates/nextblock-template/lib/ui/input.ts +1 -0
  186. package/templates/nextblock-template/lib/ui/label.ts +1 -0
  187. package/templates/nextblock-template/lib/ui/popover.ts +1 -0
  188. package/templates/nextblock-template/lib/ui/progress.ts +1 -0
  189. package/templates/nextblock-template/lib/ui/select.ts +1 -0
  190. package/templates/nextblock-template/lib/ui/separator.ts +1 -0
  191. package/templates/nextblock-template/lib/ui/table.ts +1 -0
  192. package/templates/nextblock-template/lib/ui/textarea.ts +1 -0
  193. package/templates/nextblock-template/lib/ui/tooltip.ts +1 -0
  194. package/templates/nextblock-template/lib/ui/ui.ts +1 -0
  195. package/templates/nextblock-template/middleware.ts +206 -0
  196. package/templates/nextblock-template/next-env.d.ts +6 -0
  197. package/templates/nextblock-template/next.config.js +99 -0
  198. package/templates/nextblock-template/package.json +52 -0
  199. package/templates/nextblock-template/postcss.config.js +6 -0
  200. package/templates/nextblock-template/project.json +7 -0
  201. package/templates/nextblock-template/public/.gitkeep +0 -0
  202. package/templates/nextblock-template/scripts/backfill-image-meta.ts +149 -0
  203. package/templates/nextblock-template/scripts/backup.js +53 -0
  204. package/templates/nextblock-template/scripts/test-bundle-optimization.js +114 -0
  205. package/templates/nextblock-template/tailwind.config.ts +19 -0
  206. package/templates/nextblock-template/tsconfig.json +62 -0
@@ -0,0 +1,401 @@
1
+ /*─────────────────────────────────────────────────────────────────────────────
2
+ globals.css — complete rewrite
3
+ ─────────────────────────────────────────────────────────────────────────────*/
4
+
5
+ @tailwind base;
6
+ @tailwind components;
7
+ @tailwind utilities;
8
+
9
+ /*─────────────────────────────────────────────────────────────────────────────
10
+ 1. Critical CSS Resets (formerly in <style id="critical-css">)
11
+ ─────────────────────────────────────────────────────────────────────────────*/
12
+ @layer base {
13
+ *, ::before, ::after {
14
+ box-sizing: border-box;
15
+ border-width: 0;
16
+ border-style: solid;
17
+ border-color: hsl(var(--border));
18
+ }
19
+ ::before, ::after {
20
+ --tw-content: "";
21
+ }
22
+
23
+ html, :host {
24
+ line-height: 1.5;
25
+ -webkit-text-size-adjust: 100%;
26
+ -moz-tab-size: 4;
27
+ tab-size: 4;
28
+ font-family:
29
+ ui-sans-serif,
30
+ system-ui,
31
+ sans-serif,
32
+ "Apple Color Emoji",
33
+ "Segoe UI Emoji",
34
+ "Segoe UI Symbol",
35
+ "Noto Color Emoji";
36
+ font-feature-settings: normal;
37
+ font-variation-settings: normal;
38
+ -webkit-tap-highlight-color: transparent;
39
+ }
40
+
41
+ body {
42
+ margin: 0;
43
+ line-height: inherit;
44
+ }
45
+
46
+ hr {
47
+ height: 0;
48
+ color: inherit;
49
+ border-top-width: 1px;
50
+ }
51
+
52
+ abbr:where([title]) {
53
+ -webkit-text-decoration: underline dotted;
54
+ text-decoration: underline dotted;
55
+ }
56
+
57
+ h1, h2, h3, h4, h5, h6 {
58
+ font-size: inherit;
59
+ font-weight: inherit;
60
+ margin: 0;
61
+ }
62
+
63
+ a {
64
+ color: inherit;
65
+ text-decoration: inherit;
66
+ }
67
+
68
+ b, strong {
69
+ font-weight: bolder;
70
+ }
71
+
72
+ code, kbd, pre, samp {
73
+ font-family:
74
+ ui-monospace,
75
+ SFMono-Regular,
76
+ Menlo,
77
+ Monaco,
78
+ Consolas,
79
+ "Liberation Mono",
80
+ "Courier New",
81
+ monospace;
82
+ font-feature-settings: normal;
83
+ font-variation-settings: normal;
84
+ font-size: 1em;
85
+ }
86
+
87
+ small {
88
+ font-size: 80%;
89
+ }
90
+
91
+ sub, sup {
92
+ font-size: 75%;
93
+ line-height: 0;
94
+ position: relative;
95
+ vertical-align: baseline;
96
+ }
97
+ sub { bottom: -0.25em; }
98
+ sup { top: -0.5em; }
99
+
100
+ table {
101
+ text-indent: 0;
102
+ border-color: inherit;
103
+ border-collapse: collapse;
104
+ }
105
+
106
+ button,
107
+ input,
108
+ optgroup,
109
+ select,
110
+ textarea {
111
+ font-family: inherit;
112
+ font-feature-settings: inherit;
113
+ font-variation-settings: inherit;
114
+ font-size: 100%;
115
+ font-weight: inherit;
116
+ line-height: inherit;
117
+ letter-spacing: inherit;
118
+ color: inherit;
119
+ margin: 0;
120
+ padding: 0;
121
+ }
122
+ button, select {
123
+ text-transform: none;
124
+ }
125
+ button,
126
+ input[type="button"],
127
+ input[type="reset"],
128
+ input[type="submit"] {
129
+ -webkit-appearance: button;
130
+ background-color: transparent;
131
+ background-image: none;
132
+ }
133
+
134
+ :-moz-focusring { outline: auto; }
135
+ :-moz-ui-invalid { box-shadow: none; }
136
+ progress { vertical-align: baseline; }
137
+ ::-webkit-inner-spin-button,
138
+ ::-webkit-outer-spin-button {
139
+ height: auto;
140
+ }
141
+ [type="search"] {
142
+ -webkit-appearance: textfield;
143
+ outline-offset: -2px;
144
+ }
145
+ ::-webkit-search-decoration {
146
+ -webkit-appearance: none;
147
+ }
148
+ ::-webkit-file-upload-button {
149
+ -webkit-appearance: button;
150
+ font: inherit;
151
+ }
152
+
153
+ summary { display: list-item; }
154
+ blockquote, dd, dl, figure, p, pre { margin: 0; }
155
+ fieldset { margin: 0; padding: 0; }
156
+ legend { padding: 0; }
157
+ menu, ol, ul { list-style: none; margin: 0; padding: 0; }
158
+ dialog { padding: 0; }
159
+ textarea { resize: vertical; }
160
+
161
+ input::-moz-placeholder,
162
+ textarea::-moz-placeholder {
163
+ opacity: 1;
164
+ color: hsl(var(--muted-foreground));
165
+ }
166
+ input::placeholder,
167
+ textarea::placeholder {
168
+ opacity: 1;
169
+ color: hsl(var(--muted-foreground));
170
+ }
171
+
172
+ [role="button"], button { cursor: pointer; }
173
+ :disabled { cursor: default; }
174
+
175
+ audio,
176
+ canvas,
177
+ embed,
178
+ iframe,
179
+ img,
180
+ object,
181
+ svg,
182
+ video {
183
+ display: block;
184
+ vertical-align: middle;
185
+ }
186
+ img, video {
187
+ max-width: 100%;
188
+ height: auto;
189
+ }
190
+ [hidden] { display: none; }
191
+
192
+ /*───────────────────────────────────────────────────────────────────────────
193
+ 2. Project-specific base styles (Tailwind-powered)
194
+ ───────────────────────────────────────────────────────────────────────────*/
195
+ /* Apply your border color everywhere */
196
+ * {
197
+ @apply border-border;
198
+ }
199
+
200
+ /* Body background & text */
201
+ body {
202
+ @apply bg-background text-foreground;
203
+ }
204
+
205
+ /* Semantic headings */
206
+ ul, ol {
207
+ @apply pl-6 mb-4;
208
+ }
209
+ li {
210
+ @apply mb-1;
211
+ }
212
+ blockquote {
213
+ @apply p-4 italic border-l-4 border-border bg-muted text-muted-foreground mb-4;
214
+ }
215
+ code {
216
+ @apply bg-muted text-muted-foreground px-1 py-0.5 rounded-sm font-mono text-sm;
217
+ }
218
+ pre {
219
+ @apply bg-muted p-4 rounded-md overflow-x-auto mb-4;
220
+ }
221
+
222
+ table {
223
+ @apply w-full border-collapse mb-4;
224
+ }
225
+ thead {
226
+ @apply bg-muted;
227
+ }
228
+ th, td {
229
+ @apply border border-border p-2 text-left;
230
+ }
231
+ th {
232
+ @apply font-semibold;
233
+ }
234
+ hr {
235
+ @apply border-t border-border my-8;
236
+ }
237
+ }
238
+
239
+ /* Image alignment preserved from editor output */
240
+ @layer components {
241
+ img[data-align='left'] {
242
+ display: block;
243
+ margin-left: 0;
244
+ margin-right: auto;
245
+ }
246
+ img[data-align='right'] {
247
+ display: block;
248
+ margin-left: auto;
249
+ margin-right: 0;
250
+ }
251
+ img[data-align='center'] {
252
+ display: block;
253
+ margin-left: auto;
254
+ margin-right: auto;
255
+ }
256
+ }
257
+
258
+ /*─────────────────────────────────────────────────────────────────────────────
259
+ 3. Theme color variables
260
+ ─────────────────────────────────────────────────────────────────────────────*/
261
+ :root {
262
+ --background: 220 20% 98%;
263
+ --foreground: 215 30% 15%;
264
+ --card: 0 0% 100%;
265
+ --card-foreground: 215 30% 15%;
266
+ --popover: 0 0% 100%;
267
+ --popover-foreground: 215 30% 15%;
268
+
269
+ --primary: 215 45% 30%;
270
+ --primary-foreground: 0 0% 100%;
271
+
272
+ --secondary: 215 45% 45%;
273
+ --secondary-foreground: 0 0% 100%;
274
+
275
+ --muted: 220 30% 94%;
276
+ --muted-foreground: 215 25% 45%;
277
+
278
+ --accent: 190 70% 50%;
279
+ --accent-foreground: 210 40% 98%;
280
+
281
+ --destructive: 0 70% 42%;
282
+ --destructive-foreground: 0 0% 98%;
283
+
284
+ --warning: 38 92% 50%;
285
+ --warning-foreground: 48 96% 98%;
286
+
287
+ --border: 220 20% 88%;
288
+ --input: 220 20% 88%;
289
+ --ring: 200 80% 60%;
290
+
291
+ --radius: 0.5rem;
292
+
293
+ --chart-1: 217 70% 50%;
294
+ --chart-2: 190 60% 45%;
295
+ --chart-3: 215 35% 40%;
296
+ --chart-4: 220 15% 70%;
297
+ --chart-5: 200 50% 65%;
298
+ }
299
+
300
+ .dark {
301
+ --background: 220 25% 10%;
302
+ --foreground: 220 15% 90%;
303
+ --card: 220 20% 14%;
304
+ --card-foreground: 220 15% 90%;
305
+ --popover: 220 20% 14%;
306
+ --popover-foreground: 220 15% 90%;
307
+
308
+ --primary: 210 90% 65%;
309
+ --primary-foreground: 220 20% 10%;
310
+
311
+ --secondary: 215 50% 45%;
312
+ --secondary-foreground: 220 15% 90%;
313
+
314
+ --muted: 220 15% 18%;
315
+ --muted-foreground: 220 10% 65%;
316
+
317
+ --accent: 195 80% 55%;
318
+ --accent-foreground: 220 25% 10%;
319
+
320
+ --destructive: 0 65% 42%;
321
+ --destructive-foreground: 0 0% 98%;
322
+
323
+ --warning: 38 92% 50%;
324
+ --warning-foreground: 48 96% 98%;
325
+
326
+ --border: 220 15% 25%;
327
+ --input: 220 15% 25%;
328
+ --ring: 195 90% 60%;
329
+
330
+ --chart-1: 210 80% 60%;
331
+ --chart-2: 195 70% 50%;
332
+ --chart-3: 215 40% 50%;
333
+ --chart-4: 220 10% 55%;
334
+ --chart-5: 200 60% 70%;
335
+ }
336
+
337
+ /*─────────────────────────────────────────────────────────────────────────────
338
+ 4. Explicit heading sizes & spacing
339
+ ─────────────────────────────────────────────────────────────────────────────*/
340
+ h1 {
341
+ font-size: 3.5rem;
342
+ line-height: 1;
343
+ font-weight: 700;
344
+ margin: 1.5rem 0 1rem;
345
+ }
346
+ h2 {
347
+ font-size: 2.5rem;
348
+ line-height: 1;
349
+ font-weight: 700;
350
+ margin: 1.25rem 0 0.75rem;
351
+ }
352
+ h3 {
353
+ font-size: 2.25rem;
354
+ line-height: 2.5rem;
355
+ font-weight: 700;
356
+ margin: 1rem 0 0.75rem;
357
+ }
358
+ h4 {
359
+ font-size: 1.5rem;
360
+ line-height: 2rem;
361
+ font-weight: 700;
362
+ margin: 0.75rem 0 0.5rem;
363
+ }
364
+ h5 {
365
+ font-size: 1.25rem;
366
+ line-height: 1.75rem;
367
+ font-weight: 600;
368
+ margin: 0.5rem 0 0.5rem;
369
+ }
370
+ h6 {
371
+ font-size: 1.125rem;
372
+ line-height: 1.75rem;
373
+ font-weight: 600;
374
+ margin: 0.5rem 0 0.5rem;
375
+ }
376
+
377
+ /*─────────────────────────────────────────────────────────────────────────────
378
+ 5. Shimmer loading animation
379
+ ─────────────────────────────────────────────────────────────────────────────*/
380
+ @keyframes shimmer {
381
+ 100% {
382
+ transform: translateX(100%);
383
+ }
384
+ }
385
+ .shimmer::before {
386
+ content: '';
387
+ position: absolute;
388
+ top: 0;
389
+ left: 0;
390
+ right: 0;
391
+ bottom: 0;
392
+ transform: translateX(-100%);
393
+ background-image: linear-gradient(
394
+ 90deg,
395
+ rgba(255, 255, 255, 0) 0,
396
+ rgba(255, 255, 255, 0.2) 20%,
397
+ rgba(255, 255, 255, 0.5) 60%,
398
+ rgba(255, 255, 255, 0)
399
+ );
400
+ animation: shimmer 2s infinite;
401
+ }
@@ -0,0 +1,191 @@
1
+ import '@nextblock-cms/ui/styles/globals.css';
2
+ import '@nextblock-cms/editor/styles/editor.css';
3
+ // app/layout.tsx
4
+ import { EnvVarWarning } from "@/components/env-var-warning";
5
+ import { ThemeSwitcher } from '@/components/theme-switcher';
6
+ import type { Metadata } from 'next';
7
+ import Header from "@/components/Header";
8
+ import FooterNavigation from "@/components/FooterNavigation";
9
+ import { Providers } from './providers';
10
+ import { ToasterProvider } from './ToasterProvider';
11
+ import { createClient as createSupabaseServerClient, getProfileWithRoleServerSide } from '@nextblock-cms/db/server';
12
+ import { getActiveLanguagesServerSide } from '@nextblock-cms/db/server';
13
+ import { getNavigationMenu } from '@/app/cms/navigation/actions';
14
+ import { getActiveLogo } from '@/app/cms/settings/logos/actions';
15
+ import { getCopyrightSettings } from '@/app/cms/settings/copyright/actions';
16
+ import { getTranslations } from '@/app/cms/settings/extra-translations/actions';
17
+ import type { Database } from '@nextblock-cms/db';
18
+ import { headers, cookies } from 'next/headers';
19
+
20
+ const defaultUrl = process.env.VERCEL_URL
21
+ ? `https://${process.env.VERCEL_URL}`
22
+ : "http://localhost:3000";
23
+
24
+ const DEFAULT_LOCALE_FOR_LAYOUT = 'en';
25
+
26
+ type Language = Database['public']['Tables']['languages']['Row'];
27
+ type NavigationItem = Database['public']['Tables']['navigation_items']['Row'];
28
+
29
+ async function loadLayoutData() {
30
+ const supabase = createSupabaseServerClient();
31
+
32
+ const headerList = await headers();
33
+ const cookieStore = await cookies();
34
+ const nonce = headerList.get('x-nonce') || '';
35
+
36
+ const xUserLocaleHeader = headerList.get('x-user-locale');
37
+ const nextUserLocaleCookie = cookieStore.get('NEXT_USER_LOCALE')?.value;
38
+
39
+ let serverDeterminedLocale =
40
+ xUserLocaleHeader ??
41
+ nextUserLocaleCookie ??
42
+ DEFAULT_LOCALE_FOR_LAYOUT;
43
+
44
+ const [
45
+ { data: { user } },
46
+ availableLanguagesResult,
47
+ copyrightSettingsResult,
48
+ translationsResult,
49
+ ] = await Promise.all([
50
+ supabase.auth.getUser(),
51
+ getActiveLanguagesServerSide().catch(() => []),
52
+ getCopyrightSettings().catch(() => ({ en: 'Ac {year} My Ultra-Fast CMS. All rights reserved.' })),
53
+ getTranslations().catch(() => []),
54
+ ]);
55
+
56
+ const profile = user ? await getProfileWithRoleServerSide(user.id) : null;
57
+ const availableLanguages: Language[] = availableLanguagesResult;
58
+ const defaultLanguage: Language | null =
59
+ availableLanguages.find((lang) => lang.is_default) ?? availableLanguages[0] ?? null;
60
+
61
+ if (!availableLanguages.some((lang) => lang.code === serverDeterminedLocale) && defaultLanguage) {
62
+ serverDeterminedLocale = defaultLanguage.code;
63
+ } else if (!availableLanguages.some((lang) => lang.code === serverDeterminedLocale)) {
64
+ serverDeterminedLocale = DEFAULT_LOCALE_FOR_LAYOUT;
65
+ }
66
+
67
+ const copyrightSettings = copyrightSettingsResult as Record<string, string>;
68
+ const fallbackTemplate =
69
+ copyrightSettings['en'] ?? 'Ac {year} My Ultra-Fast CMS. All rights reserved.';
70
+ const templateForLocale =
71
+ copyrightSettings[serverDeterminedLocale] ?? fallbackTemplate;
72
+ const copyrightText = templateForLocale.replace('{year}', new Date().getFullYear().toString());
73
+
74
+ const translations = Array.isArray(translationsResult) ? translationsResult : [];
75
+
76
+ const hasSupabaseEnv =
77
+ Boolean(process.env.NEXT_PUBLIC_SUPABASE_URL && process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY);
78
+
79
+ const headerNavItems: NavigationItem[] = await getNavigationMenu('HEADER', serverDeterminedLocale).catch(() => []);
80
+ const footerNavItems: NavigationItem[] = await getNavigationMenu('FOOTER', serverDeterminedLocale).catch(() => []);
81
+ const logo = await getActiveLogo().catch(() => null);
82
+
83
+ const role = profile?.role ?? null;
84
+ const canAccessCms = role === 'ADMIN' || role === 'WRITER';
85
+ const siteTitle = logo?.site_title ?? 'NRH';
86
+
87
+ return {
88
+ user,
89
+ profile,
90
+ serverDeterminedLocale,
91
+ availableLanguages,
92
+ defaultLanguage,
93
+ translations,
94
+ copyrightText,
95
+ nonce,
96
+ hasSupabaseEnv,
97
+ headerNavItems,
98
+ footerNavItems,
99
+ logo,
100
+ canAccessCms,
101
+ siteTitle,
102
+ };
103
+ }
104
+
105
+ export const metadata: Metadata = {
106
+ metadataBase: new URL(defaultUrl),
107
+ title: 'My Ultra-Fast CMS',
108
+ description: 'A block-based TypeScript CMS with Next.js and Supabase',
109
+ };
110
+
111
+ export default async function RootLayout({
112
+ children,
113
+ }: Readonly<{
114
+ children: React.ReactNode;
115
+ }>) {
116
+ const {
117
+ user,
118
+ profile,
119
+ serverDeterminedLocale,
120
+ availableLanguages,
121
+ defaultLanguage,
122
+ translations,
123
+ copyrightText,
124
+ nonce,
125
+ hasSupabaseEnv,
126
+ headerNavItems,
127
+ logo,
128
+ footerNavItems,
129
+ canAccessCms,
130
+ siteTitle,
131
+ } = await loadLayoutData();
132
+
133
+ return (
134
+ <html lang={serverDeterminedLocale} suppressHydrationWarning>
135
+ <head>
136
+ <title>{metadata.title as string}</title>
137
+ <meta name="description" content={metadata.description as string} />
138
+ <link rel="preconnect" href="https://ppcppwsfnrptznvbxnsz.supabase.co" />
139
+ <link rel="preconnect" href="https://pub-a31e3f1a87d144898aeb489a8221f92e.r2.dev" crossOrigin="anonymous" />
140
+ <link rel="dns-prefetch" href="https://ppcppwsfnrptznvbxnsz.supabase.co" />
141
+ <link rel="dns-prefetch" href="https://pub-a31e3f1a87d144898aeb489a8221f92e.r2.dev" />
142
+ <link rel="dns-prefetch" href="https://aws-0-us-east-1.pooler.supabase.com" />
143
+ <link rel="dns-prefetch" href="https://db.ppcppwsfnrptznvbxnsz.supabase.co" />
144
+ <link rel="dns-prefetch" href="https://realtime.supabase.com" />
145
+ <link rel="preload" as="image" href="https://pub-a31e3f1a87d144898aeb489a8221f92e.r2.dev/hero-bg.jpg" fetchPriority="high" />
146
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
147
+ </head>
148
+ <body className="bg-background text-foreground min-h-screen flex flex-col">
149
+ <Providers
150
+ serverUser={user}
151
+ serverProfile={profile}
152
+ serverLocale={serverDeterminedLocale}
153
+ initialAvailableLanguages={availableLanguages}
154
+ initialDefaultLanguage={defaultLanguage}
155
+ translations={translations}
156
+ nonce={nonce}
157
+ >
158
+ <ToasterProvider />
159
+ <div className="flex-1 w-full flex flex-col items-center">
160
+ <nav className="w-full flex justify-center border-b border-b-foreground/10 h-16">
161
+ <div className="w-full max-w-7xl flex justify-between items-center p-3 px-5 text-sm">
162
+ {!hasSupabaseEnv ? (
163
+ <EnvVarWarning />
164
+ ) : (
165
+ <Header
166
+ navItems={headerNavItems}
167
+ canAccessCms={canAccessCms}
168
+ logo={logo}
169
+ siteTitle={siteTitle}
170
+ />
171
+ )}
172
+ </div>
173
+ </nav>
174
+ <main className="flex-grow w-full">
175
+ {children}
176
+ </main>
177
+ <footer className="w-full border-t py-8">
178
+ <div className="mx-auto flex flex-col items-center justify-center gap-6 text-center text-xs">
179
+ <FooterNavigation navItems={footerNavItems} />
180
+ <div className="flex flex-row items-center gap-2">
181
+ <p className="text-muted-foreground">{copyrightText}</p>
182
+ <ThemeSwitcher />
183
+ </div>
184
+ </div>
185
+ </footer>
186
+ </div>
187
+ </Providers>
188
+ </body>
189
+ </html>
190
+ );
191
+ }
@@ -0,0 +1,68 @@
1
+ import { createClient } from '@nextblock-cms/db/server';
2
+
3
+ interface SitemapEntry {
4
+ path: string;
5
+ lastModified: string;
6
+ }
7
+
8
+ /**
9
+ * Fetches all published pages from Supabase and formats them for the sitemap.
10
+ * @returns {Promise<Array<SitemapEntry>>} A promise that resolves to an array of sitemap entries for pages.
11
+ */
12
+ export async function fetchAllPublishedPages(): Promise<SitemapEntry[]> {
13
+ const supabase = createClient();
14
+ try {
15
+ const { data: pages, error } = await supabase
16
+ .from('pages')
17
+ .select('slug, updated_at')
18
+ .eq('status', 'published');
19
+
20
+ if (error) {
21
+ console.error('Error fetching published pages:', error);
22
+ return [];
23
+ }
24
+
25
+ if (!pages) {
26
+ return [];
27
+ }
28
+
29
+ return pages.map((page) => ({
30
+ path: `/${page.slug}`,
31
+ lastModified: new Date(page.updated_at).toISOString(),
32
+ }));
33
+ } catch (err) {
34
+ console.error('An unexpected error occurred while fetching pages:', err);
35
+ return [];
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Fetches all published posts from Supabase and formats them for the sitemap.
41
+ * @returns {Promise<Array<SitemapEntry>>} A promise that resolves to an array of sitemap entries for posts.
42
+ */
43
+ export async function fetchAllPublishedPosts(): Promise<SitemapEntry[]> {
44
+ const supabase = createClient();
45
+ try {
46
+ const { data: posts, error } = await supabase
47
+ .from('posts')
48
+ .select('slug, updated_at')
49
+ .eq('status', 'published');
50
+
51
+ if (error) {
52
+ console.error('Error fetching published posts:', error);
53
+ return [];
54
+ }
55
+
56
+ if (!posts) {
57
+ return [];
58
+ }
59
+
60
+ return posts.map((post) => ({
61
+ path: `/blog/${post.slug}`,
62
+ lastModified: new Date(post.updated_at).toISOString(),
63
+ }));
64
+ } catch (err) {
65
+ console.error('An unexpected error occurred while fetching posts:', err);
66
+ return [];
67
+ }
68
+ }