@oktis-works/admin 0.4.18 → 0.4.20

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 (94) hide show
  1. package/dist/client/_astro/{ActivityWidget.BQVQr1OI.js → ActivityWidget.BbEtaaM-.js} +2 -2
  2. package/dist/client/_astro/{AdminBar.CeAqY2Nn.js → AdminBar.CU2eba2D.js} +2 -2
  3. package/dist/client/_astro/{AtGlanceWidget.ClnyPmqR.js → AtGlanceWidget.C1hpeDlx.js} +1 -1
  4. package/dist/client/_astro/{BaseLayout.B3A9-IS6.css → BaseLayout.Y8xW22CP.css} +1 -1
  5. package/dist/client/_astro/ContentEditor.Bc5Jq8Dv.js +1 -0
  6. package/dist/client/_astro/ContentEditor.DvpaqJBD.js +3 -0
  7. package/dist/client/_astro/ContentTypeWorkspace.DakVi21h.js +1 -0
  8. package/dist/client/_astro/{CustomFieldsManager.BZAwLlD7.js → CustomFieldsManager.B7WOTvb7.js} +1 -1
  9. package/dist/client/_astro/DashboardHome.CUfuLLDH.js +1 -0
  10. package/dist/client/_astro/DashboardLayout.CGou4voc.css +1 -0
  11. package/dist/client/_astro/DashboardLayout.astro_astro_type_script_index_0_lang.Dpnx6U2l.js +4 -0
  12. package/dist/client/_astro/{Footer.B8DQezwj.js → Footer.rUGgtlfI.js} +1 -1
  13. package/dist/client/_astro/{GeneralSettings.DzzIY4EJ.js → GeneralSettings.C4mnJ7P2.js} +1 -1
  14. package/dist/client/_astro/{MediaLibrary.CB2vN3WX.js → MediaLibrary.2geQYfXB.js} +1 -1
  15. package/dist/client/_astro/{PluginManager.CZnOmZ4H.js → PluginManager.D3pm88iu.js} +1 -1
  16. package/dist/client/_astro/PostTypesManager.Cyhr3VEJ.js +1 -0
  17. package/dist/client/_astro/{QuickDraftWidget.B8sKln0S.js → QuickDraftWidget.CPljoS31.js} +2 -2
  18. package/dist/client/_astro/{ResourceHubs.gibltbet.js → ResourceHubs.DpCJERsJ.js} +1 -1
  19. package/dist/client/_astro/{TaxonomiesManager.BVGc-hS0.js → TaxonomiesManager.P3bc-fnu.js} +1 -1
  20. package/dist/client/_astro/TaxonomyTermsManager.BxP9gwm7.js +1 -0
  21. package/dist/client/_astro/{ThemeManager.b7xZdz4_.js → ThemeManager.3MUOwqex.js} +1 -1
  22. package/dist/client/_astro/{UserCreateForm.By8e9stC.js → UserCreateForm.C3RiEJEa.js} +1 -1
  23. package/dist/client/_astro/{UsersManager.M8WCxyOI.js → UsersManager.DR9XR3FX.js} +1 -1
  24. package/dist/client/_astro/api.25OAsWO0.js +1 -0
  25. package/dist/client/_astro/{ar.DsV0P0EN.js → ar.CwC9hXB9.js} +1 -1
  26. package/dist/client/_astro/{es-ES.etPSaiLd.js → es-ES.qNcd-cVX.js} +1 -1
  27. package/dist/client/_astro/{extensions.BC-nLC12.js → extensions.Bj86CHnv.js} +1 -1
  28. package/dist/client/_astro/{he.U1Vtp3zT.js → he.DfZ0ZzM-.js} +1 -1
  29. package/dist/client/_astro/i18n.C24_pkIi.js +1 -0
  30. package/dist/client/_astro/{it.DJuI8oO_.js → it.PFmLQj2s.js} +1 -1
  31. package/dist/client/_astro/{ja.Cw8I_NUV.js → ja.CMi5cD8g.js} +1 -1
  32. package/dist/client/_astro/{login.astro_astro_type_script_index_0_lang.BPE3sVZJ.js → login.astro_astro_type_script_index_0_lang.O-AlzgHd.js} +1 -1
  33. package/dist/client/_astro/{pl.Dsz-4G_z.js → pl.CRExzLlj.js} +1 -1
  34. package/dist/client/_astro/pt-BR.BPvwNpKR.js +1 -0
  35. package/dist/client/_astro/{ru.CwIIcTdy.js → ru.CWV-tkZ3.js} +1 -1
  36. package/dist/client/_astro/{src.DmjGg1I5.js → src.Cq47pnsg.js} +1 -1
  37. package/dist/client/_astro/{sw.DwpL3sb6.js → sw.B5x6GU4s.js} +1 -1
  38. package/dist/client/_astro/{tr.CY2JWJ7Y.js → tr.BzxCGQu8.js} +1 -1
  39. package/dist/client/_astro/{uk.BudEWIU8.js → uk.DJHMwNRU.js} +1 -1
  40. package/dist/client/_astro/{version.CACx_Y2G.js → version.BQq07lEn.js} +1 -1
  41. package/dist/client/_astro/{yo.BmoZs0fO.js → yo.DNw_hOnW.js} +1 -1
  42. package/dist/client/_astro/{zh-CN.khaXV6fv.js → zh-CN.Bs7H6Unx.js} +1 -1
  43. package/dist/server/chunks/{ContentEditor_RKGob-Zo.mjs → ContentEditor_DS7flRjR.mjs} +432 -89
  44. package/dist/server/chunks/{DashboardLayout_svhEj8jj.mjs → DashboardLayout_09DepSVn.mjs} +8 -8
  45. package/dist/server/chunks/{MediaLibrary_BCzLRYs6.mjs → MediaLibrary_Xs09kLza.mjs} +2 -2
  46. package/dist/server/chunks/{ResourceHubs_BH3vCwma.mjs → ResourceHubs_Dwb0xyPZ.mjs} +1 -1
  47. package/dist/server/chunks/{_slug__qrjLegLj.mjs → _slug__l8CTaLpP.mjs} +1 -1
  48. package/dist/server/chunks/{access_DSLRN3qH.mjs → access_D93W5BWD.mjs} +2 -2
  49. package/dist/server/chunks/{ar_DMC8HaoP.mjs → ar_B0LfQbpu.mjs} +1 -1
  50. package/dist/server/chunks/{custom-fields_BwLtNm1t.mjs → custom-fields_Dp7DHJ0M.mjs} +2 -2
  51. package/dist/server/chunks/{edit_CmOvfpRE.mjs → edit_hSCrM2Gq.mjs} +12 -10
  52. package/dist/server/chunks/{es-ES_DZvHskAD.mjs → es-ES_dBEm2FNw.mjs} +1 -1
  53. package/dist/server/chunks/{general_DDYnR_op.mjs → general_Ct9MU7b-.mjs} +2 -2
  54. package/dist/server/chunks/{he_zy3stNhR.mjs → he_DRWoI1A5.mjs} +1 -1
  55. package/dist/server/chunks/{i18n_BKVjk7jX.mjs → i18n_BqFK0MIW.mjs} +64 -15
  56. package/dist/server/chunks/{index_eYmi9sSz.mjs → index_B77wdzK2.mjs} +1 -1
  57. package/dist/server/chunks/{index_w8inlUI7.mjs → index_CBfRX_VO.mjs} +93 -45
  58. package/dist/server/chunks/{index_DBrNG14W.mjs → index_Cv2j9xC_.mjs} +5 -5
  59. package/dist/server/chunks/{index_DdCS_Yn8.mjs → index_DQ5pFuPN.mjs} +2 -2
  60. package/dist/server/chunks/{index_BTLdKNys.mjs → index_uv0IHg8r.mjs} +2 -2
  61. package/dist/server/chunks/{it_BiUDLszZ.mjs → it_BDY3lerP.mjs} +1 -1
  62. package/dist/server/chunks/{ja_DjFAG7hL.mjs → ja_C-EybQiV.mjs} +1 -1
  63. package/dist/server/chunks/{login_xmDNRy6-.mjs → login_D-Z6bo6E.mjs} +1 -1
  64. package/dist/server/chunks/{new_QzmF9JQw.mjs → new_DpJfTahF.mjs} +2 -2
  65. package/dist/server/chunks/{new_BeqZaUZk.mjs → new_XOnqlHm3.mjs} +12 -10
  66. package/dist/server/chunks/{operations_CzDlpc-d.mjs → operations_0sYvzzNC.mjs} +2 -2
  67. package/dist/server/chunks/{pl_0Xe9Qom2.mjs → pl_DH5LKmVG.mjs} +1 -1
  68. package/dist/server/chunks/{plugins_Df0WEWyi.mjs → plugins_Clk92l6Q.mjs} +2 -2
  69. package/dist/server/chunks/{post-types_7UOUQOdJ.mjs → post-types_DziideHh.mjs} +40 -14
  70. package/dist/server/chunks/{pt-BR_DIZpU2Mk.mjs → pt-BR_CBnqy3xP.mjs} +47 -2
  71. package/dist/server/chunks/{resources_D5QYgIOn.mjs → resources_dPTDnpAf.mjs} +2 -2
  72. package/dist/server/chunks/{ru_DFvIxd0Y.mjs → ru_CFa1xYFr.mjs} +1 -1
  73. package/dist/server/chunks/{sw_CmaPgGYa.mjs → sw_QZf7NSsI.mjs} +1 -1
  74. package/dist/server/chunks/{taxonomies_DIuTYSy1.mjs → taxonomies_Bc6xPaKp.mjs} +2 -2
  75. package/dist/server/chunks/taxonomy_Cv4_KTTL.mjs +152 -0
  76. package/dist/server/chunks/{themes_DAbb-26x.mjs → themes_BRtDtEK7.mjs} +2 -2
  77. package/dist/server/chunks/{tr_B_acwl2e.mjs → tr_Fjry5R-K.mjs} +1 -1
  78. package/dist/server/chunks/{uk_DXnk4imL.mjs → uk_Bxc3qY45.mjs} +1 -1
  79. package/dist/server/chunks/{upload_Bbn1yjFV.mjs → upload_CB_L9tkU.mjs} +2 -2
  80. package/dist/server/chunks/{yo_DDQ8a-0v.mjs → yo_BARL-Qrh.mjs} +1 -1
  81. package/dist/server/chunks/{zh-CN_763uzTMQ.mjs → zh-CN_CkjPJL88.mjs} +1 -1
  82. package/dist/server/entry.mjs +129 -74
  83. package/dist/server/virtual_astro_middleware.mjs +1 -1
  84. package/package.json +6 -5
  85. package/dist/client/_astro/ContentEditor.CNU6JpC2.js +0 -1
  86. package/dist/client/_astro/ContentList.CND9xLTq.js +0 -1
  87. package/dist/client/_astro/DashboardHome.DYshyax_.js +0 -1
  88. package/dist/client/_astro/DashboardLayout.CUyl_vSg.css +0 -1
  89. package/dist/client/_astro/DashboardLayout.astro_astro_type_script_index_0_lang.D6MgCJGl.js +0 -1
  90. package/dist/client/_astro/PostTypesManager.QU-bDofy.js +0 -1
  91. package/dist/client/_astro/api.DD5u5p4h.js +0 -1
  92. package/dist/client/_astro/i18n.sRt_iZ5u.js +0 -1
  93. package/dist/client/_astro/pt-BR.CyQAHnXs.js +0 -1
  94. /package/dist/server/chunks/{BaseLayout_D1PbsjNG.mjs → BaseLayout_m8b8Eemd.mjs} +0 -0
