@fydemy/cms 1.0.5 → 1.0.6

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/dist/index.js CHANGED
@@ -1075,33 +1075,8 @@ function AdminDashboard() {
1075
1075
  event.target.value = "";
1076
1076
  }
1077
1077
  };
1078
- const handleFileUpload = async (event) => {
1079
- const file = event.target.files?.[0];
1080
- if (!file) return;
1081
- setUploading(true);
1082
- setMessage("");
1083
- try {
1084
- const formData = new FormData();
1085
- formData.append("file", file);
1086
- const response = await fetch("/api/cms/upload", {
1087
- method: "POST",
1088
- body: formData
1089
- });
1090
- const data = await response.json();
1091
- if (response.ok) {
1092
- setMessage(`\u2705 File uploaded! Path: ${data.url}`);
1093
- setTimeout(() => setMessage(""), 5e3);
1094
- } else {
1095
- setMessage("\u274C Failed to upload file");
1096
- }
1097
- } catch (error) {
1098
- setMessage("\u274C Error uploading file");
1099
- } finally {
1100
- setUploading(false);
1101
- event.target.value = "";
1102
- }
1103
- };
1104
1078
  const renderField = (key, field) => {
1079
+ const inputClasses = "flex h-9 w-full rounded-md border border-slate-200 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:cursor-not-allowed disabled:opacity-50";
1105
1080
  switch (field.type) {
1106
1081
  case "text":
1107
1082
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1111,12 +1086,7 @@ function AdminDashboard() {
1111
1086
  type: "text",
1112
1087
  value: field.value,
1113
1088
  onChange: (e) => updateField(key, e.target.value),
1114
- style: {
1115
- width: "100%",
1116
- padding: "0.5rem",
1117
- border: "1px solid #ddd",
1118
- borderRadius: "4px"
1119
- }
1089
+ className: inputClasses
1120
1090
  }
1121
1091
  );
1122
1092
  case "number":
@@ -1127,12 +1097,7 @@ function AdminDashboard() {
1127
1097
  type: "number",
1128
1098
  value: field.value,
1129
1099
  onChange: (e) => updateField(key, parseFloat(e.target.value) || 0),
1130
- style: {
1131
- width: "100%",
1132
- padding: "0.5rem",
1133
- border: "1px solid #ddd",
1134
- borderRadius: "4px"
1135
- }
1100
+ className: inputClasses
1136
1101
  }
1137
1102
  );
1138
1103
  case "date":
@@ -1143,12 +1108,7 @@ function AdminDashboard() {
1143
1108
  type: "date",
1144
1109
  value: field.value,
1145
1110
  onChange: (e) => updateField(key, e.target.value),
1146
- style: {
1147
- width: "100%",
1148
- padding: "0.5rem",
1149
- border: "1px solid #ddd",
1150
- borderRadius: "4px"
1151
- }
1111
+ className: inputClasses
1152
1112
  }
1153
1113
  );
1154
1114
  case "markdown":
@@ -1158,19 +1118,12 @@ function AdminDashboard() {
1158
1118
  id: key,
1159
1119
  value: field.value,
1160
1120
  onChange: (e) => updateField(key, e.target.value),
1161
- style: {
1162
- width: "100%",
1163
- minHeight: key === "content" ? "400px" : "150px",
1164
- fontFamily: "monospace",
1165
- padding: "0.5rem",
1166
- border: "1px solid #ddd",
1167
- borderRadius: "4px"
1168
- },
1121
+ className: "flex min-h-[300px] w-full rounded-md border border-slate-200 bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:cursor-not-allowed disabled:opacity-50 font-mono",
1169
1122
  placeholder: key === "content" ? "Write your markdown content here..." : "Markdown text..."
1170
1123
  }
1171
1124
  );
1172
1125
  case "image":
1173
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1126
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
1174
1127
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1175
1128
  "input",
1176
1129
  {
@@ -1179,13 +1132,7 @@ function AdminDashboard() {
1179
1132
  value: field.value,
1180
1133
  onChange: (e) => updateField(key, e.target.value),
1181
1134
  placeholder: "/uploads/image.jpg or https://...",
1182
- style: {
1183
- width: "100%",
1184
- padding: "0.5rem",
1185
- marginBottom: "0.5rem",
1186
- border: "1px solid #ddd",
1187
- borderRadius: "4px"
1188
- }
1135
+ className: inputClasses
1189
1136
  }
1190
1137
  ),
