@ampless/admin 0.2.0-alpha.7 → 0.2.0-alpha.8
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/api/index.d.ts +1 -1
- package/dist/{chunk-L5NHN3MY.js → chunk-KKM2MCM4.js} +24 -0
- package/dist/{chunk-GXPSAOES.js → chunk-QDPB5W35.js} +779 -351
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +2 -2
- package/dist/{i18n-Bc4SYgWx.d.ts → i18n-DzXXcIQQ.d.ts} +36 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/pages/index.d.ts +1 -1
- package/dist/pages/index.js +2 -2
- package/package.json +10 -9
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
publicMediaUrl,
|
|
5
5
|
setAdminMediaContext,
|
|
6
6
|
translate
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-KKM2MCM4.js";
|
|
8
8
|
import {
|
|
9
9
|
invalidateSiteSettingsCache
|
|
10
10
|
} from "./chunk-VXEVLHGL.js";
|
|
@@ -931,7 +931,7 @@ function MediaPicker({ trigger, onSelect }) {
|
|
|
931
931
|
}
|
|
932
932
|
|
|
933
933
|
// src/components/post-form.tsx
|
|
934
|
-
import { useRef as useRef3, useState as
|
|
934
|
+
import { useRef as useRef3, useState as useState6 } from "react";
|
|
935
935
|
import { useRouter } from "next/navigation";
|
|
936
936
|
import { Image as ImageIcon3 } from "lucide-react";
|
|
937
937
|
import {
|
|
@@ -947,7 +947,7 @@ import {
|
|
|
947
947
|
markdownToHtml,
|
|
948
948
|
htmlToMarkdown
|
|
949
949
|
} from "@ampless/runtime";
|
|
950
|
-
import { Button as
|
|
950
|
+
import { Button as Button8, Input as Input2, Label as Label2, Textarea } from "@ampless/runtime/ui";
|
|
951
951
|
|
|
952
952
|
// src/editor/tiptap-editor.tsx
|
|
953
953
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
@@ -1044,7 +1044,7 @@ function ImageBubbleMenu({ editor }) {
|
|
|
1044
1044
|
if (alt === null) return;
|
|
1045
1045
|
editor.chain().focus().updateAttributes("image", { alt }).run();
|
|
1046
1046
|
};
|
|
1047
|
-
const
|
|
1047
|
+
const remove3 = () => {
|
|
1048
1048
|
editor.chain().focus().deleteSelection().run();
|
|
1049
1049
|
};
|
|
1050
1050
|
const setDisplay = (display) => {
|
|
@@ -1095,7 +1095,7 @@ function ImageBubbleMenu({ editor }) {
|
|
|
1095
1095
|
/* @__PURE__ */ jsx8(Pencil, { className: "mr-1 h-3 w-3" }),
|
|
1096
1096
|
t("editor.image.alt")
|
|
1097
1097
|
] }),
|
|
1098
|
-
/* @__PURE__ */ jsxs6(Button6, { type: "button", variant: "ghost", size: "sm", onClick:
|
|
1098
|
+
/* @__PURE__ */ jsxs6(Button6, { type: "button", variant: "ghost", size: "sm", onClick: remove3, children: [
|
|
1099
1099
|
/* @__PURE__ */ jsx8(Trash2, { className: "mr-1 h-3 w-3" }),
|
|
1100
1100
|
t("editor.image.delete")
|
|
1101
1101
|
] })
|
|
@@ -1149,8 +1149,397 @@ function TiptapEditor({ initialContent, onChange }) {
|
|
|
1149
1149
|
] });
|
|
1150
1150
|
}
|
|
1151
1151
|
|
|
1152
|
-
// src/components/
|
|
1152
|
+
// src/components/static-uploader.tsx
|
|
1153
|
+
import { useState as useState5 } from "react";
|
|
1154
|
+
import { FileText, AlertTriangle, FileArchive, X } from "lucide-react";
|
|
1155
|
+
import { Button as Button7 } from "@ampless/runtime/ui";
|
|
1156
|
+
|
|
1157
|
+
// src/lib/static-bundle.ts
|
|
1158
|
+
import JSZip from "jszip";
|
|
1159
|
+
import { uploadData as uploadData2, list as list2, remove } from "aws-amplify/storage";
|
|
1160
|
+
var DEFAULT_ENTRYPOINT = "index.html";
|
|
1161
|
+
var MAX_BUNDLE_BYTES = 50 * 1024 * 1024;
|
|
1162
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".htm", ".css", ".svg"]);
|
|
1163
|
+
var MIME_TYPES = {
|
|
1164
|
+
".html": "text/html; charset=utf-8",
|
|
1165
|
+
".htm": "text/html; charset=utf-8",
|
|
1166
|
+
".css": "text/css; charset=utf-8",
|
|
1167
|
+
".js": "application/javascript; charset=utf-8",
|
|
1168
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
1169
|
+
".json": "application/json; charset=utf-8",
|
|
1170
|
+
".svg": "image/svg+xml",
|
|
1171
|
+
".png": "image/png",
|
|
1172
|
+
".jpg": "image/jpeg",
|
|
1173
|
+
".jpeg": "image/jpeg",
|
|
1174
|
+
".gif": "image/gif",
|
|
1175
|
+
".webp": "image/webp",
|
|
1176
|
+
".avif": "image/avif",
|
|
1177
|
+
".ico": "image/x-icon",
|
|
1178
|
+
".woff": "font/woff",
|
|
1179
|
+
".woff2": "font/woff2",
|
|
1180
|
+
".ttf": "font/ttf",
|
|
1181
|
+
".otf": "font/otf",
|
|
1182
|
+
".eot": "application/vnd.ms-fontobject",
|
|
1183
|
+
".txt": "text/plain; charset=utf-8",
|
|
1184
|
+
".xml": "application/xml; charset=utf-8",
|
|
1185
|
+
".map": "application/json; charset=utf-8",
|
|
1186
|
+
".pdf": "application/pdf"
|
|
1187
|
+
};
|
|
1188
|
+
function mimeTypeFor(path) {
|
|
1189
|
+
const lower = path.toLowerCase();
|
|
1190
|
+
const dot = lower.lastIndexOf(".");
|
|
1191
|
+
if (dot < 0) return "application/octet-stream";
|
|
1192
|
+
return MIME_TYPES[lower.slice(dot)] ?? "application/octet-stream";
|
|
1193
|
+
}
|
|
1194
|
+
function validateBundlePath(path) {
|
|
1195
|
+
if (path === "" || path.endsWith("/")) return "directory entry";
|
|
1196
|
+
if (path.includes("\0")) return "contains null byte";
|
|
1197
|
+
if (path.startsWith("/") || path.startsWith("\\")) return "absolute path";
|
|
1198
|
+
if (path.split(/[/\\]/).some((seg) => seg === "..")) return "parent-directory traversal";
|
|
1199
|
+
if (path.startsWith("__MACOSX/") || /(^|\/)\._/.test(path)) return "macOS resource fork";
|
|
1200
|
+
if (/(^|\/)\.DS_Store$/.test(path)) return ".DS_Store junk";
|
|
1201
|
+
if (/(^|\/)Thumbs\.db$/i.test(path)) return "Thumbs.db junk";
|
|
1202
|
+
return null;
|
|
1203
|
+
}
|
|
1204
|
+
var HTML_URL_ATTR_RE = /\b(?:href|src|action|data|poster|cite|formaction|manifest|srcset)\s*=\s*["']([^"']+)["']/gi;
|
|
1205
|
+
var CSS_URL_RE = /url\(\s*["']?([^"')\s]+)["']?\s*\)|@import\s+["']([^"']+)["']/gi;
|
|
1206
|
+
function findAbsolutePathRefs(path, content) {
|
|
1207
|
+
const ext = path.toLowerCase().slice(path.lastIndexOf("."));
|
|
1208
|
+
if (!TEXT_EXTENSIONS.has(ext)) return [];
|
|
1209
|
+
const issues = [];
|
|
1210
|
+
function check(url, lineHint) {
|
|
1211
|
+
const trimmed = url.trim();
|
|
1212
|
+
if (!trimmed) return;
|
|
1213
|
+
if (trimmed.startsWith("#")) return;
|
|
1214
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) return;
|
|
1215
|
+
if (trimmed.startsWith("//")) {
|
|
1216
|
+
issues.push({ path, reason: `protocol-relative URL: ${lineHint}` });
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
if (trimmed.startsWith("/")) {
|
|
1220
|
+
issues.push({ path, reason: `absolute path: ${lineHint}` });
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
if (ext === ".html" || ext === ".htm" || ext === ".svg") {
|
|
1225
|
+
HTML_URL_ATTR_RE.lastIndex = 0;
|
|
1226
|
+
let m;
|
|
1227
|
+
while ((m = HTML_URL_ATTR_RE.exec(content)) !== null) {
|
|
1228
|
+
const val = m[1] ?? "";
|
|
1229
|
+
if (/\bsrcset\s*=/i.test(m[0])) {
|
|
1230
|
+
for (const candidate of val.split(",")) {
|
|
1231
|
+
const urlPart = candidate.trim().split(/\s+/)[0];
|
|
1232
|
+
if (urlPart) check(urlPart, candidate.trim());
|
|
1233
|
+
}
|
|
1234
|
+
} else {
|
|
1235
|
+
check(val, m[0]);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
if (ext === ".css" || ext === ".svg") {
|
|
1240
|
+
CSS_URL_RE.lastIndex = 0;
|
|
1241
|
+
let m;
|
|
1242
|
+
while ((m = CSS_URL_RE.exec(content)) !== null) {
|
|
1243
|
+
const val = (m[1] ?? m[2] ?? "").trim();
|
|
1244
|
+
check(val, m[0]);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return issues;
|
|
1248
|
+
}
|
|
1249
|
+
async function extractZip(file) {
|
|
1250
|
+
const zip = await JSZip.loadAsync(file);
|
|
1251
|
+
const files = [];
|
|
1252
|
+
const issues = [];
|
|
1253
|
+
let totalBytes = 0;
|
|
1254
|
+
for (const entry of Object.values(zip.files)) {
|
|
1255
|
+
if (entry.dir) continue;
|
|
1256
|
+
const reason = validateBundlePath(entry.name);
|
|
1257
|
+
if (reason) {
|
|
1258
|
+
const silent = reason === "macOS resource fork" || reason === ".DS_Store junk" || reason === "Thumbs.db junk";
|
|
1259
|
+
if (!silent) issues.push({ path: entry.name, reason });
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
const data = await entry.async("uint8array");
|
|
1263
|
+
totalBytes += data.byteLength;
|
|
1264
|
+
files.push({ path: entry.name, data });
|
|
1265
|
+
}
|
|
1266
|
+
return { files: stripCommonPrefix(files), issues, totalBytes };
|
|
1267
|
+
}
|
|
1268
|
+
function stripCommonPrefix(files) {
|
|
1269
|
+
if (files.length === 0) return files;
|
|
1270
|
+
const firstSlash = files[0].path.indexOf("/");
|
|
1271
|
+
if (firstSlash < 0) return files;
|
|
1272
|
+
const prefix = files[0].path.slice(0, firstSlash + 1);
|
|
1273
|
+
if (!files.every((f) => f.path.startsWith(prefix))) return files;
|
|
1274
|
+
return files.map((f) => ({ ...f, path: f.path.slice(prefix.length) }));
|
|
1275
|
+
}
|
|
1276
|
+
function validateBundle(files) {
|
|
1277
|
+
const issues = [];
|
|
1278
|
+
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
1279
|
+
for (const f of files) {
|
|
1280
|
+
const ext = f.path.toLowerCase().slice(f.path.lastIndexOf("."));
|
|
1281
|
+
if (!TEXT_EXTENSIONS.has(ext)) continue;
|
|
1282
|
+
const text = decoder.decode(f.data);
|
|
1283
|
+
issues.push(...findAbsolutePathRefs(f.path, text));
|
|
1284
|
+
}
|
|
1285
|
+
return issues;
|
|
1286
|
+
}
|
|
1287
|
+
function bundlePrefix(siteId, slug) {
|
|
1288
|
+
return `public/static/${siteId}/${slug}/`;
|
|
1289
|
+
}
|
|
1290
|
+
async function uploadBundle(opts) {
|
|
1291
|
+
if (opts.files.length === 0) {
|
|
1292
|
+
throw new Error("Bundle is empty.");
|
|
1293
|
+
}
|
|
1294
|
+
const totalBytes = opts.files.reduce((sum, f) => sum + f.data.byteLength, 0);
|
|
1295
|
+
if (totalBytes > MAX_BUNDLE_BYTES) {
|
|
1296
|
+
throw new Error(
|
|
1297
|
+
`Bundle too large: ${Math.round(totalBytes / 1024 / 1024)} MB exceeds the ${Math.round(MAX_BUNDLE_BYTES / 1024 / 1024)} MB ceiling for browser-side upload.`
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
const entrypoint = opts.entrypoint ?? pickDefaultEntrypoint(opts.files);
|
|
1301
|
+
if (!opts.files.some((f) => f.path === entrypoint)) {
|
|
1302
|
+
throw new Error(`Entrypoint "${entrypoint}" is not present in the bundle.`);
|
|
1303
|
+
}
|
|
1304
|
+
await deleteBundle(opts.siteId, opts.slug).catch(() => void 0);
|
|
1305
|
+
const prefix = bundlePrefix(opts.siteId, opts.slug);
|
|
1306
|
+
let uploaded = 0;
|
|
1307
|
+
for (const f of opts.files) {
|
|
1308
|
+
const task = uploadData2({
|
|
1309
|
+
path: `${prefix}${f.path}`,
|
|
1310
|
+
data: f.data,
|
|
1311
|
+
// Forcing Content-Type at upload means CloudFront / browsers see
|
|
1312
|
+
// it directly when serving the file via the public bucket URL.
|
|
1313
|
+
// (The runtime route handler overrides it for the proxied path,
|
|
1314
|
+
// but tooling that hits S3 directly benefits from a correct CT.)
|
|
1315
|
+
options: { contentType: mimeTypeFor(f.path) }
|
|
1316
|
+
});
|
|
1317
|
+
await task.result;
|
|
1318
|
+
uploaded += f.data.byteLength;
|
|
1319
|
+
opts.onProgress?.(uploaded, totalBytes);
|
|
1320
|
+
}
|
|
1321
|
+
return {
|
|
1322
|
+
entrypoint,
|
|
1323
|
+
files: opts.files.map((f) => f.path).sort(),
|
|
1324
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
async function deleteBundle(siteId, slug) {
|
|
1328
|
+
const prefix = bundlePrefix(siteId, slug);
|
|
1329
|
+
const result = await list2({ path: prefix });
|
|
1330
|
+
for (const item of result.items) {
|
|
1331
|
+
await remove({ path: item.path });
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
function pickDefaultEntrypoint(files) {
|
|
1335
|
+
const exact = files.find((f) => f.path === DEFAULT_ENTRYPOINT);
|
|
1336
|
+
if (exact) return exact.path;
|
|
1337
|
+
const altRoot = files.find((f) => f.path === "index.htm");
|
|
1338
|
+
if (altRoot) return altRoot.path;
|
|
1339
|
+
const htmlRoot = files.filter((f) => /^[^/]+\.html?$/.test(f.path)).sort((a, b) => a.path.localeCompare(b.path));
|
|
1340
|
+
if (htmlRoot.length > 0) return htmlRoot[0].path;
|
|
1341
|
+
const htmlAny = files.filter((f) => /\.html?$/.test(f.path)).sort((a, b) => a.path.localeCompare(b.path));
|
|
1342
|
+
if (htmlAny.length > 0) return htmlAny[0].path;
|
|
1343
|
+
return DEFAULT_ENTRYPOINT;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// src/components/static-uploader.tsx
|
|
1153
1347
|
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1348
|
+
function StaticUploader({ initial, onFilesReady, onClear }) {
|
|
1349
|
+
const t = useT();
|
|
1350
|
+
const [pending, setPending] = useState5(null);
|
|
1351
|
+
const [issues, setIssues] = useState5([]);
|
|
1352
|
+
const [busy, setBusy] = useState5(false);
|
|
1353
|
+
const [error, setError] = useState5(null);
|
|
1354
|
+
async function handleZip(file) {
|
|
1355
|
+
setBusy(true);
|
|
1356
|
+
setError(null);
|
|
1357
|
+
try {
|
|
1358
|
+
const { files, issues: structuralIssues } = await extractZip(file);
|
|
1359
|
+
if (files.length === 0) {
|
|
1360
|
+
setError(t("posts.form.static.emptyBundle"));
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
const contentIssues = validateBundle(files);
|
|
1364
|
+
const all = [...structuralIssues, ...contentIssues];
|
|
1365
|
+
setPending(files);
|
|
1366
|
+
setIssues(all);
|
|
1367
|
+
if (all.length === 0) {
|
|
1368
|
+
onFilesReady(files, guessEntrypoint(files));
|
|
1369
|
+
}
|
|
1370
|
+
} catch (e) {
|
|
1371
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1372
|
+
} finally {
|
|
1373
|
+
setBusy(false);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
async function handleLooseFiles(files) {
|
|
1377
|
+
setBusy(true);
|
|
1378
|
+
setError(null);
|
|
1379
|
+
try {
|
|
1380
|
+
const extracted = [];
|
|
1381
|
+
const structural = [];
|
|
1382
|
+
for (const f of Array.from(files)) {
|
|
1383
|
+
const rel = f.webkitRelativePath ?? f.name;
|
|
1384
|
+
const stripped = rel.includes("/") ? rel.slice(rel.indexOf("/") + 1) : rel;
|
|
1385
|
+
const reason = validateBundlePath(stripped);
|
|
1386
|
+
if (reason) {
|
|
1387
|
+
structural.push({ path: stripped, reason });
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
const buf = new Uint8Array(await f.arrayBuffer());
|
|
1391
|
+
extracted.push({ path: stripped, data: buf });
|
|
1392
|
+
}
|
|
1393
|
+
if (extracted.length === 0) {
|
|
1394
|
+
setError(t("posts.form.static.emptyBundle"));
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const content = validateBundle(extracted);
|
|
1398
|
+
const all = [...structural, ...content];
|
|
1399
|
+
setPending(extracted);
|
|
1400
|
+
setIssues(all);
|
|
1401
|
+
if (all.length === 0) {
|
|
1402
|
+
onFilesReady(extracted, guessEntrypoint(extracted));
|
|
1403
|
+
}
|
|
1404
|
+
} catch (e) {
|
|
1405
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1406
|
+
} finally {
|
|
1407
|
+
setBusy(false);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
function onPickerChange(e) {
|
|
1411
|
+
const fl = e.target.files;
|
|
1412
|
+
if (!fl || fl.length === 0) return;
|
|
1413
|
+
e.target.value = "";
|
|
1414
|
+
if (fl.length === 1 && fl[0].name.toLowerCase().endsWith(".zip")) {
|
|
1415
|
+
void handleZip(fl[0]);
|
|
1416
|
+
} else {
|
|
1417
|
+
void handleLooseFiles(fl);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
function clearPending() {
|
|
1421
|
+
setPending(null);
|
|
1422
|
+
setIssues([]);
|
|
1423
|
+
setError(null);
|
|
1424
|
+
onClear();
|
|
1425
|
+
}
|
|
1426
|
+
const showCurrent = !pending && initial && initial.files.length > 0;
|
|
1427
|
+
return /* @__PURE__ */ jsxs8("div", { className: "space-y-4", children: [
|
|
1428
|
+
/* @__PURE__ */ jsxs8("div", { className: "rounded-md border border-dashed p-4", children: [
|
|
1429
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex flex-col items-start gap-2 text-sm", children: [
|
|
1430
|
+
/* @__PURE__ */ jsx10("span", { className: "font-medium", children: t("posts.form.static.pick") }),
|
|
1431
|
+
/* @__PURE__ */ jsx10(
|
|
1432
|
+
"input",
|
|
1433
|
+
{
|
|
1434
|
+
type: "file",
|
|
1435
|
+
accept: ".zip,application/zip,*/*",
|
|
1436
|
+
multiple: true,
|
|
1437
|
+
onChange: onPickerChange,
|
|
1438
|
+
disabled: busy
|
|
1439
|
+
}
|
|
1440
|
+
),
|
|
1441
|
+
/* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: t("posts.form.static.pickHint") })
|
|
1442
|
+
] }),
|
|
1443
|
+
busy && /* @__PURE__ */ jsx10("p", { className: "mt-2 text-sm text-muted-foreground", children: t("common.loading") }),
|
|
1444
|
+
error && /* @__PURE__ */ jsx10("p", { className: "mt-2 text-sm text-destructive", children: error })
|
|
1445
|
+
] }),
|
|
1446
|
+
showCurrent && /* @__PURE__ */ jsx10(CurrentBundle, { body: initial }),
|
|
1447
|
+
pending && /* @__PURE__ */ jsx10(
|
|
1448
|
+
PendingBundle,
|
|
1449
|
+
{
|
|
1450
|
+
files: pending,
|
|
1451
|
+
issues,
|
|
1452
|
+
onClear: clearPending
|
|
1453
|
+
}
|
|
1454
|
+
)
|
|
1455
|
+
] });
|
|
1456
|
+
}
|
|
1457
|
+
function CurrentBundle({ body }) {
|
|
1458
|
+
const t = useT();
|
|
1459
|
+
return /* @__PURE__ */ jsxs8("div", { className: "rounded-md border bg-muted/30 p-3", children: [
|
|
1460
|
+
/* @__PURE__ */ jsxs8("div", { className: "mb-2 flex items-center gap-2 text-sm font-medium", children: [
|
|
1461
|
+
/* @__PURE__ */ jsx10(FileArchive, { className: "h-4 w-4" }),
|
|
1462
|
+
t("posts.form.static.currentBundle", {
|
|
1463
|
+
count: body.files.length,
|
|
1464
|
+
entrypoint: body.entrypoint
|
|
1465
|
+
})
|
|
1466
|
+
] }),
|
|
1467
|
+
/* @__PURE__ */ jsx10(FileList, { files: body.files })
|
|
1468
|
+
] });
|
|
1469
|
+
}
|
|
1470
|
+
function PendingBundle({
|
|
1471
|
+
files,
|
|
1472
|
+
issues,
|
|
1473
|
+
onClear
|
|
1474
|
+
}) {
|
|
1475
|
+
const t = useT();
|
|
1476
|
+
const totalBytes = files.reduce((sum, f) => sum + f.data.byteLength, 0);
|
|
1477
|
+
return /* @__PURE__ */ jsxs8("div", { className: "rounded-md border p-3", children: [
|
|
1478
|
+
/* @__PURE__ */ jsxs8("div", { className: "mb-2 flex items-center justify-between gap-2 text-sm font-medium", children: [
|
|
1479
|
+
/* @__PURE__ */ jsxs8("span", { className: "flex items-center gap-2", children: [
|
|
1480
|
+
/* @__PURE__ */ jsx10(FileArchive, { className: "h-4 w-4" }),
|
|
1481
|
+
t("posts.form.static.pendingBundle", {
|
|
1482
|
+
count: files.length,
|
|
1483
|
+
size: formatBytes2(totalBytes)
|
|
1484
|
+
})
|
|
1485
|
+
] }),
|
|
1486
|
+
/* @__PURE__ */ jsxs8(Button7, { type: "button", variant: "ghost", size: "sm", onClick: onClear, children: [
|
|
1487
|
+
/* @__PURE__ */ jsx10(X, { className: "mr-1 h-3 w-3" }),
|
|
1488
|
+
t("common.cancel")
|
|
1489
|
+
] })
|
|
1490
|
+
] }),
|
|
1491
|
+
issues.length > 0 && /* @__PURE__ */ jsxs8("div", { className: "mb-3 rounded-md border border-destructive/40 bg-destructive/5 p-2 text-sm", children: [
|
|
1492
|
+
/* @__PURE__ */ jsxs8("div", { className: "mb-1 flex items-center gap-2 font-medium text-destructive", children: [
|
|
1493
|
+
/* @__PURE__ */ jsx10(AlertTriangle, { className: "h-4 w-4" }),
|
|
1494
|
+
t("posts.form.static.issuesTitle", { count: issues.length })
|
|
1495
|
+
] }),
|
|
1496
|
+
/* @__PURE__ */ jsxs8("ul", { className: "space-y-0.5 text-xs", children: [
|
|
1497
|
+
issues.slice(0, 20).map((issue, idx) => /* @__PURE__ */ jsxs8("li", { className: "font-mono", children: [
|
|
1498
|
+
issue.path,
|
|
1499
|
+
": ",
|
|
1500
|
+
issue.reason
|
|
1501
|
+
] }, `${issue.path}-${idx}`)),
|
|
1502
|
+
issues.length > 20 && /* @__PURE__ */ jsxs8("li", { className: "font-mono text-muted-foreground", children: [
|
|
1503
|
+
"\u2026 ",
|
|
1504
|
+
issues.length - 20,
|
|
1505
|
+
" more"
|
|
1506
|
+
] })
|
|
1507
|
+
] }),
|
|
1508
|
+
/* @__PURE__ */ jsx10("p", { className: "mt-2 text-xs text-muted-foreground", children: t("posts.form.static.issuesHint") })
|
|
1509
|
+
] }),
|
|
1510
|
+
/* @__PURE__ */ jsx10(FileList, { files: files.map((f) => f.path) })
|
|
1511
|
+
] });
|
|
1512
|
+
}
|
|
1513
|
+
function FileList({ files }) {
|
|
1514
|
+
return /* @__PURE__ */ jsxs8("ul", { className: "space-y-0.5 text-xs", children: [
|
|
1515
|
+
files.slice(0, 40).map((path) => /* @__PURE__ */ jsxs8("li", { className: "flex items-center gap-1.5 font-mono text-muted-foreground", children: [
|
|
1516
|
+
/* @__PURE__ */ jsx10(FileText, { className: "h-3 w-3 shrink-0" }),
|
|
1517
|
+
path
|
|
1518
|
+
] }, path)),
|
|
1519
|
+
files.length > 40 && /* @__PURE__ */ jsxs8("li", { className: "font-mono text-xs text-muted-foreground", children: [
|
|
1520
|
+
"\u2026 ",
|
|
1521
|
+
files.length - 40,
|
|
1522
|
+
" more"
|
|
1523
|
+
] })
|
|
1524
|
+
] });
|
|
1525
|
+
}
|
|
1526
|
+
function formatBytes2(bytes) {
|
|
1527
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1528
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1529
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
1530
|
+
}
|
|
1531
|
+
function guessEntrypoint(files) {
|
|
1532
|
+
const exact = files.find((f) => f.path === "index.html");
|
|
1533
|
+
if (exact) return "index.html";
|
|
1534
|
+
const alt = files.find((f) => f.path === "index.htm");
|
|
1535
|
+
if (alt) return "index.htm";
|
|
1536
|
+
const rootHtml = files.filter((f) => /^[^/]+\.html?$/.test(f.path)).sort((a, b) => a.path.localeCompare(b.path))[0];
|
|
1537
|
+
if (rootHtml) return rootHtml.path;
|
|
1538
|
+
return files[0]?.path ?? "index.html";
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// src/components/post-form.tsx
|
|
1542
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1154
1543
|
var EMPTY_TIPTAP_DOC = { type: "doc", content: [{ type: "paragraph" }] };
|
|
1155
1544
|
var IMAGE_URL_RE = /\.(jpe?g|png|gif|webp|avif|svg|bmp|tiff?)(\?|$)/i;
|
|
1156
1545
|
var STYLESHEET_URL_RE = /\.css(\?|$)/i;
|
|
@@ -1170,24 +1559,30 @@ function slugify(s) {
|
|
|
1170
1559
|
}
|
|
1171
1560
|
function defaultBodyForFormat(format) {
|
|
1172
1561
|
if (format === "tiptap") return EMPTY_TIPTAP_DOC;
|
|
1562
|
+
if (format === "static") return null;
|
|
1173
1563
|
return "";
|
|
1174
1564
|
}
|
|
1565
|
+
function isStaticBody(value) {
|
|
1566
|
+
return !!value && typeof value === "object" && "entrypoint" in value && "files" in value && Array.isArray(value.files);
|
|
1567
|
+
}
|
|
1175
1568
|
function PostForm({ post }) {
|
|
1176
1569
|
const router = useRouter();
|
|
1177
1570
|
const t = useT();
|
|
1178
1571
|
const isEdit = !!post;
|
|
1179
1572
|
const bodyTextareaRef = useRef3(null);
|
|
1180
|
-
const [title, setTitle] =
|
|
1181
|
-
const [slug, setSlug] =
|
|
1182
|
-
const [excerpt, setExcerpt] =
|
|
1183
|
-
const [format, setFormat] =
|
|
1184
|
-
const [body, setBody] =
|
|
1185
|
-
const [status, setStatus] =
|
|
1186
|
-
const [tagsInput, setTagsInput] =
|
|
1187
|
-
const [noLayout, setNoLayout] =
|
|
1188
|
-
const [saving, setSaving] =
|
|
1189
|
-
const [error, setError] =
|
|
1190
|
-
const [view, setView] =
|
|
1573
|
+
const [title, setTitle] = useState6(post?.title ?? "");
|
|
1574
|
+
const [slug, setSlug] = useState6(post?.slug ?? "");
|
|
1575
|
+
const [excerpt, setExcerpt] = useState6(post?.excerpt ?? "");
|
|
1576
|
+
const [format, setFormat] = useState6(post?.format ?? "tiptap");
|
|
1577
|
+
const [body, setBody] = useState6(post?.body ?? EMPTY_TIPTAP_DOC);
|
|
1578
|
+
const [status, setStatus] = useState6(post?.status ?? "draft");
|
|
1579
|
+
const [tagsInput, setTagsInput] = useState6((post?.tags ?? []).join(", "));
|
|
1580
|
+
const [noLayout, setNoLayout] = useState6(post?.metadata?.no_layout === true);
|
|
1581
|
+
const [saving, setSaving] = useState6(false);
|
|
1582
|
+
const [error, setError] = useState6(null);
|
|
1583
|
+
const [view, setView] = useState6("edit");
|
|
1584
|
+
const [pendingBundle, setPendingBundle] = useState6(null);
|
|
1585
|
+
const initialStaticBody = isStaticBody(post?.body) ? post.body : null;
|
|
1191
1586
|
function buildMetadata() {
|
|
1192
1587
|
const next = { ...post?.metadata ?? {} };
|
|
1193
1588
|
if (noLayout && format === "html") next.no_layout = true;
|
|
@@ -1225,31 +1620,36 @@ function PostForm({ post }) {
|
|
|
1225
1620
|
function changeFormat(next) {
|
|
1226
1621
|
if (next === format) return;
|
|
1227
1622
|
let nextBody = body;
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1623
|
+
if (next === "static" || format === "static") {
|
|
1624
|
+
nextBody = defaultBodyForFormat(next);
|
|
1625
|
+
} else {
|
|
1626
|
+
const k = `${format}\u2192${next}`;
|
|
1627
|
+
switch (k) {
|
|
1628
|
+
case "tiptap\u2192html":
|
|
1629
|
+
nextBody = tiptapToHtml(body);
|
|
1630
|
+
break;
|
|
1631
|
+
case "tiptap\u2192markdown":
|
|
1632
|
+
nextBody = tiptapToMarkdown(body);
|
|
1633
|
+
break;
|
|
1634
|
+
case "html\u2192tiptap":
|
|
1635
|
+
nextBody = String(body ?? "");
|
|
1636
|
+
break;
|
|
1637
|
+
case "markdown\u2192tiptap":
|
|
1638
|
+
nextBody = markdownToHtml(String(body ?? ""));
|
|
1639
|
+
break;
|
|
1640
|
+
case "html\u2192markdown":
|
|
1641
|
+
nextBody = htmlToMarkdown(String(body ?? ""));
|
|
1642
|
+
break;
|
|
1643
|
+
case "markdown\u2192html":
|
|
1644
|
+
nextBody = markdownToHtml(String(body ?? ""));
|
|
1645
|
+
break;
|
|
1646
|
+
default:
|
|
1647
|
+
nextBody = defaultBodyForFormat(next);
|
|
1648
|
+
}
|
|
1250
1649
|
}
|
|
1251
1650
|
setFormat(next);
|
|
1252
1651
|
setBody(nextBody);
|
|
1652
|
+
setPendingBundle(null);
|
|
1253
1653
|
if (next !== "html") setNoLayout(false);
|
|
1254
1654
|
}
|
|
1255
1655
|
async function save(e) {
|
|
@@ -1259,15 +1659,32 @@ function PostForm({ post }) {
|
|
|
1259
1659
|
try {
|
|
1260
1660
|
const tags = parseTags(tagsInput);
|
|
1261
1661
|
const metadata = buildMetadata();
|
|
1662
|
+
const finalSlug = slug || slugify(title);
|
|
1663
|
+
const finalSiteId = post?.siteId ?? readAdminSiteIdFromCookie();
|
|
1664
|
+
let nextBody = body;
|
|
1665
|
+
if (format === "static") {
|
|
1666
|
+
if (pendingBundle) {
|
|
1667
|
+
nextBody = await uploadBundle({
|
|
1668
|
+
siteId: finalSiteId,
|
|
1669
|
+
slug: finalSlug,
|
|
1670
|
+
files: pendingBundle.files,
|
|
1671
|
+
entrypoint: pendingBundle.entrypoint
|
|
1672
|
+
});
|
|
1673
|
+
} else if (initialStaticBody) {
|
|
1674
|
+
nextBody = initialStaticBody;
|
|
1675
|
+
} else {
|
|
1676
|
+
throw new Error(t("posts.form.static.noBundle"));
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1262
1679
|
if (isEdit) {
|
|
1263
1680
|
await updatePost(
|
|
1264
1681
|
post.postId,
|
|
1265
1682
|
{
|
|
1266
1683
|
title,
|
|
1267
|
-
slug:
|
|
1684
|
+
slug: finalSlug,
|
|
1268
1685
|
excerpt: excerpt || void 0,
|
|
1269
1686
|
format,
|
|
1270
|
-
body,
|
|
1687
|
+
body: nextBody,
|
|
1271
1688
|
status,
|
|
1272
1689
|
publishedAt: status === "published" ? post?.publishedAt ?? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
1273
1690
|
tags,
|
|
@@ -1277,12 +1694,12 @@ function PostForm({ post }) {
|
|
|
1277
1694
|
);
|
|
1278
1695
|
} else {
|
|
1279
1696
|
await createPost({
|
|
1280
|
-
siteId:
|
|
1281
|
-
slug:
|
|
1697
|
+
siteId: finalSiteId,
|
|
1698
|
+
slug: finalSlug,
|
|
1282
1699
|
title,
|
|
1283
1700
|
excerpt: excerpt || void 0,
|
|
1284
1701
|
format,
|
|
1285
|
-
body,
|
|
1702
|
+
body: nextBody,
|
|
1286
1703
|
status,
|
|
1287
1704
|
publishedAt: status === "published" ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
1288
1705
|
tags,
|
|
@@ -1302,6 +1719,9 @@ function PostForm({ post }) {
|
|
|
1302
1719
|
if (!confirm(t("posts.form.deleteConfirm", { title: post.title }))) return;
|
|
1303
1720
|
setSaving(true);
|
|
1304
1721
|
try {
|
|
1722
|
+
if (post.format === "static") {
|
|
1723
|
+
await deleteBundle(post.siteId, post.slug).catch(() => void 0);
|
|
1724
|
+
}
|
|
1305
1725
|
await deletePost(post.postId);
|
|
1306
1726
|
router.push("/admin/posts");
|
|
1307
1727
|
router.refresh();
|
|
@@ -1322,9 +1742,9 @@ function PostForm({ post }) {
|
|
|
1322
1742
|
publishedAt: status === "published" ? post?.publishedAt ?? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
1323
1743
|
tags: parseTags(tagsInput)
|
|
1324
1744
|
};
|
|
1325
|
-
return /* @__PURE__ */
|
|
1326
|
-
/* @__PURE__ */
|
|
1327
|
-
/* @__PURE__ */
|
|
1745
|
+
return /* @__PURE__ */ jsxs9("form", { onSubmit: save, className: "space-y-6", children: [
|
|
1746
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex gap-1 border-b", children: [
|
|
1747
|
+
/* @__PURE__ */ jsx11(
|
|
1328
1748
|
"button",
|
|
1329
1749
|
{
|
|
1330
1750
|
type: "button",
|
|
@@ -1334,7 +1754,7 @@ function PostForm({ post }) {
|
|
|
1334
1754
|
children: t("posts.form.tabEdit")
|
|
1335
1755
|
}
|
|
1336
1756
|
),
|
|
1337
|
-
/* @__PURE__ */
|
|
1757
|
+
/* @__PURE__ */ jsx11(
|
|
1338
1758
|
"button",
|
|
1339
1759
|
{
|
|
1340
1760
|
type: "button",
|
|
@@ -1345,24 +1765,24 @@ function PostForm({ post }) {
|
|
|
1345
1765
|
}
|
|
1346
1766
|
)
|
|
1347
1767
|
] }),
|
|
1348
|
-
view === "preview" && /* @__PURE__ */
|
|
1349
|
-
/* @__PURE__ */
|
|
1350
|
-
/* @__PURE__ */
|
|
1351
|
-
/* @__PURE__ */
|
|
1352
|
-
previewPost.publishedAt ? /* @__PURE__ */
|
|
1353
|
-
/* @__PURE__ */
|
|
1354
|
-
/* @__PURE__ */
|
|
1768
|
+
view === "preview" && /* @__PURE__ */ jsxs9("article", { className: "space-y-4", children: [
|
|
1769
|
+
/* @__PURE__ */ jsxs9("header", { className: "border-b pb-4", children: [
|
|
1770
|
+
/* @__PURE__ */ jsx11("h1", { className: "text-3xl font-bold tracking-tight", children: title || /* @__PURE__ */ jsx11("span", { className: "text-muted-foreground italic", children: t("posts.form.previewNoTitle") }) }),
|
|
1771
|
+
/* @__PURE__ */ jsxs9("p", { className: "mt-2 text-sm text-muted-foreground", children: [
|
|
1772
|
+
previewPost.publishedAt ? /* @__PURE__ */ jsx11("time", { dateTime: previewPost.publishedAt, children: formatDate(previewPost.publishedAt) }) : /* @__PURE__ */ jsx11("span", { children: t("common.draft") }),
|
|
1773
|
+
/* @__PURE__ */ jsx11("span", { className: "mx-2", children: "\xB7" }),
|
|
1774
|
+
/* @__PURE__ */ jsx11("span", { className: "font-mono text-xs uppercase", children: format })
|
|
1355
1775
|
] }),
|
|
1356
|
-
excerpt && /* @__PURE__ */
|
|
1776
|
+
excerpt && /* @__PURE__ */ jsx11("p", { className: "mt-3 text-base text-muted-foreground", children: excerpt })
|
|
1357
1777
|
] }),
|
|
1358
|
-
/* @__PURE__ */
|
|
1778
|
+
format === "static" ? /* @__PURE__ */ jsx11("p", { className: "text-sm text-muted-foreground", children: t("posts.form.static.previewHint") }) : /* @__PURE__ */ jsx11(
|
|
1359
1779
|
"div",
|
|
1360
1780
|
{
|
|
1361
1781
|
className: "prose prose-neutral dark:prose-invert max-w-none",
|
|
1362
1782
|
dangerouslySetInnerHTML: { __html: renderBody(previewPost) }
|
|
1363
1783
|
}
|
|
1364
1784
|
),
|
|
1365
|
-
previewPost.tags && previewPost.tags.length > 0 && /* @__PURE__ */
|
|
1785
|
+
previewPost.tags && previewPost.tags.length > 0 && /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-2 border-t pt-4 text-sm", children: previewPost.tags.map((tag) => /* @__PURE__ */ jsxs9(
|
|
1366
1786
|
"span",
|
|
1367
1787
|
{
|
|
1368
1788
|
className: "rounded-full border px-2 py-0.5 text-xs text-muted-foreground",
|
|
@@ -1373,12 +1793,12 @@ function PostForm({ post }) {
|
|
|
1373
1793
|
},
|
|
1374
1794
|
tag
|
|
1375
1795
|
)) }),
|
|
1376
|
-
/* @__PURE__ */
|
|
1796
|
+
/* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: t("posts.form.previewHint") })
|
|
1377
1797
|
] }),
|
|
1378
|
-
/* @__PURE__ */
|
|
1379
|
-
/* @__PURE__ */
|
|
1380
|
-
/* @__PURE__ */
|
|
1381
|
-
/* @__PURE__ */
|
|
1798
|
+
/* @__PURE__ */ jsxs9("div", { className: view === "edit" ? "space-y-6" : "hidden", children: [
|
|
1799
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
1800
|
+
/* @__PURE__ */ jsx11(Label2, { htmlFor: "title", children: t("posts.form.title") }),
|
|
1801
|
+
/* @__PURE__ */ jsx11(
|
|
1382
1802
|
Input2,
|
|
1383
1803
|
{
|
|
1384
1804
|
id: "title",
|
|
@@ -1391,9 +1811,9 @@ function PostForm({ post }) {
|
|
|
1391
1811
|
}
|
|
1392
1812
|
)
|
|
1393
1813
|
] }),
|
|
1394
|
-
/* @__PURE__ */
|
|
1395
|
-
/* @__PURE__ */
|
|
1396
|
-
/* @__PURE__ */
|
|
1814
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
1815
|
+
/* @__PURE__ */ jsx11(Label2, { htmlFor: "slug", children: t("posts.form.slug") }),
|
|
1816
|
+
/* @__PURE__ */ jsx11(
|
|
1397
1817
|
Input2,
|
|
1398
1818
|
{
|
|
1399
1819
|
id: "slug",
|
|
@@ -1403,9 +1823,9 @@ function PostForm({ post }) {
|
|
|
1403
1823
|
}
|
|
1404
1824
|
)
|
|
1405
1825
|
] }),
|
|
1406
|
-
/* @__PURE__ */
|
|
1407
|
-
/* @__PURE__ */
|
|
1408
|
-
/* @__PURE__ */
|
|
1826
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
1827
|
+
/* @__PURE__ */ jsx11(Label2, { htmlFor: "excerpt", children: t("posts.form.excerpt") }),
|
|
1828
|
+
/* @__PURE__ */ jsx11(
|
|
1409
1829
|
Textarea,
|
|
1410
1830
|
{
|
|
1411
1831
|
id: "excerpt",
|
|
@@ -1415,9 +1835,9 @@ function PostForm({ post }) {
|
|
|
1415
1835
|
}
|
|
1416
1836
|
)
|
|
1417
1837
|
] }),
|
|
1418
|
-
/* @__PURE__ */
|
|
1419
|
-
/* @__PURE__ */
|
|
1420
|
-
/* @__PURE__ */
|
|
1838
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
1839
|
+
/* @__PURE__ */ jsx11(Label2, { htmlFor: "format", children: t("posts.form.format") }),
|
|
1840
|
+
/* @__PURE__ */ jsxs9(
|
|
1421
1841
|
"select",
|
|
1422
1842
|
{
|
|
1423
1843
|
id: "format",
|
|
@@ -1425,33 +1845,41 @@ function PostForm({ post }) {
|
|
|
1425
1845
|
onChange: (e) => changeFormat(e.target.value),
|
|
1426
1846
|
className: "flex h-9 w-full max-w-xs rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm",
|
|
1427
1847
|
children: [
|
|
1428
|
-
/* @__PURE__ */
|
|
1429
|
-
/* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1848
|
+
/* @__PURE__ */ jsx11("option", { value: "tiptap", children: "Tiptap (rich editor)" }),
|
|
1849
|
+
/* @__PURE__ */ jsx11("option", { value: "markdown", children: "Markdown" }),
|
|
1850
|
+
/* @__PURE__ */ jsx11("option", { value: "html", children: "HTML" }),
|
|
1851
|
+
/* @__PURE__ */ jsx11("option", { value: "static", children: t("posts.form.formatStaticLabel") })
|
|
1431
1852
|
]
|
|
1432
1853
|
}
|
|
1433
1854
|
),
|
|
1434
|
-
/* @__PURE__ */
|
|
1855
|
+
/* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: t("posts.form.formatHint") })
|
|
1435
1856
|
] }),
|
|
1436
|
-
/* @__PURE__ */
|
|
1437
|
-
/* @__PURE__ */
|
|
1438
|
-
/* @__PURE__ */
|
|
1439
|
-
format !== "tiptap" && // For textarea-based formats (markdown / html) there's no
|
|
1857
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
1858
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
|
|
1859
|
+
/* @__PURE__ */ jsx11(Label2, { children: t("posts.form.body") }),
|
|
1860
|
+
format !== "tiptap" && format !== "static" && // For textarea-based formats (markdown / html) there's no
|
|
1440
1861
|
// embedded toolbar, so we surface the MediaPicker as a
|
|
1441
1862
|
// standalone button. Selecting an asset inserts a
|
|
1442
1863
|
// format-aware snippet at the cursor.
|
|
1443
|
-
/* @__PURE__ */
|
|
1864
|
+
/* @__PURE__ */ jsx11(
|
|
1444
1865
|
MediaPicker,
|
|
1445
1866
|
{
|
|
1446
1867
|
onSelect: insertMediaSnippet,
|
|
1447
|
-
trigger: /* @__PURE__ */
|
|
1448
|
-
/* @__PURE__ */
|
|
1868
|
+
trigger: /* @__PURE__ */ jsxs9(Button8, { type: "button", variant: "outline", size: "sm", children: [
|
|
1869
|
+
/* @__PURE__ */ jsx11(ImageIcon3, { className: "mr-2 h-3 w-3" }),
|
|
1449
1870
|
t("posts.form.insertMedia")
|
|
1450
1871
|
] })
|
|
1451
1872
|
}
|
|
1452
1873
|
)
|
|
1453
1874
|
] }),
|
|
1454
|
-
format === "tiptap" ? /* @__PURE__ */
|
|
1875
|
+
format === "tiptap" ? /* @__PURE__ */ jsx11(TiptapEditor, { initialContent: body, onChange: setBody }) : format === "static" ? /* @__PURE__ */ jsx11(
|
|
1876
|
+
StaticUploader,
|
|
1877
|
+
{
|
|
1878
|
+
initial: initialStaticBody,
|
|
1879
|
+
onFilesReady: (files, entrypoint) => setPendingBundle({ files, entrypoint }),
|
|
1880
|
+
onClear: () => setPendingBundle(null)
|
|
1881
|
+
}
|
|
1882
|
+
) : /* @__PURE__ */ jsx11(
|
|
1455
1883
|
Textarea,
|
|
1456
1884
|
{
|
|
1457
1885
|
ref: bodyTextareaRef,
|
|
@@ -1462,9 +1890,9 @@ function PostForm({ post }) {
|
|
|
1462
1890
|
}
|
|
1463
1891
|
)
|
|
1464
1892
|
] }),
|
|
1465
|
-
/* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
/* @__PURE__ */
|
|
1893
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
1894
|
+
/* @__PURE__ */ jsx11(Label2, { htmlFor: "tags", children: t("posts.form.tags") }),
|
|
1895
|
+
/* @__PURE__ */ jsx11(
|
|
1468
1896
|
Input2,
|
|
1469
1897
|
{
|
|
1470
1898
|
id: "tags",
|
|
@@ -1473,11 +1901,11 @@ function PostForm({ post }) {
|
|
|
1473
1901
|
placeholder: t("posts.form.tagsPlaceholder")
|
|
1474
1902
|
}
|
|
1475
1903
|
),
|
|
1476
|
-
/* @__PURE__ */
|
|
1904
|
+
/* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: t("posts.form.tagsHint") })
|
|
1477
1905
|
] }),
|
|
1478
|
-
/* @__PURE__ */
|
|
1479
|
-
/* @__PURE__ */
|
|
1480
|
-
/* @__PURE__ */
|
|
1906
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
1907
|
+
/* @__PURE__ */ jsx11(Label2, { htmlFor: "status", children: t("posts.form.status") }),
|
|
1908
|
+
/* @__PURE__ */ jsxs9(
|
|
1481
1909
|
"select",
|
|
1482
1910
|
{
|
|
1483
1911
|
id: "status",
|
|
@@ -1485,14 +1913,14 @@ function PostForm({ post }) {
|
|
|
1485
1913
|
onChange: (e) => setStatus(e.target.value),
|
|
1486
1914
|
className: "flex h-9 w-full max-w-xs rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm",
|
|
1487
1915
|
children: [
|
|
1488
|
-
/* @__PURE__ */
|
|
1489
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsx11("option", { value: "draft", children: t("common.draft") }),
|
|
1917
|
+
/* @__PURE__ */ jsx11("option", { value: "published", children: t("common.published") })
|
|
1490
1918
|
]
|
|
1491
1919
|
}
|
|
1492
1920
|
)
|
|
1493
1921
|
] }),
|
|
1494
|
-
format === "html" && /* @__PURE__ */
|
|
1495
|
-
/* @__PURE__ */
|
|
1922
|
+
format === "html" && /* @__PURE__ */ jsx11("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs9("label", { className: "flex items-start gap-2 text-sm", children: [
|
|
1923
|
+
/* @__PURE__ */ jsx11(
|
|
1496
1924
|
"input",
|
|
1497
1925
|
{
|
|
1498
1926
|
type: "checkbox",
|
|
@@ -1501,41 +1929,41 @@ function PostForm({ post }) {
|
|
|
1501
1929
|
className: "mt-1"
|
|
1502
1930
|
}
|
|
1503
1931
|
),
|
|
1504
|
-
/* @__PURE__ */
|
|
1505
|
-
/* @__PURE__ */
|
|
1506
|
-
/* @__PURE__ */
|
|
1932
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
1933
|
+
/* @__PURE__ */ jsx11("span", { className: "font-medium", children: t("posts.form.noLayout") }),
|
|
1934
|
+
/* @__PURE__ */ jsx11("span", { className: "block text-xs text-muted-foreground", children: t("posts.form.noLayoutHint") })
|
|
1507
1935
|
] })
|
|
1508
1936
|
] }) }),
|
|
1509
|
-
error && /* @__PURE__ */
|
|
1510
|
-
/* @__PURE__ */
|
|
1511
|
-
/* @__PURE__ */
|
|
1512
|
-
isEdit && /* @__PURE__ */
|
|
1937
|
+
error && /* @__PURE__ */ jsx11("p", { className: "text-sm text-destructive", children: error }),
|
|
1938
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
|
|
1939
|
+
/* @__PURE__ */ jsx11(Button8, { type: "submit", disabled: saving, children: saving ? t("common.saving") : isEdit ? t("posts.form.saveChanges") : t("posts.form.createPost") }),
|
|
1940
|
+
isEdit && /* @__PURE__ */ jsx11(Button8, { type: "button", variant: "destructive", onClick: handleDelete, disabled: saving, children: t("posts.form.delete") })
|
|
1513
1941
|
] })
|
|
1514
1942
|
] })
|
|
1515
1943
|
] });
|
|
1516
1944
|
}
|
|
1517
1945
|
|
|
1518
1946
|
// src/components/new-post-view.tsx
|
|
1519
|
-
import { jsx as
|
|
1947
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1520
1948
|
function NewPostPage() {
|
|
1521
1949
|
const t = useT();
|
|
1522
|
-
return /* @__PURE__ */
|
|
1523
|
-
/* @__PURE__ */
|
|
1524
|
-
/* @__PURE__ */
|
|
1950
|
+
return /* @__PURE__ */ jsxs10("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
|
|
1951
|
+
/* @__PURE__ */ jsx12("h1", { className: "mb-6 text-2xl font-bold md:mb-8 md:text-3xl", children: t("posts.form.newTitle") }),
|
|
1952
|
+
/* @__PURE__ */ jsx12(PostForm, {})
|
|
1525
1953
|
] });
|
|
1526
1954
|
}
|
|
1527
1955
|
|
|
1528
1956
|
// src/components/edit-post-view.tsx
|
|
1529
|
-
import { useEffect as useEffect5, useState as
|
|
1957
|
+
import { useEffect as useEffect5, useState as useState7, use } from "react";
|
|
1530
1958
|
import { notFound } from "next/navigation";
|
|
1531
1959
|
import { getPostById } from "ampless";
|
|
1532
|
-
import { jsx as
|
|
1960
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1533
1961
|
function EditPostPage({ params }) {
|
|
1534
1962
|
const t = useT();
|
|
1535
1963
|
const { postId } = use(params);
|
|
1536
|
-
const [post, setPost] =
|
|
1537
|
-
const [loading, setLoading] =
|
|
1538
|
-
const [missing, setMissing] =
|
|
1964
|
+
const [post, setPost] = useState7(null);
|
|
1965
|
+
const [loading, setLoading] = useState7(true);
|
|
1966
|
+
const [missing, setMissing] = useState7(false);
|
|
1539
1967
|
useEffect5(() => {
|
|
1540
1968
|
const siteId = readAdminSiteIdFromCookie();
|
|
1541
1969
|
getPostById(postId, { siteId }).then((p) => {
|
|
@@ -1544,21 +1972,21 @@ function EditPostPage({ params }) {
|
|
|
1544
1972
|
}).finally(() => setLoading(false));
|
|
1545
1973
|
}, [postId]);
|
|
1546
1974
|
if (loading)
|
|
1547
|
-
return /* @__PURE__ */
|
|
1975
|
+
return /* @__PURE__ */ jsx13("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: t("common.loading") });
|
|
1548
1976
|
if (missing) notFound();
|
|
1549
|
-
return /* @__PURE__ */
|
|
1550
|
-
/* @__PURE__ */
|
|
1551
|
-
post && /* @__PURE__ */
|
|
1977
|
+
return /* @__PURE__ */ jsxs11("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
|
|
1978
|
+
/* @__PURE__ */ jsx13("h1", { className: "mb-6 text-2xl font-bold md:mb-8 md:text-3xl", children: t("posts.form.editTitle") }),
|
|
1979
|
+
post && /* @__PURE__ */ jsx13(PostForm, { post })
|
|
1552
1980
|
] });
|
|
1553
1981
|
}
|
|
1554
1982
|
|
|
1555
1983
|
// src/components/media-uploader.tsx
|
|
1556
|
-
import { useState as
|
|
1557
|
-
import { uploadData as
|
|
1984
|
+
import { useState as useState8, useEffect as useEffect6, useCallback, useRef as useRef4 } from "react";
|
|
1985
|
+
import { uploadData as uploadData3, list as list3, remove as remove2, isCancelError } from "aws-amplify/storage";
|
|
1558
1986
|
import { processImage as processImage2 } from "ampless/media";
|
|
1559
|
-
import { Button as
|
|
1560
|
-
import { Trash2 as Trash22, Copy, Check, FileText, Code2 } from "lucide-react";
|
|
1561
|
-
import { jsx as
|
|
1987
|
+
import { Button as Button9, Input as Input3 } from "@ampless/runtime/ui";
|
|
1988
|
+
import { Trash2 as Trash22, Copy, Check, FileText as FileText2, Code2 } from "lucide-react";
|
|
1989
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1562
1990
|
var IMAGE_EXT_RE = /\.(jpe?g|png|gif|webp|avif|svg|bmp|tiff?)$/i;
|
|
1563
1991
|
var STYLESHEET_EXT_RE = /\.css$/i;
|
|
1564
1992
|
var SCRIPT_EXT_RE = /\.m?js$/i;
|
|
@@ -1583,16 +2011,16 @@ function sanitizeName2(name) {
|
|
|
1583
2011
|
}
|
|
1584
2012
|
function MediaUploader() {
|
|
1585
2013
|
const t = useT();
|
|
1586
|
-
const [items, setItems] =
|
|
1587
|
-
const [queue, setQueue] =
|
|
1588
|
-
const [uploading, setUploading] =
|
|
1589
|
-
const [error, setError] =
|
|
1590
|
-
const [copiedPath, setCopiedPath] =
|
|
2014
|
+
const [items, setItems] = useState8([]);
|
|
2015
|
+
const [queue, setQueue] = useState8([]);
|
|
2016
|
+
const [uploading, setUploading] = useState8(false);
|
|
2017
|
+
const [error, setError] = useState8(null);
|
|
2018
|
+
const [copiedPath, setCopiedPath] = useState8(null);
|
|
1591
2019
|
const uploadTaskRef = useRef4(null);
|
|
1592
2020
|
const cancelTokenRef = useRef4({ cancelled: false });
|
|
1593
2021
|
const refresh = useCallback(async () => {
|
|
1594
2022
|
try {
|
|
1595
|
-
const result = await
|
|
2023
|
+
const result = await list3({ path: "public/media/" });
|
|
1596
2024
|
setItems(
|
|
1597
2025
|
result.items.map((item) => ({
|
|
1598
2026
|
path: item.path,
|
|
@@ -1630,7 +2058,7 @@ function MediaUploader() {
|
|
|
1630
2058
|
const yyyy = now.getFullYear();
|
|
1631
2059
|
const mm = String(now.getMonth() + 1).padStart(2, "0");
|
|
1632
2060
|
const path = `public/media/${yyyy}/${mm}/${Date.now()}-${safeName}`;
|
|
1633
|
-
const task =
|
|
2061
|
+
const task = uploadData3({
|
|
1634
2062
|
path,
|
|
1635
2063
|
data: processed.blob,
|
|
1636
2064
|
options: { contentType: processed.mime }
|
|
@@ -1664,7 +2092,7 @@ function MediaUploader() {
|
|
|
1664
2092
|
async function handleDelete(path) {
|
|
1665
2093
|
if (!confirm(t("media.deleteConfirm"))) return;
|
|
1666
2094
|
try {
|
|
1667
|
-
await
|
|
2095
|
+
await remove2({ path });
|
|
1668
2096
|
await refresh();
|
|
1669
2097
|
} catch (err) {
|
|
1670
2098
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -1678,9 +2106,9 @@ function MediaUploader() {
|
|
|
1678
2106
|
setTimeout(() => setCopiedPath((p) => p === key ? null : p), 1500);
|
|
1679
2107
|
}
|
|
1680
2108
|
const currentFile = queue[0] ?? null;
|
|
1681
|
-
return /* @__PURE__ */
|
|
1682
|
-
/* @__PURE__ */
|
|
1683
|
-
/* @__PURE__ */
|
|
2109
|
+
return /* @__PURE__ */ jsxs12("div", { className: "space-y-6", children: [
|
|
2110
|
+
/* @__PURE__ */ jsxs12("div", { className: "rounded-md border p-4", children: [
|
|
2111
|
+
/* @__PURE__ */ jsx14(
|
|
1684
2112
|
Input3,
|
|
1685
2113
|
{
|
|
1686
2114
|
type: "file",
|
|
@@ -1689,11 +2117,11 @@ function MediaUploader() {
|
|
|
1689
2117
|
disabled: uploading
|
|
1690
2118
|
}
|
|
1691
2119
|
),
|
|
1692
|
-
uploading && /* @__PURE__ */
|
|
1693
|
-
!uploading && queue.length > 0 && /* @__PURE__ */
|
|
2120
|
+
uploading && /* @__PURE__ */ jsx14("p", { className: "mt-2 text-sm text-muted-foreground", children: t("media.uploading") }),
|
|
2121
|
+
!uploading && queue.length > 0 && /* @__PURE__ */ jsx14("p", { className: "mt-2 text-sm text-muted-foreground", children: t("media.queued", { count: queue.length }) })
|
|
1694
2122
|
] }),
|
|
1695
|
-
error && /* @__PURE__ */
|
|
1696
|
-
/* @__PURE__ */
|
|
2123
|
+
error && /* @__PURE__ */ jsx14("p", { className: "text-sm text-destructive", children: error }),
|
|
2124
|
+
/* @__PURE__ */ jsx14(
|
|
1697
2125
|
ImageUploadDialog,
|
|
1698
2126
|
{
|
|
1699
2127
|
file: currentFile,
|
|
@@ -1705,7 +2133,7 @@ function MediaUploader() {
|
|
|
1705
2133
|
onCancel: handleDialogCancel
|
|
1706
2134
|
}
|
|
1707
2135
|
),
|
|
1708
|
-
/* @__PURE__ */
|
|
2136
|
+
/* @__PURE__ */ jsx14("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4", children: items.map((item) => {
|
|
1709
2137
|
const isImage = IMAGE_EXT_RE.test(item.path);
|
|
1710
2138
|
const isStylesheet = STYLESHEET_EXT_RE.test(item.path);
|
|
1711
2139
|
const isScript = SCRIPT_EXT_RE.test(item.path);
|
|
@@ -1715,14 +2143,14 @@ function MediaUploader() {
|
|
|
1715
2143
|
const tagDiffersFromUrl = tagSnippet !== item.url;
|
|
1716
2144
|
const urlCopied = copiedPath === `${item.path}:url`;
|
|
1717
2145
|
const tagCopied = copiedPath === `${item.path}:tag`;
|
|
1718
|
-
return /* @__PURE__ */
|
|
2146
|
+
return /* @__PURE__ */ jsxs12(
|
|
1719
2147
|
"div",
|
|
1720
2148
|
{
|
|
1721
2149
|
className: "group relative overflow-hidden rounded-md border bg-[var(--card)]",
|
|
1722
2150
|
children: [
|
|
1723
2151
|
isImage ? (
|
|
1724
2152
|
// eslint-disable-next-line @next/next/no-img-element
|
|
1725
|
-
/* @__PURE__ */
|
|
2153
|
+
/* @__PURE__ */ jsx14(
|
|
1726
2154
|
"img",
|
|
1727
2155
|
{
|
|
1728
2156
|
src: item.url,
|
|
@@ -1730,18 +2158,18 @@ function MediaUploader() {
|
|
|
1730
2158
|
className: "aspect-square w-full object-cover"
|
|
1731
2159
|
}
|
|
1732
2160
|
)
|
|
1733
|
-
) : /* @__PURE__ */
|
|
1734
|
-
isStylesheet || isScript ? /* @__PURE__ */
|
|
1735
|
-
/* @__PURE__ */
|
|
2161
|
+
) : /* @__PURE__ */ jsxs12("div", { className: "flex aspect-square w-full flex-col items-center justify-center gap-2 bg-muted text-muted-foreground", children: [
|
|
2162
|
+
isStylesheet || isScript ? /* @__PURE__ */ jsx14(Code2, { className: "h-8 w-8" }) : /* @__PURE__ */ jsx14(FileText2, { className: "h-8 w-8" }),
|
|
2163
|
+
/* @__PURE__ */ jsxs12("span", { className: "font-mono text-xs font-semibold", children: [
|
|
1736
2164
|
".",
|
|
1737
2165
|
ext.toLowerCase()
|
|
1738
2166
|
] })
|
|
1739
2167
|
] }),
|
|
1740
|
-
/* @__PURE__ */
|
|
1741
|
-
/* @__PURE__ */
|
|
1742
|
-
/* @__PURE__ */
|
|
1743
|
-
/* @__PURE__ */
|
|
1744
|
-
|
|
2168
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between border-t px-2 py-1 text-xs", children: [
|
|
2169
|
+
/* @__PURE__ */ jsx14("span", { className: "truncate", title: filename, children: filename }),
|
|
2170
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex shrink-0 items-center gap-0.5", children: [
|
|
2171
|
+
/* @__PURE__ */ jsx14(
|
|
2172
|
+
Button9,
|
|
1745
2173
|
{
|
|
1746
2174
|
type: "button",
|
|
1747
2175
|
variant: "ghost",
|
|
@@ -1749,11 +2177,11 @@ function MediaUploader() {
|
|
|
1749
2177
|
className: "h-6 w-6",
|
|
1750
2178
|
onClick: () => handleCopy(item, "url"),
|
|
1751
2179
|
title: t("media.copyUrl"),
|
|
1752
|
-
children: urlCopied ? /* @__PURE__ */
|
|
2180
|
+
children: urlCopied ? /* @__PURE__ */ jsx14(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx14(Copy, { className: "h-3 w-3" })
|
|
1753
2181
|
}
|
|
1754
2182
|
),
|
|
1755
|
-
tagDiffersFromUrl && /* @__PURE__ */
|
|
1756
|
-
|
|
2183
|
+
tagDiffersFromUrl && /* @__PURE__ */ jsx14(
|
|
2184
|
+
Button9,
|
|
1757
2185
|
{
|
|
1758
2186
|
type: "button",
|
|
1759
2187
|
variant: "ghost",
|
|
@@ -1761,11 +2189,11 @@ function MediaUploader() {
|
|
|
1761
2189
|
className: "h-6 w-6",
|
|
1762
2190
|
onClick: () => handleCopy(item, "tag"),
|
|
1763
2191
|
title: t("media.copyTag"),
|
|
1764
|
-
children: tagCopied ? /* @__PURE__ */
|
|
2192
|
+
children: tagCopied ? /* @__PURE__ */ jsx14(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx14(Code2, { className: "h-3 w-3" })
|
|
1765
2193
|
}
|
|
1766
2194
|
),
|
|
1767
|
-
/* @__PURE__ */
|
|
1768
|
-
|
|
2195
|
+
/* @__PURE__ */ jsx14(
|
|
2196
|
+
Button9,
|
|
1769
2197
|
{
|
|
1770
2198
|
type: "button",
|
|
1771
2199
|
variant: "ghost",
|
|
@@ -1773,7 +2201,7 @@ function MediaUploader() {
|
|
|
1773
2201
|
className: "h-6 w-6",
|
|
1774
2202
|
onClick: () => handleDelete(item.path),
|
|
1775
2203
|
title: t("media.delete"),
|
|
1776
|
-
children: /* @__PURE__ */
|
|
2204
|
+
children: /* @__PURE__ */ jsx14(Trash22, { className: "h-3 w-3" })
|
|
1777
2205
|
}
|
|
1778
2206
|
)
|
|
1779
2207
|
] })
|
|
@@ -1783,22 +2211,22 @@ function MediaUploader() {
|
|
|
1783
2211
|
item.path
|
|
1784
2212
|
);
|
|
1785
2213
|
}) }),
|
|
1786
|
-
items.length === 0 && /* @__PURE__ */
|
|
2214
|
+
items.length === 0 && /* @__PURE__ */ jsx14("p", { className: "text-center text-sm text-muted-foreground", children: t("media.empty") })
|
|
1787
2215
|
] });
|
|
1788
2216
|
}
|
|
1789
2217
|
|
|
1790
2218
|
// src/components/media-view.tsx
|
|
1791
|
-
import { jsx as
|
|
2219
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1792
2220
|
function MediaPage() {
|
|
1793
2221
|
const t = useT();
|
|
1794
|
-
return /* @__PURE__ */
|
|
1795
|
-
/* @__PURE__ */
|
|
1796
|
-
/* @__PURE__ */
|
|
2222
|
+
return /* @__PURE__ */ jsxs13("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
|
|
2223
|
+
/* @__PURE__ */ jsx15("h1", { className: "mb-6 text-2xl font-bold md:mb-8 md:text-3xl", children: t("media.title") }),
|
|
2224
|
+
/* @__PURE__ */ jsx15(MediaUploader, {})
|
|
1797
2225
|
] });
|
|
1798
2226
|
}
|
|
1799
2227
|
|
|
1800
2228
|
// src/components/login-view.tsx
|
|
1801
|
-
import { useState as
|
|
2229
|
+
import { useState as useState9 } from "react";
|
|
1802
2230
|
import { useRouter as useRouter2 } from "next/navigation";
|
|
1803
2231
|
import {
|
|
1804
2232
|
signIn,
|
|
@@ -1808,7 +2236,7 @@ import {
|
|
|
1808
2236
|
confirmResetPassword
|
|
1809
2237
|
} from "aws-amplify/auth";
|
|
1810
2238
|
import {
|
|
1811
|
-
Button as
|
|
2239
|
+
Button as Button10,
|
|
1812
2240
|
Input as Input4,
|
|
1813
2241
|
Label as Label3,
|
|
1814
2242
|
Card as Card2,
|
|
@@ -1817,17 +2245,17 @@ import {
|
|
|
1817
2245
|
CardTitle as CardTitle2,
|
|
1818
2246
|
CardDescription
|
|
1819
2247
|
} from "@ampless/runtime/ui";
|
|
1820
|
-
import { Fragment as Fragment4, jsx as
|
|
2248
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1821
2249
|
function LoginPage() {
|
|
1822
2250
|
const router = useRouter2();
|
|
1823
2251
|
const t = useT();
|
|
1824
|
-
const [mode, setMode] =
|
|
1825
|
-
const [email, setEmail] =
|
|
1826
|
-
const [password, setPassword] =
|
|
1827
|
-
const [code, setCode] =
|
|
1828
|
-
const [error, setError] =
|
|
1829
|
-
const [info, setInfo] =
|
|
1830
|
-
const [loading, setLoading] =
|
|
2252
|
+
const [mode, setMode] = useState9("signIn");
|
|
2253
|
+
const [email, setEmail] = useState9("");
|
|
2254
|
+
const [password, setPassword] = useState9("");
|
|
2255
|
+
const [code, setCode] = useState9("");
|
|
2256
|
+
const [error, setError] = useState9(null);
|
|
2257
|
+
const [info, setInfo] = useState9(null);
|
|
2258
|
+
const [loading, setLoading] = useState9(false);
|
|
1831
2259
|
function go(next) {
|
|
1832
2260
|
setMode(next);
|
|
1833
2261
|
setError(null);
|
|
@@ -1891,15 +2319,15 @@ function LoginPage() {
|
|
|
1891
2319
|
const showEmail = mode !== "confirm" && mode !== "reset";
|
|
1892
2320
|
const showPassword = mode === "signIn" || mode === "signUp" || mode === "reset";
|
|
1893
2321
|
const showCode = mode === "confirm" || mode === "reset";
|
|
1894
|
-
return /* @__PURE__ */
|
|
1895
|
-
/* @__PURE__ */
|
|
1896
|
-
/* @__PURE__ */
|
|
1897
|
-
/* @__PURE__ */
|
|
2322
|
+
return /* @__PURE__ */ jsx16("main", { className: "flex min-h-screen items-center justify-center bg-muted/30 p-4", children: /* @__PURE__ */ jsxs14(Card2, { className: "w-full max-w-md", children: [
|
|
2323
|
+
/* @__PURE__ */ jsxs14(CardHeader2, { children: [
|
|
2324
|
+
/* @__PURE__ */ jsx16(CardTitle2, { children: t(`auth.${mode}.title`) }),
|
|
2325
|
+
/* @__PURE__ */ jsx16(CardDescription, { children: t(`auth.${mode}.description`) })
|
|
1898
2326
|
] }),
|
|
1899
|
-
/* @__PURE__ */
|
|
1900
|
-
showEmail && /* @__PURE__ */
|
|
1901
|
-
/* @__PURE__ */
|
|
1902
|
-
/* @__PURE__ */
|
|
2327
|
+
/* @__PURE__ */ jsx16(CardContent2, { children: /* @__PURE__ */ jsxs14("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
2328
|
+
showEmail && /* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
|
|
2329
|
+
/* @__PURE__ */ jsx16(Label3, { htmlFor: "email", children: t("auth.common.email") }),
|
|
2330
|
+
/* @__PURE__ */ jsx16(
|
|
1903
2331
|
Input4,
|
|
1904
2332
|
{
|
|
1905
2333
|
id: "email",
|
|
@@ -1911,9 +2339,9 @@ function LoginPage() {
|
|
|
1911
2339
|
}
|
|
1912
2340
|
)
|
|
1913
2341
|
] }),
|
|
1914
|
-
showCode && /* @__PURE__ */
|
|
1915
|
-
/* @__PURE__ */
|
|
1916
|
-
/* @__PURE__ */
|
|
2342
|
+
showCode && /* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
|
|
2343
|
+
/* @__PURE__ */ jsx16(Label3, { htmlFor: "code", children: t("auth.common.code") }),
|
|
2344
|
+
/* @__PURE__ */ jsx16(
|
|
1917
2345
|
Input4,
|
|
1918
2346
|
{
|
|
1919
2347
|
id: "code",
|
|
@@ -1924,9 +2352,9 @@ function LoginPage() {
|
|
|
1924
2352
|
}
|
|
1925
2353
|
)
|
|
1926
2354
|
] }),
|
|
1927
|
-
showPassword && /* @__PURE__ */
|
|
1928
|
-
/* @__PURE__ */
|
|
1929
|
-
/* @__PURE__ */
|
|
2355
|
+
showPassword && /* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
|
|
2356
|
+
/* @__PURE__ */ jsx16(Label3, { htmlFor: "password", children: mode === "reset" ? t("auth.common.newPassword") : t("auth.common.password") }),
|
|
2357
|
+
/* @__PURE__ */ jsx16(
|
|
1930
2358
|
Input4,
|
|
1931
2359
|
{
|
|
1932
2360
|
id: "password",
|
|
@@ -1938,14 +2366,14 @@ function LoginPage() {
|
|
|
1938
2366
|
autoComplete: mode === "signIn" ? "current-password" : "new-password"
|
|
1939
2367
|
}
|
|
1940
2368
|
),
|
|
1941
|
-
(mode === "signUp" || mode === "reset") && /* @__PURE__ */
|
|
2369
|
+
(mode === "signUp" || mode === "reset") && /* @__PURE__ */ jsx16("p", { className: "text-xs text-muted-foreground", children: t("auth.common.passwordHint") })
|
|
1942
2370
|
] }),
|
|
1943
|
-
info && /* @__PURE__ */
|
|
1944
|
-
error && /* @__PURE__ */
|
|
1945
|
-
/* @__PURE__ */
|
|
1946
|
-
/* @__PURE__ */
|
|
1947
|
-
mode === "signIn" && /* @__PURE__ */
|
|
1948
|
-
/* @__PURE__ */
|
|
2371
|
+
info && /* @__PURE__ */ jsx16("p", { className: "text-sm text-muted-foreground", children: info }),
|
|
2372
|
+
error && /* @__PURE__ */ jsx16("p", { className: "text-sm text-destructive", children: error }),
|
|
2373
|
+
/* @__PURE__ */ jsx16(Button10, { type: "submit", className: "w-full", disabled: loading, children: loading ? t("auth.common.working") : t(`auth.${mode}.submit`) }),
|
|
2374
|
+
/* @__PURE__ */ jsxs14("div", { className: "space-y-1 text-center text-sm", children: [
|
|
2375
|
+
mode === "signIn" && /* @__PURE__ */ jsxs14(Fragment4, { children: [
|
|
2376
|
+
/* @__PURE__ */ jsx16("p", { children: /* @__PURE__ */ jsx16(
|
|
1949
2377
|
"button",
|
|
1950
2378
|
{
|
|
1951
2379
|
type: "button",
|
|
@@ -1954,7 +2382,7 @@ function LoginPage() {
|
|
|
1954
2382
|
children: t("auth.signIn.forgotPassword")
|
|
1955
2383
|
}
|
|
1956
2384
|
) }),
|
|
1957
|
-
/* @__PURE__ */
|
|
2385
|
+
/* @__PURE__ */ jsx16("p", { children: /* @__PURE__ */ jsx16(
|
|
1958
2386
|
"button",
|
|
1959
2387
|
{
|
|
1960
2388
|
type: "button",
|
|
@@ -1964,7 +2392,7 @@ function LoginPage() {
|
|
|
1964
2392
|
}
|
|
1965
2393
|
) })
|
|
1966
2394
|
] }),
|
|
1967
|
-
(mode === "signUp" || mode === "forgot" || mode === "reset") && /* @__PURE__ */
|
|
2395
|
+
(mode === "signUp" || mode === "forgot" || mode === "reset") && /* @__PURE__ */ jsx16("p", { children: /* @__PURE__ */ jsx16(
|
|
1968
2396
|
"button",
|
|
1969
2397
|
{
|
|
1970
2398
|
type: "button",
|
|
@@ -1973,7 +2401,7 @@ function LoginPage() {
|
|
|
1973
2401
|
children: t("auth.signUp.backToSignIn")
|
|
1974
2402
|
}
|
|
1975
2403
|
) }),
|
|
1976
|
-
mode === "reset" && /* @__PURE__ */
|
|
2404
|
+
mode === "reset" && /* @__PURE__ */ jsx16("p", { children: /* @__PURE__ */ jsx16(
|
|
1977
2405
|
"button",
|
|
1978
2406
|
{
|
|
1979
2407
|
type: "button",
|
|
@@ -1988,26 +2416,26 @@ function LoginPage() {
|
|
|
1988
2416
|
}
|
|
1989
2417
|
|
|
1990
2418
|
// src/components/sidebar.tsx
|
|
1991
|
-
import { useEffect as useEffect7, useState as
|
|
2419
|
+
import { useEffect as useEffect7, useState as useState10 } from "react";
|
|
1992
2420
|
import Link4 from "next/link";
|
|
1993
2421
|
import { usePathname } from "next/navigation";
|
|
1994
2422
|
import { signOut } from "aws-amplify/auth";
|
|
1995
2423
|
import {
|
|
1996
2424
|
LayoutDashboard,
|
|
1997
|
-
FileText as
|
|
2425
|
+
FileText as FileText3,
|
|
1998
2426
|
Image as Image2,
|
|
1999
2427
|
Globe,
|
|
2000
2428
|
Users,
|
|
2001
2429
|
LogOut,
|
|
2002
2430
|
ExternalLink,
|
|
2003
2431
|
Menu,
|
|
2004
|
-
X
|
|
2432
|
+
X as X2
|
|
2005
2433
|
} from "lucide-react";
|
|
2006
|
-
import { Button as
|
|
2007
|
-
import { Fragment as Fragment5, jsx as
|
|
2434
|
+
import { Button as Button11, cn as cn3 } from "@ampless/runtime/ui";
|
|
2435
|
+
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2008
2436
|
var navItems = [
|
|
2009
2437
|
{ href: "/admin", key: "sidebar.dashboard", icon: LayoutDashboard },
|
|
2010
|
-
{ href: "/admin/posts", key: "sidebar.posts", icon:
|
|
2438
|
+
{ href: "/admin/posts", key: "sidebar.posts", icon: FileText3 },
|
|
2011
2439
|
{ href: "/admin/media", key: "sidebar.media", icon: Image2 },
|
|
2012
2440
|
{ href: "/admin/sites", key: "sidebar.sites", icon: Globe },
|
|
2013
2441
|
{ href: "/admin/users", key: "sidebar.users", icon: Users, adminOnly: true }
|
|
@@ -2019,7 +2447,7 @@ function Sidebar({
|
|
|
2019
2447
|
}) {
|
|
2020
2448
|
const pathname = usePathname();
|
|
2021
2449
|
const t = useT();
|
|
2022
|
-
const [open, setOpen] =
|
|
2450
|
+
const [open, setOpen] = useState10(false);
|
|
2023
2451
|
useEffect7(() => {
|
|
2024
2452
|
setOpen(false);
|
|
2025
2453
|
}, [pathname]);
|
|
@@ -2031,22 +2459,22 @@ function Sidebar({
|
|
|
2031
2459
|
document.body.style.overflow = prev;
|
|
2032
2460
|
};
|
|
2033
2461
|
}, [open]);
|
|
2034
|
-
return /* @__PURE__ */
|
|
2035
|
-
/* @__PURE__ */
|
|
2036
|
-
/* @__PURE__ */
|
|
2037
|
-
/* @__PURE__ */
|
|
2038
|
-
|
|
2462
|
+
return /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
2463
|
+
/* @__PURE__ */ jsxs15("header", { className: "sticky top-0 z-30 flex h-14 items-center justify-between border-b bg-background px-4 md:hidden", children: [
|
|
2464
|
+
/* @__PURE__ */ jsx17(Link4, { href: "/admin", className: "font-semibold", children: t("sidebar.brand") }),
|
|
2465
|
+
/* @__PURE__ */ jsx17(
|
|
2466
|
+
Button11,
|
|
2039
2467
|
{
|
|
2040
2468
|
variant: "ghost",
|
|
2041
2469
|
size: "icon",
|
|
2042
2470
|
"aria-label": t("sidebar.openMenu"),
|
|
2043
2471
|
"aria-expanded": open,
|
|
2044
2472
|
onClick: () => setOpen(true),
|
|
2045
|
-
children: /* @__PURE__ */
|
|
2473
|
+
children: /* @__PURE__ */ jsx17(Menu, { className: "h-5 w-5" })
|
|
2046
2474
|
}
|
|
2047
2475
|
)
|
|
2048
2476
|
] }),
|
|
2049
|
-
open && /* @__PURE__ */
|
|
2477
|
+
open && /* @__PURE__ */ jsx17(
|
|
2050
2478
|
"div",
|
|
2051
2479
|
{
|
|
2052
2480
|
className: "fixed inset-0 z-40 bg-black/40 md:hidden",
|
|
@@ -2054,7 +2482,7 @@ function Sidebar({
|
|
|
2054
2482
|
onClick: () => setOpen(false)
|
|
2055
2483
|
}
|
|
2056
2484
|
),
|
|
2057
|
-
/* @__PURE__ */
|
|
2485
|
+
/* @__PURE__ */ jsxs15(
|
|
2058
2486
|
"aside",
|
|
2059
2487
|
{
|
|
2060
2488
|
className: cn3(
|
|
@@ -2063,26 +2491,26 @@ function Sidebar({
|
|
|
2063
2491
|
),
|
|
2064
2492
|
"aria-label": t("sidebar.brand"),
|
|
2065
2493
|
children: [
|
|
2066
|
-
/* @__PURE__ */
|
|
2067
|
-
/* @__PURE__ */
|
|
2068
|
-
/* @__PURE__ */
|
|
2069
|
-
|
|
2494
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between border-b p-4", children: [
|
|
2495
|
+
/* @__PURE__ */ jsx17(Link4, { href: "/admin", className: "font-semibold", children: t("sidebar.brand") }),
|
|
2496
|
+
/* @__PURE__ */ jsx17(
|
|
2497
|
+
Button11,
|
|
2070
2498
|
{
|
|
2071
2499
|
variant: "ghost",
|
|
2072
2500
|
size: "icon",
|
|
2073
2501
|
className: "md:hidden",
|
|
2074
2502
|
"aria-label": t("sidebar.closeMenu"),
|
|
2075
2503
|
onClick: () => setOpen(false),
|
|
2076
|
-
children: /* @__PURE__ */
|
|
2504
|
+
children: /* @__PURE__ */ jsx17(X2, { className: "h-5 w-5" })
|
|
2077
2505
|
}
|
|
2078
2506
|
)
|
|
2079
2507
|
] }),
|
|
2080
|
-
siteSelector ? /* @__PURE__ */
|
|
2081
|
-
/* @__PURE__ */
|
|
2508
|
+
siteSelector ? /* @__PURE__ */ jsx17("div", { className: "border-b", children: siteSelector }) : null,
|
|
2509
|
+
/* @__PURE__ */ jsx17("nav", { className: "flex-1 space-y-1 overflow-y-auto p-2", children: navItems.map((item) => {
|
|
2082
2510
|
if (item.adminOnly && !isAdmin) return null;
|
|
2083
2511
|
const Icon = item.icon;
|
|
2084
2512
|
const isActive = pathname === item.href || item.href !== "/admin" && pathname?.startsWith(item.href);
|
|
2085
|
-
return /* @__PURE__ */
|
|
2513
|
+
return /* @__PURE__ */ jsxs15(
|
|
2086
2514
|
Link4,
|
|
2087
2515
|
{
|
|
2088
2516
|
href: item.href,
|
|
@@ -2091,29 +2519,29 @@ function Sidebar({
|
|
|
2091
2519
|
isActive ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
|
|
2092
2520
|
),
|
|
2093
2521
|
children: [
|
|
2094
|
-
/* @__PURE__ */
|
|
2522
|
+
/* @__PURE__ */ jsx17(Icon, { className: "h-4 w-4" }),
|
|
2095
2523
|
t(item.key)
|
|
2096
2524
|
]
|
|
2097
2525
|
},
|
|
2098
2526
|
item.href
|
|
2099
2527
|
);
|
|
2100
2528
|
}) }),
|
|
2101
|
-
/* @__PURE__ */
|
|
2102
|
-
/* @__PURE__ */
|
|
2529
|
+
/* @__PURE__ */ jsxs15("div", { className: "border-t p-2 space-y-1", children: [
|
|
2530
|
+
/* @__PURE__ */ jsxs15(
|
|
2103
2531
|
Link4,
|
|
2104
2532
|
{
|
|
2105
2533
|
href: "/",
|
|
2106
2534
|
target: "_blank",
|
|
2107
2535
|
className: "flex items-center gap-3 rounded-md px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
2108
2536
|
children: [
|
|
2109
|
-
/* @__PURE__ */
|
|
2537
|
+
/* @__PURE__ */ jsx17(ExternalLink, { className: "h-4 w-4" }),
|
|
2110
2538
|
t("sidebar.viewSite")
|
|
2111
2539
|
]
|
|
2112
2540
|
}
|
|
2113
2541
|
),
|
|
2114
|
-
/* @__PURE__ */
|
|
2115
|
-
/* @__PURE__ */
|
|
2116
|
-
|
|
2542
|
+
/* @__PURE__ */ jsx17("div", { className: "px-3 py-2 text-xs text-muted-foreground truncate", children: email }),
|
|
2543
|
+
/* @__PURE__ */ jsxs15(
|
|
2544
|
+
Button11,
|
|
2117
2545
|
{
|
|
2118
2546
|
variant: "ghost",
|
|
2119
2547
|
size: "sm",
|
|
@@ -2123,7 +2551,7 @@ function Sidebar({
|
|
|
2123
2551
|
window.location.href = "/login";
|
|
2124
2552
|
},
|
|
2125
2553
|
children: [
|
|
2126
|
-
/* @__PURE__ */
|
|
2554
|
+
/* @__PURE__ */ jsx17(LogOut, { className: "h-4 w-4" }),
|
|
2127
2555
|
t("sidebar.signOut")
|
|
2128
2556
|
]
|
|
2129
2557
|
}
|
|
@@ -2137,7 +2565,7 @@ function Sidebar({
|
|
|
2137
2565
|
|
|
2138
2566
|
// src/components/site-selector.tsx
|
|
2139
2567
|
import { useRouter as useRouter3 } from "next/navigation";
|
|
2140
|
-
import { jsx as
|
|
2568
|
+
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2141
2569
|
function SiteSelector({ current, sites }) {
|
|
2142
2570
|
const router = useRouter3();
|
|
2143
2571
|
const t = useT();
|
|
@@ -2146,26 +2574,26 @@ function SiteSelector({ current, sites }) {
|
|
|
2146
2574
|
document.cookie = `${ADMIN_SITE_COOKIE}=${encodeURIComponent(next)}; Path=/; Max-Age=${60 * 60 * 24 * 365}; SameSite=Lax`;
|
|
2147
2575
|
router.refresh();
|
|
2148
2576
|
}
|
|
2149
|
-
return /* @__PURE__ */
|
|
2150
|
-
/* @__PURE__ */
|
|
2151
|
-
/* @__PURE__ */
|
|
2577
|
+
return /* @__PURE__ */ jsxs16("div", { className: "px-3 py-2", children: [
|
|
2578
|
+
/* @__PURE__ */ jsx18("label", { className: "block text-xs uppercase tracking-wide text-muted-foreground mb-1", children: t("sites.selector.label") }),
|
|
2579
|
+
/* @__PURE__ */ jsx18(
|
|
2152
2580
|
"select",
|
|
2153
2581
|
{
|
|
2154
2582
|
value: current,
|
|
2155
2583
|
onChange,
|
|
2156
2584
|
className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
|
|
2157
|
-
children: sites.map((s) => /* @__PURE__ */
|
|
2585
|
+
children: sites.map((s) => /* @__PURE__ */ jsx18("option", { value: s.id, children: s.name }, s.id))
|
|
2158
2586
|
}
|
|
2159
2587
|
)
|
|
2160
2588
|
] });
|
|
2161
2589
|
}
|
|
2162
2590
|
|
|
2163
2591
|
// src/components/site-settings-form.tsx
|
|
2164
|
-
import { useState as
|
|
2592
|
+
import { useState as useState11 } from "react";
|
|
2165
2593
|
import { useRouter as useRouter4 } from "next/navigation";
|
|
2166
2594
|
import { setSiteSetting } from "ampless";
|
|
2167
|
-
import { Button as
|
|
2168
|
-
import { jsx as
|
|
2595
|
+
import { Button as Button12, Input as Input5, Label as Label4, Textarea as Textarea2 } from "@ampless/runtime/ui";
|
|
2596
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2169
2597
|
var KEYS = [
|
|
2170
2598
|
"site.name",
|
|
2171
2599
|
"site.url",
|
|
@@ -2178,10 +2606,10 @@ var KEYS = [
|
|
|
2178
2606
|
function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
2179
2607
|
const router = useRouter4();
|
|
2180
2608
|
const t = useT();
|
|
2181
|
-
const [values, setValues] =
|
|
2182
|
-
const [saving, setSaving] =
|
|
2183
|
-
const [error, setError] =
|
|
2184
|
-
const [info, setInfo] =
|
|
2609
|
+
const [values, setValues] = useState11(initial);
|
|
2610
|
+
const [saving, setSaving] = useState11(false);
|
|
2611
|
+
const [error, setError] = useState11(null);
|
|
2612
|
+
const [info, setInfo] = useState11(null);
|
|
2185
2613
|
function update(key, value) {
|
|
2186
2614
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
2187
2615
|
}
|
|
@@ -2206,12 +2634,12 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2206
2634
|
setSaving(false);
|
|
2207
2635
|
}
|
|
2208
2636
|
}
|
|
2209
|
-
return /* @__PURE__ */
|
|
2210
|
-
/* @__PURE__ */
|
|
2211
|
-
/* @__PURE__ */
|
|
2212
|
-
/* @__PURE__ */
|
|
2213
|
-
/* @__PURE__ */
|
|
2214
|
-
/* @__PURE__ */
|
|
2637
|
+
return /* @__PURE__ */ jsxs17("form", { onSubmit: save, className: "space-y-6 max-w-xl", children: [
|
|
2638
|
+
/* @__PURE__ */ jsxs17("fieldset", { className: "space-y-4", children: [
|
|
2639
|
+
/* @__PURE__ */ jsx19("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: t("sites.edit.site") }),
|
|
2640
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2641
|
+
/* @__PURE__ */ jsx19(Label4, { htmlFor: "name", children: t("common.name") }),
|
|
2642
|
+
/* @__PURE__ */ jsx19(
|
|
2215
2643
|
Input5,
|
|
2216
2644
|
{
|
|
2217
2645
|
id: "name",
|
|
@@ -2221,9 +2649,9 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2221
2649
|
}
|
|
2222
2650
|
)
|
|
2223
2651
|
] }),
|
|
2224
|
-
/* @__PURE__ */
|
|
2225
|
-
/* @__PURE__ */
|
|
2226
|
-
/* @__PURE__ */
|
|
2652
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2653
|
+
/* @__PURE__ */ jsx19(Label4, { htmlFor: "url", children: t("common.url") }),
|
|
2654
|
+
/* @__PURE__ */ jsx19(
|
|
2227
2655
|
Input5,
|
|
2228
2656
|
{
|
|
2229
2657
|
id: "url",
|
|
@@ -2233,9 +2661,9 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2233
2661
|
}
|
|
2234
2662
|
)
|
|
2235
2663
|
] }),
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
/* @__PURE__ */
|
|
2238
|
-
/* @__PURE__ */
|
|
2664
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2665
|
+
/* @__PURE__ */ jsx19(Label4, { htmlFor: "description", children: t("common.description") }),
|
|
2666
|
+
/* @__PURE__ */ jsx19(
|
|
2239
2667
|
Textarea2,
|
|
2240
2668
|
{
|
|
2241
2669
|
id: "description",
|
|
@@ -2247,11 +2675,11 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2247
2675
|
)
|
|
2248
2676
|
] })
|
|
2249
2677
|
] }),
|
|
2250
|
-
/* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2253
|
-
/* @__PURE__ */
|
|
2254
|
-
/* @__PURE__ */
|
|
2678
|
+
/* @__PURE__ */ jsxs17("fieldset", { className: "space-y-4", children: [
|
|
2679
|
+
/* @__PURE__ */ jsx19("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: t("sites.edit.media") }),
|
|
2680
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2681
|
+
/* @__PURE__ */ jsx19(Label4, { htmlFor: "imageDisplay", children: t("sites.edit.imageDisplay") }),
|
|
2682
|
+
/* @__PURE__ */ jsxs17(
|
|
2255
2683
|
"select",
|
|
2256
2684
|
{
|
|
2257
2685
|
id: "imageDisplay",
|
|
@@ -2259,18 +2687,18 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2259
2687
|
value: values["media.imageDisplay"] ?? "",
|
|
2260
2688
|
onChange: (e) => update("media.imageDisplay", e.target.value),
|
|
2261
2689
|
children: [
|
|
2262
|
-
/* @__PURE__ */
|
|
2690
|
+
/* @__PURE__ */ jsx19("option", { value: "", children: t("sites.edit.defaultPlaceholder", {
|
|
2263
2691
|
value: fallback["media.imageDisplay"] ?? "inline"
|
|
2264
2692
|
}) }),
|
|
2265
|
-
/* @__PURE__ */
|
|
2266
|
-
/* @__PURE__ */
|
|
2693
|
+
/* @__PURE__ */ jsx19("option", { value: "inline", children: t("sites.edit.imageDisplayInline") }),
|
|
2694
|
+
/* @__PURE__ */ jsx19("option", { value: "lightbox", children: t("sites.edit.imageDisplayLightbox") })
|
|
2267
2695
|
]
|
|
2268
2696
|
}
|
|
2269
2697
|
)
|
|
2270
2698
|
] }),
|
|
2271
|
-
/* @__PURE__ */
|
|
2272
|
-
/* @__PURE__ */
|
|
2273
|
-
/* @__PURE__ */
|
|
2699
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2700
|
+
/* @__PURE__ */ jsx19(Label4, { htmlFor: "imageMaxWidth", children: t("sites.edit.imageMaxWidth") }),
|
|
2701
|
+
/* @__PURE__ */ jsx19(
|
|
2274
2702
|
Input5,
|
|
2275
2703
|
{
|
|
2276
2704
|
id: "imageMaxWidth",
|
|
@@ -2281,11 +2709,11 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2281
2709
|
)
|
|
2282
2710
|
] })
|
|
2283
2711
|
] }),
|
|
2284
|
-
/* @__PURE__ */
|
|
2285
|
-
/* @__PURE__ */
|
|
2286
|
-
/* @__PURE__ */
|
|
2287
|
-
/* @__PURE__ */
|
|
2288
|
-
/* @__PURE__ */
|
|
2712
|
+
/* @__PURE__ */ jsxs17("fieldset", { className: "space-y-4", children: [
|
|
2713
|
+
/* @__PURE__ */ jsx19("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: t("sites.edit.dateDisplay") }),
|
|
2714
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2715
|
+
/* @__PURE__ */ jsx19(Label4, { htmlFor: "dateFormat", children: t("sites.edit.dateFormat") }),
|
|
2716
|
+
/* @__PURE__ */ jsxs17(
|
|
2289
2717
|
"select",
|
|
2290
2718
|
{
|
|
2291
2719
|
id: "dateFormat",
|
|
@@ -2293,19 +2721,19 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2293
2721
|
value: values["dateFormat"] ?? "",
|
|
2294
2722
|
onChange: (e) => update("dateFormat", e.target.value),
|
|
2295
2723
|
children: [
|
|
2296
|
-
/* @__PURE__ */
|
|
2724
|
+
/* @__PURE__ */ jsx19("option", { value: "", children: t("sites.edit.defaultPlaceholder", {
|
|
2297
2725
|
value: fallback["dateFormat"] ?? "iso"
|
|
2298
2726
|
}) }),
|
|
2299
|
-
/* @__PURE__ */
|
|
2300
|
-
/* @__PURE__ */
|
|
2301
|
-
/* @__PURE__ */
|
|
2727
|
+
/* @__PURE__ */ jsx19("option", { value: "iso", children: t("sites.edit.dateFormatIso") }),
|
|
2728
|
+
/* @__PURE__ */ jsx19("option", { value: "long", children: t("sites.edit.dateFormatLong") }),
|
|
2729
|
+
/* @__PURE__ */ jsx19("option", { value: "locale", children: t("sites.edit.dateFormatLocale") })
|
|
2302
2730
|
]
|
|
2303
2731
|
}
|
|
2304
2732
|
)
|
|
2305
2733
|
] }),
|
|
2306
|
-
/* @__PURE__ */
|
|
2307
|
-
/* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
2734
|
+
/* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
|
|
2735
|
+
/* @__PURE__ */ jsx19(Label4, { htmlFor: "timezone", children: t("sites.edit.timezone") }),
|
|
2736
|
+
/* @__PURE__ */ jsx19(
|
|
2309
2737
|
Input5,
|
|
2310
2738
|
{
|
|
2311
2739
|
id: "timezone",
|
|
@@ -2316,14 +2744,14 @@ function SiteSettingsForm({ siteId, initial, fallback }) {
|
|
|
2316
2744
|
)
|
|
2317
2745
|
] })
|
|
2318
2746
|
] }),
|
|
2319
|
-
info && /* @__PURE__ */
|
|
2320
|
-
error && /* @__PURE__ */
|
|
2321
|
-
/* @__PURE__ */
|
|
2747
|
+
info && /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: info }),
|
|
2748
|
+
error && /* @__PURE__ */ jsx19("p", { className: "text-sm text-destructive", children: error }),
|
|
2749
|
+
/* @__PURE__ */ jsx19(Button12, { type: "submit", disabled: saving, children: saving ? t("common.saving") : t("sites.edit.saveButton") })
|
|
2322
2750
|
] });
|
|
2323
2751
|
}
|
|
2324
2752
|
|
|
2325
2753
|
// src/components/theme-settings-form.tsx
|
|
2326
|
-
import { useState as
|
|
2754
|
+
import { useState as useState12 } from "react";
|
|
2327
2755
|
import { useRouter as useRouter5 } from "next/navigation";
|
|
2328
2756
|
import {
|
|
2329
2757
|
setSiteSetting as setSiteSetting2,
|
|
@@ -2334,8 +2762,8 @@ import {
|
|
|
2334
2762
|
parseLinkList,
|
|
2335
2763
|
stringifyLinkList
|
|
2336
2764
|
} from "ampless";
|
|
2337
|
-
import { Button as
|
|
2338
|
-
import { jsx as
|
|
2765
|
+
import { Button as Button13, Input as Input6, Label as Label5 } from "@ampless/runtime/ui";
|
|
2766
|
+
import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2339
2767
|
var CACHE_REBUILD_DELAY_MS = 8e3;
|
|
2340
2768
|
function ThemeSettingsForm({
|
|
2341
2769
|
siteId,
|
|
@@ -2347,14 +2775,14 @@ function ThemeSettingsForm({
|
|
|
2347
2775
|
const router = useRouter5();
|
|
2348
2776
|
const t = useT();
|
|
2349
2777
|
const locale = useLocale();
|
|
2350
|
-
const [state, setState] =
|
|
2351
|
-
const [pendingTheme, setPendingTheme] =
|
|
2352
|
-
const [optimisticActive, setOptimisticActive] =
|
|
2353
|
-
const [saving, setSaving] =
|
|
2354
|
-
const [switching, setSwitching] =
|
|
2355
|
-
const [error, setError] =
|
|
2356
|
-
const [info, setInfo] =
|
|
2357
|
-
const [invalid, setInvalid] =
|
|
2778
|
+
const [state, setState] = useState12({ values: initial, touched: {} });
|
|
2779
|
+
const [pendingTheme, setPendingTheme] = useState12(activeTheme);
|
|
2780
|
+
const [optimisticActive, setOptimisticActive] = useState12(activeTheme);
|
|
2781
|
+
const [saving, setSaving] = useState12(false);
|
|
2782
|
+
const [switching, setSwitching] = useState12(false);
|
|
2783
|
+
const [error, setError] = useState12(null);
|
|
2784
|
+
const [info, setInfo] = useState12(null);
|
|
2785
|
+
const [invalid, setInvalid] = useState12({});
|
|
2358
2786
|
function update(key, value) {
|
|
2359
2787
|
setState((prev) => ({
|
|
2360
2788
|
values: { ...prev.values, [key]: value },
|
|
@@ -2440,23 +2868,23 @@ function ThemeSettingsForm({
|
|
|
2440
2868
|
}
|
|
2441
2869
|
}
|
|
2442
2870
|
const groups = groupFields(manifest.fields);
|
|
2443
|
-
return /* @__PURE__ */
|
|
2444
|
-
/* @__PURE__ */
|
|
2445
|
-
/* @__PURE__ */
|
|
2446
|
-
/* @__PURE__ */
|
|
2447
|
-
/* @__PURE__ */
|
|
2871
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-8", children: [
|
|
2872
|
+
/* @__PURE__ */ jsxs18("form", { onSubmit: switchTheme, className: "max-w-xl space-y-3 rounded-md border p-4", children: [
|
|
2873
|
+
/* @__PURE__ */ jsxs18("div", { className: "space-y-1", children: [
|
|
2874
|
+
/* @__PURE__ */ jsx20(Label5, { htmlFor: "active-theme", className: "text-sm font-medium", children: t("theme.activeLabel") }),
|
|
2875
|
+
/* @__PURE__ */ jsxs18("p", { className: "text-xs text-muted-foreground", children: [
|
|
2448
2876
|
t("theme.currentlyActive", { theme: optimisticActive }),
|
|
2449
2877
|
optimisticActive !== activeTheme && t("theme.propagating")
|
|
2450
2878
|
] })
|
|
2451
2879
|
] }),
|
|
2452
|
-
/* @__PURE__ */
|
|
2880
|
+
/* @__PURE__ */ jsx20(
|
|
2453
2881
|
"select",
|
|
2454
2882
|
{
|
|
2455
2883
|
id: "active-theme",
|
|
2456
2884
|
className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
|
|
2457
2885
|
value: pendingTheme,
|
|
2458
2886
|
onChange: (e) => setPendingTheme(e.target.value),
|
|
2459
|
-
children: themeOptions.map((opt) => /* @__PURE__ */
|
|
2887
|
+
children: themeOptions.map((opt) => /* @__PURE__ */ jsxs18("option", { value: opt.value, children: [
|
|
2460
2888
|
resolveLocalized(opt.label, locale),
|
|
2461
2889
|
" (",
|
|
2462
2890
|
opt.value,
|
|
@@ -2466,10 +2894,10 @@ function ThemeSettingsForm({
|
|
|
2466
2894
|
),
|
|
2467
2895
|
(() => {
|
|
2468
2896
|
const desc = themeOptions.find((o) => o.value === pendingTheme)?.description;
|
|
2469
|
-
return desc ? /* @__PURE__ */
|
|
2897
|
+
return desc ? /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: resolveLocalized(desc, locale) }) : null;
|
|
2470
2898
|
})(),
|
|
2471
|
-
/* @__PURE__ */
|
|
2472
|
-
|
|
2899
|
+
/* @__PURE__ */ jsx20(
|
|
2900
|
+
Button13,
|
|
2473
2901
|
{
|
|
2474
2902
|
type: "submit",
|
|
2475
2903
|
disabled: switching || pendingTheme === optimisticActive,
|
|
@@ -2478,9 +2906,9 @@ function ThemeSettingsForm({
|
|
|
2478
2906
|
}
|
|
2479
2907
|
)
|
|
2480
2908
|
] }),
|
|
2481
|
-
/* @__PURE__ */
|
|
2482
|
-
/* @__PURE__ */
|
|
2483
|
-
/* @__PURE__ */
|
|
2909
|
+
/* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2910
|
+
/* @__PURE__ */ jsx20(Label5, { className: "text-sm font-medium", children: t("theme.previewLabel") }),
|
|
2911
|
+
/* @__PURE__ */ jsx20(
|
|
2484
2912
|
"iframe",
|
|
2485
2913
|
{
|
|
2486
2914
|
src: `/?previewTheme=${encodeURIComponent(pendingTheme)}`,
|
|
@@ -2489,19 +2917,19 @@ function ThemeSettingsForm({
|
|
|
2489
2917
|
},
|
|
2490
2918
|
pendingTheme
|
|
2491
2919
|
),
|
|
2492
|
-
/* @__PURE__ */
|
|
2920
|
+
/* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: t("theme.previewHint") })
|
|
2493
2921
|
] }),
|
|
2494
|
-
/* @__PURE__ */
|
|
2495
|
-
/* @__PURE__ */
|
|
2496
|
-
/* @__PURE__ */
|
|
2922
|
+
/* @__PURE__ */ jsxs18("form", { onSubmit: save, className: "max-w-xl space-y-6", children: [
|
|
2923
|
+
/* @__PURE__ */ jsxs18("div", { children: [
|
|
2924
|
+
/* @__PURE__ */ jsx20("h2", { className: "text-lg font-semibold", children: t("theme.customizationHeading", {
|
|
2497
2925
|
theme: resolveLocalized(manifest.label, locale)
|
|
2498
2926
|
}) }),
|
|
2499
|
-
manifest.description && /* @__PURE__ */
|
|
2500
|
-
/* @__PURE__ */
|
|
2927
|
+
manifest.description && /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: resolveLocalized(manifest.description, locale) }),
|
|
2928
|
+
/* @__PURE__ */ jsx20("p", { className: "mt-1 text-xs text-muted-foreground", children: t("theme.customizationHint") })
|
|
2501
2929
|
] }),
|
|
2502
|
-
groups.map(({ key, name, fields }) => /* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
2504
|
-
fields.map((field) => /* @__PURE__ */
|
|
2930
|
+
groups.map(({ key, name, fields }) => /* @__PURE__ */ jsxs18("fieldset", { className: "space-y-4", children: [
|
|
2931
|
+
/* @__PURE__ */ jsx20("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: resolveLocalized(name, locale) }),
|
|
2932
|
+
fields.map((field) => /* @__PURE__ */ jsx20(
|
|
2505
2933
|
FieldRow,
|
|
2506
2934
|
{
|
|
2507
2935
|
field,
|
|
@@ -2512,9 +2940,9 @@ function ThemeSettingsForm({
|
|
|
2512
2940
|
field.key
|
|
2513
2941
|
))
|
|
2514
2942
|
] }, key)),
|
|
2515
|
-
info && /* @__PURE__ */
|
|
2516
|
-
error && /* @__PURE__ */
|
|
2517
|
-
/* @__PURE__ */
|
|
2943
|
+
info && /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: info }),
|
|
2944
|
+
error && /* @__PURE__ */ jsx20("p", { className: "text-sm text-destructive", children: error }),
|
|
2945
|
+
/* @__PURE__ */ jsx20(Button13, { type: "submit", disabled: saving, children: saving ? t("theme.saving") : t("theme.saveButton") })
|
|
2518
2946
|
] })
|
|
2519
2947
|
] });
|
|
2520
2948
|
}
|
|
@@ -2536,16 +2964,16 @@ function FieldRow({ field, value, invalid, onChange }) {
|
|
|
2536
2964
|
const t = useT();
|
|
2537
2965
|
const locale = useLocale();
|
|
2538
2966
|
const id = `theme-${field.key}`;
|
|
2539
|
-
const labelEl = /* @__PURE__ */
|
|
2540
|
-
const description = field.description ? /* @__PURE__ */
|
|
2967
|
+
const labelEl = /* @__PURE__ */ jsx20(Label5, { htmlFor: id, className: invalid ? "text-destructive" : void 0, children: resolveLocalized(field.label, locale) });
|
|
2968
|
+
const description = field.description ? /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: resolveLocalized(field.description, locale) }) : null;
|
|
2541
2969
|
switch (field.type) {
|
|
2542
2970
|
case "color":
|
|
2543
|
-
return /* @__PURE__ */
|
|
2971
|
+
return /* @__PURE__ */ jsx20(ColorField, { field, id, labelEl, description, value, invalid, onChange });
|
|
2544
2972
|
case "length":
|
|
2545
|
-
return /* @__PURE__ */
|
|
2973
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2546
2974
|
labelEl,
|
|
2547
2975
|
description,
|
|
2548
|
-
/* @__PURE__ */
|
|
2976
|
+
/* @__PURE__ */ jsx20(
|
|
2549
2977
|
Input6,
|
|
2550
2978
|
{
|
|
2551
2979
|
id,
|
|
@@ -2556,14 +2984,14 @@ function FieldRow({ field, value, invalid, onChange }) {
|
|
|
2556
2984
|
className: "font-mono text-xs"
|
|
2557
2985
|
}
|
|
2558
2986
|
),
|
|
2559
|
-
/* @__PURE__ */
|
|
2987
|
+
/* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: t("theme.lengthHelp") })
|
|
2560
2988
|
] });
|
|
2561
2989
|
case "select":
|
|
2562
2990
|
case "fontFamily":
|
|
2563
|
-
return /* @__PURE__ */
|
|
2991
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2564
2992
|
labelEl,
|
|
2565
2993
|
description,
|
|
2566
|
-
/* @__PURE__ */
|
|
2994
|
+
/* @__PURE__ */ jsxs18(
|
|
2567
2995
|
"select",
|
|
2568
2996
|
{
|
|
2569
2997
|
id,
|
|
@@ -2572,17 +3000,17 @@ function FieldRow({ field, value, invalid, onChange }) {
|
|
|
2572
3000
|
onChange: (e) => onChange(e.target.value),
|
|
2573
3001
|
"aria-invalid": invalid,
|
|
2574
3002
|
children: [
|
|
2575
|
-
/* @__PURE__ */
|
|
2576
|
-
field.options.map((opt) => /* @__PURE__ */
|
|
3003
|
+
/* @__PURE__ */ jsx20("option", { value: "", children: t("common.default") }),
|
|
3004
|
+
field.options.map((opt) => /* @__PURE__ */ jsx20("option", { value: opt.value, children: resolveLocalized(opt.label, locale) }, opt.value))
|
|
2577
3005
|
]
|
|
2578
3006
|
}
|
|
2579
3007
|
)
|
|
2580
3008
|
] });
|
|
2581
3009
|
case "image":
|
|
2582
|
-
return /* @__PURE__ */
|
|
3010
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2583
3011
|
labelEl,
|
|
2584
3012
|
description,
|
|
2585
|
-
/* @__PURE__ */
|
|
3013
|
+
/* @__PURE__ */ jsx20(
|
|
2586
3014
|
Input6,
|
|
2587
3015
|
{
|
|
2588
3016
|
id,
|
|
@@ -2594,10 +3022,10 @@ function FieldRow({ field, value, invalid, onChange }) {
|
|
|
2594
3022
|
)
|
|
2595
3023
|
] });
|
|
2596
3024
|
case "text":
|
|
2597
|
-
return /* @__PURE__ */
|
|
3025
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2598
3026
|
labelEl,
|
|
2599
3027
|
description,
|
|
2600
|
-
/* @__PURE__ */
|
|
3028
|
+
/* @__PURE__ */ jsx20(
|
|
2601
3029
|
Input6,
|
|
2602
3030
|
{
|
|
2603
3031
|
id,
|
|
@@ -2610,7 +3038,7 @@ function FieldRow({ field, value, invalid, onChange }) {
|
|
|
2610
3038
|
)
|
|
2611
3039
|
] });
|
|
2612
3040
|
case "linkList":
|
|
2613
|
-
return /* @__PURE__ */
|
|
3041
|
+
return /* @__PURE__ */ jsx20(
|
|
2614
3042
|
LinkListField,
|
|
2615
3043
|
{
|
|
2616
3044
|
field,
|
|
@@ -2635,7 +3063,7 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2635
3063
|
if (items.length >= max) return;
|
|
2636
3064
|
commit([...items, { label: "", url: "" }]);
|
|
2637
3065
|
}
|
|
2638
|
-
function
|
|
3066
|
+
function remove3(idx) {
|
|
2639
3067
|
commit(items.filter((_, i) => i !== idx));
|
|
2640
3068
|
}
|
|
2641
3069
|
function move(idx, delta) {
|
|
@@ -2646,16 +3074,16 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2646
3074
|
next.splice(target, 0, moved);
|
|
2647
3075
|
commit(next);
|
|
2648
3076
|
}
|
|
2649
|
-
return /* @__PURE__ */
|
|
3077
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2650
3078
|
labelEl,
|
|
2651
3079
|
description,
|
|
2652
|
-
/* @__PURE__ */
|
|
2653
|
-
items.length === 0 && /* @__PURE__ */
|
|
3080
|
+
/* @__PURE__ */ jsxs18("div", { className: "space-y-2 rounded-md border bg-muted/20 p-3", children: [
|
|
3081
|
+
items.length === 0 && /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: "No links yet." }),
|
|
2654
3082
|
items.map((item, idx) => {
|
|
2655
3083
|
const isTagRef = /^tag:/.test(item.url.trim());
|
|
2656
|
-
return /* @__PURE__ */
|
|
2657
|
-
/* @__PURE__ */
|
|
2658
|
-
/* @__PURE__ */
|
|
3084
|
+
return /* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap items-start gap-2", children: [
|
|
3085
|
+
/* @__PURE__ */ jsxs18("div", { className: "grid flex-1 grid-cols-1 gap-2 sm:grid-cols-2", children: [
|
|
3086
|
+
/* @__PURE__ */ jsx20(
|
|
2659
3087
|
Input6,
|
|
2660
3088
|
{
|
|
2661
3089
|
value: item.label,
|
|
@@ -2663,7 +3091,7 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2663
3091
|
onChange: (e) => update(idx, { label: e.target.value })
|
|
2664
3092
|
}
|
|
2665
3093
|
),
|
|
2666
|
-
/* @__PURE__ */
|
|
3094
|
+
/* @__PURE__ */ jsx20(
|
|
2667
3095
|
Input6,
|
|
2668
3096
|
{
|
|
2669
3097
|
value: item.url,
|
|
@@ -2673,9 +3101,9 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2673
3101
|
}
|
|
2674
3102
|
)
|
|
2675
3103
|
] }),
|
|
2676
|
-
/* @__PURE__ */
|
|
2677
|
-
/* @__PURE__ */
|
|
2678
|
-
|
|
3104
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
3105
|
+
/* @__PURE__ */ jsx20(
|
|
3106
|
+
Button13,
|
|
2679
3107
|
{
|
|
2680
3108
|
type: "button",
|
|
2681
3109
|
variant: "ghost",
|
|
@@ -2686,8 +3114,8 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2686
3114
|
children: "\u2191"
|
|
2687
3115
|
}
|
|
2688
3116
|
),
|
|
2689
|
-
/* @__PURE__ */
|
|
2690
|
-
|
|
3117
|
+
/* @__PURE__ */ jsx20(
|
|
3118
|
+
Button13,
|
|
2691
3119
|
{
|
|
2692
3120
|
type: "button",
|
|
2693
3121
|
variant: "ghost",
|
|
@@ -2698,13 +3126,13 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2698
3126
|
children: "\u2193"
|
|
2699
3127
|
}
|
|
2700
3128
|
),
|
|
2701
|
-
/* @__PURE__ */
|
|
2702
|
-
|
|
3129
|
+
/* @__PURE__ */ jsx20(
|
|
3130
|
+
Button13,
|
|
2703
3131
|
{
|
|
2704
3132
|
type: "button",
|
|
2705
3133
|
variant: "ghost",
|
|
2706
3134
|
size: "icon",
|
|
2707
|
-
onClick: () =>
|
|
3135
|
+
onClick: () => remove3(idx),
|
|
2708
3136
|
"aria-label": "Remove",
|
|
2709
3137
|
children: "\xD7"
|
|
2710
3138
|
}
|
|
@@ -2712,8 +3140,8 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2712
3140
|
] })
|
|
2713
3141
|
] }, idx);
|
|
2714
3142
|
}),
|
|
2715
|
-
/* @__PURE__ */
|
|
2716
|
-
|
|
3143
|
+
/* @__PURE__ */ jsx20(
|
|
3144
|
+
Button13,
|
|
2717
3145
|
{
|
|
2718
3146
|
type: "button",
|
|
2719
3147
|
variant: "outline",
|
|
@@ -2723,9 +3151,9 @@ function LinkListField({ field, labelEl, description, value, onChange }) {
|
|
|
2723
3151
|
children: "+ Add link"
|
|
2724
3152
|
}
|
|
2725
3153
|
),
|
|
2726
|
-
/* @__PURE__ */
|
|
3154
|
+
/* @__PURE__ */ jsxs18("p", { className: "text-xs text-muted-foreground", children: [
|
|
2727
3155
|
"Tip: use ",
|
|
2728
|
-
/* @__PURE__ */
|
|
3156
|
+
/* @__PURE__ */ jsx20("code", { children: "tag:<name>" }),
|
|
2729
3157
|
" as a URL to render a list of posts with that tag instead of a single link."
|
|
2730
3158
|
] })
|
|
2731
3159
|
] })
|
|
@@ -2742,11 +3170,11 @@ function ColorField({
|
|
|
2742
3170
|
}) {
|
|
2743
3171
|
const effective = value || field.default;
|
|
2744
3172
|
const hex = useColorAsHex(effective);
|
|
2745
|
-
return /* @__PURE__ */
|
|
3173
|
+
return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
|
|
2746
3174
|
labelEl,
|
|
2747
3175
|
description,
|
|
2748
|
-
/* @__PURE__ */
|
|
2749
|
-
/* @__PURE__ */
|
|
3176
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
|
|
3177
|
+
/* @__PURE__ */ jsx20(
|
|
2750
3178
|
"input",
|
|
2751
3179
|
{
|
|
2752
3180
|
type: "color",
|
|
@@ -2756,7 +3184,7 @@ function ColorField({
|
|
|
2756
3184
|
"aria-label": `${field.label} swatch`
|
|
2757
3185
|
}
|
|
2758
3186
|
),
|
|
2759
|
-
/* @__PURE__ */
|
|
3187
|
+
/* @__PURE__ */ jsx20(
|
|
2760
3188
|
Input6,
|
|
2761
3189
|
{
|
|
2762
3190
|
id,
|
|
@@ -2768,8 +3196,8 @@ function ColorField({
|
|
|
2768
3196
|
}
|
|
2769
3197
|
)
|
|
2770
3198
|
] }),
|
|
2771
|
-
/* @__PURE__ */
|
|
2772
|
-
/* @__PURE__ */
|
|
3199
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
|
|
3200
|
+
/* @__PURE__ */ jsx20(
|
|
2773
3201
|
"span",
|
|
2774
3202
|
{
|
|
2775
3203
|
className: "inline-block h-4 w-4 rounded border",
|
|
@@ -2777,7 +3205,7 @@ function ColorField({
|
|
|
2777
3205
|
"aria-hidden": true
|
|
2778
3206
|
}
|
|
2779
3207
|
),
|
|
2780
|
-
/* @__PURE__ */
|
|
3208
|
+
/* @__PURE__ */ jsx20("code", { className: "text-xs text-muted-foreground", children: effective })
|
|
2781
3209
|
] })
|
|
2782
3210
|
] });
|
|
2783
3211
|
}
|