@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.
@@ -4,7 +4,7 @@ import {
4
4
  publicMediaUrl,
5
5
  setAdminMediaContext,
6
6
  translate
7
- } from "./chunk-L5NHN3MY.js";
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 useState5 } from "react";
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 Button7, Input as Input2, Label as Label2, Textarea } from "@ampless/runtime/ui";
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 remove2 = () => {
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: remove2, children: [
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/post-form.tsx
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] = useState5(post?.title ?? "");
1181
- const [slug, setSlug] = useState5(post?.slug ?? "");
1182
- const [excerpt, setExcerpt] = useState5(post?.excerpt ?? "");
1183
- const [format, setFormat] = useState5(post?.format ?? "tiptap");
1184
- const [body, setBody] = useState5(post?.body ?? EMPTY_TIPTAP_DOC);
1185
- const [status, setStatus] = useState5(post?.status ?? "draft");
1186
- const [tagsInput, setTagsInput] = useState5((post?.tags ?? []).join(", "));
1187
- const [noLayout, setNoLayout] = useState5(post?.metadata?.no_layout === true);
1188
- const [saving, setSaving] = useState5(false);
1189
- const [error, setError] = useState5(null);
1190
- const [view, setView] = useState5("edit");
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
- const k = `${format}\u2192${next}`;
1229
- switch (k) {
1230
- case "tiptap\u2192html":
1231
- nextBody = tiptapToHtml(body);
1232
- break;
1233
- case "tiptap\u2192markdown":
1234
- nextBody = tiptapToMarkdown(body);
1235
- break;
1236
- case "html\u2192tiptap":
1237
- nextBody = String(body ?? "");
1238
- break;
1239
- case "markdown\u2192tiptap":
1240
- nextBody = markdownToHtml(String(body ?? ""));
1241
- break;
1242
- case "html\u2192markdown":
1243
- nextBody = htmlToMarkdown(String(body ?? ""));
1244
- break;
1245
- case "markdown\u2192html":
1246
- nextBody = markdownToHtml(String(body ?? ""));
1247
- break;
1248
- default:
1249
- nextBody = defaultBodyForFormat(next);
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: slug || slugify(title),
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: readAdminSiteIdFromCookie(),
1281
- slug: slug || slugify(title),
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__ */ jsxs8("form", { onSubmit: save, className: "space-y-6", children: [
1326
- /* @__PURE__ */ jsxs8("div", { className: "flex gap-1 border-b", children: [
1327
- /* @__PURE__ */ jsx10(
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__ */ jsx10(
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__ */ jsxs8("article", { className: "space-y-4", children: [
1349
- /* @__PURE__ */ jsxs8("header", { className: "border-b pb-4", children: [
1350
- /* @__PURE__ */ jsx10("h1", { className: "text-3xl font-bold tracking-tight", children: title || /* @__PURE__ */ jsx10("span", { className: "text-muted-foreground italic", children: t("posts.form.previewNoTitle") }) }),
1351
- /* @__PURE__ */ jsxs8("p", { className: "mt-2 text-sm text-muted-foreground", children: [
1352
- previewPost.publishedAt ? /* @__PURE__ */ jsx10("time", { dateTime: previewPost.publishedAt, children: formatDate(previewPost.publishedAt) }) : /* @__PURE__ */ jsx10("span", { children: t("common.draft") }),
1353
- /* @__PURE__ */ jsx10("span", { className: "mx-2", children: "\xB7" }),
1354
- /* @__PURE__ */ jsx10("span", { className: "font-mono text-xs uppercase", children: format })
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__ */ jsx10("p", { className: "mt-3 text-base text-muted-foreground", children: excerpt })
1776
+ excerpt && /* @__PURE__ */ jsx11("p", { className: "mt-3 text-base text-muted-foreground", children: excerpt })
1357
1777
  ] }),
1358
- /* @__PURE__ */ jsx10(
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__ */ jsx10("div", { className: "flex flex-wrap gap-2 border-t pt-4 text-sm", children: previewPost.tags.map((tag) => /* @__PURE__ */ jsxs8(
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__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t("posts.form.previewHint") })
1796
+ /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: t("posts.form.previewHint") })
1377
1797
  ] }),
1378
- /* @__PURE__ */ jsxs8("div", { className: view === "edit" ? "space-y-6" : "hidden", children: [
1379
- /* @__PURE__ */ jsxs8("div", { className: "space-y-2", children: [
1380
- /* @__PURE__ */ jsx10(Label2, { htmlFor: "title", children: t("posts.form.title") }),
1381
- /* @__PURE__ */ jsx10(
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__ */ jsxs8("div", { className: "space-y-2", children: [
1395
- /* @__PURE__ */ jsx10(Label2, { htmlFor: "slug", children: t("posts.form.slug") }),
1396
- /* @__PURE__ */ jsx10(
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__ */ jsxs8("div", { className: "space-y-2", children: [
1407
- /* @__PURE__ */ jsx10(Label2, { htmlFor: "excerpt", children: t("posts.form.excerpt") }),
1408
- /* @__PURE__ */ jsx10(
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__ */ jsxs8("div", { className: "space-y-2", children: [
1419
- /* @__PURE__ */ jsx10(Label2, { htmlFor: "format", children: t("posts.form.format") }),
1420
- /* @__PURE__ */ jsxs8(
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__ */ jsx10("option", { value: "tiptap", children: "Tiptap (rich editor)" }),
1429
- /* @__PURE__ */ jsx10("option", { value: "markdown", children: "Markdown" }),
1430
- /* @__PURE__ */ jsx10("option", { value: "html", children: "HTML" })
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__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t("posts.form.formatHint") })
1855
+ /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: t("posts.form.formatHint") })
1435
1856
  ] }),
1436
- /* @__PURE__ */ jsxs8("div", { className: "space-y-2", children: [
1437
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between", children: [
1438
- /* @__PURE__ */ jsx10(Label2, { children: t("posts.form.body") }),
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__ */ jsx10(
1864
+ /* @__PURE__ */ jsx11(
1444
1865
  MediaPicker,
1445
1866
  {
1446
1867
  onSelect: insertMediaSnippet,
1447
- trigger: /* @__PURE__ */ jsxs8(Button7, { type: "button", variant: "outline", size: "sm", children: [
1448
- /* @__PURE__ */ jsx10(ImageIcon3, { className: "mr-2 h-3 w-3" }),
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__ */ jsx10(TiptapEditor, { initialContent: body, onChange: setBody }) : /* @__PURE__ */ jsx10(
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__ */ jsxs8("div", { className: "space-y-2", children: [
1466
- /* @__PURE__ */ jsx10(Label2, { htmlFor: "tags", children: t("posts.form.tags") }),
1467
- /* @__PURE__ */ jsx10(
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__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t("posts.form.tagsHint") })
1904
+ /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: t("posts.form.tagsHint") })
1477
1905
  ] }),
1478
- /* @__PURE__ */ jsxs8("div", { className: "space-y-2", children: [
1479
- /* @__PURE__ */ jsx10(Label2, { htmlFor: "status", children: t("posts.form.status") }),
1480
- /* @__PURE__ */ jsxs8(
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__ */ jsx10("option", { value: "draft", children: t("common.draft") }),
1489
- /* @__PURE__ */ jsx10("option", { value: "published", children: t("common.published") })
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__ */ jsx10("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs8("label", { className: "flex items-start gap-2 text-sm", children: [
1495
- /* @__PURE__ */ jsx10(
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__ */ jsxs8("span", { children: [
1505
- /* @__PURE__ */ jsx10("span", { className: "font-medium", children: t("posts.form.noLayout") }),
1506
- /* @__PURE__ */ jsx10("span", { className: "block text-xs text-muted-foreground", children: t("posts.form.noLayoutHint") })
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__ */ jsx10("p", { className: "text-sm text-destructive", children: error }),
1510
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
1511
- /* @__PURE__ */ jsx10(Button7, { type: "submit", disabled: saving, children: saving ? t("common.saving") : isEdit ? t("posts.form.saveChanges") : t("posts.form.createPost") }),
1512
- isEdit && /* @__PURE__ */ jsx10(Button7, { type: "button", variant: "destructive", onClick: handleDelete, disabled: saving, children: t("posts.form.delete") })
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 jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1947
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1520
1948
  function NewPostPage() {
1521
1949
  const t = useT();
1522
- return /* @__PURE__ */ jsxs9("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
1523
- /* @__PURE__ */ jsx11("h1", { className: "mb-6 text-2xl font-bold md:mb-8 md:text-3xl", children: t("posts.form.newTitle") }),
1524
- /* @__PURE__ */ jsx11(PostForm, {})
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 useState6, use } from "react";
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 jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
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] = useState6(null);
1537
- const [loading, setLoading] = useState6(true);
1538
- const [missing, setMissing] = useState6(false);
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__ */ jsx12("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: t("common.loading") });
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__ */ jsxs10("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
1550
- /* @__PURE__ */ jsx12("h1", { className: "mb-6 text-2xl font-bold md:mb-8 md:text-3xl", children: t("posts.form.editTitle") }),
1551
- post && /* @__PURE__ */ jsx12(PostForm, { post })
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 useState7, useEffect as useEffect6, useCallback, useRef as useRef4 } from "react";
1557
- import { uploadData as uploadData2, list as list2, remove, isCancelError } from "aws-amplify/storage";
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 Button8, Input as Input3 } from "@ampless/runtime/ui";
1560
- import { Trash2 as Trash22, Copy, Check, FileText, Code2 } from "lucide-react";
1561
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
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] = useState7([]);
1587
- const [queue, setQueue] = useState7([]);
1588
- const [uploading, setUploading] = useState7(false);
1589
- const [error, setError] = useState7(null);
1590
- const [copiedPath, setCopiedPath] = useState7(null);
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 list2({ path: "public/media/" });
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 = uploadData2({
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 remove({ path });
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__ */ jsxs11("div", { className: "space-y-6", children: [
1682
- /* @__PURE__ */ jsxs11("div", { className: "rounded-md border p-4", children: [
1683
- /* @__PURE__ */ jsx13(
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__ */ jsx13("p", { className: "mt-2 text-sm text-muted-foreground", children: t("media.uploading") }),
1693
- !uploading && queue.length > 0 && /* @__PURE__ */ jsx13("p", { className: "mt-2 text-sm text-muted-foreground", children: t("media.queued", { count: queue.length }) })
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__ */ jsx13("p", { className: "text-sm text-destructive", children: error }),
1696
- /* @__PURE__ */ jsx13(
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__ */ jsx13("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4", children: items.map((item) => {
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__ */ jsxs11(
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__ */ jsx13(
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__ */ jsxs11("div", { className: "flex aspect-square w-full flex-col items-center justify-center gap-2 bg-muted text-muted-foreground", children: [
1734
- isStylesheet || isScript ? /* @__PURE__ */ jsx13(Code2, { className: "h-8 w-8" }) : /* @__PURE__ */ jsx13(FileText, { className: "h-8 w-8" }),
1735
- /* @__PURE__ */ jsxs11("span", { className: "font-mono text-xs font-semibold", children: [
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__ */ jsxs11("div", { className: "flex items-center justify-between border-t px-2 py-1 text-xs", children: [
1741
- /* @__PURE__ */ jsx13("span", { className: "truncate", title: filename, children: filename }),
1742
- /* @__PURE__ */ jsxs11("div", { className: "flex shrink-0 items-center gap-0.5", children: [
1743
- /* @__PURE__ */ jsx13(
1744
- Button8,
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__ */ jsx13(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx13(Copy, { className: "h-3 w-3" })
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__ */ jsx13(
1756
- Button8,
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__ */ jsx13(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx13(Code2, { className: "h-3 w-3" })
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__ */ jsx13(
1768
- Button8,
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__ */ jsx13(Trash22, { className: "h-3 w-3" })
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__ */ jsx13("p", { className: "text-center text-sm text-muted-foreground", children: t("media.empty") })
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 jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2219
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1792
2220
  function MediaPage() {
1793
2221
  const t = useT();
1794
- return /* @__PURE__ */ jsxs12("div", { className: "mx-auto max-w-7xl p-4 md:p-8", children: [
1795
- /* @__PURE__ */ jsx14("h1", { className: "mb-6 text-2xl font-bold md:mb-8 md:text-3xl", children: t("media.title") }),
1796
- /* @__PURE__ */ jsx14(MediaUploader, {})
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 useState8 } from "react";
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 Button9,
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 jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
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] = useState8("signIn");
1825
- const [email, setEmail] = useState8("");
1826
- const [password, setPassword] = useState8("");
1827
- const [code, setCode] = useState8("");
1828
- const [error, setError] = useState8(null);
1829
- const [info, setInfo] = useState8(null);
1830
- const [loading, setLoading] = useState8(false);
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__ */ jsx15("main", { className: "flex min-h-screen items-center justify-center bg-muted/30 p-4", children: /* @__PURE__ */ jsxs13(Card2, { className: "w-full max-w-md", children: [
1895
- /* @__PURE__ */ jsxs13(CardHeader2, { children: [
1896
- /* @__PURE__ */ jsx15(CardTitle2, { children: t(`auth.${mode}.title`) }),
1897
- /* @__PURE__ */ jsx15(CardDescription, { children: t(`auth.${mode}.description`) })
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__ */ jsx15(CardContent2, { children: /* @__PURE__ */ jsxs13("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
1900
- showEmail && /* @__PURE__ */ jsxs13("div", { className: "space-y-2", children: [
1901
- /* @__PURE__ */ jsx15(Label3, { htmlFor: "email", children: t("auth.common.email") }),
1902
- /* @__PURE__ */ jsx15(
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__ */ jsxs13("div", { className: "space-y-2", children: [
1915
- /* @__PURE__ */ jsx15(Label3, { htmlFor: "code", children: t("auth.common.code") }),
1916
- /* @__PURE__ */ jsx15(
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__ */ jsxs13("div", { className: "space-y-2", children: [
1928
- /* @__PURE__ */ jsx15(Label3, { htmlFor: "password", children: mode === "reset" ? t("auth.common.newPassword") : t("auth.common.password") }),
1929
- /* @__PURE__ */ jsx15(
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__ */ jsx15("p", { className: "text-xs text-muted-foreground", children: t("auth.common.passwordHint") })
2369
+ (mode === "signUp" || mode === "reset") && /* @__PURE__ */ jsx16("p", { className: "text-xs text-muted-foreground", children: t("auth.common.passwordHint") })
1942
2370
  ] }),
1943
- info && /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: info }),
1944
- error && /* @__PURE__ */ jsx15("p", { className: "text-sm text-destructive", children: error }),
1945
- /* @__PURE__ */ jsx15(Button9, { type: "submit", className: "w-full", disabled: loading, children: loading ? t("auth.common.working") : t(`auth.${mode}.submit`) }),
1946
- /* @__PURE__ */ jsxs13("div", { className: "space-y-1 text-center text-sm", children: [
1947
- mode === "signIn" && /* @__PURE__ */ jsxs13(Fragment4, { children: [
1948
- /* @__PURE__ */ jsx15("p", { children: /* @__PURE__ */ jsx15(
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__ */ jsx15("p", { children: /* @__PURE__ */ jsx15(
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__ */ jsx15("p", { children: /* @__PURE__ */ jsx15(
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__ */ jsx15("p", { children: /* @__PURE__ */ jsx15(
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 useState9 } from "react";
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 FileText2,
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 Button10, cn as cn3 } from "@ampless/runtime/ui";
2007
- import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
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: FileText2 },
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] = useState9(false);
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__ */ jsxs14(Fragment5, { children: [
2035
- /* @__PURE__ */ jsxs14("header", { className: "sticky top-0 z-30 flex h-14 items-center justify-between border-b bg-background px-4 md:hidden", children: [
2036
- /* @__PURE__ */ jsx16(Link4, { href: "/admin", className: "font-semibold", children: t("sidebar.brand") }),
2037
- /* @__PURE__ */ jsx16(
2038
- Button10,
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__ */ jsx16(Menu, { className: "h-5 w-5" })
2473
+ children: /* @__PURE__ */ jsx17(Menu, { className: "h-5 w-5" })
2046
2474
  }
2047
2475
  )
2048
2476
  ] }),
2049
- open && /* @__PURE__ */ jsx16(
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__ */ jsxs14(
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__ */ jsxs14("div", { className: "flex items-center justify-between border-b p-4", children: [
2067
- /* @__PURE__ */ jsx16(Link4, { href: "/admin", className: "font-semibold", children: t("sidebar.brand") }),
2068
- /* @__PURE__ */ jsx16(
2069
- Button10,
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__ */ jsx16(X, { className: "h-5 w-5" })
2504
+ children: /* @__PURE__ */ jsx17(X2, { className: "h-5 w-5" })
2077
2505
  }
2078
2506
  )
2079
2507
  ] }),
2080
- siteSelector ? /* @__PURE__ */ jsx16("div", { className: "border-b", children: siteSelector }) : null,
2081
- /* @__PURE__ */ jsx16("nav", { className: "flex-1 space-y-1 overflow-y-auto p-2", children: navItems.map((item) => {
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__ */ jsxs14(
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__ */ jsx16(Icon, { className: "h-4 w-4" }),
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__ */ jsxs14("div", { className: "border-t p-2 space-y-1", children: [
2102
- /* @__PURE__ */ jsxs14(
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__ */ jsx16(ExternalLink, { className: "h-4 w-4" }),
2537
+ /* @__PURE__ */ jsx17(ExternalLink, { className: "h-4 w-4" }),
2110
2538
  t("sidebar.viewSite")
2111
2539
  ]
2112
2540
  }
2113
2541
  ),
2114
- /* @__PURE__ */ jsx16("div", { className: "px-3 py-2 text-xs text-muted-foreground truncate", children: email }),
2115
- /* @__PURE__ */ jsxs14(
2116
- Button10,
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__ */ jsx16(LogOut, { className: "h-4 w-4" }),
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 jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
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__ */ jsxs15("div", { className: "px-3 py-2", children: [
2150
- /* @__PURE__ */ jsx17("label", { className: "block text-xs uppercase tracking-wide text-muted-foreground mb-1", children: t("sites.selector.label") }),
2151
- /* @__PURE__ */ jsx17(
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__ */ jsx17("option", { value: s.id, children: s.name }, s.id))
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 useState10 } from "react";
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 Button11, Input as Input5, Label as Label4, Textarea as Textarea2 } from "@ampless/runtime/ui";
2168
- import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
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] = useState10(initial);
2182
- const [saving, setSaving] = useState10(false);
2183
- const [error, setError] = useState10(null);
2184
- const [info, setInfo] = useState10(null);
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__ */ jsxs16("form", { onSubmit: save, className: "space-y-6 max-w-xl", children: [
2210
- /* @__PURE__ */ jsxs16("fieldset", { className: "space-y-4", children: [
2211
- /* @__PURE__ */ jsx18("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: t("sites.edit.site") }),
2212
- /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
2213
- /* @__PURE__ */ jsx18(Label4, { htmlFor: "name", children: t("common.name") }),
2214
- /* @__PURE__ */ jsx18(
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__ */ jsxs16("div", { className: "space-y-2", children: [
2225
- /* @__PURE__ */ jsx18(Label4, { htmlFor: "url", children: t("common.url") }),
2226
- /* @__PURE__ */ jsx18(
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__ */ jsxs16("div", { className: "space-y-2", children: [
2237
- /* @__PURE__ */ jsx18(Label4, { htmlFor: "description", children: t("common.description") }),
2238
- /* @__PURE__ */ jsx18(
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__ */ jsxs16("fieldset", { className: "space-y-4", children: [
2251
- /* @__PURE__ */ jsx18("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: t("sites.edit.media") }),
2252
- /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
2253
- /* @__PURE__ */ jsx18(Label4, { htmlFor: "imageDisplay", children: t("sites.edit.imageDisplay") }),
2254
- /* @__PURE__ */ jsxs16(
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__ */ jsx18("option", { value: "", children: t("sites.edit.defaultPlaceholder", {
2690
+ /* @__PURE__ */ jsx19("option", { value: "", children: t("sites.edit.defaultPlaceholder", {
2263
2691
  value: fallback["media.imageDisplay"] ?? "inline"
2264
2692
  }) }),
2265
- /* @__PURE__ */ jsx18("option", { value: "inline", children: t("sites.edit.imageDisplayInline") }),
2266
- /* @__PURE__ */ jsx18("option", { value: "lightbox", children: t("sites.edit.imageDisplayLightbox") })
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__ */ jsxs16("div", { className: "space-y-2", children: [
2272
- /* @__PURE__ */ jsx18(Label4, { htmlFor: "imageMaxWidth", children: t("sites.edit.imageMaxWidth") }),
2273
- /* @__PURE__ */ jsx18(
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__ */ jsxs16("fieldset", { className: "space-y-4", children: [
2285
- /* @__PURE__ */ jsx18("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: t("sites.edit.dateDisplay") }),
2286
- /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
2287
- /* @__PURE__ */ jsx18(Label4, { htmlFor: "dateFormat", children: t("sites.edit.dateFormat") }),
2288
- /* @__PURE__ */ jsxs16(
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__ */ jsx18("option", { value: "", children: t("sites.edit.defaultPlaceholder", {
2724
+ /* @__PURE__ */ jsx19("option", { value: "", children: t("sites.edit.defaultPlaceholder", {
2297
2725
  value: fallback["dateFormat"] ?? "iso"
2298
2726
  }) }),
2299
- /* @__PURE__ */ jsx18("option", { value: "iso", children: t("sites.edit.dateFormatIso") }),
2300
- /* @__PURE__ */ jsx18("option", { value: "long", children: t("sites.edit.dateFormatLong") }),
2301
- /* @__PURE__ */ jsx18("option", { value: "locale", children: t("sites.edit.dateFormatLocale") })
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__ */ jsxs16("div", { className: "space-y-2", children: [
2307
- /* @__PURE__ */ jsx18(Label4, { htmlFor: "timezone", children: t("sites.edit.timezone") }),
2308
- /* @__PURE__ */ jsx18(
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__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: info }),
2320
- error && /* @__PURE__ */ jsx18("p", { className: "text-sm text-destructive", children: error }),
2321
- /* @__PURE__ */ jsx18(Button11, { type: "submit", disabled: saving, children: saving ? t("common.saving") : t("sites.edit.saveButton") })
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 useState11 } from "react";
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 Button12, Input as Input6, Label as Label5 } from "@ampless/runtime/ui";
2338
- import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
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] = useState11({ values: initial, touched: {} });
2351
- const [pendingTheme, setPendingTheme] = useState11(activeTheme);
2352
- const [optimisticActive, setOptimisticActive] = useState11(activeTheme);
2353
- const [saving, setSaving] = useState11(false);
2354
- const [switching, setSwitching] = useState11(false);
2355
- const [error, setError] = useState11(null);
2356
- const [info, setInfo] = useState11(null);
2357
- const [invalid, setInvalid] = useState11({});
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__ */ jsxs17("div", { className: "space-y-8", children: [
2444
- /* @__PURE__ */ jsxs17("form", { onSubmit: switchTheme, className: "max-w-xl space-y-3 rounded-md border p-4", children: [
2445
- /* @__PURE__ */ jsxs17("div", { className: "space-y-1", children: [
2446
- /* @__PURE__ */ jsx19(Label5, { htmlFor: "active-theme", className: "text-sm font-medium", children: t("theme.activeLabel") }),
2447
- /* @__PURE__ */ jsxs17("p", { className: "text-xs text-muted-foreground", children: [
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__ */ jsx19(
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__ */ jsxs17("option", { value: opt.value, children: [
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__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: resolveLocalized(desc, locale) }) : null;
2897
+ return desc ? /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: resolveLocalized(desc, locale) }) : null;
2470
2898
  })(),
2471
- /* @__PURE__ */ jsx19(
2472
- Button12,
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__ */ jsxs17("div", { className: "space-y-2", children: [
2482
- /* @__PURE__ */ jsx19(Label5, { className: "text-sm font-medium", children: t("theme.previewLabel") }),
2483
- /* @__PURE__ */ jsx19(
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__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: t("theme.previewHint") })
2920
+ /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground", children: t("theme.previewHint") })
2493
2921
  ] }),
2494
- /* @__PURE__ */ jsxs17("form", { onSubmit: save, className: "max-w-xl space-y-6", children: [
2495
- /* @__PURE__ */ jsxs17("div", { children: [
2496
- /* @__PURE__ */ jsx19("h2", { className: "text-lg font-semibold", children: t("theme.customizationHeading", {
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__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: resolveLocalized(manifest.description, locale) }),
2500
- /* @__PURE__ */ jsx19("p", { className: "mt-1 text-xs text-muted-foreground", children: t("theme.customizationHint") })
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__ */ jsxs17("fieldset", { className: "space-y-4", children: [
2503
- /* @__PURE__ */ jsx19("legend", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: resolveLocalized(name, locale) }),
2504
- fields.map((field) => /* @__PURE__ */ jsx19(
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__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: info }),
2516
- error && /* @__PURE__ */ jsx19("p", { className: "text-sm text-destructive", children: error }),
2517
- /* @__PURE__ */ jsx19(Button12, { type: "submit", disabled: saving, children: saving ? t("theme.saving") : t("theme.saveButton") })
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__ */ jsx19(Label5, { htmlFor: id, className: invalid ? "text-destructive" : void 0, children: resolveLocalized(field.label, locale) });
2540
- const description = field.description ? /* @__PURE__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: resolveLocalized(field.description, locale) }) : null;
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__ */ jsx19(ColorField, { field, id, labelEl, description, value, invalid, onChange });
2971
+ return /* @__PURE__ */ jsx20(ColorField, { field, id, labelEl, description, value, invalid, onChange });
2544
2972
  case "length":
2545
- return /* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
2973
+ return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
2546
2974
  labelEl,
2547
2975
  description,
2548
- /* @__PURE__ */ jsx19(
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__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: t("theme.lengthHelp") })
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__ */ jsxs17("div", { className: "space-y-2", children: [
2991
+ return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
2564
2992
  labelEl,
2565
2993
  description,
2566
- /* @__PURE__ */ jsxs17(
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__ */ jsx19("option", { value: "", children: t("common.default") }),
2576
- field.options.map((opt) => /* @__PURE__ */ jsx19("option", { value: opt.value, children: resolveLocalized(opt.label, locale) }, opt.value))
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__ */ jsxs17("div", { className: "space-y-2", children: [
3010
+ return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
2583
3011
  labelEl,
2584
3012
  description,
2585
- /* @__PURE__ */ jsx19(
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__ */ jsxs17("div", { className: "space-y-2", children: [
3025
+ return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
2598
3026
  labelEl,
2599
3027
  description,
2600
- /* @__PURE__ */ jsx19(
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__ */ jsx19(
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 remove2(idx) {
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__ */ jsxs17("div", { className: "space-y-2", children: [
3077
+ return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
2650
3078
  labelEl,
2651
3079
  description,
2652
- /* @__PURE__ */ jsxs17("div", { className: "space-y-2 rounded-md border bg-muted/20 p-3", children: [
2653
- items.length === 0 && /* @__PURE__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: "No links yet." }),
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__ */ jsxs17("div", { className: "flex flex-wrap items-start gap-2", children: [
2657
- /* @__PURE__ */ jsxs17("div", { className: "grid flex-1 grid-cols-1 gap-2 sm:grid-cols-2", children: [
2658
- /* @__PURE__ */ jsx19(
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__ */ jsx19(
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__ */ jsxs17("div", { className: "flex shrink-0 items-center gap-1", children: [
2677
- /* @__PURE__ */ jsx19(
2678
- Button12,
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__ */ jsx19(
2690
- Button12,
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__ */ jsx19(
2702
- Button12,
3129
+ /* @__PURE__ */ jsx20(
3130
+ Button13,
2703
3131
  {
2704
3132
  type: "button",
2705
3133
  variant: "ghost",
2706
3134
  size: "icon",
2707
- onClick: () => remove2(idx),
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__ */ jsx19(
2716
- Button12,
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__ */ jsxs17("p", { className: "text-xs text-muted-foreground", children: [
3154
+ /* @__PURE__ */ jsxs18("p", { className: "text-xs text-muted-foreground", children: [
2727
3155
  "Tip: use ",
2728
- /* @__PURE__ */ jsx19("code", { children: "tag:<name>" }),
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__ */ jsxs17("div", { className: "space-y-2", children: [
3173
+ return /* @__PURE__ */ jsxs18("div", { className: "space-y-2", children: [
2746
3174
  labelEl,
2747
3175
  description,
2748
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
2749
- /* @__PURE__ */ jsx19(
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__ */ jsx19(
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__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
2772
- /* @__PURE__ */ jsx19(
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__ */ jsx19("code", { className: "text-xs text-muted-foreground", children: effective })
3208
+ /* @__PURE__ */ jsx20("code", { className: "text-xs text-muted-foreground", children: effective })
2781
3209
  ] })
2782
3210
  ] });
2783
3211
  }