1191
1138
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1195,7 +1142,7 @@ function AdminDashboard() {
1195
1142
  accept: "image/*",
1196
1143
  onChange: (e) => handleImageUpload(key, e),
1197
1144
  disabled: uploading,
1198
- style: { marginBottom: "0.5rem" }
1145
+ className: "block w-full text-sm text-slate-500\n file:mr-4 file:py-2 file:px-4\n file:rounded-md file:border-0\n file:text-sm file:font-semibold\n file:bg-slate-50 file:text-slate-700\n hover:file:bg-slate-100"
1199
1146
  }
1200
1147
  ),
1201
1148
  field.value && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1203,13 +1150,7 @@ function AdminDashboard() {
1203
1150
  {
1204
1151
  src: field.value,
1205
1152
  alt: key,
1206
- style: {
1207
- maxWidth: "200px",
1208
- display: "block",
1209
- marginTop: "0.5rem",
1210
- borderRadius: "4px",
1211
- border: "1px solid #ddd"
1212
- }
1153
+ className: "max-w-[200px] rounded-md border border-slate-200"
1213
1154
  }
1214
1155
  )
1215
1156
  ] });
@@ -1220,497 +1161,202 @@ function AdminDashboard() {
1220
1161
  id: key,
1221
1162
  type: "text",
1222
1163
  value: field.value,
1223
- onChange: (e) => updateField(key, e.target.value)
1164
+ onChange: (e) => updateField(key, e.target.value),
1165
+ className: inputClasses
1224
1166
  }
1225
1167
  );
1226
1168
  }
1227
1169
  };
1228
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1229
- "div",
1230
- {
1231
- className: "container",
1232
- style: { maxWidth: "1200px", margin: "0 auto", padding: "2rem" },
1233
- children: [
1234
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1235
- "div",
1170
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-h-screen bg-slate-50/50 pb-8", children: [
1171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border-b border-slate-200 bg-white px-4 py-4 mb-6", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "container mx-auto max-w-7xl flex items-center justify-between", children: [
1172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "text-xl font-bold tracking-tight", children: "\u{1F4DD} Admin Dashboard" }) }),
1173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1174
+ "button",
1175
+ {
1176
+ onClick: handleLogout2,
1177
+ className: "inline-flex h-9 items-center justify-center rounded-md border border-slate-200 bg-white px-4 py-2 text-sm font-medium transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950",
1178
+ children: "Logout"
1179
+ }
1180
+ )
1181
+ ] }) }),
1182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "container mx-auto max-w-7xl px-4 grid grid-cols-1 md:grid-cols-[300px_1fr] gap-6", children: [
1183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rounded-xl border border-slate-200 bg-white text-slate-950 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-6", children: [
1184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-lg font-semibold leading-none tracking-tight mb-4", children: "Files" }),
1185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-4 text-sm text-slate-500 flex items-center gap-1 overflow-hidden", children: [
1186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1187
+ "button",
1188
+ {
1189
+ onClick: () => setCurrentPath(""),
1190
+ className: "hover:text-slate-900 hover:underline shrink-0",
1191
+ children: "content"
1192
+ }
1193
+ ),
1194
+ currentPath && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-slate-300", children: "/" }),
1196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-medium truncate text-slate-900", children: currentPath })
1197
+ ] })
1198
+ ] }),
1199
+ currentPath && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1200
+ "button",
1236
1201
  {
1237
- style: {
1238
- display: "flex",
1239
- justifyContent: "space-between",
1240
- alignItems: "center",
1241
- marginBottom: "2rem"
1242
- },
1243
- children: [
1244
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "\u{1F4DD} Admin Dashboard" }),
1245
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1246
- "button",
1247
- {
1248
- onClick: handleLogout2,
1249
- style: {
1250
- padding: "0.5rem 1rem",
1251
- background: "#f5f5f5",
1252
- border: "1px solid #ddd",
1253
- borderRadius: "4px",
1254
- cursor: "pointer"
1255
- },
1256
- children: "Logout"
1257
- }
1258
- )
1259
- ]
1202
+ onClick: goUp,
1203
+ className: "mb-4 inline-flex h-8 w-full items-center justify-center rounded-md bg-slate-100 px-3 text-xs font-medium text-slate-900 hover:bg-slate-200/80",
1204
+ children: "\u2B06\uFE0F Go Up"
1260
1205
  }
1261
1206
  ),
