@ampless/admin 1.0.0-beta.110 → 1.0.0-beta.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/api/index.js +2 -3
  3. package/dist/{chunk-XQAPTM5X.js → chunk-2DB3EDD7.js} +1 -1
  4. package/dist/{chunk-IMWUSMAA.js → chunk-7UEIAZPY.js} +16 -2
  5. package/dist/{chunk-L6CFURU6.js → chunk-GNZZ33OH.js} +1 -1
  6. package/dist/{chunk-DR2HES7B.js → chunk-HEBAGP4Q.js} +288 -146
  7. package/dist/{chunk-CRHOOLCJ.js → chunk-HNI5IJWI.js} +1 -1
  8. package/dist/{chunk-TQUMC5HE.js → chunk-JSJ6OFNB.js} +1 -1
  9. package/dist/{chunk-IYBKPYWB.js → chunk-L3LY5KUM.js} +1 -1
  10. package/dist/{chunk-MQT2ULYX.js → chunk-LSEBYLVJ.js} +2 -2
  11. package/dist/{chunk-4X3HTOYV.js → chunk-OT4JJTAW.js} +1 -1
  12. package/dist/{chunk-N4JBA225.js → chunk-OZ2MSV43.js} +3 -3
  13. package/dist/{chunk-XQNR5GZE.js → chunk-RAAQWTZ2.js} +6 -5
  14. package/dist/{chunk-Q2EB5CFY.js → chunk-RP5YCLR4.js} +1 -1
  15. package/dist/{chunk-TLON56UA.js → chunk-XAEQWVMZ.js} +2 -2
  16. package/dist/{chunk-LE4INBQU.js → chunk-XDV7HADD.js} +6 -5
  17. package/dist/{chunk-CRZNBNEF.js → chunk-XGI3Y5KL.js} +1 -1
  18. package/dist/{chunk-OR6TTYYM.js → chunk-YB2GFJU7.js} +1 -1
  19. package/dist/components/account-view.js +3 -3
  20. package/dist/components/admin-dashboard.js +3 -3
  21. package/dist/components/edit-post-view.d.ts +7 -1
  22. package/dist/components/edit-post-view.js +5 -5
  23. package/dist/components/index.d.ts +1 -1
  24. package/dist/components/index.js +6 -6
  25. package/dist/components/login-view.js +3 -3
  26. package/dist/components/mcp-tokens-view.js +3 -3
  27. package/dist/components/media-view.js +5 -5
  28. package/dist/components/new-post-view.d.ts +7 -1
  29. package/dist/components/new-post-view.js +5 -5
  30. package/dist/components/plugin-settings-form.js +3 -3
  31. package/dist/components/posts-list-view.js +3 -3
  32. package/dist/components/users-list-view.js +3 -3
  33. package/dist/{i18n-CiexA8rJ.d.ts → i18n-Cx6FCYEy.d.ts} +22 -1
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.js +1 -1
  36. package/dist/metafile-esm.json +1 -1
  37. package/dist/pages/index.d.ts +10 -11
  38. package/dist/pages/index.js +45 -24
  39. package/dist/table-classes-context-CQvkORm3.d.ts +12 -0
  40. package/package.json +19 -19
@@ -4,7 +4,7 @@ import {
4
4
  getAdminCmsConfig,
5
5
  getMediaProcessingDefaults,
6
6
  uploadProcessedImage
7
- } from "./chunk-CRZNBNEF.js";
7
+ } from "./chunk-XGI3Y5KL.js";
8
8
  import {
9
9
  getAdminEditorExtensions,
10
10
  getAdminTiptapNodeHtml,
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-2ITWLRYF.js";
16
16
  import {
17
17
  useT
18
- } from "./chunk-TQUMC5HE.js";
18
+ } from "./chunk-JSJ6OFNB.js";
19
19
 
20
20
  // src/components/media-picker.tsx
21
21
  import { useEffect, useRef, useState } from "react";
@@ -159,6 +159,20 @@ function MediaPicker({ trigger, onSelect }) {
159
159
  ] });
160
160
  }
161
161
 
162
+ // src/editor/table-classes-context.tsx
163
+ import { createContext, useContext } from "react";
164
+ import { jsx as jsx2 } from "react/jsx-runtime";
165
+ var TableClassesContext = createContext([]);
166
+ function TableClassesProvider({
167
+ classes,
168
+ children
169
+ }) {
170
+ return /* @__PURE__ */ jsx2(TableClassesContext.Provider, { value: classes ?? [], children });
171
+ }
172
+ function useTableClasses() {
173
+ return useContext(TableClassesContext);
174
+ }
175
+
162
176
  // src/components/post-form.tsx
163
177
  import { useEffect as useEffect5, useRef as useRef4, useState as useState6 } from "react";
164
178
  import { useRouter } from "next/navigation";
@@ -200,13 +214,31 @@ import { Button as Button3, cn as cn2 } from "@ampless/runtime/ui";
200
214
 
201
215
  // src/editor/table-controls.tsx
202
216
  import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
203
- import { Table as TableIcon } from "lucide-react";
217
+ import { Table as TableIcon, Check } from "lucide-react";
218
+ import { isValidCustomClassId } from "ampless";
204
219
  import { Button as Button2, cn } from "@ampless/runtime/ui";