@@ -1,5 +1,5 @@
1
- import { n as useTranslation, r as apiClient } from "./i18n_BKVjk7jX.mjs";
2
- import { a as For2, d as createMemo$1, f as createSignal$1, l as Show2 } from "./DashboardLayout_svhEj8jj.mjs";
1
+ import { n as useTranslation, r as apiClient } from "./i18n_BqFK0MIW.mjs";
2
+ import { a as For2, d as createMemo$1, f as createSignal$1, l as Show2 } from "./DashboardLayout_09DepSVn.mjs";
3
3
  import { t as FieldTypePicker } from "./FieldTypePicker_CXwpmcQ3.mjs";
4
4
  import { createComponent, escape, memo, ssr, ssrAttribute, ssrHydrationKey } from "solid-js/web";
5
5
  import { For, Show, createMemo, createSignal, onMount } from "solid-js";
@@ -512,6 +512,312 @@ function expandCloneFields(fields) {
512
512
  return result;
513
513
  }
514
514
  //#endregion
515
+ //#region src/lib/sanitize.ts
516
+ /**
517
+ * Sanitizador de HTML em allowlist para pontos de injeção (innerHTML).
518
+ *
519
+ * O admin injeta HTML apenas no conteúdo das abas de ajuda (HelpTabs), que
520
+ * vem do i18n — mas todo innerHTML passa por aqui por defesa em profundidade.
521
+ *
522
+ * Garantias:
523
+ * - Allowlist de tags e atributos (script/style/svg/iframe/form são removidos,
524
+ * incluindo o conteúdo de tags de texto bruto como <script> e <style>).
525
+ * - Atributos de evento (on*), style e demais fora da allowlist são descartados.
526
+ * - href/src têm o protocolo validado DEPOIS de decodificar entidades e remover
527
+ * tab/newline — o valor emitido é re-escapado a partir do valor decodificado,
528
+ * de modo que o que o navegador decodifica é exatamente o que foi validado
529
+ * (bloqueia `javascript:`, `&#106;avascript:`, `javascript&colon;`, etc).
530
+ * - Texto é escapado (& < >), preservando entidades já válidas.
531
+ * - Tags de fechamento sem âncora são ignoradas: conteúdo malicioso não consegue
532
+ * "escapar" do container nem desbalancear a árvore (o quebra-cabeça de abas
533
+ * e widgets continua íntegro).
534
+ *
535
+ * Puro JavaScript, sem APIs de DOM: roda igual no SSR (Node) e no navegador.
536
+ */
537
+ /** Tags permitidas (conteúdo rico simples: ajuda, títulos, listas, links). */
538
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
539
+ "a",
540
+ "abbr",
541
+ "b",
542
+ "blockquote",
543
+ "br",
544
+ "code",
545
+ "dd",
546
+ "div",
547
+ "dl",
548
+ "dt",
549
+ "em",
550
+ "figcaption",
551
+ "figure",
552
+ "h1",
553
+ "h2",
554
+ "h3",
555
+ "h4",
556
+ "h5",
557
+ "h6",
558
+ "hr",
559
+ "i",
560
+ "img",
561
+ "li",
562
+ "ol",
563
+ "p",
564
+ "pre",
565
+ "q",
566
+ "s",
567
+ "span",
568
+ "strong",
569
+ "sub",
570
+ "sup",
571
+ "u",
572
+ "ul"
573
+ ]);
574
+ /** Tags vazias (emitidas com fechamento próprio, nunca empilhadas). */
575
+ var VOID_TAGS = /* @__PURE__ */ new Set([
576
+ "br",
577
+ "hr",
578
+ "img"
579
+ ]);
580
+ /**
581
+ * Tags cujo conteúdo é texto bruto no HTML. Ao encontrá-las abertas, todo o
582
+ * miolo é descartado até o fechamento correspondente (senão, o conteúdo do
583
+ * <script> viraria texto visível ou vetor de parse).
584
+ */
585
+ var RAW_TEXT_TAGS = /* @__PURE__ */ new Set([
586
+ "script",
587
+ "style",
588
+ "iframe",
589
+ "object",
590
+ "embed",
591
+ "svg",
592
+ "math",
593
+ "textarea",
594
+ "title",
595
+ "noscript",
596
+ "template"
597
+ ]);
598
+ /** Atributos permitidos (globais; validação extra por atributo abaixo). */
599
+ var ALLOWED_ATTRS = /* @__PURE__ */ new Set([
600
+ "class",
601
+ "title",
602
+ "alt",
603
+ "width",
604
+ "height",
605
+ "href",
606
+ "src",
607
+ "target",
608
+ "rel"
609
+ ]);
610
+ /** Esquemas aceitos em href (links externos, e-mail, telefone). */
611
+ var HREF_SCHEMES = /* @__PURE__ */ new Set([
612
+ "http",
613
+ "https",
614
+ "mailto",
615
+ "tel"
616
+ ]);
617
+ /** Esquemas aceitos em src (somente rede). */
618
+ var SRC_SCHEMES = /* @__PURE__ */ new Set(["http", "https"]);
619
+ /** Entidades nomeadas relevantes (as que poderiam alterar a validação de URL). */
620
+ var NAMED_ENTITIES = {
621
+ amp: "&",
622
+ lt: "<",
623
+ gt: ">",
624
+ quot: "\"",
625
+ apos: "'",
626
+ colon: ":",
627
+ tab: " ",
628
+ newline: "\n",
629
+ sol: "/",
630
+ lpar: "(",
631
+ rpar: ")",
632
+ num: "#",
633
+ period: ".",
634
+ excl: "!",
635
+ comma: ","
636
+ };
637
+ /** Decodifica referências numéricas e nomeadas (uma passada, como o navegador). */
638
+ function decodeEntities(value) {
639
+ return value.replace(/&#x([0-9a-fA-F]+);?/g, (_m, hex) => fromCodePoint(parseInt(hex, 16))).replace(/&#(\d+);?/g, (_m, dec) => fromCodePoint(parseInt(dec, 10))).replace(/&([a-zA-Z][a-zA-Z0-9]{1,30});?/g, (m, name) => {
640
+ return NAMED_ENTITIES[name in NAMED_ENTITIES ? name : name.toLowerCase()] ?? m;
641
+ });
642
+ }
643
+ function fromCodePoint(cp) {
644
+ if (!Number.isFinite(cp) || cp < 0 || cp > 1114111) return "";
645
+ try {
646
+ return String.fromCodePoint(cp);
647
+ } catch {
648
+ return "";
649
+ }
650
+ }
651
+ /** Escapa valor para uso dentro de atributo entre aspas duplas. */
652
+ function escapeAttr(value) {
653
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
654
+ }
655
+ /**
656
+ * Escapa texto preservando entidades já válidas (evita exibir `&amp;`
657
+ * literalmente quando a tradução usa `&amp;` de propósito).
658
+ */
659
+ function escapeText(text) {
660
+ return text.replace(/&(?![a-zA-Z][a-zA-Z0-9]{1,30};|#[0-9]{1,7};|#[xX][0-9a-fA-F]{1,6};)|[<>]/g, (m) => m === "&" ? "&amp;" : m === "<" ? "&lt;" : "&gt;");
661
+ }
662
+ /**
663
+ * Valida URL decodificando entidades e removendo tab/newline (mesmo tratamento
664
+ * do parser de URL do navegador) antes de inspecionar o esquema.
665
+ */
666
+ function isSafeUrl(raw, allowedSchemes) {
667
+ const decoded = decodeEntities(raw).replace(/[\t\n\r]/g, "").trim();
668
+ const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(decoded);
669
+ if (!scheme) return true;
670
+ return allowedSchemes.has(scheme[1].toLowerCase());
671
+ }
672
+ /** Normaliza valor de atributo: decodifica (como o navegador faria) e re-escapa. */
673
+ function normalizeAttrValue(raw) {
674
+ return escapeAttr(decodeEntities(raw));
675
+ }
676
+ /**
677
+ * Extrai pares nome/valor de uma tag, respeitando aspas (um `>` dentro de
678
+ * aspas não termina a tag) e valores não-aspados do HTML.
679
+ */
680
+ function parseAttrs(body) {
681
+ const pairs = [];
682
+ const n = body.length;
683
+ let i = 0;
684
+ const isWs = (c) => c === " " || c === " " || c === "\n" || c === "\r" || c === "\f";
685
+ while (i < n) {
686
+ while (i < n && (isWs(body.charAt(i)) || body.charAt(i) === "/")) i++;
687
+ if (i >= n) break;
688
+ let name = "";
689
+ while (i < n && !isWs(body.charAt(i)) && body.charAt(i) !== "=" && body.charAt(i) !== ">" && body.charAt(i) !== "/" && body.charAt(i) !== "<") {
690
+ name += body.charAt(i);
691
+ i++;
692
+ }
693
+ if (!name) {
694
+ i++;
695
+ continue;
696
+ }
697
+ let value = "";
698
+ let j = i;
699
+ while (j < n && isWs(body.charAt(j))) j++;
700
+ if (j < n && body.charAt(j) === "=") {
701
+ j++;
702
+ while (j < n && isWs(body.charAt(j))) j++;
703
+ if (j < n && (body.charAt(j) === "\"" || body.charAt(j) === "'")) {
704
+ const quote = body.charAt(j);
705
+ j++;
706
+ const start = j;
707
+ while (j < n && body.charAt(j) !== quote) j++;
708
+ value = body.slice(start, j);
709
+ j++;
710
+ } else {
711
+ const start = j;
712
+ while (j < n && !isWs(body.charAt(j)) && body.charAt(j) !== ">") j++;
713
+ value = body.slice(start, j);
714
+ }
715
+ i = j;
716
+ }
717
+ pairs.push([name.toLowerCase(), value]);
718
+ }
719
+ return pairs;
720
+ }
721
+ /** Filtra atributos: allowlist + validação de protocolo; força rel seguro. */
722
+ function filterAttrs(tag, pairs) {
723
+ const kept = [];
724
+ const seen = /* @__PURE__ */ new Set();
725
+ let targetBlank = false;
726
+ for (const [name, raw] of pairs) {
727
+ if (!ALLOWED_ATTRS.has(name) || seen.has(name)) continue;
728
+ if (name === "href" && !isSafeUrl(raw, HREF_SCHEMES)) continue;
729
+ if (name === "src" && !isSafeUrl(raw, SRC_SCHEMES)) continue;
730
+ if (name === "target") {
731
+ if (normalizeAttrValue(raw) !== "_blank") continue;
732
+ targetBlank = true;
733
+ continue;
734
+ }
735
+ if (name === "rel") continue;
736
+ seen.add(name);
737
+ kept.push(`${name}="${normalizeAttrValue(raw)}"`);
738
+ }
739
+ if (tag === "a" && targetBlank) {
740
+ kept.push("target=\"_blank\"");
741
+ kept.push("rel=\"noopener noreferrer\"");
742
+ }
743
+ return kept.length > 0 ? ` ${kept.join(" ")}` : "";
744
+ }
745
+ /**
746
+ * Sanitiza uma string de HTML. Retorna apenas marcação da allowlist.
747
+ * Idempotente e sem dependências de DOM (seguro no SSR e no cliente).
748
+ */
749
+ function sanitizeHtml(input) {
750
+ if (typeof input !== "string" || input.length === 0) return "";
751
+ const lower = input.toLowerCase();
752
+ const n = input.length;
753
+ const stack = [];
754
+ let out = "";
755
+ let i = 0;
756
+ while (i < n) {
757
+ const lt = input.indexOf("<", i);
758
+ if (lt === -1) {
759
+ out += escapeText(input.slice(i));
760
+ break;
761
+ }
762
+ if (lt > i) out += escapeText(input.slice(i, lt));
763
+ if (input.startsWith("<!--", lt)) {
764
+ const end = input.indexOf("-->", lt + 4);
765
+ i = end === -1 ? n : end + 3;
766
+ continue;
767
+ }
768
+ if (input.charAt(lt + 1) === "!" || input.charAt(lt + 1) === "?") {
769
+ const end = input.indexOf(">", lt + 2);
770
+ i = end === -1 ? n : end + 1;
771
+ continue;
772
+ }
773
+ const tagMatch = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)(?=[\s/>])/.exec(input.slice(lt, Math.min(lt + 64, n)));
774
+ if (!tagMatch) {
775
+ out += "&lt;";
776
+ i = lt + 1;
777
+ continue;
778
+ }
779
+ const isClosing = tagMatch[1] === "/";
780
+ const tag = tagMatch[2].toLowerCase();
781
+ let j = lt + tagMatch[0].length;
782
+ let quote = "";
783
+ while (j < n) {
784
+ const ch = input[j];
785
+ if (quote) {
786
+ if (ch === quote) quote = "";
787
+ } else if (ch === "\"" || ch === "'") quote = ch;
788
+ else if (ch === ">") break;
789
+ j++;
790
+ }
791
+ if (j >= n) break;
792
+ const body = input.slice(lt + tagMatch[0].length, j);
793
+ i = j + 1;
794
+ if (!isClosing && RAW_TEXT_TAGS.has(tag)) {
795
+ const closeAt = lower.indexOf(`</${tag}`, i);
796
+ if (closeAt === -1) break;
797
+ const closeEnd = input.indexOf(">", closeAt);
798
+ i = closeEnd === -1 ? n : closeEnd + 1;
799
+ continue;
800
+ }
801
+ if (isClosing) {
802
+ const anchor = stack.lastIndexOf(tag);
803
+ if (anchor !== -1) {
804
+ for (let k = stack.length - 1; k >= anchor; k--) out += `</${stack[k]}>`;
805
+ stack.length = anchor;
806
+ }
807
+ continue;
808
+ }
809
+ if (!ALLOWED_TAGS.has(tag)) continue;
810
+ const attrs = filterAttrs(tag, parseAttrs(body));
811
+ if (VOID_TAGS.has(tag)) out += `<${tag}${attrs} />`;
812
+ else {
813
+ out += `<${tag}${attrs}>`;
814
+ stack.push(tag);
815
+ }
816
+ }
817
+ for (let k = stack.length - 1; k >= 0; k--) out += `</${stack[k]}>`;
818
+ return out;
819
+ }
820
+ //#endregion
515
821
  //#region src/components/content/RelationshipPicker.tsx
516
822
  var _tmpl$$2 = [
517
823
  "<ul",
@@ -636,8 +942,8 @@ var _tmpl$8$1 = [
636
942
  ">"
637
943
  ];
638
944
  var _tmpl$9$1 = ["<input", " type=\"email\" class=\"input\">"];
639
- var _tmpl$0 = ["<input", " type=\"url\" class=\"input\" placeholder=\"https://\">"];
640
- var _tmpl$1 = ["<input", " type=\"password\" class=\"input\">"];
945
+ var _tmpl$0$1 = ["<input", " type=\"url\" class=\"input\" placeholder=\"https://\">"];
946
+ var _tmpl$1$1 = ["<input", " type=\"password\" class=\"input\">"];
641
947
  var _tmpl$10 = [
642
948
  "<input",
643
949
  " type=\"checkbox\"",
@@ -833,8 +1139,8 @@ function FieldControl(props) {
833
1139
  case "number":
834
1140
  case "range": return ssr(_tmpl$8$1, ssrHydrationKey(), ssrAttribute("min", escape(config()["minValue"], true), false) + ssrAttribute("max", escape(config()["maxValue"], true), false) + ssrAttribute("step", escape(config()["step"], true), false));
835
1141
  case "email": return ssr(_tmpl$9$1, ssrHydrationKey());
836
- case "url": return ssr(_tmpl$0, ssrHydrationKey());
837
- case "password": return ssr(_tmpl$1, ssrHydrationKey());
1142
+ case "url": return ssr(_tmpl$0$1, ssrHydrationKey());
1143
+ case "password": return ssr(_tmpl$1$1, ssrHydrationKey());
838
1144
  case "true_false": return ssr(_tmpl$10, ssrHydrationKey(), ssrAttribute("checked", Boolean(props.value), true));
839
1145
  case "select": return ssr(_tmpl$11, ssrHydrationKey(), ssrAttribute("multiple", config()["multiple"] === true, true), escape(createComponent(For, {
840
1146
  get each() {
@@ -1045,73 +1351,90 @@ var _tmpl$ = [
1045
1351
  "</div>"
1046
1352
  ];
1047
1353
  var _tmpl$2 = [
1048
- "<label",
1049
- "><!--$-->",
1050
- "<!--/--><input class=\"input\" required",
1051
- "></label>"
1354
+ "<input",
1355
+ " class=\"classic-editor__title\" required",
1356
+ ">"
1052
1357
  ];
1053
1358
  var _tmpl$3 = [
1054
- "<label",
1055
- "><!--$-->",
1056
- "<!--/--><textarea class=\"input\" rows=\"3\"",
1057
- "></textarea></label>"
1359
+ "<div",
1360
+ " class=\"classic-editor__toolbar\" role=\"toolbar\"",
1361
+ "><button type=\"button\"><strong>B</strong></button><button type=\"button\"><em>I</em></button><button type=\"button\"><u>U</u></button><span class=\"classic-editor__toolbar-divider\"></span><button type=\"button\">❝</button><button type=\"button\">• List</button><button type=\"button\">1. List</button><button type=\"button\">Link</button></div>"
1058
1362
  ];
1059
1363
  var _tmpl$4 = [
1060
- "<label",
1061
- "><!--$-->",
1062
- "<!--/--><input class=\"input\"",
1063
- "></label>"
1364
+ "<div",
1365
+ " class=\"classic-editor__body\" contenteditable=\"true\" data-classic-editor role=\"textbox\" aria-multiline=\"true\"",
1366
+ ">",
1367
+ "</div>"
1064
1368
  ];
1065
1369
  var _tmpl$5 = [
1066
1370
  "<label",
1067
- "><!--$-->",
1068
- "<!--/--><select class=\"input\"",
1069
- "><option value>",
1070
- "</option><!--$-->",
1071
- "<!--/--></select></label>"
1371
+ " class=\"classic-editor__excerpt\"><span>",
1372
+ "</span><textarea class=\"input\" rows=\"4\"",
1373
+ "></textarea></label>"
1072
1374
  ];
1073
1375
  var _tmpl$6 = [
1074
- "<div",
1376
+ "<section",
1075
1377
  " class=\"card custom-fields\"><h3>",
1076
1378
  "</h3><!--$-->",
1077
1379
  "<!--/--><details><summary>",
1078
1380
  "</summary><!--$-->",
1079
- "<!--/--></details></div>"
1381
+ "<!--/--></details></section>"
1080
1382
  ];
1081
1383
  var _tmpl$7 = [
1384
+ "<section",
1385
+ " class=\"card classic-editor__panel\"><h3>",
1386
+ "</h3><input class=\"input\"",
1387
+ "><p class=\"muted\">",
1388
+ "</p></section>"
1389
+ ];
1390
+ var _tmpl$8 = [
1391
+ "<section",
1392
+ " class=\"card classic-editor__panel\"><h3>",
1393
+ "</h3><label><!--$-->",
1394
+ "<!--/--><select class=\"input\"",
1395
+ "><option value>",
1396
+ "</option><!--$-->",
1397
+ "<!--/--></select></label></section>"
1398
+ ];
1399
+ var _tmpl$9 = [
1082
1400
  "<form",
1083
- " class=\"content-editor\"><!--$-->",
1401
+ " class=\"content-editor classic-editor\"><!--$-->",
1084
1402
  "<!--/--><!--$-->",
1085
- "<!--/--><div class=\"card\"><label><!--$-->",
1086
- "<!--/--><select class=\"input\"",
1403
+ "<!--/--><div class=\"classic-editor__topbar\"><div><span class=\"classic-editor__eyebrow\">",
1404
+ "</span><h2>",
1405
+ "</h2></div><div class=\"classic-editor__actions\"><a class=\"btn btn-secondary\" href=\"",
1406
+ "\">",
1407
+ "</a><button type=\"submit\" class=\"btn btn-primary\"",
1087
1408
  ">",
1088
- "</select></label><!--$-->",
1089
- "<!--/--><label><!--$-->",
1090
- "<!--/--><input class=\"input\"",
1091
- "></label><!--$-->",
1409
+ "</button></div></div><div class=\"classic-editor__columns\"><main class=\"classic-editor__main\"><section class=\"card classic-editor__canvas-card\"><!--$-->",
1410
+ "<!--/--><div class=\"classic-editor__permalink\"><span><!--$-->",
1411
+ "<!--/-->:</span><input class=\"input\"",
1412
+ "></div><!--$-->",
1092
1413
  "<!--/--><!--$-->",
1093
- "<!--/--><label><!--$-->",
1414
+ "<!--/--></section><!--$-->",
1415
+ "<!--/--></main><aside class=\"classic-editor__sidebar\"><section class=\"card classic-editor__panel\"><h3>",
1416
+ "</h3><label><!--$-->",
1094
1417
  "<!--/--><select class=\"input\"",
1095
1418
  "><option value=\"DRAFT\">",
1096
1419
  "</option><option value=\"PUBLISHED\">",
1097
1420
  "</option><option value=\"ARCHIVED\">",
1098
- "</option></select></label><!--$-->",
1099
- "<!--/--></div><!--$-->",
1100
- "<!--/--><button type=\"submit\" class=\"btn btn-primary\"",
1421
+ "</option></select></label><button type=\"submit\" class=\"btn btn-primary classic-editor__full-button\"",
1101
1422
  ">",
1102
- "</button></form>"
1423
+ "</button></section><!--$-->",
1424
+ "<!--/--><!--$-->",
1425
+ "<!--/--></aside></div></form>"
1103
1426
  ];
1104
- var _tmpl$8 = [
1427
+ var _tmpl$0 = [
1105
1428
  "<div",
1106
1429
  " class=\"notice notice--error\">",
1107
1430
  "</div>"
1108
1431
  ];
1109
- var _tmpl$9 = [
1432
+ var _tmpl$1 = [
1110
1433
  "<option",
1111
1434
  ">",
1112
1435
  "</option>"
1113
1436
  ];
1114
- function ContentEditor() {
1437
+ function ContentEditor(props) {
1115
1438
  const { t } = useTranslation();
1116
1439
  const [types, setTypes] = createSignal([]);
1117
1440
  const [fieldTypes, setFieldTypes] = createSignal([]);
@@ -1123,20 +1446,40 @@ function ContentEditor() {
1123
1446
  const [saving, setSaving] = createSignal(false);
1124
1447
  const [message, setMessage] = createSignal("");
1125
1448
  const [draft, setDraft] = createSignal({
1126
- type: "post",
1449
+ type: props.initialType || "post",
1127
1450
  title: "",
1128
1451
  status: "DRAFT"
1129
1452
  });
1130
- const supports = () => new Set(types().find((t) => t.slug === draft().type)?.supports ?? ["title", "editor"]);
1453
+ const supports = () => new Set(types().find((type) => type.slug === draft().type)?.supports ?? ["title", "editor"]);
1131
1454
  const hasSupport = (support) => supports().has(support);
1455
+ const typeListFallback = (available, requested) => requested || available[0]?.slug || "post";
1456
+ const availableLayouts = () => {
1457
+ const layouts = /* @__PURE__ */ new Set();
1458
+ for (const theme of themes()) for (const layout of theme.manifest?.provides?.layouts ?? []) layouts.add(layout);
1459
+ return [...layouts].sort();
1460
+ };
1461
+ const loadGroups = async (context) => {
1462
+ try {
1463
+ const query = typeof context === "string" ? { type: context } : context;
1464
+ const all = await apiClient.getFieldGroups(query.type ? query : void 0);
1465
+ const resolved = await Promise.all(all.filter((group) => group.active).map(async (group) => {
1466
+ return (await apiClient.getFieldGroup(group.id)).fields ?? [];
1467
+ }));
1468
+ setGroups(expandCloneFields(resolved.flat()));
1469
+ } catch (err) {
1470
+ setErrors([err instanceof Error ? err.message : String(err)]);
1471
+ }
1472
+ };
1132
1473
  onMount(async () => {
1474
+ let typeList = [];
1133
1475
  try {
1134
- const [typeList, fieldTypeCatalog, themeList] = await Promise.all([
1476
+ const [typeCatalog, fieldTypeCatalog, themeList] = await Promise.all([
1135
1477
  apiClient.getContentTypes(),
1136
1478
  apiClient.getFieldTypes(),
1137
1479
  apiClient.getThemes().catch(() => [])
1138
1480
  ]);
1139
- setTypes(typeList);
1481
+ typeList = typeCatalog;
1482
+ setTypes(typeCatalog);
1140
1483
  setFieldTypes(fieldTypeCatalog.types);
1141
1484
  setCategories(fieldTypeCatalog.categories);
1142
1485
  setThemes(themeList);
@@ -1144,8 +1487,15 @@ function ContentEditor() {
1144
1487
  setErrors([err instanceof Error ? err.message : String(err)]);
1145
1488
  }
1146
1489
  const editId = new URLSearchParams(window.location.search).get("id");
1147
- if (editId) try {
1148
- const existing = await apiClient.getContentById(editId);
1490
+ if (editId || props.singleton) try {
1491
+ const existing = editId ? await apiClient.getContentById(editId) : await apiClient.getSingletonContent(draft().type);
1492
+ if (!existing) {
1493
+ await loadGroups({
1494
+ type: draft().type,
1495
+ status: draft().status
1496
+ });
1497
+ return;
1498
+ }
1149
1499
  const rawRow = existing;
1150
1500
  setDraft({
1151
1501
  id: existing.id,
@@ -1161,30 +1511,21 @@ function ContentEditor() {
1161
1511
  } catch (err) {
1162
1512
  setErrors([err instanceof Error ? err.message : String(err)]);
1163
1513
  }
1514
+ else if (!typeList.some((type) => type.slug === draft().type)) setDraft({
1515
+ ...draft(),
1516
+ type: typeListFallback(typeList, props.initialType)
1517
+ });
1164
1518
  await loadGroups({
1165
1519
  type: draft().type,
1166
1520
  slug: draft().slug,
1167
1521
  status: draft().status
1168
1522
  });
1169
1523
  });
1170
- const availableLayouts = () => {
1171
- const layouts = /* @__PURE__ */ new Set();
1172
- for (const theme of themes()) for (const layout of theme.manifest?.provides?.layouts ?? []) layouts.add(layout);
1173
- return [...layouts].sort();
1174
- };
1175
- const loadGroups = async (context) => {
1176
- try {
1177
- const query = typeof context === "string" ? { type: context } : context;
1178
- const all = await apiClient.getFieldGroups(query.type ? query : void 0);
1179
- const resolved = await Promise.all(all.filter((group) => group.active).map(async (group) => {
1180
- return (await apiClient.getFieldGroup(group.id)).fields ?? [];
1181
- }));
1182
- setGroups(expandCloneFields(resolved.flat()));
1183
- } catch (err) {
1184
- setErrors([err instanceof Error ? err.message : String(err)]);
1185
- }
1524
+ const editorContent = () => {
1525
+ const value = data()["content"];
1526
+ return typeof value === "string" ? value : "";
1186
1527
  };
1187
- return ssr(_tmpl$7, ssrHydrationKey(), escape(createComponent(Show, {
1528
+ return ssr(_tmpl$9, ssrHydrationKey(), escape(createComponent(Show, {
1188
1529
  get when() {
1189
1530
  return errors().length > 0;
1190
1531
  },
@@ -1193,7 +1534,7 @@ function ContentEditor() {
1193
1534
  get each() {
1194
1535
  return errors();
1195
1536
  },
1196
- children: (err) => ssr(_tmpl$8, ssrHydrationKey(), escape(err))
1537
+ children: (err) => ssr(_tmpl$0, ssrHydrationKey(), escape(err))
1197
1538
  });
1198
1539
  }
1199
1540
  })), escape(createComponent(Show, {
@@ -1203,43 +1544,26 @@ function ContentEditor() {
1203
1544
  get children() {
1204
1545
  return ssr(_tmpl$, ssrHydrationKey(), escape(message()));
1205
1546
  }
1206
- })), escape(t("content.editor.type")), ssrAttribute("value", escape(draft().type, true), false), escape(createComponent(For, {
1207
- get each() {
1208
- return types();
1209
- },
1210
- children: (type) => ssr(_tmpl$9, ssrHydrationKey() + ssrAttribute("value", escape(type.slug, true), false), escape(type.pluralLabel))
1211
- })), escape(createComponent(Show, {
1547
+ })), escape(types().find((type) => type.slug === draft().type)?.singularLabel || draft().type), draft().id ? escape(t("content.editor.editing")) : escape(t("content.editor.adding")), `/content/${escape(encodeURIComponent(draft().type), true)}`, escape(t("content.editor.backToList")), ssrAttribute("disabled", saving(), true), saving() ? escape(t("content.editor.saving")) : escape(t("content.editor.save")), escape(createComponent(Show, {
1212
1548
  get when() {
1213
1549
  return hasSupport("title");
1214
1550
  },
1215
1551
  get children() {
1216
- return ssr(_tmpl$2, ssrHydrationKey(), escape(t("content.editor.title")), ssrAttribute("value", escape(draft().title, true), false));
1552
+ return ssr(_tmpl$2, ssrHydrationKey(), ssrAttribute("aria-label", escape(t("content.editor.title"), true), false) + ssrAttribute("placeholder", escape(t("content.editor.titlePlaceholder"), true), false) + ssrAttribute("value", escape(draft().title, true), false));
1217
1553
  }
1218
- })), escape(t("content.editor.slug")), ssrAttribute("value", escape(draft().slug ?? "", true), false), escape(createComponent(Show, {
1554
+ })), escape(t("content.editor.slug")), ssrAttribute("value", escape(draft().slug ?? "", true), false) + ssrAttribute("placeholder", escape(t("content.editor.slugPlaceholder"), true), false), escape(createComponent(Show, {
1219
1555
  get when() {
1220
- return hasSupport("excerpt");
1556
+ return hasSupport("editor");
1221
1557
  },
1222
1558
  get children() {
1223
- return ssr(_tmpl$3, ssrHydrationKey(), escape(t("content.editor.excerpt")), ssrAttribute("value", escape(draft().excerpt ?? "", true), false));
1559
+ return [ssr(_tmpl$3, ssrHydrationKey(), ssrAttribute("aria-label", escape(t("content.editor.toolbar"), true), false)), ssr(_tmpl$4, ssrHydrationKey(), ssrAttribute("data-placeholder", escape(t("content.editor.bodyPlaceholder"), true), false), sanitizeHtml(editorContent()))];
1224
1560
  }
1225
1561
  })), escape(createComponent(Show, {
1226
1562
  get when() {
1227
- return hasSupport("thumbnail");
1563
+ return hasSupport("excerpt");
1228
1564
  },
1229
1565
  get children() {
1230
- return ssr(_tmpl$4, ssrHydrationKey(), escape(t("content.editor.featuredImage")), ssrAttribute("value", escape(draft().featuredImageId ?? "", true), false));
1231
- }
1232
- })), escape(t("content.editor.status")), ssrAttribute("value", escape(draft().status, true), false), escape(t("content.editor.statusOptions.draft")), escape(t("content.editor.statusOptions.published")), escape(t("content.editor.statusOptions.archived")), escape(createComponent(Show, {
1233
- get when() {
1234
- return availableLayouts().length > 0;
1235
- },
1236
- get children() {
1237
- return ssr(_tmpl$5, ssrHydrationKey(), escape(t("content.editor.layout")), ssrAttribute("value", escape(draft().layout ?? "", true), false), escape(t("content.editor.layoutAuto")), escape(createComponent(For, {
1238
- get each() {
1239
- return availableLayouts();
1240
- },
1241
- children: (layout) => ssr(_tmpl$9, ssrHydrationKey() + ssrAttribute("value", escape(layout, true), false), escape(layout))
1242
- })));
1566
+ return ssr(_tmpl$5, ssrHydrationKey(), escape(t("content.editor.excerpt")), ssrAttribute("value", escape(draft().excerpt ?? "", true), false));
1243
1567
  }
1244
1568
  })), escape(createComponent(Show, {
1245
1569
  get when() {
@@ -1271,7 +1595,26 @@ function ContentEditor() {
1271
1595
  onChange: () => void 0
1272
1596
  })));
1273
1597
  }
1274
- })), ssrAttribute("disabled", saving(), true), escape(t("content.editor.save")));
1598
+ })), escape(t("content.editor.publishPanel")), escape(t("content.editor.status")), ssrAttribute("value", escape(draft().status, true), false), escape(t("content.editor.statusOptions.draft")), escape(t("content.editor.statusOptions.published")), escape(t("content.editor.statusOptions.archived")), ssrAttribute("disabled", saving(), true), saving() ? escape(t("content.editor.saving")) : escape(t("content.editor.save")), escape(createComponent(Show, {
1599
+ get when() {
1600
+ return hasSupport("thumbnail");
1601
+ },
1602
+ get children() {
1603
+ return ssr(_tmpl$7, ssrHydrationKey(), escape(t("content.editor.featuredImage")), ssrAttribute("value", escape(draft().featuredImageId ?? "", true), false) + ssrAttribute("placeholder", escape(t("content.editor.mediaIdPlaceholder"), true), false), escape(t("content.editor.featuredImageHint")));
1604
+ }
1605
+ })), escape(createComponent(Show, {
1606
+ get when() {
1607
+ return availableLayouts().length > 0;
1608
+ },
1609
+ get children() {
1610
+ return ssr(_tmpl$8, ssrHydrationKey(), escape(t("content.editor.pageAttributes")), escape(t("content.editor.layout")), ssrAttribute("value", escape(draft().layout ?? "", true), false), escape(t("content.editor.layoutAuto")), escape(createComponent(For, {
1611
+ get each() {
1612
+ return availableLayouts();
1613
+ },
1614
+ children: (layout) => ssr(_tmpl$1, ssrHydrationKey() + ssrAttribute("value", escape(layout, true), false), escape(layout))
1615
+ })));
1616
+ }
1617
+ })));
1275
1618
  }
1276
1619
  //#endregion
1277
1620
  export { ContentEditor as t };