1262
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-6 space-y-2 border-b border-slate-100 pb-6", children: [
1208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1209
+ "input",
1210
+ {
1211
+ type: "text",
1212
+ placeholder: "new-file.md",
1213
+ value: newFileName,
1214
+ onChange: (e) => setNewFileName(e.target.value),
1215
+ className: "flex h-9 w-full rounded-md border border-slate-200 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950"
1216
+ }
1217
+ ),
1218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1219
+ "button",
1220
+ {
1221
+ onClick: createFile,
1222
+ disabled: loading || !newFileName,
1223
+ className: "inline-flex h-9 w-full items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-slate-50 shadow hover:bg-slate-900/90 disabled:pointer-events-none disabled:opacity-50",
1224
+ children: "+ New File"
1225
+ }
1226
+ )
1227
+ ] }),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ul", { className: "space-y-1", children: [
1229
+ entries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "text-sm text-slate-500 italic px-2", children: "Empty directory" }),
1230
+ entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: entry.type === "directory" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1231
+ "button",
1232
+ {
1233
+ onClick: () => navigateToDir(entry.path),
1234
+ className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm font-medium hover:bg-slate-100 text-slate-700 hover:text-slate-900",
1235
+ children: [
1236
+ "\u{1F4C1} ",
1237
+ entry.name
1238
+ ]
1239
+ }
1240
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "group flex items-center justify-between rounded-md hover:bg-slate-100 px-2 py-1.5", children: [
1241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1242
+ "button",
1243
+ {
1244
+ onClick: () => loadFile(entry.path),
1245
+ className: `flex-1 text-left text-sm truncate ${selectedFile === entry.path ? "font-semibold text-slate-900" : "text-slate-600 group-hover:text-slate-900"}`,
1246
+ children: [
1247
+ "\u{1F4C4} ",
1248
+ entry.name
1249
+ ]
1250
+ }
1251
+ ),
1252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1253
+ "button",
1254
+ {
1255
+ onClick: () => deleteFile(entry.path),
1256
+ disabled: loading,
1257
+ className: "opacity-0 group-hover:opacity-100 ml-2 text-slate-400 hover:text-red-500 transition-opacity",
1258
+ title: "Delete file",
1259
+ children: "\u2715"
1260
+ }
1261
+ )
1262
+ ] }) }, entry.path))
1263
+ ] })
1264
+ ] }) }) }),
1265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rounded-xl border border-slate-200 bg-white text-slate-950 shadow-sm min-h-[500px]", children: selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-6", children: [
1266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6 pb-6 border-b border-slate-100", children: [
1267
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", { className: "text-xl font-semibold tracking-tight break-all", children: [
1268
+ "Edit: ",
1269
+ selectedFile
1270
+ ] }),
1271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2 shrink-0", children: [
1272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1273
+ "button",
1274
+ {
1275
+ onClick: duplicateFile,
1276
+ disabled: loading,
1277
+ className: "inline-flex h-9 items-center justify-center rounded-md border border-slate-200 bg-white px-4 py-2 text-sm font-medium shadow-sm transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:pointer-events-none disabled:opacity-50",
1278
+ children: "\u{1F4CB} Duplicate"
1279
+ }
1280
+ ),
1281
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1282
+ "button",
1283
+ {
1284
+ onClick: saveFile,
1285
+ disabled: loading,
1286
+ className: "inline-flex h-9 items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-slate-50 shadow hover:bg-slate-900/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:pointer-events-none disabled:opacity-50",
1287
+ children: loading ? "Saving..." : "\u{1F4BE} Save Changes"
1288
+ }
1289
+ )
1290
+ ] })
1291
+ ] }),
1292
+ message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1263
1293
  "div",