205
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
220
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
221
+ function currentTableClass(editor) {
222
+ if (!editor.isActive("table")) return null;
223
+ const v = editor.getAttributes("table").customClass;
224
+ return isValidCustomClassId(v) ? v : null;
225
+ }
206
226
  function TableControls({ editor }) {
207
227
  const t = useT();
208
228
  const [open, setOpen] = useState2(false);
209
229
  const rootRef = useRef2(null);
230
+ const themeClasses = useTableClasses();
231
+ const [activeClass, setActiveClass] = useState2(() => currentTableClass(editor));
232
+ useEffect2(() => {
233
+ const sync = () => setActiveClass(currentTableClass(editor));
234
+ sync();
235
+ editor.on("selectionUpdate", sync);
236
+ editor.on("transaction", sync);
237
+ return () => {
238
+ editor.off("selectionUpdate", sync);
239
+ editor.off("transaction", sync);
240
+ };
241
+ }, [editor]);
210
242
  useEffect2(() => {
211
243
  if (!open) return;
212
244
  function onPointerDown(e) {
@@ -281,8 +313,15 @@ function TableControls({ editor }) {
281
313
  enabled: inTable && editor.can().deleteTable()
282
314
  }
283
315
  ];
316
+ const declared = themeClasses.find((c) => c.id === activeClass);
317
+ const unknownActive = activeClass !== null && !declared;
318
+ const styleLabel = declared?.label ?? activeClass;
319
+ const setStyle = (id) => {
320
+ editor.chain().focus().updateAttributes("table", { customClass: id }).run();
321
+ setOpen(false);
322
+ };
284
323
  return /* @__PURE__ */ jsxs2("div", { ref: rootRef, className: "relative", children: [
285
- /* @__PURE__ */ jsx2(
324
+ /* @__PURE__ */ jsx3(
286
325
  Button2,
287
326
  {
288
327
  type: "button",
@@ -290,36 +329,69 @@ function TableControls({ editor }) {
290
329
  size: "icon",
291
330
  onClick: () => setOpen((v) => !v),
292
331
  className: cn(inTable && "bg-accent text-accent-foreground"),
293
- title: t("editor.table.title"),
332
+ title: styleLabel ? t("editor.table.titleWithStyle", { style: styleLabel }) : t("editor.table.title"),
294
333
  "aria-expanded": open,
295
334
  "aria-haspopup": "menu",
296
- children: /* @__PURE__ */ jsx2(TableIcon, { className: "h-4 w-4" })
335
+ "data-table-style": activeClass ?? void 0,
336
+ children: /* @__PURE__ */ jsx3(TableIcon, { className: "h-4 w-4" })
297
337
  }
298
338
  ),
299
- open && /* @__PURE__ */ jsx2(
339
+ open && /* @__PURE__ */ jsxs2(
300
340
  "div",
301
341
  {
302
342
  role: "menu",
303
343
  className: "absolute left-0 top-full z-50 mt-1 min-w-[12rem] rounded-md border bg-popover p-1 shadow",
304
- children: items.map((item) => /* @__PURE__ */ jsx2(
305
- "button",
306
- {
307
- type: "button",
308
- role: "menuitem",
309
- disabled: !item.enabled,
310
- onClick: () => {
311
- if (!item.enabled) return;
312
- item.run();
313
- setOpen(false);
344
+ children: [
345
+ items.map((item) => /* @__PURE__ */ jsx3(
346
+ "button",
347
+ {
348
+ type: "button",
349
+ role: "menuitem",
350
+ disabled: !item.enabled,
351
+ onClick: () => {
352
+ if (!item.enabled) return;
353
+ item.run();
354
+ setOpen(false);
355
+ },
356
+ className: cn(
357
+ "block w-full rounded-sm px-2 py-1 text-left text-sm",
358
+ item.enabled ? "hover:bg-accent hover:text-accent-foreground" : "cursor-not-allowed text-muted-foreground opacity-50"
359
+ ),
360
+ children: item.label
314
361
  },
315
- className: cn(
316
- "block w-full rounded-sm px-2 py-1 text-left text-sm",
317
- item.enabled ? "hover:bg-accent hover:text-accent-foreground" : "cursor-not-allowed text-muted-foreground opacity-50"
318
- ),
319
- children: item.label
320
- },
321
- item.key
322
- ))
362
+ item.key
363
+ )),
364
+ inTable && /* @__PURE__ */ jsxs2("div", { role: "group", "aria-label": t("editor.table.style"), className: "mt-1 border-t pt-1", children: [
365
+ /* @__PURE__ */ jsx3("div", { className: "px-2 py-1 text-xs font-medium text-muted-foreground", children: t("editor.table.style") }),
366
+ [
367
+ { id: null, label: t("editor.table.styleDefault"), description: void 0, unknown: false },
368
+ ...themeClasses.map((c) => ({ id: c.id, label: c.label, description: c.description, unknown: false })),
369
+ ...unknownActive ? [{ id: activeClass, label: t("editor.table.styleUnknown", { id: activeClass ?? "" }), description: t("editor.table.styleUnknownHint"), unknown: true }] : []
370
+ ].map((opt) => {
371
+ const selected = opt.id === activeClass;
372
+ return /* @__PURE__ */ jsxs2(
373
+ "button",
374
+ {
375
+ type: "button",
376
+ role: "menuitemradio",
377
+ "aria-checked": selected,
378
+ title: opt.description,
379
+ onClick: () => setStyle(opt.id),
380
+ className: cn(
381
+ "flex w-full items-center gap-2 rounded-sm px-2 py-1 text-left text-sm hover:bg-accent hover:text-accent-foreground",
382
+ opt.unknown && "text-amber-700 dark:text-amber-400"
383
+ ),
384
+ children: [
385
+ /* @__PURE__ */ jsx3(Check, { className: cn("h-3.5 w-3.5 shrink-0", selected ? "opacity-100" : "opacity-0") }),
386
+ /* @__PURE__ */ jsx3("span", { children: opt.label })
387
+ ]
388
+ },
389
+ opt.id ?? "__default"
390
+ );
391
+ }),
392
+ themeClasses.length === 0 && /* @__PURE__ */ jsx3("div", { className: "px-2 py-1 text-xs text-muted-foreground", children: t("editor.table.styleNoneDeclared") })
393
+ ] })
394
+ ]
323
395
  }
324
396
  )
325
397
  ] });
@@ -338,7 +410,7 @@ function normalizeCodeLanguage(value) {
338
410
  }
339
411
 
340
412
  // src/editor/code-block-language-control.tsx
341
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
413
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
342
414
  function CodeBlockLanguageControl({ editor }) {
343
415
  const t = useT();
344
416
  const [state, setState] = useState3({ active: false, language: "" });
@@ -365,8 +437,8 @@ function CodeBlockLanguageControl({ editor }) {
365
437
  }, [editor]);
366
438
  if (!state.active) return null;
367
439
  return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
368
- /* @__PURE__ */ jsx3(Braces, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
369
- /* @__PURE__ */ jsx3(
440
+ /* @__PURE__ */ jsx4(Braces, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
441
+ /* @__PURE__ */ jsx4(
370
442
  Input,
371
443
  {
372
444
  type: "text",
@@ -395,7 +467,7 @@ function CodeBlockLanguageControl({ editor }) {
395
467
  }
396
468
 
397
469
  // src/editor/toolbar.tsx
398
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
470
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
399
471
  function Toolbar({ editor }) {
400
472
  const t = useT();
401
473
  if (!editor) return null;
@@ -434,7 +506,7 @@ function Toolbar({ editor }) {
434
506
  return /* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap gap-1 border-b p-2", children: [
435
507
  tools.map((tool) => {
436
508
  const Icon = tool.icon;
437
- return /* @__PURE__ */ jsx4(
509
+ return /* @__PURE__ */ jsx5(
438
510
  Button3,
439
511
  {
440
512
  type: "button",
@@ -442,12 +514,12 @@ function Toolbar({ editor }) {
442
514
  size: "icon",
443
515
  onClick: tool.action,
444
516
  className: cn2(tool.isActive() && "bg-accent text-accent-foreground"),
445
- children: /* @__PURE__ */ jsx4(Icon, { className: "h-4 w-4" })
517
+ children: /* @__PURE__ */ jsx5(Icon, { className: "h-4 w-4" })
446
518
  },
447
519
  tool.name
448
520
  );
449
521
  }),
450
- /* @__PURE__ */ jsx4(
522
+ /* @__PURE__ */ jsx5(
451
523
  Button3,
452
524
  {
453
525
  type: "button",
@@ -455,18 +527,18 @@ function Toolbar({ editor }) {
455
527
  size: "icon",
456
528
  onClick: setLink,
457
529
  className: cn2(editor.isActive("link") && "bg-accent text-accent-foreground"),
458
- children: /* @__PURE__ */ jsx4(LinkIcon, { className: "h-4 w-4" })
530
+ children: /* @__PURE__ */ jsx5(LinkIcon, { className: "h-4 w-4" })
459
531
  }
460
532
  ),
461
- /* @__PURE__ */ jsx4(
533
+ /* @__PURE__ */ jsx5(
462
534
  MediaPicker,
463
535
  {
464
536
  onSelect: insertImage,
465
- trigger: /* @__PURE__ */ jsx4(Button3, { type: "button", variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx4(ImageIcon, { className: "h-4 w-4" }) })
537
+ trigger: /* @__PURE__ */ jsx5(Button3, { type: "button", variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx5(ImageIcon, { className: "h-4 w-4" }) })
466
538
  }
467
539
  ),
468
- /* @__PURE__ */ jsx4(TableControls, { editor }),
469
- /* @__PURE__ */ jsx4(CodeBlockLanguageControl, { editor })
540
+ /* @__PURE__ */ jsx5(TableControls, { editor }),
541
+ /* @__PURE__ */ jsx5(CodeBlockLanguageControl, { editor })
470
542
  ] });
471
543
  }
472
544
 
@@ -474,7 +546,7 @@ function Toolbar({ editor }) {
474
546
  import { BubbleMenu } from "@tiptap/react/menus";
475
547
  import { Trash2, Pencil, ImageIcon as ImageIcon2, Maximize2 } from "lucide-react";
476
548
  import { Button as Button4, cn as cn3 } from "@ampless/runtime/ui";
477
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
549
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
478
550
  function ImageBubbleMenu({ editor }) {
479
551
  const t = useT();
480
552
  const editAlt = () => {
@@ -492,7 +564,7 @@ function ImageBubbleMenu({ editor }) {
492
564
  editor.chain().focus().updateAttributes("image", { display: next }).run();
493
565
  };
494
566
  const currentDisplay = editor.getAttributes("image").display ?? null;
495
- return /* @__PURE__ */ jsx5(
567
+ return /* @__PURE__ */ jsx6(
496
568
  BubbleMenu,
497
569
  {
498
570
  editor,
@@ -509,7 +581,7 @@ function ImageBubbleMenu({ editor }) {
509
581
  className: cn3(currentDisplay === "inline" && "bg-accent text-accent-foreground"),
510
582
  title: t("editor.image.inlineTitle"),
511
583
  children: [
512
- /* @__PURE__ */ jsx5(ImageIcon2, { className: "mr-1 h-3 w-3" }),
584
+ /* @__PURE__ */ jsx6(ImageIcon2, { className: "mr-1 h-3 w-3" }),
513
585
  t("editor.image.inline")
514
586
  ]
515
587
  }
@@ -524,18 +596,18 @@ function ImageBubbleMenu({ editor }) {
524
596
  className: cn3(currentDisplay === "lightbox" && "bg-accent text-accent-foreground"),
525
597
  title: t("editor.image.lightboxTitle"),
526
598
  children: [
527
- /* @__PURE__ */ jsx5(Maximize2, { className: "mr-1 h-3 w-3" }),
599
+ /* @__PURE__ */ jsx6(Maximize2, { className: "mr-1 h-3 w-3" }),
528
600
  t("editor.image.lightbox")
529
601
  ]
530
602
  }
531
603
  ),
532
- /* @__PURE__ */ jsx5("span", { className: "mx-1 h-4 w-px bg-border" }),
604
+ /* @__PURE__ */ jsx6("span", { className: "mx-1 h-4 w-px bg-border" }),
533
605
  /* @__PURE__ */ jsxs5(Button4, { type: "button", variant: "ghost", size: "sm", onClick: editAlt, children: [
534
- /* @__PURE__ */ jsx5(Pencil, { className: "mr-1 h-3 w-3" }),
606
+ /* @__PURE__ */ jsx6(Pencil, { className: "mr-1 h-3 w-3" }),
535
607
  t("editor.image.alt")
536
608
  ] }),
537
609
  /* @__PURE__ */ jsxs5(Button4, { type: "button", variant: "ghost", size: "sm", onClick: remove2, children: [
538
- /* @__PURE__ */ jsx5(Trash2, { className: "mr-1 h-3 w-3" }),
610
+ /* @__PURE__ */ jsx6(Trash2, { className: "mr-1 h-3 w-3" }),
539
611
  t("editor.image.delete")
540
612
  ] })
541
613
  ] })
@@ -556,6 +628,31 @@ import { TaskItem } from "@tiptap/extension-task-item";
556
628
  import { Underline as Underline2 } from "@tiptap/extension-underline";
557
629
  import { Highlight } from "@tiptap/extension-highlight";
558
630
  import { TextAlign } from "@tiptap/extension-text-align";
631
+ import { isValidCustomClassId as isValidCustomClassId2, tableCustomClassName } from "ampless";
632
+ var TABLE_CLASS_PREFIX = tableCustomClassName("");
633
+ function parseTableCustomClass(className) {
634
+ for (const cls of (className ?? "").split(/\s+/)) {
635
+ if (!cls.startsWith(TABLE_CLASS_PREFIX)) continue;
636
+ const id = cls.slice(TABLE_CLASS_PREFIX.length);
637
+ if (isValidCustomClassId2(id)) return id;
638
+ }
639
+ return null;
640
+ }
641
+ var AmplessTable = Table.extend({
642
+ addAttributes() {
643
+ return {
644
+ ...this.parent?.(),
645
+ customClass: {
646
+ default: null,
647
+ parseHTML: (el) => parseTableCustomClass(el.getAttribute("class")),
648
+ renderHTML: (attrs) => {
649
+ const v = attrs.customClass;
650
+ return isValidCustomClassId2(v) ? { class: tableCustomClassName(v) } : {};
651
+ }
652
+ }
653
+ };
654
+ }
655
+ });
559
656
  var AmplessImage = Image.extend({
560
657
  addAttributes() {
561
658
  return {
@@ -575,7 +672,7 @@ var BASE_TIPTAP_EXTENSIONS = [
575
672
  StarterKit,
576
673
  Link.configure({ openOnClick: false }),
577
674
  AmplessImage.configure({ inline: false, allowBase64: false }),
578
- Table.configure({ resizable: true, HTMLAttributes: { class: "tiptap-table" } }),
675
+ AmplessTable.configure({ resizable: true, HTMLAttributes: { class: "tiptap-table" } }),
579
676
  TableRow,
580
677
  TableHeader,
581
678
  TableCell,
@@ -587,7 +684,7 @@ var BASE_TIPTAP_EXTENSIONS = [
587
684
  ];
588
685
 
589
686
  // src/editor/tiptap-editor.tsx
590
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
687
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
591
688
  var EDITOR_STYLES = `
592
689
  .ProseMirror .tiptap-table,
593
690
  .ProseMirror table {
@@ -688,10 +785,10 @@ function TiptapEditor({ initialContent, onChange, onUserEdit }) {
688
785
  }
689
786
  });
690
787
  return /* @__PURE__ */ jsxs6("div", { className: "rounded-md border", children: [
691
- /* @__PURE__ */ jsx6("style", { children: EDITOR_STYLES }),
692
- /* @__PURE__ */ jsx6(Toolbar, { editor }),
693
- editor && /* @__PURE__ */ jsx6(ImageBubbleMenu, { editor }),
694
- /* @__PURE__ */ jsx6(EditorContent, { editor })
788
+ /* @__PURE__ */ jsx7("style", { children: EDITOR_STYLES }),
789
+ /* @__PURE__ */ jsx7(Toolbar, { editor }),
790
+ editor && /* @__PURE__ */ jsx7(ImageBubbleMenu, { editor }),
791
+ /* @__PURE__ */ jsx7(EditorContent, { editor })
695
792
  ] });
696
793
  }
697
794
 
@@ -757,7 +854,7 @@ function buildPreviewSrcDoc(html) {
757
854
  }
758
855
 
759
856
  // src/components/post-history-panel.tsx
760
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
857
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
761
858
  var PAGE_SIZE = 20;
762
859
  function formatRevisedAt(iso, timezone, locale) {
763
860
  const d = new Date(iso);
@@ -859,30 +956,30 @@ function PostHistoryPanel({
859
956
  "aria-expanded": open,
860
957
  className: "flex w-full items-center justify-between px-4 py-3 text-left text-sm font-medium",
861
958
  children: [
862
- /* @__PURE__ */ jsx7("span", { children: t("posts.history.title") }),
863
- /* @__PURE__ */ jsx7("span", { className: "text-muted-foreground", "aria-hidden": "true", children: open ? "\u2212" : "+" })
959
+ /* @__PURE__ */ jsx8("span", { children: t("posts.history.title") }),
960
+ /* @__PURE__ */ jsx8("span", { className: "text-muted-foreground", "aria-hidden": "true", children: open ? "\u2212" : "+" })
864
961
  ]
865
962
  }
866
963
  ),
867
964
  open && /* @__PURE__ */ jsxs7("div", { className: "space-y-4 border-t px-4 py-4", children: [
868
- error && /* @__PURE__ */ jsx7("p", { className: "text-sm text-destructive", children: error }),
869
- loading && revisions.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "text-sm text-muted-foreground", children: t("common.loading") }) : revisions.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "text-sm text-muted-foreground", children: t("posts.history.empty") }) : /* @__PURE__ */ jsx7("div", { className: "overflow-x-auto rounded-md border", children: /* @__PURE__ */ jsxs7("table", { className: "w-full text-sm", children: [
870
- /* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsxs7("tr", { className: "border-b text-left text-xs text-muted-foreground", children: [
871
- /* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium", children: t("posts.history.columnRevisedAt") }),
872
- /* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium", children: t("posts.history.columnStatus") }),
873
- /* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium", children: t("posts.history.columnTitle") }),
874
- /* @__PURE__ */ jsx7("th", { className: "px-3 py-2 font-medium text-right", children: t("common.actions") })
965
+ error && /* @__PURE__ */ jsx8("p", { className: "text-sm text-destructive", children: error }),
966
+ loading && revisions.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: t("common.loading") }) : revisions.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "text-sm text-muted-foreground", children: t("posts.history.empty") }) : /* @__PURE__ */ jsx8("div", { className: "overflow-x-auto rounded-md border", children: /* @__PURE__ */ jsxs7("table", { className: "w-full text-sm", children: [
967
+ /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsxs7("tr", { className: "border-b text-left text-xs text-muted-foreground", children: [
968
+ /* @__PURE__ */ jsx8("th", { className: "px-3 py-2 font-medium", children: t("posts.history.columnRevisedAt") }),
969
+ /* @__PURE__ */ jsx8("th", { className: "px-3 py-2 font-medium", children: t("posts.history.columnStatus") }),
970
+ /* @__PURE__ */ jsx8("th", { className: "px-3 py-2 font-medium", children: t("posts.history.columnTitle") }),
971
+ /* @__PURE__ */ jsx8("th", { className: "px-3 py-2 font-medium text-right", children: t("common.actions") })
875
972
  ] }) }),
876
- /* @__PURE__ */ jsx7("tbody", { children: revisions.map((rev) => /* @__PURE__ */ jsxs7(
973
+ /* @__PURE__ */ jsx8("tbody", { children: revisions.map((rev) => /* @__PURE__ */ jsxs7(
877
974
  "tr",
878
975
  {
879
976
  className: selected?.postHistoryId === rev.postHistoryId ? "border-b bg-muted/50" : "border-b",
880
977
  children: [
881
- /* @__PURE__ */ jsx7("td", { className: "whitespace-nowrap px-3 py-2 font-mono text-xs", children: formatRevisedAt(rev.revisedAt, timezone, locale) }),
882
- /* @__PURE__ */ jsx7("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx7("span", { className: "text-xs text-muted-foreground", children: rev.status ? t(`common.${rev.status}`) : "\u2014" }) }),
883
- /* @__PURE__ */ jsx7("td", { className: "px-3 py-2", children: rev.title || "\u2014" }),
884
- /* @__PURE__ */ jsx7("td", { className: "px-3 py-2 text-right", children: /* @__PURE__ */ jsxs7("div", { className: "flex justify-end gap-2", children: [
885
- /* @__PURE__ */ jsx7(
978
+ /* @__PURE__ */ jsx8("td", { className: "whitespace-nowrap px-3 py-2 font-mono text-xs", children: formatRevisedAt(rev.revisedAt, timezone, locale) }),
979
+ /* @__PURE__ */ jsx8("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx8("span", { className: "text-xs text-muted-foreground", children: rev.status ? t(`common.${rev.status}`) : "\u2014" }) }),
980
+ /* @__PURE__ */ jsx8("td", { className: "px-3 py-2", children: rev.title || "\u2014" }),
981
+ /* @__PURE__ */ jsx8("td", { className: "px-3 py-2 text-right", children: /* @__PURE__ */ jsxs7("div", { className: "flex justify-end gap-2", children: [
982
+ /* @__PURE__ */ jsx8(
886
983
  Button5,
887
984
  {
888
985
  type: "button",
@@ -892,7 +989,7 @@ function PostHistoryPanel({
892
989
  children: t("posts.history.view")
893
990
  }
894
991
  ),
895
- /* @__PURE__ */ jsx7(
992
+ /* @__PURE__ */ jsx8(
896
993
  Button5,
897
994
  {
898
995
  type: "button",
@@ -910,7 +1007,7 @@ function PostHistoryPanel({
910
1007
  rev.postHistoryId
911
1008
  )) })
912
1009
  ] }) }),
913
- nextToken && /* @__PURE__ */ jsx7("div", { children: /* @__PURE__ */ jsx7(
1010
+ nextToken && /* @__PURE__ */ jsx8("div", { children: /* @__PURE__ */ jsx8(
914
1011
  Button5,
915
1012
  {
916
1013
  type: "button",
@@ -923,10 +1020,10 @@ function PostHistoryPanel({
923
1020
  ) }),
924
1021
  selected && /* @__PURE__ */ jsxs7("div", { className: "space-y-3 rounded-md border p-4", children: [
925
1022
  /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
926
- /* @__PURE__ */ jsx7("h4", { className: "text-sm font-semibold", children: t("posts.history.viewing", {
1023
+ /* @__PURE__ */ jsx8("h4", { className: "text-sm font-semibold", children: t("posts.history.viewing", {
927
1024
  date: formatRevisedAt(selected.revisedAt, timezone, locale)
928
1025
  }) }),
929
- /* @__PURE__ */ jsx7(
1026
+ /* @__PURE__ */ jsx8(
930
1027
  Button5,
931
1028
  {
932
1029
  type: "button",
@@ -938,13 +1035,13 @@ function PostHistoryPanel({
938
1035
  )
939
1036
  ] }),
940
1037
  /* @__PURE__ */ jsxs7("p", { className: "text-xs text-muted-foreground", children: [
941
- /* @__PURE__ */ jsx7("span", { className: "font-mono uppercase", children: selected.format ?? "\u2014" }),
1038
+ /* @__PURE__ */ jsx8("span", { className: "font-mono uppercase", children: selected.format ?? "\u2014" }),
942
1039
  selected.slug && /* @__PURE__ */ jsxs7(Fragment2, { children: [
943
- /* @__PURE__ */ jsx7("span", { className: "mx-2", children: "\xB7" }),
944
- /* @__PURE__ */ jsx7("span", { className: "font-mono", children: selected.slug })
1040
+ /* @__PURE__ */ jsx8("span", { className: "mx-2", children: "\xB7" }),
1041
+ /* @__PURE__ */ jsx8("span", { className: "font-mono", children: selected.slug })
945
1042
  ] })
946
1043
  ] }),
947
- selected.format === "static" ? /* @__PURE__ */ jsx7("p", { className: "rounded-md bg-amber-50 p-2 text-xs text-amber-700 dark:bg-amber-950 dark:text-amber-400", children: t("posts.history.staticCaveat") }) : (
1044
+ selected.format === "static" ? /* @__PURE__ */ jsx8("p", { className: "rounded-md bg-amber-50 p-2 text-xs text-amber-700 dark:bg-amber-950 dark:text-amber-400", children: t("posts.history.staticCaveat") }) : (
948
1045
  // Sandbox is `allow-scripts allow-same-origin`. With
949
1046
  // srcDoc, this gives the iframe the admin's origin so
950
1047
  // 3rd-party embed widgets (YouTube SDK, x.com widgets.js)
@@ -971,7 +1068,7 @@ function PostHistoryPanel({
971
1068
  // don't style its srcDoc contents. Typography comes from
972
1069
  // the full document the preview route now returns
973
1070
  // (PREVIEW_BASE_CSS + theme vars in createPreviewRouteHandler).
974
- /* @__PURE__ */ jsx7(
1071
+ /* @__PURE__ */ jsx8(
975
1072
  "iframe",
976
1073
  {
977
1074
  title: "revision-preview",
@@ -1083,7 +1180,7 @@ async function deleteBundle(slug) {
1083
1180
  }
1084
1181
 
1085
1182
  // src/components/static-uploader.tsx
1086
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1183
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1087
1184
  function StaticUploader({ initial, onFilesReady, onClear }) {
1088
1185
  const t = useT();
1089
1186
  const [pending, setPending] = useState5(null);
@@ -1167,8 +1264,8 @@ function StaticUploader({ initial, onFilesReady, onClear }) {
1167
1264
  return /* @__PURE__ */ jsxs8("div", { className: "space-y-4", children: [
1168
1265
  /* @__PURE__ */ jsxs8("div", { className: "rounded-md border border-dashed p-4", children: [
1169
1266
  /* @__PURE__ */ jsxs8("label", { className: "flex flex-col items-start gap-2 text-sm", children: [
1170
- /* @__PURE__ */ jsx8("span", { className: "font-medium", children: t("posts.form.static.pick") }),
1171
- /* @__PURE__ */ jsx8(
1267
+ /* @__PURE__ */ jsx9("span", { className: "font-medium", children: t("posts.form.static.pick") }),
1268
+ /* @__PURE__ */ jsx9(
1172
1269
  "input",
1173
1270
  {
1174
1271
  type: "file",
@@ -1178,13 +1275,13 @@ function StaticUploader({ initial, onFilesReady, onClear }) {
1178
1275
  disabled: busy
1179
1276
  }
1180
1277
  ),
1181
- /* @__PURE__ */ jsx8("span", { className: "text-xs text-muted-foreground", children: t("posts.form.static.pickHint") })
1278
+ /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: t("posts.form.static.pickHint") })
1182
1279
  ] }),
1183
- busy && /* @__PURE__ */ jsx8("p", { className: "mt-2 text-sm text-muted-foreground", children: t("common.loading") }),
1184
- error && /* @__PURE__ */ jsx8("p", { className: "mt-2 text-sm text-destructive", children: error })
1280
+ busy && /* @__PURE__ */ jsx9("p", { className: "mt-2 text-sm text-muted-foreground", children: t("common.loading") }),
1281
+ error && /* @__PURE__ */ jsx9("p", { className: "mt-2 text-sm text-destructive", children: error })
1185
1282
  ] }),
1186
- showCurrent && /* @__PURE__ */ jsx8(CurrentBundle, { body: initial }),
1187
- pending && /* @__PURE__ */ jsx8(
1283
+ showCurrent && /* @__PURE__ */ jsx9(CurrentBundle, { body: initial }),
1284
+ pending && /* @__PURE__ */ jsx9(
1188
1285
  PendingBundle,
1189
1286
  {
1190
1287
  files: pending,
@@ -1198,13 +1295,13 @@ function CurrentBundle({ body }) {
1198
1295
  const t = useT();
1199
1296
  return /* @__PURE__ */ jsxs8("div", { className: "rounded-md border bg-muted/30 p-3", children: [
1200
1297
  /* @__PURE__ */ jsxs8("div", { className: "mb-2 flex items-center gap-2 text-sm font-medium", children: [
1201
- /* @__PURE__ */ jsx8(FileArchive, { className: "h-4 w-4" }),
1298
+ /* @__PURE__ */ jsx9(FileArchive, { className: "h-4 w-4" }),
1202
1299
  t("posts.form.static.currentBundle", {
1203
1300
  count: body.files.length,
1204
1301
  entrypoint: body.entrypoint
1205
1302
  })
1206
1303
  ] }),
1207
- /* @__PURE__ */ jsx8(FileList, { files: body.files })
1304
+ /* @__PURE__ */ jsx9(FileList, { files: body.files })
1208
1305
  ] });
1209
1306
  }
1210
1307
  function PendingBundle({
@@ -1217,20 +1314,20 @@ function PendingBundle({
1217
1314
  return /* @__PURE__ */ jsxs8("div", { className: "rounded-md border p-3", children: [
1218
1315
  /* @__PURE__ */ jsxs8("div", { className: "mb-2 flex items-center justify-between gap-2 text-sm font-medium", children: [
1219
1316
  /* @__PURE__ */ jsxs8("span", { className: "flex items-center gap-2", children: [
1220
- /* @__PURE__ */ jsx8(FileArchive, { className: "h-4 w-4" }),
1317
+ /* @__PURE__ */ jsx9(FileArchive, { className: "h-4 w-4" }),
1221
1318
  t("posts.form.static.pendingBundle", {
1222
1319
  count: files.length,
1223
1320
  size: formatBytes(totalBytes)
1224
1321
  })
1225
1322
  ] }),
1226
1323
  /* @__PURE__ */ jsxs8(Button6, { type: "button", variant: "ghost", size: "sm", onClick: onClear, children: [
1227
- /* @__PURE__ */ jsx8(X, { className: "mr-1 h-3 w-3" }),
1324
+ /* @__PURE__ */ jsx9(X, { className: "mr-1 h-3 w-3" }),
1228
1325
  t("common.cancel")
1229
1326
  ] })
1230
1327
  ] }),
1231
1328
  issues.length > 0 && /* @__PURE__ */ jsxs8("div", { className: "mb-3 rounded-md border border-destructive/40 bg-destructive/5 p-2 text-sm", children: [
1232
1329
  /* @__PURE__ */ jsxs8("div", { className: "mb-1 flex items-center gap-2 font-medium text-destructive", children: [
1233
- /* @__PURE__ */ jsx8(AlertTriangle, { className: "h-4 w-4" }),
1330
+ /* @__PURE__ */ jsx9(AlertTriangle, { className: "h-4 w-4" }),
1234
1331
  t("posts.form.static.issuesTitle", { count: issues.length })
1235
1332
  ] }),
1236
1333
  /* @__PURE__ */ jsxs8("ul", { className: "space-y-0.5 text-xs", children: [
@@ -1245,15 +1342,15 @@ function PendingBundle({
1245
1342
  " more"
1246
1343
  ] })
1247
1344
  ] }),
1248
- /* @__PURE__ */ jsx8("p", { className: "mt-2 text-xs text-muted-foreground", children: t("posts.form.static.issuesHint") })
1345
+ /* @__PURE__ */ jsx9("p", { className: "mt-2 text-xs text-muted-foreground", children: t("posts.form.static.issuesHint") })
1249
1346
  ] }),
1250
- /* @__PURE__ */ jsx8(FileList, { files: files.map((f) => f.path) })
1347
+ /* @__PURE__ */ jsx9(FileList, { files: files.map((f) => f.path) })
1251
1348
  ] });
1252
1349
  }
1253
1350
  function FileList({ files }) {
1254
1351
  return /* @__PURE__ */ jsxs8("ul", { className: "space-y-0.5 text-xs", children: [
1255
1352
  files.slice(0, 40).map((path) => /* @__PURE__ */ jsxs8("li", { className: "flex items-center gap-1.5 font-mono text-muted-foreground", children: [
1256
- /* @__PURE__ */ jsx8(FileText, { className: "h-3 w-3 shrink-0" }),
1353
+ /* @__PURE__ */ jsx9(FileText, { className: "h-3 w-3 shrink-0" }),
1257
1354
  path
1258
1355
  ] }, path)),
1259
1356
  files.length > 40 && /* @__PURE__ */ jsxs8("li", { className: "font-mono text-xs text-muted-foreground", children: [
@@ -1420,6 +1517,7 @@ function deepEqual(a, b) {
1420
1517
  }
1421
1518
 
1422
1519
  // src/editor/format-switch.ts
1520
+ import { isValidCustomClassId as isValidCustomClassId3, tableCustomClassName as tableCustomClassName2 } from "ampless";
1423
1521
  import {
1424
1522
  tiptapToHtml,
1425
1523
  tiptapToMarkdown,
@@ -1427,6 +1525,40 @@ import {
1427
1525
  htmlToMarkdown
1428
1526
  } from "@ampless/runtime";
1429
1527
  import { generateJSON } from "@tiptap/core";
1528
+ var TABLE_CLASS_PREFIX2 = tableCustomClassName2("");
1529
+ function collectTiptapTableClasses(node, out) {
1530
+ if (!node || typeof node !== "object") return;
1531
+ const n = node;
1532
+ if (n.type === "table" && isValidCustomClassId3(n.attrs?.customClass)) out.add(n.attrs.customClass);
1533
+ if (Array.isArray(n.content)) for (const c of n.content) collectTiptapTableClasses(c, out);
1534
+ }
1535
+ function addTableClass(cls, out) {
1536
+ if (!cls.startsWith(TABLE_CLASS_PREFIX2)) return;
1537
+ const id = cls.slice(TABLE_CLASS_PREFIX2.length);
1538
+ if (isValidCustomClassId3(id)) out.add(id);
1539
+ }
1540
+ function collectHtmlTableClasses(html, out) {
1541
+ if (typeof DOMParser !== "undefined") {
1542
+ const doc = new DOMParser().parseFromString(html, "text/html");
1543
+ for (const table of Array.from(doc.querySelectorAll("table"))) {
1544
+ for (const cls of Array.from(table.classList)) addTableClass(cls, out);
1545
+ }
1546
+ return;
1547
+ }
1548
+ for (const m of html.matchAll(/tiptap-table--[a-z0-9-]+/g)) addTableClass(m[0], out);
1549
+ }
1550
+ function tableClassesLostOnConversion(body, from, to) {
1551
+ if (to !== "markdown" || from === "markdown" || from === "static") return [];
1552
+ const found = /* @__PURE__ */ new Set();
1553
+ if (typeof body === "string") collectHtmlTableClasses(body, found);
1554
+ else if (from === "tiptap") collectTiptapTableClasses(body, found);
1555
+ return [...found];
1556
+ }
1557
+ function confirmLossyFormatChange(body, from, to, confirm2, message) {
1558
+ const lost = tableClassesLostOnConversion(body, from, to);
1559
+ if (lost.length === 0) return true;
1560
+ return confirm2(message(lost));
1561
+ }
1430
1562
  function convertBodyFormat(body, from, to, registries) {
1431
1563
  if (from === to) return body;
1432
1564
  const k = `${from}\u2192${to}`;
@@ -1450,7 +1582,7 @@ function convertBodyFormat(body, from, to, registries) {
1450
1582
  }
1451
1583
 
1452
1584
  // src/components/post-form.tsx
1453
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1585
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1454
1586
  var EMPTY_TIPTAP_DOC = { type: "doc", content: [{ type: "paragraph" }] };
1455
1587
  var IMAGE_URL_RE = /\.(jpe?g|png|gif|webp|avif|svg|bmp|tiff?)(\?|$)/i;
1456
1588
  var STYLESHEET_URL_RE = /\.css(\?|$)/i;
@@ -1645,6 +1777,15 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1645
1777
  }
1646
1778
  function changeFormat(next) {
1647
1779
  if (next === format) return;
1780
+ if (!confirmLossyFormatChange(
1781
+ body,
1782
+ format,
1783
+ next,
1784
+ (message) => window.confirm(message),
1785
+ (classes) => t("posts.form.formatLossTableClasses", { classes: classes.join(", ") })
1786
+ )) {
1787
+ return;
1788
+ }
1648
1789
  let nextBody = body;
1649
1790
  if (next === "static" || format === "static") {
1650
1791
  nextBody = defaultBodyForFormat(next);
@@ -1817,7 +1958,7 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1817
1958
  ]);
1818
1959
  return /* @__PURE__ */ jsxs9("form", { onSubmit: save, className: "space-y-6", children: [
1819
1960
  /* @__PURE__ */ jsxs9("div", { className: "flex gap-1 border-b", children: [
1820
- /* @__PURE__ */ jsx9(
1961
+ /* @__PURE__ */ jsx10(
1821
1962
  "button",
1822
1963
  {
1823
1964
  type: "button",
@@ -1827,7 +1968,7 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1827
1968
  children: t("posts.form.tabEdit")
1828
1969
  }
1829
1970
  ),
1830
- /* @__PURE__ */ jsx9(
1971
+ /* @__PURE__ */ jsx10(
1831
1972
  "button",
1832
1973
  {
1833
1974
  type: "button",
@@ -1840,15 +1981,15 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1840
1981
  ] }),
1841
1982
  view === "preview" && /* @__PURE__ */ jsxs9("article", { className: "space-y-4", children: [
1842
1983
  /* @__PURE__ */ jsxs9("header", { className: "border-b pb-4", children: [
1843
- /* @__PURE__ */ jsx9("h1", { className: "text-3xl font-bold tracking-tight", children: title || /* @__PURE__ */ jsx9("span", { className: "text-muted-foreground italic", children: t("posts.form.previewNoTitle") }) }),
1984
+ /* @__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") }) }),
1844
1985
  /* @__PURE__ */ jsxs9("p", { className: "mt-2 text-sm text-muted-foreground", children: [
1845
- previewPost.publishedAt ? /* @__PURE__ */ jsx9("time", { dateTime: previewPost.publishedAt, children: formatDate(previewPost.publishedAt) }) : /* @__PURE__ */ jsx9("span", { children: t("common.draft") }),
1846
- /* @__PURE__ */ jsx9("span", { className: "mx-2", children: "\xB7" }),
1847
- /* @__PURE__ */ jsx9("span", { className: "font-mono text-xs uppercase", children: format })
1986
+ previewPost.publishedAt ? /* @__PURE__ */ jsx10("time", { dateTime: previewPost.publishedAt, children: formatDate(previewPost.publishedAt) }) : /* @__PURE__ */ jsx10("span", { children: t("common.draft") }),
1987
+ /* @__PURE__ */ jsx10("span", { className: "mx-2", children: "\xB7" }),
1988
+ /* @__PURE__ */ jsx10("span", { className: "font-mono text-xs uppercase", children: format })
1848
1989
  ] }),
1849
- excerpt && /* @__PURE__ */ jsx9("p", { className: "mt-3 text-base text-muted-foreground", children: excerpt })
1990
+ excerpt && /* @__PURE__ */ jsx10("p", { className: "mt-3 text-base text-muted-foreground", children: excerpt })
1850
1991
  ] }),
1851
- format === "static" ? /* @__PURE__ */ jsx9("p", { className: "text-sm text-muted-foreground", children: t("posts.form.static.previewHint") }) : (
1992
+ format === "static" ? /* @__PURE__ */ jsx10("p", { className: "text-sm text-muted-foreground", children: t("posts.form.static.previewHint") }) : (
1852
1993
  // Phase 7: preview HTML is fetched from the template's
1853
1994
  // `/admin/preview` Route Handler so `ampless.renderBody`
1854
1995
  // (async ReactNode) + `ampless.publicPostScriptsForPage(
@@ -1886,7 +2027,7 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1886
2027
  // document — an iframe srcDoc does not inherit the parent page's
1887
2028
  // CSS. Typography is now applied inside the document returned by
1888
2029
  // createPreviewRouteHandler (PREVIEW_BASE_CSS + theme cssVars).
1889
- /* @__PURE__ */ jsx9(
2030
+ /* @__PURE__ */ jsx10(
1890
2031
  "iframe",
1891
2032
  {
1892
2033
  title: "post-preview",
@@ -1896,7 +2037,7 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1896
2037
  }
1897
2038
  )
1898
2039
  ),
1899
- previewPost.tags && previewPost.tags.length > 0 && /* @__PURE__ */ jsx9("div", { className: "flex flex-wrap gap-2 border-t pt-4 text-sm", children: previewPost.tags.map((tag) => /* @__PURE__ */ jsxs9(
2040
+ 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__ */ jsxs9(
1900
2041
  "span",
1901
2042
  {
1902
2043
  className: "rounded-full border px-2 py-0.5 text-xs text-muted-foreground",
@@ -1907,7 +2048,7 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1907
2048
  },
1908
2049
  tag
1909
2050
  )) }),
1910
- /* @__PURE__ */ jsx9("p", { className: "text-xs text-muted-foreground", children: t("posts.form.previewHint") })
2051
+ /* @__PURE__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t("posts.form.previewHint") })
1911
2052
  ] }),
1912
2053
  /* @__PURE__ */ jsxs9("div", { className: view === "edit" ? "space-y-6" : "hidden", children: [
1913
2054
  recovery && /* @__PURE__ */ jsxs9(
@@ -1916,24 +2057,24 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1916
2057
  role: "alert",
1917
2058
  className: recovery.decision === "stale" ? "space-y-2 rounded-md border border-amber-300 bg-amber-50 p-4 text-sm dark:border-amber-800 dark:bg-amber-950" : "space-y-2 rounded-md border border-[var(--primary)]/40 bg-[var(--primary)]/5 p-4 text-sm",
1918
2059
  children: [
1919
- /* @__PURE__ */ jsx9("p", { className: "font-medium", children: recovery.decision === "stale" ? t("posts.draft.staleTitle") : t("posts.draft.recoverTitle") }),
1920
- /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground", children: recovery.decision === "stale" ? t("posts.draft.staleBody", {
2060
+ /* @__PURE__ */ jsx10("p", { className: "font-medium", children: recovery.decision === "stale" ? t("posts.draft.staleTitle") : t("posts.draft.recoverTitle") }),
2061
+ /* @__PURE__ */ jsx10("p", { className: "text-muted-foreground", children: recovery.decision === "stale" ? t("posts.draft.staleBody", {
1921
2062
  when: formatDraftTime(recovery.draft.savedAt, draftTimezone, draftLocale)
1922
2063
  }) : t("posts.draft.recoverBody", {
1923
2064
  when: formatDraftTime(recovery.draft.savedAt, draftTimezone, draftLocale)
1924
2065
  }) }),
1925
2066
  /* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
1926
- /* @__PURE__ */ jsx9(Button7, { type: "button", size: "sm", onClick: acceptRecovery, children: recovery.decision === "stale" ? t("posts.draft.restoreAnyway") : t("posts.draft.restore") }),
1927
- /* @__PURE__ */ jsx9(Button7, { type: "button", size: "sm", variant: "outline", onClick: discardRecovery, children: t("posts.draft.discard") })
2067
+ /* @__PURE__ */ jsx10(Button7, { type: "button", size: "sm", onClick: acceptRecovery, children: recovery.decision === "stale" ? t("posts.draft.restoreAnyway") : t("posts.draft.restore") }),
2068
+ /* @__PURE__ */ jsx10(Button7, { type: "button", size: "sm", variant: "outline", onClick: discardRecovery, children: t("posts.draft.discard") })
1928
2069
  ] })
1929
2070
  ]
1930
2071
  }
1931
2072
  ),
1932
- restoredHint && !recovery && /* @__PURE__ */ jsx9("p", { className: "rounded-md border border-[var(--primary)]/40 bg-[var(--primary)]/5 px-3 py-2 text-xs text-muted-foreground", children: t("posts.draft.restored") }),
1933
- isStaticFormat && /* @__PURE__ */ jsx9("p", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: t("posts.draft.staticUnsupported") }),
2073
+ restoredHint && !recovery && /* @__PURE__ */ jsx10("p", { className: "rounded-md border border-[var(--primary)]/40 bg-[var(--primary)]/5 px-3 py-2 text-xs text-muted-foreground", children: t("posts.draft.restored") }),
2074
+ isStaticFormat && /* @__PURE__ */ jsx10("p", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: t("posts.draft.staticUnsupported") }),
1934
2075
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
1935
- /* @__PURE__ */ jsx9(Label, { htmlFor: "title", children: t("posts.form.title") }),
1936
- /* @__PURE__ */ jsx9(
2076
+ /* @__PURE__ */ jsx10(Label, { htmlFor: "title", children: t("posts.form.title") }),
2077
+ /* @__PURE__ */ jsx10(
1937
2078
  Input2,
1938
2079
  {
1939
2080
  id: "title",
@@ -1948,8 +2089,8 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1948
2089
  )
1949
2090
  ] }),
1950
2091
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
1951
- /* @__PURE__ */ jsx9(Label, { htmlFor: "slug", children: t("posts.form.slug") }),
1952
- /* @__PURE__ */ jsx9(
2092
+ /* @__PURE__ */ jsx10(Label, { htmlFor: "slug", children: t("posts.form.slug") }),
2093
+ /* @__PURE__ */ jsx10(
1953
2094
  Input2,
1954
2095
  {
1955
2096
  id: "slug",
@@ -1963,8 +2104,8 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1963
2104
  )
1964
2105
  ] }),
1965
2106
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
1966
- /* @__PURE__ */ jsx9(Label, { htmlFor: "excerpt", children: t("posts.form.excerpt") }),
1967
- /* @__PURE__ */ jsx9(
2107
+ /* @__PURE__ */ jsx10(Label, { htmlFor: "excerpt", children: t("posts.form.excerpt") }),
2108
+ /* @__PURE__ */ jsx10(
1968
2109
  Textarea,
1969
2110
  {
1970
2111
  id: "excerpt",
@@ -1978,7 +2119,7 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1978
2119
  )
1979
2120
  ] }),
1980
2121
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
1981
- /* @__PURE__ */ jsx9(Label, { htmlFor: "format", children: t("posts.form.format") }),
2122
+ /* @__PURE__ */ jsx10(Label, { htmlFor: "format", children: t("posts.form.format") }),
1982
2123
  /* @__PURE__ */ jsxs9(
1983
2124
  "select",
1984
2125
  {
@@ -1987,34 +2128,34 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
1987
2128
  onChange: (e) => changeFormat(e.target.value),
1988
2129
  className: "flex h-9 w-full max-w-xs rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm",
1989
2130
  children: [
1990
- /* @__PURE__ */ jsx9("option", { value: "tiptap", children: "Tiptap (rich editor)" }),
1991
- /* @__PURE__ */ jsx9("option", { value: "markdown", children: "Markdown" }),
1992
- /* @__PURE__ */ jsx9("option", { value: "html", children: "HTML" }),
1993
- /* @__PURE__ */ jsx9("option", { value: "static", children: t("posts.form.formatStaticLabel") })
2131
+ /* @__PURE__ */ jsx10("option", { value: "tiptap", children: "Tiptap (rich editor)" }),
2132
+ /* @__PURE__ */ jsx10("option", { value: "markdown", children: "Markdown" }),
2133
+ /* @__PURE__ */ jsx10("option", { value: "html", children: "HTML" }),
2134
+ /* @__PURE__ */ jsx10("option", { value: "static", children: t("posts.form.formatStaticLabel") })
1994
2135
  ]
1995
2136
  }
1996
2137
  ),
1997
- /* @__PURE__ */ jsx9("p", { className: "text-xs text-muted-foreground", children: t("posts.form.formatHint") })
2138
+ /* @__PURE__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t("posts.form.formatHint") })
1998
2139
  ] }),
1999
2140
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
2000
2141
  /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
2001
- /* @__PURE__ */ jsx9(Label, { children: t("posts.form.body") }),
2142
+ /* @__PURE__ */ jsx10(Label, { children: t("posts.form.body") }),
2002
2143
  format !== "tiptap" && format !== "static" && // For textarea-based formats (markdown / html) there's no
2003
2144
  // embedded toolbar, so we surface the MediaPicker as a
2004
2145
  // standalone button. Selecting an asset inserts a
2005
2146
  // format-aware snippet at the cursor.
2006
- /* @__PURE__ */ jsx9(
2147
+ /* @__PURE__ */ jsx10(
2007
2148
  MediaPicker,
2008
2149
  {
2009
2150
  onSelect: insertMediaSnippet,
2010
2151
  trigger: /* @__PURE__ */ jsxs9(Button7, { type: "button", variant: "outline", size: "sm", children: [
2011
- /* @__PURE__ */ jsx9(ImageIcon3, { className: "mr-2 h-3 w-3" }),
2152
+ /* @__PURE__ */ jsx10(ImageIcon3, { className: "mr-2 h-3 w-3" }),
2012
2153
  t("posts.form.insertMedia")
2013
2154
  ] })
2014
2155
  }
2015
2156
  )
2016
2157
  ] }),
2017
- format === "tiptap" ? /* @__PURE__ */ jsx9(
2158
+ format === "tiptap" ? /* @__PURE__ */ jsx10(
2018
2159
  TiptapEditor,
2019
2160
  {
2020
2161
  initialContent: body,
@@ -2022,14 +2163,14 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
2022
2163
  onUserEdit: markDirty
2023
2164
  },
2024
2165
  editorEpoch
2025
- ) : format === "static" ? /* @__PURE__ */ jsx9(
2166
+ ) : format === "static" ? /* @__PURE__ */ jsx10(
2026
2167
  StaticUploader,
2027
2168
  {
2028
2169
  initial: initialStaticBody,
2029
2170
  onFilesReady: (files, entrypoint) => setPendingBundle({ files, entrypoint }),
2030
2171
  onClear: () => setPendingBundle(null)
2031
2172
  }
2032
- ) : /* @__PURE__ */ jsx9(
2173
+ ) : /* @__PURE__ */ jsx10(
2033
2174
  Textarea,
2034
2175
  {
2035
2176
  ref: bodyTextareaRef,
@@ -2044,8 +2185,8 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
2044
2185
  )
2045
2186
  ] }),
2046
2187
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
2047
- /* @__PURE__ */ jsx9(Label, { htmlFor: "tags", children: t("posts.form.tags") }),
2048
- /* @__PURE__ */ jsx9(
2188
+ /* @__PURE__ */ jsx10(Label, { htmlFor: "tags", children: t("posts.form.tags") }),
2189
+ /* @__PURE__ */ jsx10(
2049
2190
  Input2,
2050
2191
  {
2051
2192
  id: "tags",
@@ -2057,10 +2198,10 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
2057
2198
  placeholder: t("posts.form.tagsPlaceholder")
2058
2199
  }
2059
2200
  ),
2060
- /* @__PURE__ */ jsx9("p", { className: "text-xs text-muted-foreground", children: t("posts.form.tagsHint") })
2201
+ /* @__PURE__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t("posts.form.tagsHint") })
2061
2202
  ] }),
2062
2203
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
2063
- /* @__PURE__ */ jsx9(Label, { htmlFor: "status", children: t("posts.form.status") }),
2204
+ /* @__PURE__ */ jsx10(Label, { htmlFor: "status", children: t("posts.form.status") }),
2064
2205
  /* @__PURE__ */ jsxs9(
2065
2206
  "select",
2066
2207
  {
@@ -2072,15 +2213,15 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
2072
2213
  },
2073
2214
  className: "flex h-9 w-full max-w-xs rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm",
2074
2215
  children: [
2075
- /* @__PURE__ */ jsx9("option", { value: "draft", children: t("common.draft") }),
2076
- /* @__PURE__ */ jsx9("option", { value: "published", children: t("common.published") })
2216
+ /* @__PURE__ */ jsx10("option", { value: "draft", children: t("common.draft") }),
2217
+ /* @__PURE__ */ jsx10("option", { value: "published", children: t("common.published") })
2077
2218
  ]
2078
2219
  }
2079
2220
  )
2080
2221
  ] }),
2081
2222
  /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
2082
- /* @__PURE__ */ jsx9(Label, { htmlFor: "publishedAt", children: t("posts.form.publishedAt") }),
2083
- /* @__PURE__ */ jsx9(
2223
+ /* @__PURE__ */ jsx10(Label, { htmlFor: "publishedAt", children: t("posts.form.publishedAt") }),
2224
+ /* @__PURE__ */ jsx10(
2084
2225
  "input",
2085
2226
  {
2086
2227
  id: "publishedAt",
@@ -2093,11 +2234,11 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
2093
2234
  className: "flex h-9 w-full max-w-xs rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm"
2094
2235
  }
2095
2236
  ),
2096
- /* @__PURE__ */ jsx9("p", { className: "text-xs text-muted-foreground", children: t("posts.form.publishedAtHint") }),
2097
- status === "published" && isFuture(resolvedPublishedAt) && /* @__PURE__ */ jsx9("p", { className: "text-xs text-amber-600 dark:text-amber-400", children: t("posts.form.scheduledNotice", { date: publishedAtInput }) })
2237
+ /* @__PURE__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: t("posts.form.publishedAtHint") }),
2238
+ status === "published" && isFuture(resolvedPublishedAt) && /* @__PURE__ */ jsx10("p", { className: "text-xs text-amber-600 dark:text-amber-400", children: t("posts.form.scheduledNotice", { date: publishedAtInput }) })
2098
2239
  ] }),
2099
- format === "html" && /* @__PURE__ */ jsx9("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs9("label", { className: "flex items-start gap-2 text-sm", children: [
2100
- /* @__PURE__ */ jsx9(
2240
+ format === "html" && /* @__PURE__ */ jsx10("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs9("label", { className: "flex items-start gap-2 text-sm", children: [
2241
+ /* @__PURE__ */ jsx10(
2101
2242
  "input",
2102
2243
  {
2103
2244
  type: "checkbox",
@@ -2110,16 +2251,16 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
2110
2251
  }
2111
2252
  ),
2112
2253
  /* @__PURE__ */ jsxs9("span", { children: [
2113
- /* @__PURE__ */ jsx9("span", { className: "font-medium", children: t("posts.form.noLayout") }),
2114
- /* @__PURE__ */ jsx9("span", { className: "block text-xs text-muted-foreground", children: t("posts.form.noLayoutHint") })
2254
+ /* @__PURE__ */ jsx10("span", { className: "font-medium", children: t("posts.form.noLayout") }),
2255
+ /* @__PURE__ */ jsx10("span", { className: "block text-xs text-muted-foreground", children: t("posts.form.noLayoutHint") })
2115
2256
  ] })
2116
2257
  ] }) }),
2117
- error && /* @__PURE__ */ jsx9("p", { className: "text-sm text-destructive", children: error }),
2258
+ error && /* @__PURE__ */ jsx10("p", { className: "text-sm text-destructive", children: error }),
2118
2259
  /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
2119
- /* @__PURE__ */ jsx9(Button7, { type: "submit", disabled: saving, children: saving ? t("common.saving") : isEdit ? t("posts.form.saveChanges") : t("posts.form.createPost") }),
2120
- isEdit && /* @__PURE__ */ jsx9(Button7, { type: "button", variant: "destructive", onClick: handleDelete, disabled: saving, children: t("posts.form.delete") })
2260
+ /* @__PURE__ */ jsx10(Button7, { type: "submit", disabled: saving, children: saving ? t("common.saving") : isEdit ? t("posts.form.saveChanges") : t("posts.form.createPost") }),
2261
+ isEdit && /* @__PURE__ */ jsx10(Button7, { type: "button", variant: "destructive", onClick: handleDelete, disabled: saving, children: t("posts.form.delete") })
2121
2262
  ] }),
2122
- isEdit && post && /* @__PURE__ */ jsx9(
2263
+ isEdit && post && /* @__PURE__ */ jsx10(
2123
2264
  PostHistoryPanel,
2124
2265
  {
2125
2266
  postId: post.postId,
@@ -2133,6 +2274,7 @@ function PostForm({ post, previewEndpoint = "/admin/preview" }) {
2133
2274
 
2134
2275
  export {
2135
2276
  MediaPicker,
2277
+ TableClassesProvider,
2136
2278
  clearAllDrafts,
2137
2279
  PostForm
2138
2280
  };