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