1264
1294
  {
1265
- style: {
1266
- display: "grid",
1267
- gridTemplateColumns: "minmax(250px, 300px) 1fr",
1268
- gap: "2rem"
1269
- },
1270
- children: [
1271
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1272
- "div",
1273
- {
1274
- style: {
1275
- background: "white",
1276
- padding: "1.5rem",
1277
- borderRadius: "8px",
1278
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)"
1279
- },
1280
- children: [
1281
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: { marginTop: 0, marginBottom: "1rem" }, children: "Files" }),
1282
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1283
- "div",
1284
- {
1285
- style: {
1286
- marginBottom: "1rem",
1287
- fontSize: "0.875rem",
1288
- color: "#666"
1289
- },
1290
- children: [
1291
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1292
- "span",
1293
- {
1294
- onClick: () => setCurrentPath(""),
1295
- style: {
1296
- cursor: "pointer",
1297
- color: "#0070f3",
1298
- textDecoration: "underline"
1299
- },
1300
- children: "content"
1301
- }
1302
- ),
1303
- currentPath && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1304
- " / ",
1305
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontWeight: "bold" }, children: currentPath })
1306
- ] })
1307
- ]
1308
- }
1309
- ),
1310
- currentPath && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1311
- "button",
1312
- {
1313
- onClick: goUp,
1314
- style: {
1315
- width: "100%",
1316
- marginBottom: "1rem",
1317
- padding: "0.5rem",
1318
- background: "#f5f5f5",
1319
- border: "1px solid #ddd",
1320
- borderRadius: "4px",
1321
- cursor: "pointer"
1322
- },
1323
- children: "\u2B06\uFE0F Go Up"
1324
- }
1325
- ),
1326
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1327
- "div",
1328
- {
1329
- style: {
1330
- marginBottom: "1.5rem",
1331
- paddingBottom: "1.5rem",
1332
- borderBottom: "1px solid #eee"
1333
- },
1334
- children: [
1335
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1336
- "input",
1337
- {
1338
- type: "text",
1339
- placeholder: "new-file.md",
1340
- value: newFileName,
1341
- onChange: (e) => setNewFileName(e.target.value),
1342
- style: {
1343
- width: "100%",
1344
- padding: "0.5rem",
1345
- marginBottom: "0.5rem",
1346
- border: "1px solid #ddd",
1347
- borderRadius: "4px"
1348
- }
1349
- }
1350
- ),
1351
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1352
- "button",
1353
- {
1354
- onClick: createFile,
1355
- disabled: loading || !newFileName,
1356
- style: {
1357
- width: "100%",
1358
- padding: "0.5rem",
1359
- background: "#0070f3",
1360
- color: "white",
1361
- border: "none",
1362
- borderRadius: "4px",
1363
- cursor: "pointer",
1364
- opacity: loading ? 0.7 : 1
1365
- },
1366
- children: "+ New File"
1367
- }
1368
- )
1369
- ]
1370
- }
1371
- ),
1372
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ul", { style: { listStyle: "none", padding: 0, margin: 0 }, children: [
1373
- entries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { style: { color: "#999", fontStyle: "italic" }, children: "Empty directory" }),
1374
- entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1375
- "li",
1376
- {
1377
- style: {
1378
- marginBottom: "0.5rem",
1379
- display: "flex",
1380
- flexDirection: "column"
1381
- },
1382
- children: entry.type === "directory" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1383
- "button",
1384
- {
1385
- onClick: () => navigateToDir(entry.path),
1386
- style: {
1387
- background: "none",
1388
- border: "none",
1389
- color: "#0070f3",
1390
- cursor: "pointer",
1391
- textAlign: "left",
1392
- fontSize: "1rem",
1393
- padding: "0.25rem 0",
1394
- fontWeight: "bold"
1395
- },
1396
- children: [
1397
- "\u{1F4C1} ",
1398
- entry.name
1399
- ]
1400
- }
1401
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1402
- "div",
1403
- {
1404
- style: {
1405
- display: "flex",
1406
- justifyContent: "space-between",
1407
- alignItems: "center"
1408
- },
1409
- children: [
1410
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1411
- "button",
1412
- {
1413
- onClick: () => loadFile(entry.path),
1414
- style: {
1415
- background: "none",
1416
- border: "none",
1417
- color: selectedFile === entry.path ? "#000" : "#444",
1418
- cursor: "pointer",
1419
- textAlign: "left",
1420
- padding: "0.25rem 0",
1421
- fontWeight: selectedFile === entry.path ? "bold" : "normal",
1422
- textDecoration: selectedFile === entry.path ? "underline" : "none",
1423
- flex: 1
1424
- },
1425
- children: [
1426
- "\u{1F4C4} ",
1427
- entry.name
1428
- ]
1429
- }
1430
- ),
1431
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1432
- "button",
1433
- {
1434
- onClick: () => deleteFile(entry.path),
1435
- disabled: loading,
1436
- style: {
1437
- background: "none",
1438
- border: "none",
1439
- color: "#d32f2f",
1440
- cursor: "pointer",
1441
- fontSize: "0.8rem",
1442
- opacity: 0.7
1443
- },
1444
- children: "\u2715"
1445
- }
1446
- )
1447
- ]
1448
- }
1449
- )
1450
- },
1451
- entry.path
1452
- ))
1453
- ] })
1454
- ]
1455
- }
1456
- ),
1295
+ className: `mb-6 rounded-md p-3 text-sm flex items-center gap-2 ${message.includes("\u2705") ? "bg-green-50 text-green-900 border border-green-200" : "bg-red-50 text-red-900 border border-red-200"}`,
1296
+ children: message
1297
+ }
1298
+ ),
1299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-6", children: [
1300
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1301
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-sm font-medium leading-none text-slate-500 uppercase tracking-wider", children: "Frontmatter Fields" }),
1302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1303
+ "button",
1304
+ {
1305
+ onClick: addField,
1306
+ className: "inline-flex h-8 items-center justify-center rounded-md bg-slate-100 px-3 text-xs font-medium text-slate-900 hover:bg-slate-200",
1307
+ children: "+ Add Field"
1308
+ }
1309
+ )
1310
+ ] }),
1311
+ Object.keys(fields).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-center py-12 text-slate-500 border-2 border-dashed border-slate-200 rounded-lg", children: "No fields found. Add one or load a file layout." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-4", children: [
1312
+ Object.entries(fields).filter(([key]) => key !== "content").map(([key, field]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
1313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
1314
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1315
+ "label",
1316
+ {
1317
+ htmlFor: key,
1318
+ className: "text-sm font-medium bg-slate-100 px-2 py-0.5 rounded text-slate-700",
1319
+ children: [
1320
+ key,
1321
+ " ",
1322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-slate-400 font-normal text-xs ml-1", children: [
1323
+ "(",
1324
+ field.type,
1325
+ ")"
1326
+ ] })
1327
+ ]
1328
+ }
1329
+ ),
1330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1331
+ "button",
1332
+ {
1333
+ onClick: () => removeField(key),
1334
+ className: "text-xs text-red-500 hover:text-red-700 hover:underline",
1335
+ children: "Remove"
1336
+ }
1337
+ )
1338
+ ] }),
1339
+ renderField(key, field)
1340
+ ] }, key)),
1341
+ fields["content"] && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pt-6 mt-6 border-t border-slate-100", children: [
1457
1342
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1458
- "div",
1343
+ "label",
1459
1344
  {
1460
- style: {
1461
- background: "white",
1462
- padding: "2rem",
1463
- borderRadius: "8px",
1464
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)"
1465
- },
1466
- children: selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1467
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1468
- "div",
1469
- {
1470
- style: {
1471
- display: "flex",
1472
- justifyContent: "space-between",
1473
- alignItems: "center",
1474
- marginBottom: "2rem",
1475
- paddingBottom: "1rem",
1476
- borderBottom: "1px solid #eee"
1477
- },
1478
- children: [
1479
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", { style: { margin: 0 }, children: [
1480
- "Edit: ",
1481
- selectedFile
1482
- ] }),
1483
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: "0.5rem" }, children: [
1484
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1485
- "button",
1486
- {
1487
- onClick: duplicateFile,
1488
- disabled: loading,
1489
- style: {
1490
- padding: "0.5rem 1rem",
1491
- background: "#f5f5f5",
1492
- border: "1px solid #ddd",
1493
- borderRadius: "4px",
1494
- cursor: "pointer"
1495
- },
1496
- children: "\u{1F4CB} Duplicate"
1497
- }
1498
- ),
1499
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1500
- "button",
1501
- {
1502
- onClick: saveFile,
1503
- disabled: loading,
1504
- style: {
1505
- padding: "0.5rem 1rem",
1506
- background: "#0070f3",
1507
- color: "white",
1508
- border: "none",
1509
- borderRadius: "4px",
1510
- cursor: "pointer",
1511
- opacity: loading ? 0.7 : 1
1512
- },
1513
- children: loading ? "Saving..." : "\u{1F4BE} Save Changes"
1514
- }
1515
- )
1516
- ] })
1517
- ]
1518
- }
1519
- ),
1520
- message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1521
- "div",
1522
- {
1523
- style: {
1524
- marginBottom: "1.5rem",
1525
- padding: "0.75rem",
1526
- background: message.includes("\u2705") ? "#e6ffe6" : "#ffe6e6",
1527
- borderRadius: "4px",
1528
- border: message.includes("\u2705") ? "1px solid #a5d6a7" : "1px solid #ef9a9a"
1529
- },
1530
- children: message
1531
- }
1532
- ),
1533
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: "2rem" }, children: [
1534
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1535
- "div",
1536
- {
1537
- style: {
1538
- display: "flex",
1539
- justifyContent: "space-between",
1540
- alignItems: "center",
1541
- marginBottom: "1rem"
1542
- },
1543
- children: [
1544
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { fontSize: "1.1rem", margin: 0 }, children: "Fields" }),
1545
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1546
- "button",
1547
- {
1548
- onClick: addField,
1549
- style: {
1550
- fontSize: "0.875rem",
1551
- padding: "0.4rem 0.8rem",
1552
- background: "#e1f5fe",
1553
- color: "#0288d1",
1554
- border: "none",
1555
- borderRadius: "4px",
1556
- cursor: "pointer"
1557
- },
1558
- children: "+ Add Field"
1559
- }
1560
- )
1561
- ]
1562
- }
1563
- ),
1564
- Object.keys(fields).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1565
- "div",
1566
- {
1567
- style: {
1568
- padding: "2rem",
1569
- textAlign: "center",
1570
- background: "#f9f9f9",
1571
- borderRadius: "4px",
1572
- border: "1px dashed #ccc"
1573
- },
1574
- children: [
1575
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1576
- "p",
1577
- {
1578
- style: { color: "#666", fontStyle: "italic", margin: 0 },
1579
- children: 'No fields yet. Click "+ Add Field" to start.'
1580
- }
1581
- ),
1582
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1583
- "small",
1584
- {
1585
- style: {
1586
- color: "#999",
1587
- marginTop: "0.5rem",
1588
- display: "block"
1589
- },
1590
- children: "Add 'content' field for the main body"
1591
- }
1592
- )
1593
- ]
1594
- }
1595
- ) : Object.entries(fields).map(([key, field]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1596
- "div",
1597
- {
1598
- style: {
1599
- marginBottom: "1.5rem",
1600
- background: "#fff",
1601
- border: "1px solid #eee",
1602
- padding: "1rem",
1603
- borderRadius: "6px"
1604
- },
1605
- children: [
1606
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1607
- "div",
1608
- {
1609
- style: {
1610
- display: "flex",
1611
- justifyContent: "space-between",
1612
- alignItems: "center",
1613
- marginBottom: "0.5rem"
1614
- },
1615
- children: [
1616
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor: key, style: { fontWeight: 500 }, children: [
1617
- key,
1618
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1619
- "span",
1620
- {
1621
- style: {
1622
- marginLeft: "0.5rem",
1623
- fontSize: "0.75rem",
1624
- color: "#999",
1625
- background: "#f0f0f0",
1626
- padding: "0.1rem 0.4rem",
1627
- borderRadius: "4px"
1628
- },
1629
- children: field.type
1630
- }
1631
- )
1632
- ] }),
1633
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1634
- "button",
1635
- {
1636
- onClick: () => removeField(key),
1637
- style: {
1638
- background: "none",
1639
- border: "none",
1640
- color: "#e57373",
1641
- cursor: "pointer",
1642
- fontSize: "0.875rem"
1643
- },
1644
- children: "\u2715 Remove"
1645
- }
1646
- )
1647
- ]
1648
- }
1649
- ),
1650
- renderField(key, field)
1651
- ]
1652
- },
1653
- key
1654
- ))
1655
- ] }),
1656
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1657
- "div",
1658
- {
1659
- style: {
1660
- marginTop: "3rem",
1661
- paddingTop: "1rem",
1662
- borderTop: "1px solid #eee"
1663
- },
1664
- children: [
1665
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1666
- "h3",
1667
- {
1668
- style: {
1669
- fontSize: "1rem",
1670
- marginBottom: "0.5rem",
1671
- color: "#666"
1672
- },
1673
- children: "Quick File Upload"
1674
- }
1675
- ),
1676
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: "0.5rem" }, children: [
1677
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1678
- "input",
1679
- {
1680
- type: "file",
1681
- onChange: handleFileUpload,
1682
- disabled: uploading,
1683
- style: { flex: 1 }
1684
- }
1685
- ),
1686
- uploading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#666" }, children: "Uploading..." })
1687
- ] })
1688
- ]
1689
- }
1690
- )
1691
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1692
- "div",
1693
- {
1694
- style: {
1695
- textAlign: "center",
1696
- padding: "5rem 2rem",
1697
- color: "#999"
1698
- },
1699
- children: [
1700
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: "3rem", marginBottom: "1rem" }, children: "\u{1F448}" }),
1701
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: "1.1rem" }, children: "Select a file from the sidebar to edit" }),
1702
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "or create a new file to get started" })
1703
- ]
1704
- }
1705
- )
1345
+ htmlFor: "content",
1346
+ className: "block text-sm font-medium mb-3 text-slate-900",
1347
+ children: "Content (Markdown)"
1706
1348
  }
1707
- )
1708
- ]
1709
- }
1710
- )
1711
- ]
1712
- }
1713
- );
1349
+ ),
1350
+ renderField("content", fields["content"])
1351
+ ] })
1352
+ ] })
1353
+ ] })
1354
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center justify-center h-[500px] text-slate-500", children: [
1355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-4xl mb-4", children: "\u{1F4C4}" }),
1356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Select a file to edit or create a new one." })
1357
+ ] }) }) })
1358
+ ] })
1359
+ ] });
1714
1360
  }
1715
1361
 
1716
1362
  // src/ui/Login.tsx
@@ -1743,133 +1389,67 @@ function Login() {
1743
1389
  setLoading(false);
1744
1390
  }
1745
1391
  };
1746
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1747
- "div",
1748
- {
1749
- style: {
1750
- display: "flex",
1751
- justifyContent: "center",
1752
- alignItems: "center",
1753
- minHeight: "100vh",
1754
- background: "#f5f5f5",
1755
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
1756
- },
1757
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1758
- "div",
1392
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex min-h-screen items-center justify-center bg-slate-50 p-4 font-sans text-slate-900", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full max-w-[350px] rounded-xl border border-slate-200 bg-white shadow-sm", children: [
1393
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col space-y-1.5 p-6", children: [
1394
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { className: "text-2xl font-semibold leading-none tracking-tight", children: "Admin Login" }),
1395
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-slate-500", children: "Enter your credentials to access the CMS" })
1396
+ ] }),
1397
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "p-6 pt-0", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
1398
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-2", children: [
1399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1400
+ "label",
1401
+ {
1402
+ htmlFor: "username",
1403
+ className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
1404
+ children: "Username"
1405
+ }
1406
+ ),
1407
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1408
+ "input",
1409
+ {
1410
+ id: "username",
1411
+ type: "text",
1412
+ value: username,
1413
+ onChange: (e) => setUsername(e.target.value),
1414
+ required: true,
1415
+ autoFocus: true,
1416
+ className: "flex h-9 w-full rounded-md border border-slate-200 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:cursor-not-allowed disabled:opacity-50"
1417
+ }
1418
+ )
1419
+ ] }),
1420
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-2", children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1422
+ "label",
1423
+ {
1424
+ htmlFor: "password",
1425
+ className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
1426
+ children: "Password"
1427
+ }
1428
+ ),
1429
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1430
+ "input",
1431
+ {
1432
+ id: "password",
1433
+ type: "password",
1434
+ value: password,
1435
+ onChange: (e) => setPassword(e.target.value),
1436
+ required: true,
1437
+ className: "flex h-9 w-full rounded-md border border-slate-200 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-slate-400 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:cursor-not-allowed disabled:opacity-50"
1438
+ }
1439
+ )
1440
+ ] }),
1441
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "rounded-md bg-red-50 p-3 text-sm text-red-500 border border-red-200", children: error }),
1442
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1443
+ "button",
1759
1444
  {
1760
- style: {
1761
- width: "100%",
1762
- maxWidth: "400px",
1763
- padding: "2rem",
1764
- background: "white",
1765
- borderRadius: "8px",
1766
- boxShadow: "0 4px 6px rgba(0,0,0,0.1)"
1767
- },
1768
- children: [
1769
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: { marginBottom: "0.5rem", textAlign: "center" }, children: "Admin Login" }),
1770
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { color: "#666", marginBottom: "2rem", textAlign: "center" }, children: "Enter your credentials to access the CMS" }),
1771
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleSubmit, children: [
1772
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "1rem" }, children: [
1773
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1774
- "label",
1775
- {
1776
- htmlFor: "username",
1777
- style: {
1778
- display: "block",
1779
- marginBottom: "0.5rem",
1780
- fontWeight: 500
1781
- },
1782
- children: "Username"
1783
- }
1784
- ),
1785
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1786
- "input",
1787
- {
1788
- id: "username",
1789
- type: "text",
1790
- value: username,
1791
- onChange: (e) => setUsername(e.target.value),
1792
- required: true,
1793
- autoFocus: true,
1794
- style: {
1795
- width: "100%",
1796
- padding: "0.75rem",
1797
- border: "1px solid #ddd",
1798
- borderRadius: "4px"
1799
- }
1800
- }
1801
- )
1802
- ] }),
1803
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "1.5rem" }, children: [
1804
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1805
- "label",
1806
- {
1807
- htmlFor: "password",
1808
- style: {
1809
- display: "block",
1810
- marginBottom: "0.5rem",
1811
- fontWeight: 500
1812
- },
1813
- children: "Password"
1814
- }
1815
- ),
1816
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1817
- "input",
1818
- {
1819
- id: "password",
1820
- type: "password",
1821
- value: password,
1822
- onChange: (e) => setPassword(e.target.value),
1823
- required: true,
1824
- style: {
1825
- width: "100%",
1826
- padding: "0.75rem",
1827
- border: "1px solid #ddd",
1828
- borderRadius: "4px"
1829
- }
1830
- }
1831
- )
1832
- ] }),
1833
- error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1834
- "div",
1835
- {
1836
- style: {
1837
- marginBottom: "1rem",
1838
- padding: "0.75rem",
1839
- background: "#ffe6e6",
1840
- color: "#d32f2f",
1841
- borderRadius: "4px",
1842
- fontSize: "0.875rem"
1843
- },
1844
- children: error
1845
- }
1846
- ),
1847
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1848
- "button",
1849
- {
1850
- type: "submit",
1851
- disabled: loading,
1852
- style: {
1853
- width: "100%",
1854
- padding: "0.75rem",
1855
- background: "#0070f3",
1856
- color: "white",
1857
- border: "none",
1858
- borderRadius: "4px",
1859
- fontSize: "1rem",
1860
- fontWeight: 500,
1861
- cursor: "pointer",
1862
- opacity: loading ? 0.7 : 1
1863
- },
1864
- children: loading ? "Logging in..." : "Login"
1865
- }
1866
- )
1867
- ] })
1868
- ]
1445
+ type: "submit",
1446
+ disabled: loading,
1447
+ className: "inline-flex h-9 w-full items-center justify-center whitespace-nowrap rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-slate-50 shadow transition-colors hover:bg-slate-900/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:pointer-events-none disabled:opacity-50",
1448
+ children: loading ? "Logging in..." : "Login"
1869
1449
  }
1870
1450
  )
1871
- }
1872
- );
1451
+ ] }) })
1452
+ ] }) });
1873
1453
  }
1874
1454
 
1875
1455
  // src/middleware/auth.ts
@@ -2153,20 +1733,48 @@ export const config = {
2153
1733
  );
2154
1734
  console.log("\u2705 Created middleware.ts");
2155
1735
  }
2156
- const envExamplePath = import_path3.default.join(process.cwd(), ".env.local.example");
2157
- await import_promises2.default.writeFile(
2158
- envExamplePath,
2159
- `CMS_ADMIN_USERNAME=admin
1736
+ console.log("\n\u2699\uFE0F Configuration");
1737
+ const rl = await import("readline");
1738
+ const askQuestion = (query) => {
1739
+ const interface_ = rl.createInterface({
1740
+ input: process.stdin,
1741
+ output: process.stdout
1742
+ });
1743
+ return new Promise((resolve) => {
1744
+ interface_.question(query, (ans) => {
1745
+ interface_.close();
1746
+ resolve(ans);
1747
+ });
1748
+ });
1749
+ };
1750
+ const storageChoice = await askQuestion(
1751
+ "Select production storage provider:\n 1. GitHub (default)\n 2. S3 / Cloudflare R2 / Vercel Blob\nEnter choice [1]: "
1752
+ );
1753
+ const isS3 = storageChoice.trim() === "2";
1754
+ let envContent = `CMS_ADMIN_USERNAME=admin
2160
1755
  CMS_ADMIN_PASSWORD=password
2161
1756
  CMS_SESSION_SECRET=ensure_this_is_at_least_32_chars_long_random_string
2162
1757
 
2163
- # GitHub Storage (Production)
1758
+ `;
1759
+ if (isS3) {
1760
+ envContent += `# S3 / Cloudflare R2 / Vercel Blob Storage
1761
+ STORAGE_BUCKET=my-bucket
1762
+ STORAGE_ENDPOINT=https://<accountid>.r2.cloudflarestorage.com
1763
+ STORAGE_ACCESS_KEY_ID=
1764
+ STORAGE_SECRET_ACCESS_KEY=
1765
+ STORAGE_REGION=auto
1766
+ STORAGE_PUBLIC_URL=https://pub-<id>.r2.dev
1767
+ `;
1768
+ } else {
1769
+ envContent += `# GitHub Storage (Production)
1770
+ # Generate at https://github.com/settings/tokens (Needs 'repo' or 'contents:write' scope)
2164
1771
  GITHUB_TOKEN=
2165
1772
  GITHUB_REPO=owner/repo
2166
1773
  GITHUB_BRANCH=main
2167
- `,
2168
- "utf-8"
2169
- );
1774
+ `;
1775
+ }
1776
+ const envExamplePath = import_path3.default.join(process.cwd(), ".env.local.example");
1777
+ await import_promises2.default.writeFile(envExamplePath, envContent, "utf-8");
2170
1778
  console.log("");
2171
1779
  console.log("\u{1F389} CMS initialized successfully!");
2172
1780
  console.log(