@webx-ui/module-seo 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { defineComponent as E, computed as U, openBlock as y, createBlock as W, unref as e, withCtx as t, renderSlot as ve, createVNode as l, createTextVNode as w, toDisplayString as h, ref as C, isRef as P, createElementVNode as j, withKeys as We, createElementBlock as F, Fragment as se, createCommentVNode as O, renderList as ge, useModel as Ce, resolveDynamicComponent as Te, mergeModels as ze, watch as be, createSlots as ye, markRaw as De } from "vue";
1
+ import { defineComponent as E, computed as U, openBlock as y, createBlock as W, unref as e, withCtx as t, renderSlot as ve, createVNode as l, createTextVNode as x, toDisplayString as h, ref as C, isRef as P, createElementVNode as j, withKeys as We, createElementBlock as F, Fragment as se, createCommentVNode as O, renderList as ge, useModel as Ce, resolveDynamicComponent as Te, mergeModels as ze, watch as be, createSlots as ye, markRaw as De } from "vue";
2
2
  import { useAdmin as K, useTranslate as J, WxListScreen as Oe, WxDate as _e, useErrorText as le, WxRowMenu as te } from "@webx-ui/module-admin";
3
- import { WxButton as I, useModal as ie, WxDialog as de, WxSpace as X, WxInput as N, WxAlert as Y, WxText as R, WxBadge as M, WxDescriptions as Re, WxDescriptionsItem as Le, WxEmpty as je, createModal as Z, WxTable as re, WxLink as Ae, useLocales as Be, WxTabs as Pe, WxTab as oe, WxFormItem as z, WxTextarea as me, WxCheckboxGroup as Fe, WxCodeEditor as Ne, localizedValue as xe, WxSelect as ae, WxSwitch as we, toast as B, confirm as ke, WxInputNumber as Ie } from "@webx-ui/core";
3
+ import { WxButton as I, useModal as ie, WxDialog as de, WxSpace as X, WxInput as N, WxAlert as Y, WxText as R, WxBadge as M, WxDescriptions as Re, WxDescriptionsItem as Le, WxEmpty as je, createModal as Z, WxTable as re, WxLink as Ae, useLocales as Be, WxTabs as Pe, WxTab as oe, WxFormItem as z, WxTextarea as me, WxCheckboxGroup as Fe, WxCodeEditor as Ne, localizedValue as we, WxSelect as ae, WxSwitch as xe, toast as B, confirm as ke, WxInputNumber as Ie } from "@webx-ui/core";
4
4
  import { useRouter as Me, useRoute as Ee } from "vue-router";
5
5
  const Ke = {
6
6
  module: {
@@ -133,7 +133,7 @@ const ue = /* @__PURE__ */ E({
133
133
  onClick: u[0] || (u[0] = (V) => c("test"))
134
134
  }, {
135
135
  default: t(() => [
136
- w(h(e(s)("page.test")), 1)
136
+ x(h(e(s)("page.test")), 1)
137
137
  ]),
138
138
  _: 1
139
139
  })
@@ -246,7 +246,7 @@ const Je = { class: "wx-seo-test" }, qe = {
246
246
  onClick: k
247
247
  }, {
248
248
  default: t(() => [
249
- w(h(e(a)("page.test-run")), 1)
249
+ x(h(e(a)("page.test-run")), 1)
250
250
  ]),
251
251
  _: 1
252
252
  }, 8, ["loading"])
@@ -273,7 +273,7 @@ const Je = { class: "wx-seo-test" }, qe = {
273
273
  tone: "muted"
274
274
  }, {
275
275
  default: t(() => [
276
- w(h(e(a)("page.test-matched")), 1)
276
+ x(h(e(a)("page.test-matched")), 1)
277
277
  ]),
278
278
  _: 1
279
279
  }),
@@ -284,7 +284,7 @@ const Je = { class: "wx-seo-test" }, qe = {
284
284
  default: t(() => [
285
285
  l(e(M), null, {
286
286
  default: t(() => [
287
- w(h(e(a)(`page.${s.value.matched.match_type}`)), 1)
287
+ x(h(e(a)(`page.${s.value.matched.match_type}`)), 1)
288
288
  ]),
289
289
  _: 1
290
290
  }),
@@ -293,7 +293,7 @@ const Je = { class: "wx-seo-test" }, qe = {
293
293
  size: "sm"
294
294
  }, {
295
295
  default: t(() => [
296
- w(h(s.value.matched.pattern), 1)
296
+ x(h(s.value.matched.pattern), 1)
297
297
  ]),
298
298
  _: 1
299
299
  })
@@ -311,7 +311,7 @@ const Je = { class: "wx-seo-test" }, qe = {
311
311
  tone: "muted"
312
312
  }, {
313
313
  default: t(() => [
314
- w(h(e(a)("page.test-result")), 1)
314
+ x(h(e(a)("page.test-result")), 1)
315
315
  ]),
316
316
  _: 1
317
317
  }),
@@ -326,7 +326,7 @@ const Je = { class: "wx-seo-test" }, qe = {
326
326
  label: u.label
327
327
  }, {
328
328
  default: t(() => [
329
- w(h(u.value), 1)
329
+ x(h(u.value), 1)
330
330
  ]),
331
331
  _: 2
332
332
  }, 1032, ["label"]))), 128))
@@ -340,7 +340,7 @@ const Je = { class: "wx-seo-test" }, qe = {
340
340
  tone: "muted"
341
341
  }, {
342
342
  default: t(() => [
343
- w(h(e(a)("page.test-chain")), 1)
343
+ x(h(e(a)("page.test-chain")), 1)
344
344
  ]),
345
345
  _: 1
346
346
  }),
@@ -353,7 +353,7 @@ const Je = { class: "wx-seo-test" }, qe = {
353
353
  key: u.source
354
354
  }, {
355
355
  default: t(() => [
356
- w(h(u.source) + " · " + h(u.priority), 1)
356
+ x(h(u.source) + " · " + h(u.priority), 1)
357
357
  ]),
358
358
  _: 2
359
359
  }, 1024))), 128))
@@ -437,7 +437,7 @@ const Je = { class: "wx-seo-test" }, qe = {
437
437
  size: "sm"
438
438
  }, {
439
439
  default: t(() => [
440
- w(h(V.pattern), 1)
440
+ x(h(V.pattern), 1)
441
441
  ]),
442
442
  _: 2
443
443
  }, 1024)
@@ -455,7 +455,7 @@ const Je = { class: "wx-seo-test" }, qe = {
455
455
  size: "sm"
456
456
  }, {
457
457
  default: t(() => [
458
- w(h(V.target), 1)
458
+ x(h(V.target), 1)
459
459
  ]),
460
460
  _: 2
461
461
  }, 1024)
@@ -466,7 +466,7 @@ const Je = { class: "wx-seo-test" }, qe = {
466
466
  type: "warning"
467
467
  }, {
468
468
  default: t(() => [
469
- w(h(e(a)("page.gone")), 1)
469
+ x(h(e(a)("page.gone")), 1)
470
470
  ]),
471
471
  _: 1
472
472
  }))
@@ -474,7 +474,7 @@ const Je = { class: "wx-seo-test" }, qe = {
474
474
  "cell-locale": t(({ row: V }) => [
475
475
  l(e(M), null, {
476
476
  default: t(() => [
477
- w(h(V.locale), 1)
477
+ x(h(V.locale), 1)
478
478
  ]),
479
479
  _: 2
480
480
  }, 1024)
@@ -550,7 +550,7 @@ const Je = { class: "wx-seo-test" }, qe = {
550
550
  }
551
551
  }), o = U(
552
552
  () => ne.map((_) => ({ value: _, label: c(`card.${_}`) }))
553
- ), x = U({
553
+ ), w = U({
554
554
  get: () => {
555
555
  if (i.value !== null) return i.value;
556
556
  const _ = d.value?.json_ld;
@@ -569,7 +569,7 @@ const Je = { class: "wx-seo-test" }, qe = {
569
569
  }
570
570
  });
571
571
  function v(_) {
572
- return xe(_, a.active.value);
572
+ return we(_, a.active.value);
573
573
  }
574
574
  const ee = U(() => ({
575
575
  title: v(S.value).length,
@@ -609,7 +609,7 @@ const Je = { class: "wx-seo-test" }, qe = {
609
609
  tone: pe(ee.value.title, n.titleLimit)
610
610
  }, {
611
611
  default: t(() => [
612
- w(h(ee.value.title) + " / " + h(n.titleLimit), 1)
612
+ x(h(ee.value.title) + " / " + h(n.titleLimit), 1)
613
613
  ]),
614
614
  _: 1
615
615
  }, 8, ["tone"])
@@ -647,7 +647,7 @@ const Je = { class: "wx-seo-test" }, qe = {
647
647
  tone: pe(ee.value.description, n.descriptionLimit)
648
648
  }, {
649
649
  default: t(() => [
650
- w(h(ee.value.description) + " / " + h(n.descriptionLimit), 1)
650
+ x(h(ee.value.description) + " / " + h(n.descriptionLimit), 1)
651
651
  ]),
652
652
  _: 1
653
653
  }, 8, ["tone"])
@@ -674,7 +674,7 @@ const Je = { class: "wx-seo-test" }, qe = {
674
674
  tone: "muted"
675
675
  }, {
676
676
  default: t(() => [
677
- w(h(e(c)("card.preview")), 1)
677
+ x(h(e(c)("card.preview")), 1)
678
678
  ]),
679
679
  _: 1
680
680
  }),
@@ -778,8 +778,8 @@ const Je = { class: "wx-seo-test" }, qe = {
778
778
  }, {
779
779
  default: t(() => [
780
780
  l(e(Ne), {
781
- modelValue: x.value,
782
- "onUpdate:modelValue": $[9] || ($[9] = (T) => x.value = T),
781
+ modelValue: w.value,
782
+ "onUpdate:modelValue": $[9] || ($[9] = (T) => w.value = T),
783
783
  language: "json",
784
784
  lint: "",
785
785
  "min-height": "120px",
@@ -853,15 +853,15 @@ const Je = { class: "wx-seo-test" }, qe = {
853
853
  const o = r.redirect ? await f.updateRedirect(r.redirect.id, u.value) : await f.createRedirect(u.value);
854
854
  B.success(s("page.saved")), d(o);
855
855
  } catch (o) {
856
- const x = o.body;
857
- x?.errors ? (g.value = x.errors, B.danger(s("page.failed"))) : B.danger(S(o, s("page.failed")));
856
+ const w = o.body;
857
+ w?.errors ? (g.value = w.errors, B.danger(s("page.failed"))) : B.danger(S(o, s("page.failed")));
858
858
  } finally {
859
859
  k.value = !1;
860
860
  }
861
861
  }
862
- return (o, x) => (y(), W(e(de), {
862
+ return (o, w) => (y(), W(e(de), {
863
863
  open: e(a),
864
- "onUpdate:open": x[6] || (x[6] = (v) => P(a) ? a.value = v : null),
864
+ "onUpdate:open": w[6] || (w[6] = (v) => P(a) ? a.value = v : null),
865
865
  title: e(s)("page.redirect"),
866
866
  width: 640
867
867
  }, {
@@ -870,10 +870,10 @@ const Je = { class: "wx-seo-test" }, qe = {
870
870
  default: t(() => [
871
871
  l(e(I), {
872
872
  variant: "outline",
873
- onClick: x[5] || (x[5] = (v) => e(c)())
873
+ onClick: w[5] || (w[5] = (v) => e(c)())
874
874
  }, {
875
875
  default: t(() => [
876
- w(h(e(s)("page.cancel")), 1)
876
+ x(h(e(s)("page.cancel")), 1)
877
877
  ]),
878
878
  _: 1
879
879
  }),
@@ -883,7 +883,7 @@ const Je = { class: "wx-seo-test" }, qe = {
883
883
  onClick: b
884
884
  }, {
885
885
  default: t(() => [
886
- w(h(V.value ? e(s)("page.save") : e(s)("page.new-redirect")), 1)
886
+ x(h(V.value ? e(s)("page.save") : e(s)("page.new-redirect")), 1)
887
887
  ]),
888
888
  _: 1
889
889
  }, 8, ["loading"])
@@ -899,7 +899,7 @@ const Je = { class: "wx-seo-test" }, qe = {
899
899
  default: t(() => [
900
900
  l(e(ae), {
901
901
  modelValue: u.value.match_type,
902
- "onUpdate:modelValue": x[0] || (x[0] = (v) => u.value.match_type = v),
902
+ "onUpdate:modelValue": w[0] || (w[0] = (v) => u.value.match_type = v),
903
903
  options: L.value,
904
904
  onChange: D
905
905
  }, null, 8, ["modelValue", "options"])
@@ -914,7 +914,7 @@ const Je = { class: "wx-seo-test" }, qe = {
914
914
  default: t(() => [
915
915
  l(e(N), {
916
916
  modelValue: u.value.pattern,
917
- "onUpdate:modelValue": x[1] || (x[1] = (v) => u.value.pattern = v),
917
+ "onUpdate:modelValue": w[1] || (w[1] = (v) => u.value.pattern = v),
918
918
  placeholder: "/old-address",
919
919
  onBlur: D
920
920
  }, null, 8, ["modelValue"])
@@ -935,7 +935,7 @@ const Je = { class: "wx-seo-test" }, qe = {
935
935
  default: t(() => [
936
936
  l(e(N), {
937
937
  modelValue: u.value.target,
938
- "onUpdate:modelValue": x[2] || (x[2] = (v) => u.value.target = v),
938
+ "onUpdate:modelValue": w[2] || (w[2] = (v) => u.value.target = v),
939
939
  placeholder: "/new-address"
940
940
  }, null, 8, ["modelValue"])
941
941
  ]),
@@ -948,7 +948,7 @@ const Je = { class: "wx-seo-test" }, qe = {
948
948
  default: t(() => [
949
949
  l(e(ae), {
950
950
  modelValue: u.value.status,
951
- "onUpdate:modelValue": x[3] || (x[3] = (v) => u.value.status = v),
951
+ "onUpdate:modelValue": w[3] || (w[3] = (v) => u.value.status = v),
952
952
  options: A
953
953
  }, null, 8, ["modelValue"])
954
954
  ]),
@@ -958,9 +958,9 @@ const Je = { class: "wx-seo-test" }, qe = {
958
958
  label: e(s)("page.state")
959
959
  }, {
960
960
  default: t(() => [
961
- l(e(we), {
961
+ l(e(xe), {
962
962
  modelValue: u.value.is_active,
963
- "onUpdate:modelValue": x[4] || (x[4] = (v) => u.value.is_active = v)
963
+ "onUpdate:modelValue": w[4] || (w[4] = (v) => u.value.is_active = v)
964
964
  }, null, 8, ["modelValue"])
965
965
  ]),
966
966
  _: 1
@@ -1063,7 +1063,7 @@ const Je = { class: "wx-seo-test" }, qe = {
1063
1063
  size: "sm"
1064
1064
  }, {
1065
1065
  default: t(() => [
1066
- w(h(o.pattern), 1)
1066
+ x(h(o.pattern), 1)
1067
1067
  ]),
1068
1068
  _: 2
1069
1069
  }, 1024),
@@ -1072,7 +1072,7 @@ const Je = { class: "wx-seo-test" }, qe = {
1072
1072
  type: "warning"
1073
1073
  }, {
1074
1074
  default: t(() => [
1075
- w(h(e(a)("page.loop")), 1)
1075
+ x(h(e(a)("page.loop")), 1)
1076
1076
  ]),
1077
1077
  _: 1
1078
1078
  })) : O("", !0)
@@ -1083,7 +1083,7 @@ const Je = { class: "wx-seo-test" }, qe = {
1083
1083
  size: "sm"
1084
1084
  }, {
1085
1085
  default: t(() => [
1086
- w(h(o.target), 1)
1086
+ x(h(o.target), 1)
1087
1087
  ]),
1088
1088
  _: 2
1089
1089
  }, 1024)
@@ -1100,7 +1100,7 @@ const Je = { class: "wx-seo-test" }, qe = {
1100
1100
  dot: ""
1101
1101
  }, {
1102
1102
  default: t(() => [
1103
- w(h(o.is_active ? e(a)("page.active") : e(a)("page.inactive")), 1)
1103
+ x(h(o.is_active ? e(a)("page.active") : e(a)("page.inactive")), 1)
1104
1104
  ]),
1105
1105
  _: 2
1106
1106
  }, 1032, ["type"])
@@ -1132,7 +1132,7 @@ const Je = { class: "wx-seo-test" }, qe = {
1132
1132
  onClick: b[0] || (b[0] = (o) => A(null))
1133
1133
  }, {
1134
1134
  default: t(() => [
1135
- w(h(e(a)("page.new-redirect")), 1)
1135
+ x(h(e(a)("page.new-redirect")), 1)
1136
1136
  ]),
1137
1137
  _: 1
1138
1138
  })
@@ -1154,7 +1154,7 @@ const Je = { class: "wx-seo-test" }, qe = {
1154
1154
  "json_ld"
1155
1155
  ];
1156
1156
  function fe(n, r) {
1157
- return xe(n.title, r);
1157
+ return we(n.title, r);
1158
1158
  }
1159
1159
  function ht(n) {
1160
1160
  if (!n) return {};
@@ -1232,7 +1232,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1232
1232
  onClick: b[5] || (b[5] = (o) => e(c)())
1233
1233
  }, {
1234
1234
  default: t(() => [
1235
- w(h(e(s)("page.cancel")), 1)
1235
+ x(h(e(s)("page.cancel")), 1)
1236
1236
  ]),
1237
1237
  _: 1
1238
1238
  }),
@@ -1242,7 +1242,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1242
1242
  onClick: D
1243
1243
  }, {
1244
1244
  default: t(() => [
1245
- w(h(V.value ? e(s)("page.save") : e(s)("page.new-rule")), 1)
1245
+ x(h(V.value ? e(s)("page.save") : e(s)("page.new-rule")), 1)
1246
1246
  ]),
1247
1247
  _: 1
1248
1248
  }, 8, ["loading"])
@@ -1297,7 +1297,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1297
1297
  label: e(s)("page.state")
1298
1298
  }, {
1299
1299
  default: t(() => [
1300
- l(e(we), {
1300
+ l(e(xe), {
1301
1301
  modelValue: p.value.is_active,
1302
1302
  "onUpdate:modelValue": b[3] || (b[3] = (o) => p.value.is_active = o)
1303
1303
  }, null, 8, ["modelValue"])
@@ -1315,7 +1315,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1315
1315
  _: 1
1316
1316
  }, 8, ["open", "title"]));
1317
1317
  }
1318
- }), xt = /* @__PURE__ */ Q(_t, [["__scopeId", "data-v-fa975d05"]]), wt = /* @__PURE__ */ E({
1318
+ }), wt = /* @__PURE__ */ Q(_t, [["__scopeId", "data-v-fa975d05"]]), xt = /* @__PURE__ */ E({
1319
1319
  __name: "SeoUrlsPage",
1320
1320
  props: {
1321
1321
  base: { default: "/seo" },
@@ -1327,7 +1327,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1327
1327
  const a = J("webx-seo"), i = le(), f = C(null), s = C(!1), S = C("any");
1328
1328
  let k = { page: 1, perPage: 20, sort: null, search: "" };
1329
1329
  const g = d.can("seo.manage"), p = Z(
1330
- xt
1330
+ wt
1331
1331
  ), u = Z(ce), V = U(() => [
1332
1332
  { value: "any", label: a("page.any-kind") },
1333
1333
  { value: "exact", label: a("page.exact") },
@@ -1382,10 +1382,10 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1382
1382
  B.danger(i(v));
1383
1383
  }
1384
1384
  }
1385
- return (o, x) => (y(), W(ue, {
1385
+ return (o, w) => (y(), W(ue, {
1386
1386
  base: r.base,
1387
1387
  current: "rules",
1388
- onTest: x[2] || (x[2] = (v) => e(u)({}))
1388
+ onTest: w[2] || (w[2] = (v) => e(u)({}))
1389
1389
  }, ye({
1390
1390
  default: t(() => [
1391
1391
  l(e(re), {
@@ -1405,7 +1405,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1405
1405
  actions: t(() => [
1406
1406
  l(e(ae), {
1407
1407
  modelValue: S.value,
1408
- "onUpdate:modelValue": x[1] || (x[1] = (v) => S.value = v),
1408
+ "onUpdate:modelValue": w[1] || (w[1] = (v) => S.value = v),
1409
1409
  options: V.value,
1410
1410
  class: "wx-seo-urls__kind"
1411
1411
  }, null, 8, ["modelValue", "options"])
@@ -1416,7 +1416,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1416
1416
  size: "sm"
1417
1417
  }, {
1418
1418
  default: t(() => [
1419
- w(h(v.pattern), 1)
1419
+ x(h(v.pattern), 1)
1420
1420
  ]),
1421
1421
  _: 2
1422
1422
  }, 1024)
@@ -1424,7 +1424,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1424
1424
  "cell-match_type": t(({ row: v }) => [
1425
1425
  l(e(M), null, {
1426
1426
  default: t(() => [
1427
- w(h(e(a)(`page.${v.match_type}`)), 1)
1427
+ x(h(e(a)(`page.${v.match_type}`)), 1)
1428
1428
  ]),
1429
1429
  _: 2
1430
1430
  }, 1024)
@@ -1435,7 +1435,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1435
1435
  size: "sm"
1436
1436
  }, {
1437
1437
  default: t(() => [
1438
- w(h(e(fe)(v)), 1)
1438
+ x(h(e(fe)(v)), 1)
1439
1439
  ]),
1440
1440
  _: 2
1441
1441
  }, 1024)) : (y(), W(e(R), {
@@ -1443,8 +1443,8 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1443
1443
  size: "sm",
1444
1444
  tone: "muted"
1445
1445
  }, {
1446
- default: t(() => [...x[3] || (x[3] = [
1447
- w("—", -1)
1446
+ default: t(() => [...w[3] || (w[3] = [
1447
+ x("—", -1)
1448
1448
  ])]),
1449
1449
  _: 1
1450
1450
  }))
@@ -1455,7 +1455,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1455
1455
  dot: ""
1456
1456
  }, {
1457
1457
  default: t(() => [
1458
- w(h(v.is_active ? e(a)("page.active") : e(a)("page.inactive")), 1)
1458
+ x(h(v.is_active ? e(a)("page.active") : e(a)("page.inactive")), 1)
1459
1459
  ]),
1460
1460
  _: 2
1461
1461
  }, 1032, ["type"])
@@ -1484,10 +1484,10 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1484
1484
  l(e(I), {
1485
1485
  type: "primary",
1486
1486
  icon: "add",
1487
- onClick: x[0] || (x[0] = (v) => m(null))
1487
+ onClick: w[0] || (w[0] = (v) => m(null))
1488
1488
  }, {
1489
1489
  default: t(() => [
1490
- w(h(e(a)("page.new-rule")), 1)
1490
+ x(h(e(a)("page.new-rule")), 1)
1491
1491
  ]),
1492
1492
  _: 1
1493
1493
  })
@@ -1496,7 +1496,7 @@ const bt = { class: "wx-seo-rule" }, yt = { class: "wx-seo-rule__where" }, _t =
1496
1496
  } : void 0
1497
1497
  ]), 1032, ["base"]));
1498
1498
  }
1499
- }), kt = /* @__PURE__ */ Q(wt, [["__scopeId", "data-v-7a990610"]]);
1499
+ }), kt = /* @__PURE__ */ Q(xt, [["__scopeId", "data-v-7a990610"]]);
1500
1500
  function Wt(n = {}) {
1501
1501
  const r = n.path ?? "/seo", d = n.mediaField ? De(n.mediaField) : void 0;
1502
1502
  return {
@@ -1533,6 +1533,7 @@ function Wt(n = {}) {
1533
1533
  "wx-seo": {
1534
1534
  component: Ve,
1535
1535
  kind: "field",
1536
+ wide: !0,
1536
1537
  bind: () => ({ mediaField: d })
1537
1538
  }
1538
1539
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/messages.ts","../src/i18n.ts","../src/SeoLayout.vue","../src/api.ts","../src/TestUrlDialog.vue","../src/SeoAliasesPage.vue","../src/types.ts","../src/SeoCard.vue","../src/SeoRedirectDialog.vue","../src/SeoRedirectsPage.vue","../src/rule.ts","../src/SeoUrlDialog.vue","../src/SeoUrlsPage.vue","../src/module.ts"],"sourcesContent":["import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * What this package says, in English — the same keys `webx-ui/module-seo` ships as\n * `lang/en/*.php`, so a key never reaches the screen when a translation has not arrived yet.\n *\n * The server's dictionary wins over these. There is no second dictionary inside this package:\n * a module translated into ten languages with an English dialog in the middle of it is worse\n * than one that is not translated at all.\n *\n * Placeholders are Laravel's, `:like-this` — the same line is read from a PHP file and from\n * here, and only one of the two spellings can be right.\n */\nexport const seoMessages: Record<string, Messages> = {\n module: {\n title: 'SEO',\n },\n\n page: {\n rules: 'Rules',\n redirects: 'Redirects',\n test: 'Check an address',\n\n 'new-rule': 'New rule',\n 'new-redirect': 'New redirect',\n 'search-rules': 'Search by address',\n 'search-redirects': 'Search by address or destination',\n empty: 'Nothing here yet.',\n\n address: 'Address',\n kind: 'Kind',\n priority: 'Priority',\n state: 'State',\n active: 'Active',\n inactive: 'Off',\n title: 'Title',\n target: 'Destination',\n status: 'Code',\n hits: 'Hits',\n 'last-hit': 'Last used',\n loop: 'Points at itself',\n\n 'any-kind': 'Any kind',\n exact: 'Exact',\n mask: 'Mask',\n regex: 'Regular expression',\n\n rule: 'Rule',\n redirect: 'Redirect',\n save: 'Save',\n cancel: 'Cancel',\n delete: 'Delete',\n saved: 'Saved.',\n deleted: 'Deleted.',\n failed: 'Some values were not accepted. Check the highlighted fields.',\n 'delete-title': 'Delete :pattern?',\n 'delete-text': 'This cannot be undone.',\n\n 'address-help':\n 'A path with its query string. In a mask, * is one segment and ** is any number of them; a regular expression is stored as written, delimiters and all.',\n 'target-help':\n 'A path on this site, or a full address elsewhere. $1 puts back what a mask caught.',\n 'priority-help': 'Among rules of the same kind, the higher number wins.',\n\n 'test-placeholder': '/catalog/shoes?page=2',\n 'test-run': 'Check',\n 'test-empty': 'Type an address to see what the site will say about it.',\n 'test-matched': 'Matched rule',\n 'test-none': 'No rule matches this address.',\n 'test-redirected': 'This address is redirected to :target with a :status.',\n 'test-result': 'What the page will say',\n 'test-chain': 'Where each part came from',\n\n automatic: 'Automatic',\n 'search-aliases': 'Search by address or destination',\n 'aliases-empty': 'Nothing has moved yet.',\n 'aliases-help':\n 'The site writes these itself: renaming or moving a page leaves its old address behind answering 301, so nothing that linked to it dies. They cannot be edited here — a redirect of your own is tried first and wins.',\n language: 'Language',\n 'moved-at': 'Moved',\n gone: 'Leads nowhere',\n occupied:\n 'A page of the site answers at :path. A redirect is tried before it, so the page stops being reachable there.',\n 'occupied-alias':\n 'This address already leads to :target — what a move left behind. A redirect written here is tried first.',\n },\n\n card: {\n 'section-page': 'The page',\n 'section-share': 'Sharing',\n 'section-advanced': 'Advanced',\n\n title: 'Title',\n h1: 'Heading',\n description: 'Description',\n keywords: 'Keywords',\n canonical: 'Canonical address',\n 'canonical-help': 'The address this page should be indexed under, when it is not its own.',\n\n robots: 'Indexing',\n 'robots-kept': 'Also kept, as written: :directives',\n noindex: 'Keep out of the index',\n nofollow: 'Do not follow the links',\n noarchive: 'No cached copy',\n nosnippet: 'No snippet',\n noimageindex: 'Do not index the images',\n\n 'og-title': 'Share title',\n 'og-description': 'Share description',\n 'og-image': 'Share image',\n 'og-help': 'Left empty, a share preview uses the title and description above.',\n\n 'json-ld': 'Structured data',\n 'json-ld-help':\n 'A JSON-LD object, or a list of them. What the site says about itself comes from the settings; this is for a page that needs markup of its own.',\n 'json-ld-invalid': 'This is not valid JSON.',\n\n preview: 'In search results',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { seoMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/** Puts this package's English under the panel's dictionary, once per panel. */\nexport function useSeoMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-seo', seoMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { useAdmin, useTranslate, WxListScreen } from '@webx-ui/module-admin'\nimport { WxButton, type TabItem, type TabValue } from '@webx-ui/core'\nimport { useSeoMessages } from './i18n'\n\n/**\n * The head of the section: its name, the views of what it holds, and the one tool that belongs\n * to the whole of it.\n *\n * Rules, redirects and the trail of renames are three routes rather than three panels of one,\n * because they are three tables with their own paging and their own search — and a panel that\n * quietly resets both when you come back to it is worse than a second address. What they share\n * is the frame: one heading, one strip of tabs, one card under it (§19).\n */\nconst props = defineProps<{ base: string; current: 'rules' | 'redirects' | 'aliases' }>()\n\nconst emit = defineEmits<{ test: [] }>()\n\ndefineSlots<{\n /** The table. */\n default?: () => unknown\n /** The view's own main action — `New rule` — before the section's `Check an address`. */\n actions?: () => unknown\n}>()\n\nconst context = useAdmin()\nconst router = useRouter()\nconst route = useRoute()\n\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n\n/** The section's name as the server translated it; the built-in English until it arrives. */\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'seo')?.title ??\n t('module.title'),\n)\n\nconst views = computed<TabItem[]>(() => [\n { value: 'rules', label: t('page.rules') },\n { value: 'redirects', label: t('page.redirects') },\n { value: 'aliases', label: t('page.automatic') },\n])\n\nconst where = computed<TabValue>({\n get: () => props.current,\n set: (next) => {\n const path = next === 'rules' ? props.base : `${props.base}/${String(next)}`\n\n if (path !== route.path) void router.push(path)\n },\n})\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"where\" :title=\"title\" :views=\"views\">\n <template #actions>\n <slot name=\"actions\" />\n\n <wx-button variant=\"outline\" icon=\"search\" @click=\"emit('test')\">\n {{ t('page.test') }}\n </wx-button>\n </template>\n\n <slot />\n </wx-list-screen>\n</template>\n","import type { AdminContext } from '@webx-ui/module-admin'\nimport type {\n SeoAlias,\n SeoAliasQuery,\n SeoPage,\n SeoRedirect,\n SeoRedirectInput,\n SeoRedirectQuery,\n SeoTestResult,\n SeoUrlInput,\n SeoUrlQuery,\n SeoUrlRule,\n} from './types'\n\nexport interface SeoApi {\n /** The rules, in the order the site tries them. */\n urls(query?: SeoUrlQuery): Promise<SeoPage<SeoUrlRule>>\n url(id: number): Promise<SeoUrlRule>\n createUrl(input: SeoUrlInput): Promise<SeoUrlRule>\n updateUrl(id: number, input: SeoUrlInput): Promise<SeoUrlRule>\n removeUrl(id: number): Promise<void>\n\n redirects(query?: SeoRedirectQuery): Promise<SeoPage<SeoRedirect>>\n createRedirect(input: SeoRedirectInput): Promise<SeoRedirect>\n updateRedirect(id: number, input: SeoRedirectInput): Promise<SeoRedirect>\n removeRedirect(id: number): Promise<void>\n\n /** The redirects nobody wrote: what the address registry kept after a rename. Read only. */\n aliases(query?: SeoAliasQuery): Promise<SeoPage<SeoAlias>>\n\n /** What an address ends up saying, and where every part of it came from. */\n test(url: string, locale?: string | null): Promise<SeoTestResult>\n}\n\n/** Everything under `/seo`, below the panel's API path. */\nexport function createSeoApi(admin: AdminContext): SeoApi {\n const base = `${admin.apiPath}/seo`\n const data = <T>(body: { data: T }): T => body.data\n\n /* A paginated list arrives as a resource collection — rows under `data`, numbers under\n `meta`. The table wants them on one object, so they are joined here once rather than in\n every screen. */\n const page = <T>(body: { data: T[]; meta: Omit<SeoPage<T>, 'data'> }): SeoPage<T> => ({\n ...body.meta,\n data: body.data,\n })\n\n /* `undefined` drops the parameter; `null` and `''` would be sent as words and read as a\n filter nobody asked for. */\n const listQuery = (\n query: SeoRedirectQuery,\n ): Record<string, string | number | boolean | null | undefined> => ({\n q: query.q || undefined,\n match_type: query.match_type ?? undefined,\n is_active: query.is_active ?? undefined,\n sort: query.sort || undefined,\n page: query.page,\n per_page: query.per_page,\n })\n\n return {\n urls: (query = {}) =>\n admin.http\n .get<{ data: SeoUrlRule[]; meta: Omit<SeoPage<SeoUrlRule>, 'data'> }>(`${base}/urls`, {\n query: listQuery(query),\n })\n .then(page),\n\n url: (id) => admin.http.get<{ data: SeoUrlRule }>(`${base}/urls/${id}`).then(data),\n\n createUrl: (input) => admin.http.post<{ data: SeoUrlRule }>(`${base}/urls`, input).then(data),\n\n updateUrl: (id, input) =>\n admin.http.put<{ data: SeoUrlRule }>(`${base}/urls/${id}`, input).then(data),\n\n removeUrl: (id) => admin.http.delete<void>(`${base}/urls/${id}`),\n\n redirects: (query = {}) =>\n admin.http\n .get<{ data: SeoRedirect[]; meta: Omit<SeoPage<SeoRedirect>, 'data'> }>(\n `${base}/redirects`,\n { query: listQuery(query) },\n )\n .then(page),\n\n createRedirect: (input) =>\n admin.http.post<{ data: SeoRedirect }>(`${base}/redirects`, input).then(data),\n\n updateRedirect: (id, input) =>\n admin.http.put<{ data: SeoRedirect }>(`${base}/redirects/${id}`, input).then(data),\n\n removeRedirect: (id) => admin.http.delete<void>(`${base}/redirects/${id}`),\n\n aliases: (query = {}) =>\n admin.http\n .get<{ data: SeoAlias[]; meta: Omit<SeoPage<SeoAlias>, 'data'> }>(`${base}/aliases`, {\n query: {\n q: query.q || undefined,\n locale: query.locale ?? undefined,\n page: query.page,\n per_page: query.per_page,\n },\n })\n .then(page),\n\n // A POST because it carries an address in its body, and an address in a query string is an\n // address somebody has to escape twice.\n test: (url, locale = null) =>\n admin.http\n .post<{ data: SeoTestResult }>(`${base}/test-url`, { url, locale: locale ?? undefined })\n .then(data),\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport {\n WxAlert,\n WxBadge,\n WxButton,\n WxDescriptions,\n WxDescriptionsItem,\n WxDialog,\n WxEmpty,\n WxInput,\n WxSpace,\n WxText,\n useModal,\n} from '@webx-ui/core'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { SeoTestResult } from './types'\n\n/**\n * \"Why does this page have the wrong title?\"\n *\n * The most common question this section gets, and the reason it should take one call to answer\n * rather than a reading of the code: which redirect catches the address, which rule matches it,\n * what every source contributed, and what the page ends up saying.\n */\nconst { open } = useModal<void>()\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n\nconst url = ref('')\nconst running = ref(false)\nconst result = ref<SeoTestResult | null>(null)\n\nconst fields = computed(() => {\n const seo = result.value?.seo\n\n if (!seo) return []\n\n return [\n { label: t('card.title'), value: seo.title },\n { label: t('card.h1'), value: seo.h1 },\n { label: t('card.description'), value: seo.description },\n { label: t('card.keywords'), value: seo.keywords },\n { label: t('card.canonical'), value: seo.canonical },\n { label: t('card.robots'), value: seo.robots },\n ].filter((one) => one.value)\n})\n\nasync function run(): Promise<void> {\n if (url.value.trim() === '') return\n\n running.value = true\n\n try {\n result.value = await api.test(url.value)\n } finally {\n running.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('page.test')\" :width=\"720\">\n <div class=\"wx-seo-test\">\n <wx-space size=\"sm\" class=\"wx-seo-test__ask\">\n <wx-input\n v-model=\"url\"\n :placeholder=\"t('page.test-placeholder')\"\n class=\"wx-seo-test__input\"\n @keyup.enter=\"run\"\n />\n <wx-button type=\"primary\" :loading=\"running\" @click=\"run\">\n {{ t('page.test-run') }}\n </wx-button>\n </wx-space>\n\n <template v-if=\"result\">\n <!-- First, because it happens first: a redirected address never reaches the rules. -->\n <wx-alert\n v-if=\"result.redirect\"\n type=\"warning\"\n :description=\"\n t('page.test-redirected', {\n target: result.redirect.target,\n status: result.redirect.status,\n })\n \"\n />\n\n <!-- Who answers at this address at all. A rule of the site's own comes before any of\n this, so an address can be redirected away from a page that still exists. -->\n <wx-alert\n v-if=\"result.route\"\n type=\"info\"\n :description=\"\n result.route.kind === 'alias'\n ? t('page.occupied-alias', { target: result.route.target ?? result.route.path })\n : t('page.occupied', { path: result.route.path })\n \"\n />\n\n <wx-alert v-if=\"!result.matched\" type=\"info\" :description=\"t('page.test-none')\" />\n <div v-else class=\"wx-seo-test__matched\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('page.test-matched') }}</wx-text>\n <wx-space size=\"sm\" align=\"center\">\n <wx-badge>{{ t(`page.${result.matched.match_type}`) }}</wx-badge>\n <wx-text mono size=\"sm\">{{ result.matched.pattern }}</wx-text>\n </wx-space>\n </div>\n\n <div v-if=\"fields.length > 0\" class=\"wx-seo-test__result\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('page.test-result') }}</wx-text>\n <wx-descriptions :columns=\"1\" bordered size=\"sm\">\n <wx-descriptions-item v-for=\"one in fields\" :key=\"one.label\" :label=\"one.label\">\n {{ one.value }}\n </wx-descriptions-item>\n </wx-descriptions>\n </div>\n\n <div v-if=\"result.chain.length > 0\" class=\"wx-seo-test__chain\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('page.test-chain') }}</wx-text>\n <wx-space size=\"xs\" wrap>\n <wx-badge v-for=\"step in result.chain\" :key=\"step.source\">\n {{ step.source }} · {{ step.priority }}\n </wx-badge>\n </wx-space>\n </div>\n </template>\n\n <wx-empty v-else-if=\"!running\" icon=\"search\" :description=\"t('page.test-empty')\" />\n </div>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-seo-test {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n}\n\n.wx-seo-test__ask {\n width: 100%;\n}\n\n.wx-seo-test__input {\n flex: 1 1 auto;\n}\n\n.wx-seo-test__matched,\n.wx-seo-test__result,\n.wx-seo-test__chain {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-6);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useTranslate, useAdmin, WxDate } from '@webx-ui/module-admin'\nimport {\n createModal,\n WxAlert,\n WxBadge,\n WxLink,\n WxTable,\n WxText,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport SeoLayout from './SeoLayout.vue'\nimport TestUrlDialog from './TestUrlDialog.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { SeoAlias, SeoPage } from './types'\n\n/**\n * The redirects nobody wrote.\n *\n * Renaming a page or moving a branch leaves its old address behind, answering 301 — which is\n * the only reason an external link, a bookmark or a search result survives an edit in the panel.\n * They are shown here because a reader who lands on a dead address does not care which half of\n * the system made the redirect, and an editor chasing one should not have to.\n *\n * Nothing on this screen writes. These rows belong to the entity that moved: it makes them, and\n * deleting it takes them with it. An editor who wants a different answer for an old address\n * writes a rule of their own on the next tab — those are tried first, and this one stops\n * mattering.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/seo' })\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n\nconst page = ref<SeoPage<SeoAlias> | null>(null)\nconst loading = ref(false)\n\nconst test = createModal<void, Record<string, never>>(TestUrlDialog)\n\n/** More than one language on the site is the only reason to say which one a row is in. */\nconst multilingual = computed(() => (context.state.manifest?.locales?.length ?? 1) > 1)\n\nconst columns = computed<TableColumn<SeoAlias>[]>(() => [\n { key: 'pattern', label: t('page.address'), sortable: false },\n { key: 'target', label: t('page.target') },\n {\n key: 'locale',\n label: t('page.language'),\n align: 'center',\n hidden: !multilingual.value,\n hideBelow: 760,\n },\n { key: 'created_at', label: t('page.moved-at'), hideBelow: 900, hideOnCards: true },\n])\n\nasync function load(state: TableState): Promise<void> {\n loading.value = true\n\n try {\n page.value = await api.aliases({\n q: state.search,\n page: state.page,\n per_page: state.perPage,\n })\n } finally {\n loading.value = false\n }\n}\n</script>\n\n<template>\n <seo-layout :base=\"props.base\" current=\"aliases\" @test=\"test({})\">\n <wx-alert\n type=\"info\"\n variant=\"soft\"\n :description=\"t('page.aliases-help')\"\n class=\"wx-seo-aliases__note\"\n />\n\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('page.search-aliases')\"\n :empty-text=\"t('page.aliases-empty')\"\n @state-change=\"load\"\n >\n <template #cell-pattern=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.pattern }}</wx-text>\n </template>\n\n <!-- The address it leads to now, as a link: this is the one place in the panel where an\n editor can walk from a dead address to the live page without knowing the section it\n lives in. -->\n <template #cell-target=\"{ row }\">\n <wx-link v-if=\"row.target && row.target_url\" :href=\"row.target_url\" external size=\"sm\">\n <wx-text mono size=\"sm\">{{ row.target }}</wx-text>\n </wx-link>\n <wx-badge v-else type=\"warning\">{{ t('page.gone') }}</wx-badge>\n </template>\n\n <template #cell-locale=\"{ row }\">\n <wx-badge>{{ row.locale }}</wx-badge>\n </template>\n\n <template #cell-created_at=\"{ row }\">\n <wx-date v-if=\"row.created_at\" :value=\"row.created_at\" />\n </template>\n </wx-table>\n </seo-layout>\n</template>\n\n<style scoped>\n.wx-seo-aliases__note {\n margin-bottom: var(--wx-space-12);\n}\n</style>\n","import type { LocalizedValue } from '@webx-ui/core'\n\n/** The shapes `webx-ui/module-seo` answers with, and what its forms send back. */\n\n/** How a pattern is compared with an address. */\nexport type MatchType = 'exact' | 'mask' | 'regex'\n\n/** What `wx-media` stores, plus the address the server works out on every read. */\nexport interface SeoImage {\n path: string\n alt?: string\n title?: string\n url?: string | null\n}\n\n/**\n * What a page says about itself.\n *\n * The text fields are language maps — the same `{ en: '…', ru: '…' }` every localized field in\n * the panel uses. The picture is not: there are no per-language images anywhere yet, and a\n * column that already holds an object would read `path` as a language code.\n */\nexport interface SeoFields {\n title: LocalizedValue\n h1: LocalizedValue\n description: LocalizedValue\n keywords: LocalizedValue\n og_title: LocalizedValue\n og_description: LocalizedValue\n og_image: SeoImage | null\n canonical: string | null\n robots: string | null\n /** A JSON-LD object, a list of them, or null. */\n json_ld: unknown\n}\n\n/**\n * The value of a `wx-seo` field: the same fields, none of them required.\n *\n * A record that has never been given any SEO stores nothing rather than a shape full of nulls.\n */\nexport type SeoValue = Partial<SeoFields>\n\nexport interface SeoUrlRule extends SeoFields {\n id: number\n match_type: MatchType\n pattern: string\n priority: number\n is_active: boolean\n created_at: string | null\n updated_at: string | null\n /**\n * What a table row is, as far as `WxTable` is concerned. Without it this type does not\n * extend `TableRow` and every cell slot hands back `unknown`.\n */\n [key: string]: unknown\n}\n\n/** A `PUT` replaces the whole rule: the form edits every field at once. */\nexport interface SeoUrlInput extends SeoValue {\n match_type: MatchType\n pattern: string\n priority?: number\n is_active?: boolean\n}\n\nexport interface SeoRedirect {\n id: number\n match_type: MatchType\n pattern: string\n target: string\n status: number\n is_active: boolean\n hits: number\n last_hit_at: string | null\n /** The server says so rather than refusing the row: a loop is skipped, not rejected. */\n is_loop: boolean\n created_at: string | null\n updated_at: string | null\n [key: string]: unknown\n}\n\n/**\n * An address a rename left behind, from `webx-ui/routing`.\n *\n * The same words a manual redirect uses where the two overlap, so one screen can show both\n * without learning two vocabularies. What it has instead of the rest is an entity: these are\n * written by whatever moved, always exact, always 301, and never edited here.\n */\nexport interface SeoAlias {\n id: number\n locale: string\n /** The old address. */\n pattern: string\n /** The address it leads to now; null when the row it pointed at is gone. */\n target: string | null\n url: string\n target_url: string | null\n /** `page`, `article`, `product` — what the content module called itself. */\n entity_type: string\n entity_id: number\n created_at: string | null\n [key: string]: unknown\n}\n\nexport interface SeoAliasQuery {\n q?: string\n locale?: string | null\n page?: number\n per_page?: number\n}\n\n/**\n * What the address registry holds at an address, when something does.\n *\n * A rule written by hand is tried before any of it — that is deliberate — so this is a warning\n * and never a refusal: the page at this address is about to stop being reachable.\n */\nexport interface SeoRoute {\n path: string\n url: string\n kind: 'canonical' | 'alias'\n /** Where an old address leads now; null on a live page, which has nowhere to lead. */\n target: string | null\n entity_type: string\n entity_id: number\n /** False when a shorter address matched: the page at `/parts` answering for `/parts/bobcat`. */\n exact: boolean\n}\n\nexport interface SeoRedirectInput {\n match_type: MatchType\n pattern: string\n target: string\n status?: number\n is_active?: boolean\n}\n\n/**\n * A page, flat — what `->paginate()` serialises and `WxTable` reads as it arrives. A resource\n * collection nests the same numbers under `meta`; the client flattens them once, here.\n */\nexport interface SeoPage<T> {\n data: T[]\n current_page: number\n last_page: number\n per_page: number\n total: number\n from: number | null\n to: number | null\n}\n\nexport interface SeoUrlQuery {\n q?: string\n match_type?: MatchType | null\n is_active?: boolean | null\n page?: number\n per_page?: number\n}\n\nexport interface SeoRedirectQuery extends SeoUrlQuery {\n sort?: string\n}\n\n/** What a page ends up saying, every source merged. */\nexport interface SeoResolved {\n title: string | null\n h1: string | null\n description: string | null\n keywords: string | null\n canonical: string | null\n robots: string | null\n og: Record<string, string>\n json_ld: Record<string, unknown>[]\n}\n\n/** One source's contribution, before the merge. */\nexport interface SeoChainStep {\n source: string\n priority: number\n data: SeoResolved\n}\n\n/** The answer to \"why does this page say that\". */\nexport interface SeoTestResult {\n url: string\n /** Said first because it happens first: a redirected address never reaches the rules. */\n redirect: SeoRedirect | null\n /** What the address registry has here — a live page, or the trail of one that moved. */\n route: SeoRoute | null\n matched: SeoUrlRule | null\n chain: SeoChainStep[]\n seo: SeoResolved\n}\n\n/** The meta directives the card offers as checkboxes. Anything else is kept as written. */\nexport const robotsDirectives = [\n 'noindex',\n 'nofollow',\n 'noarchive',\n 'nosnippet',\n 'noimageindex',\n] as const\n\nexport type RobotsDirective = (typeof robotsDirectives)[number]\n","<script setup lang=\"ts\">\nimport { computed, ref, type Component } from 'vue'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport {\n localizedValue,\n useLocales,\n WxCheckboxGroup,\n WxCodeEditor,\n WxFormItem,\n WxInput,\n WxTabs,\n WxTab,\n WxText,\n WxTextarea,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { useSeoMessages } from './i18n'\nimport { robotsDirectives, type SeoImage, type SeoValue } from './types'\n\n/**\n * What a page says about itself, as a field.\n *\n * Registered as `wx-seo`, so it can be dropped into any screen by a patch, and used directly by\n * the rule form in this package. The value is one object — the same shape the server stores —\n * rather than a dozen loose keys, because SEO is edited and reasoned about as a unit.\n *\n * The text fields are language maps and say so: the same chip every other localized field in the\n * panel grows, moving together with them.\n */\ndefineOptions({ name: 'WxSeo' })\n\nconst props = withDefaults(\n defineProps<{\n /**\n * The field that picks the share image — `WxMediaField` in a panel that has the library.\n *\n * Handed in rather than imported: this package does not depend on the media one, and a panel\n * without a library still edits everything else about a page.\n */\n mediaField?: Component\n disabled?: boolean\n /**\n * Soft limits, for the counters only. Long is not wrong — search engines shorten what they\n * shorten — so nothing here refuses a longer line, it only says how long it is.\n */\n titleLimit?: number\n descriptionLimit?: number\n /** What the snippet preview shows above the title. */\n site?: string\n /** Hidden when a screen has no use for them. */\n structuredData?: boolean\n }>(),\n {\n mediaField: undefined,\n disabled: false,\n titleLimit: 60,\n descriptionLimit: 160,\n site: undefined,\n structuredData: true,\n },\n)\n\nconst value = defineModel<SeoValue | null>({ default: null })\n\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\nconst locales = useLocales()\n\nconst jsonLdText = ref<string | null>(null)\nconst jsonLdBroken = ref(false)\n\n/** One field of the value, without rebuilding the object in every handler. */\nfunction field<K extends keyof SeoValue>(key: K) {\n return computed({\n get: () => value.value?.[key],\n set: (next: SeoValue[K]) => {\n value.value = { ...(value.value ?? {}), [key]: next }\n },\n })\n}\n\nconst title = field('title')\nconst h1 = field('h1')\nconst description = field('description')\nconst keywords = field('keywords')\nconst ogTitle = field('og_title')\nconst ogDescription = field('og_description')\n\nconst canonical = computed<string>({\n get: () => value.value?.canonical ?? '',\n set: (next: string) => {\n value.value = { ...(value.value ?? {}), canonical: next.trim() === '' ? null : next }\n },\n})\n\nconst image = computed({\n get: () => value.value?.og_image ?? null,\n set: (next: SeoImage | null) => {\n value.value = { ...(value.value ?? {}), og_image: next }\n },\n})\n\n/* The directives the checkboxes cover. Anything else somebody wrote — `max-snippet:20` and its\n kind — is kept and shown, because silently dropping a line a person typed is worse than not\n offering a box for it. */\nconst directives = computed(() => (value.value?.robots ?? '').split(',').map((one) => one.trim()))\n\nconst kept = computed(() =>\n directives.value.filter((one) => one !== '' && !robotsDirectives.includes(one as never)),\n)\n\nconst known = computed({\n get: () => directives.value.filter((one) => robotsDirectives.includes(one as never)),\n set: (next: readonly string[]) => {\n const merged = [...next, ...kept.value].join(', ')\n value.value = { ...(value.value ?? {}), robots: merged === '' ? null : merged }\n },\n})\n\nconst robotsOptions = computed(() =>\n robotsDirectives.map((one) => ({ value: one, label: t(`card.${one}`) })),\n)\n\nconst jsonLd = computed<string>({\n get: () => {\n if (jsonLdText.value !== null) return jsonLdText.value\n\n const stored = value.value?.json_ld\n\n return stored === null || stored === undefined ? '' : JSON.stringify(stored, null, 2)\n },\n set: (next: string) => {\n /* The text is kept as typed while it is being typed: reformatting somebody's JSON the\n moment it happens to parse moves the caret out from under them. */\n jsonLdText.value = next\n\n if (next.trim() === '') {\n jsonLdBroken.value = false\n value.value = { ...(value.value ?? {}), json_ld: null }\n\n return\n }\n\n try {\n value.value = { ...(value.value ?? {}), json_ld: JSON.parse(next) }\n jsonLdBroken.value = false\n } catch {\n jsonLdBroken.value = true\n }\n },\n})\n\n/** What the counters and the preview read: the language being edited. */\nfunction inLocale(map: LocalizedValue | undefined): string {\n return localizedValue(map, locales.active.value)\n}\n\nconst counted = computed(() => ({\n title: inLocale(title.value as LocalizedValue | undefined).length,\n description: inLocale(description.value as LocalizedValue | undefined).length,\n}))\n\nconst preview = computed(() => ({\n title: inLocale(title.value as LocalizedValue | undefined),\n description: inLocale(description.value as LocalizedValue | undefined),\n address: canonical.value || (props.site ?? ''),\n}))\n\nconst hasPreview = computed(() => preview.value.title !== '' || preview.value.description !== '')\n\nconst keptHelp = computed(() =>\n kept.value.length === 0\n ? undefined\n : t('card.robots-kept', { directives: kept.value.join(', ') }),\n)\n\nfunction tone(count: number, limit: number): 'muted' | 'warning' {\n return count > limit ? 'warning' : 'muted'\n}\n</script>\n\n<template>\n <div class=\"wx-seo\">\n <wx-tabs keep-alive>\n <wx-tab :label=\"t('card.section-page')\" value=\"page\">\n <div class=\"wx-seo__fields\">\n <wx-form-item :label=\"t('card.title')\" :disabled=\"disabled\">\n <wx-input v-model=\"title\" localized />\n <div class=\"wx-seo__count\">\n <wx-text size=\"sm\" :tone=\"tone(counted.title, titleLimit)\">\n {{ counted.title }} / {{ titleLimit }}\n </wx-text>\n </div>\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.h1')\" :disabled=\"disabled\">\n <wx-input v-model=\"h1\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.description')\" :disabled=\"disabled\">\n <wx-textarea v-model=\"description\" localized :rows=\"3\" />\n <div class=\"wx-seo__count\">\n <wx-text size=\"sm\" :tone=\"tone(counted.description, descriptionLimit)\">\n {{ counted.description }} / {{ descriptionLimit }}\n </wx-text>\n </div>\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.keywords')\" :disabled=\"disabled\">\n <wx-input v-model=\"keywords\" localized />\n </wx-form-item>\n\n <div v-if=\"hasPreview\" class=\"wx-seo__preview\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('card.preview') }}</wx-text>\n <div class=\"wx-seo__snippet\">\n <div v-if=\"preview.address\" class=\"wx-seo__snippet-address\">\n {{ preview.address }}\n </div>\n <div class=\"wx-seo__snippet-title\">{{ preview.title }}</div>\n <div class=\"wx-seo__snippet-text\">{{ preview.description }}</div>\n </div>\n </div>\n </div>\n </wx-tab>\n\n <wx-tab :label=\"t('card.section-share')\" value=\"share\">\n <div class=\"wx-seo__fields\">\n <component\n :is=\"mediaField\"\n v-if=\"mediaField\"\n v-model=\"image\"\n :label=\"t('card.og-image')\"\n aspect=\"16/9\"\n accept=\"image\"\n :disabled=\"disabled\"\n />\n\n <wx-form-item :label=\"t('card.og-title')\" :help=\"t('card.og-help')\" :disabled=\"disabled\">\n <wx-input v-model=\"ogTitle\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.og-description')\" :disabled=\"disabled\">\n <wx-textarea v-model=\"ogDescription\" localized :rows=\"2\" />\n </wx-form-item>\n </div>\n </wx-tab>\n\n <wx-tab :label=\"t('card.section-advanced')\" value=\"advanced\">\n <div class=\"wx-seo__fields\">\n <wx-form-item\n :label=\"t('card.canonical')\"\n :help=\"t('card.canonical-help')\"\n :disabled=\"disabled\"\n >\n <wx-input v-model=\"canonical\" placeholder=\"https://example.com/page\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.robots')\" :help=\"keptHelp\" :disabled=\"disabled\">\n <wx-checkbox-group v-model=\"known\" :options=\"robotsOptions\" />\n </wx-form-item>\n\n <wx-form-item\n v-if=\"structuredData\"\n :label=\"t('card.json-ld')\"\n :help=\"t('card.json-ld-help')\"\n :error=\"jsonLdBroken ? t('card.json-ld-invalid') : undefined\"\n :disabled=\"disabled\"\n >\n <wx-code-editor\n v-model=\"jsonLd\"\n language=\"json\"\n lint\n min-height=\"120px\"\n max-height=\"420px\"\n />\n </wx-form-item>\n </div>\n </wx-tab>\n </wx-tabs>\n </div>\n</template>\n\n<style scoped>\n.wx-seo {\n container-type: inline-size;\n}\n\n.wx-seo__fields {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n padding-block-start: var(--wx-space-12);\n}\n\n.wx-seo__count {\n display: flex;\n justify-content: flex-end;\n}\n\n.wx-seo__preview {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n}\n\n/* Not a copy of any one search engine's styling — a reminder of how much of a line survives. */\n.wx-seo__snippet {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-2);\n padding: var(--wx-space-12);\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-md);\n background: var(--wx-bg-subtle);\n}\n\n.wx-seo__snippet-address {\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n overflow-wrap: anywhere;\n}\n\n.wx-seo__snippet-title {\n color: var(--wx-color-primary);\n font-size: var(--wx-font-size-lg);\n overflow-wrap: anywhere;\n}\n\n.wx-seo__snippet-text {\n color: var(--wx-text-default);\n font-size: var(--wx-font-size-sm);\n overflow-wrap: anywhere;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxAlert,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n WxSwitch,\n} from '@webx-ui/core'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { MatchType, SeoRedirect, SeoRoute } from './types'\n\n/** One address that has moved, and where to. Four fields, so four fields. */\nconst props = withDefaults(defineProps<{ redirect?: SeoRedirect | null }>(), { redirect: null })\n\nconst { resolve, dismiss, open } = useModal<SeoRedirect>()\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst occupant = ref<SeoRoute | null>(null)\n\nconst form = ref({\n match_type: 'exact' as MatchType,\n pattern: '',\n target: '',\n status: 301,\n is_active: true,\n})\n\nconst editing = computed(() => props.redirect !== null)\n\nconst kindOptions = computed(() => [\n { value: 'exact', label: t('page.exact') },\n { value: 'mask', label: t('page.mask') },\n { value: 'regex', label: t('page.regex') },\n])\n\n/* Numbers, not words: 301 and 302 are what a person types into a search when they want to know\n what one is, and a label would only stand between them and that. */\nconst statusOptions = [\n { value: 301, label: '301' },\n { value: 302, label: '302' },\n]\n\nwatch(\n () => props.redirect,\n (redirect) => {\n form.value = {\n match_type: redirect?.match_type ?? 'exact',\n pattern: redirect?.pattern ?? '',\n target: redirect?.target ?? '',\n status: redirect?.status ?? 301,\n is_active: redirect?.is_active ?? true,\n }\n errors.value = {}\n occupant.value = null\n void shadowed()\n },\n { immediate: true },\n)\n\n/**\n * Whether a live page is standing at the address this rule is about to take over.\n *\n * A rule wins: `RedirectRequests` runs before routing, deliberately, so that an editor can\n * redirect an address the site still answers at. Which is exactly why this has to be said out\n * loud — the page does not disappear from the panel, it only stops being reachable, and the\n * person who finds out otherwise is a reader.\n *\n * Only for an exact address. A mask covers addresses that do not exist yet and a regular\n * expression covers ones nobody can enumerate; guessing at either would produce a warning that\n * is wrong often enough to be ignored, and a warning that is ignored is worse than none.\n */\nasync function shadowed(): Promise<void> {\n const pattern = form.value.pattern.trim()\n\n if (form.value.match_type !== 'exact' || pattern === '') {\n occupant.value = null\n\n return\n }\n\n try {\n occupant.value = (await api.test(pattern)).route\n } catch {\n // The check is a courtesy; a rule is not held up because it could not be made.\n occupant.value = null\n }\n}\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n try {\n const saved = props.redirect\n ? await api.updateRedirect(props.redirect.id, form.value)\n : await api.createRedirect(form.value)\n\n toast.success(t('page.saved'))\n resolve(saved)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]>; message?: string } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('page.failed'))\n } else {\n toast.danger(message(error, t('page.failed')))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('page.redirect')\" :width=\"640\">\n <div class=\"wx-seo-redirect\">\n <wx-form-item :label=\"t('page.kind')\">\n <wx-select v-model=\"form.match_type\" :options=\"kindOptions\" @change=\"shadowed\" />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('page.address')\"\n :help=\"t('page.address-help')\"\n :error=\"errorOf('pattern')\"\n >\n <wx-input v-model=\"form.pattern\" placeholder=\"/old-address\" @blur=\"shadowed\" />\n </wx-form-item>\n\n <!-- Said, not refused: the rule is allowed to shadow the page, and whoever writes it\n should know that is what they are doing. -->\n <wx-alert\n v-if=\"occupant\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"\n occupant.kind === 'alias'\n ? t('page.occupied-alias', { target: occupant.target ?? occupant.path })\n : t('page.occupied', { path: occupant.path })\n \"\n />\n\n <wx-form-item\n :label=\"t('page.target')\"\n :help=\"t('page.target-help')\"\n :error=\"errorOf('target')\"\n >\n <wx-input v-model=\"form.target\" placeholder=\"/new-address\" />\n </wx-form-item>\n\n <div class=\"wx-seo-redirect__row\">\n <wx-form-item :label=\"t('page.status')\">\n <wx-select v-model=\"form.status\" :options=\"statusOptions\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('page.state')\">\n <wx-switch v-model=\"form.is_active\" />\n </wx-form-item>\n </div>\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('page.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">\n {{ editing ? t('page.save') : t('page.new-redirect') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-seo-redirect {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n\n.wx-seo-redirect__row {\n display: grid;\n grid-template-columns: 140px 1fr;\n gap: var(--wx-space-12);\n align-items: start;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n WxBadge,\n WxButton,\n WxTable,\n WxText,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport SeoLayout from './SeoLayout.vue'\nimport SeoRedirectDialog from './SeoRedirectDialog.vue'\nimport TestUrlDialog from './TestUrlDialog.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { SeoPage, SeoRedirect } from './types'\n\n/**\n * The addresses that have moved.\n *\n * Busiest first by default, which is the order this list is worked on: a redirect nobody has\n * followed since the site was rebuilt is the one worth deleting, and it sits at the bottom.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/seo' })\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst page = ref<SeoPage<SeoRedirect> | null>(null)\nconst loading = ref(false)\n\nlet last: TableState = { page: 1, perPage: 20, sort: null, search: '' }\n\nconst canManage = context.can('seo.manage')\n\nconst edit = createModal<SeoRedirect, { redirect: SeoRedirect | null }>(SeoRedirectDialog)\nconst test = createModal<void, Record<string, never>>(TestUrlDialog)\n\nconst columns = computed<TableColumn<SeoRedirect>[]>(() => [\n { key: 'pattern', label: t('page.address'), sortable: true },\n { key: 'target', label: t('page.target') },\n { key: 'status', label: t('page.status'), align: 'center', hideBelow: 560 },\n { key: 'hits', label: t('page.hits'), align: 'center', sortable: true, hideBelow: 760 },\n {\n key: 'last_hit_at',\n label: t('page.last-hit'),\n sortable: true,\n hideBelow: 900,\n hideOnCards: true,\n },\n { key: 'is_active', label: t('page.state'), align: 'center', hideBelow: 660 },\n { key: 'actions', label: '', width: 56, align: 'right', hidden: !canManage, hideOnCards: true },\n])\n\n/** One line, and the same menu every other list of the panel puts a record's actions in. */\nfunction actionsFor(redirect: SeoRedirect): RowAction[] {\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('page.delete'),\n danger: true,\n run: () => remove(redirect),\n },\n ]\n}\n\nasync function load(state: TableState): Promise<void> {\n last = state\n loading.value = true\n\n try {\n page.value = await api.redirects({\n q: state.search,\n sort: state.sort ? `${state.sort.order === 'desc' ? '-' : ''}${state.sort.key}` : '-hits',\n page: state.page,\n per_page: state.perPage,\n })\n } finally {\n loading.value = false\n }\n}\n\n/**\n * Opening a redirect is editing it, so a reader who may not manage these has nowhere to go: the\n * table is told so (`:clickable=\"canManage\"`) and withholds the pointer, the highlight and the\n * click together, rather than promising a dialog that would not open (§13).\n */\nasync function open(redirect: SeoRedirect | null): Promise<void> {\n const saved = await edit({ redirect })\n\n if (saved) void load(last)\n}\n\nasync function remove(redirect: SeoRedirect): Promise<void> {\n const agreed = await confirm({\n title: t('page.delete-title', { pattern: redirect.pattern }),\n message: t('page.delete-text'),\n confirmText: t('page.delete'),\n cancelText: t('page.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeRedirect(redirect.id)\n toast.success(t('page.deleted'))\n void load(last)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n</script>\n\n<template>\n <seo-layout :base=\"props.base\" current=\"redirects\" @test=\"test({})\">\n <template v-if=\"canManage\" #actions>\n <wx-button type=\"primary\" icon=\"add\" @click=\"open(null)\">\n {{ t('page.new-redirect') }}\n </wx-button>\n </template>\n\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"canManage\"\n :hover=\"canManage\"\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('page.search-redirects')\"\n :empty-text=\"t('page.empty')\"\n @row-click=\"open\"\n @state-change=\"load\"\n >\n <template #cell-pattern=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.pattern }}</wx-text>\n <!-- Said out loud rather than refused on save: the middleware steps over it, and a\n row that quietly does nothing is a row nobody ever fixes. -->\n <wx-badge v-if=\"row.is_loop\" type=\"warning\">{{ t('page.loop') }}</wx-badge>\n </template>\n\n <template #cell-target=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.target }}</wx-text>\n </template>\n\n <template #cell-last_hit_at=\"{ row }\">\n <wx-date :value=\"row.last_hit_at\" :tone=\"row.last_hit_at ? 'default' : 'muted'\" />\n </template>\n\n <template #cell-is_active=\"{ row }\">\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot>\n {{ row.is_active ? t('page.active') : t('page.inactive') }}\n </wx-badge>\n </template>\n\n <template #card-actions=\"{ row }\">\n <wx-row-menu v-if=\"canManage\" :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n </wx-table>\n </seo-layout>\n</template>\n","import { localizedValue, type LocalizedValue } from '@webx-ui/core'\nimport type { SeoUrlInput, SeoUrlRule, SeoValue } from './types'\n\n/** The fields the card owns, as opposed to the ones the rule form owns. */\nconst SEO_FIELDS = [\n 'title',\n 'h1',\n 'description',\n 'keywords',\n 'og_title',\n 'og_description',\n 'og_image',\n 'canonical',\n 'robots',\n 'json_ld',\n] as const\n\n/**\n * What a rule is called in a table.\n *\n * Any language rather than the current one: a record half translated is the normal state of a\n * site being worked on, and a blank cell says less than the language that does have the words.\n */\nexport function ruleTitle(rule: SeoUrlRule, locale?: string): string {\n return localizedValue(rule.title as LocalizedValue | undefined, locale)\n}\n\n/** The SEO half of a rule, for the card to edit. */\nexport function seoOf(rule: SeoUrlRule | null): SeoValue {\n if (!rule) return {}\n\n const value: SeoValue = {}\n\n for (const key of SEO_FIELDS) {\n const one = rule[key]\n\n if (one !== null && one !== undefined) {\n Object.assign(value, { [key]: one })\n }\n }\n\n return value\n}\n\n/**\n * What the form sends: the rule's own fields, with the card's value spread back over them.\n *\n * Every field of the card goes, present or not — a `PUT` replaces the whole rule, so a\n * description somebody cleared has to arrive as nothing rather than not arrive at all.\n */\nexport function ruleInput(\n own: Pick<SeoUrlInput, 'match_type' | 'pattern' | 'priority' | 'is_active'>,\n seo: SeoValue,\n): SeoUrlInput {\n const input: SeoUrlInput = { ...own }\n\n for (const key of SEO_FIELDS) {\n Object.assign(input, { [key]: seo[key] ?? null })\n }\n\n return input\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, type Component } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxInputNumber,\n WxSelect,\n WxSpace,\n WxSwitch,\n} from '@webx-ui/core'\nimport SeoCard from './SeoCard.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport { ruleInput, seoOf } from './rule'\nimport type { MatchType, SeoUrlRule, SeoValue } from './types'\n\n/**\n * One rule: which addresses it covers, and what it says about them.\n *\n * A dialog rather than a second route, and a template rather than a described screen — the same\n * decision `admins.form` is on, until the screens mechanism has been round the block a few more\n * times. What it edits below the address is `WxSeo`, the same card a content module will get by\n * patching it into its own form.\n */\nconst props = withDefaults(defineProps<{ rule?: SeoUrlRule | null; mediaField?: Component }>(), {\n rule: null,\n mediaField: undefined,\n})\n\nconst { resolve, dismiss, open } = useModal<SeoUrlRule>()\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst form = ref({\n match_type: 'exact' as MatchType,\n pattern: '',\n priority: 0,\n is_active: true,\n})\n\nconst seo = ref<SeoValue>({})\n\nconst editing = computed(() => props.rule !== null)\n\nconst kindOptions = computed(() => [\n { value: 'exact', label: t('page.exact') },\n { value: 'mask', label: t('page.mask') },\n { value: 'regex', label: t('page.regex') },\n])\n\nwatch(\n () => props.rule,\n (rule) => {\n form.value = {\n match_type: rule?.match_type ?? 'exact',\n pattern: rule?.pattern ?? '',\n priority: rule?.priority ?? 0,\n is_active: rule?.is_active ?? true,\n }\n seo.value = seoOf(rule)\n errors.value = {}\n },\n { immediate: true },\n)\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n const input = ruleInput(form.value, seo.value)\n\n try {\n const saved = props.rule\n ? await api.updateUrl(props.rule.id, input)\n : await api.createUrl(input)\n\n toast.success(t('page.saved'))\n resolve(saved)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]>; message?: string } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('page.failed'))\n } else {\n toast.danger(message(error, t('page.failed')))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('page.rule')\" :width=\"860\">\n <div class=\"wx-seo-rule\">\n <div class=\"wx-seo-rule__where\">\n <wx-form-item :label=\"t('page.kind')\">\n <wx-select v-model=\"form.match_type\" :options=\"kindOptions\" />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('page.address')\"\n :help=\"t('page.address-help')\"\n :error=\"errorOf('pattern')\"\n class=\"wx-seo-rule__address\"\n >\n <wx-input v-model=\"form.pattern\" placeholder=\"/catalog/shoes\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('page.priority')\" :help=\"t('page.priority-help')\">\n <wx-input-number v-model=\"form.priority\" :step=\"10\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('page.state')\">\n <wx-switch v-model=\"form.is_active\" />\n </wx-form-item>\n </div>\n\n <seo-card v-model=\"seo\" :media-field=\"props.mediaField\" />\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('page.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">\n {{ editing ? t('page.save') : t('page.new-rule') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-seo-rule {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n container-type: inline-size;\n}\n\n/* The address and its kind on one line: they are one decision, and reading them apart is\n reading half of it. */\n.wx-seo-rule__where {\n display: grid;\n grid-template-columns: 180px 1fr 140px 100px;\n gap: var(--wx-space-12);\n align-items: start;\n}\n\n@container (max-width: 720px) {\n .wx-seo-rule__where {\n grid-template-columns: 1fr 1fr;\n }\n\n .wx-seo-rule__address {\n grid-column: 1 / -1;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, type Component } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n WxBadge,\n WxButton,\n WxSelect,\n WxTable,\n WxText,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport SeoUrlDialog from './SeoUrlDialog.vue'\nimport TestUrlDialog from './TestUrlDialog.vue'\nimport SeoLayout from './SeoLayout.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport { ruleTitle } from './rule'\nimport type { SeoPage, SeoUrlRule } from './types'\n\n/**\n * The rules written for addresses.\n *\n * Listed in the order the site tries them — exact, then mask, then regular expression, by\n * priority inside each group — so that reading the table top to bottom is reading what will\n * happen. Sorting it any other way would hide the one thing a person comes here to work out.\n */\nconst props = withDefaults(defineProps<{ base?: string; mediaField?: Component }>(), {\n base: '/seo',\n mediaField: undefined,\n})\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst page = ref<SeoPage<SeoUrlRule> | null>(null)\nconst loading = ref(false)\n/* A word rather than an empty string: a select reads \"\" as nothing chosen and shows a blank\n control where the option said \"any kind\". */\nconst kind = ref('any')\n\nlet last: TableState = { page: 1, perPage: 20, sort: null, search: '' }\n\nconst canManage = context.can('seo.manage')\n\nconst edit = createModal<SeoUrlRule, { rule: SeoUrlRule | null; mediaField?: Component }>(\n SeoUrlDialog,\n)\nconst test = createModal<void, Record<string, never>>(TestUrlDialog)\n\nconst kindOptions = computed(() => [\n { value: 'any', label: t('page.any-kind') },\n { value: 'exact', label: t('page.exact') },\n { value: 'mask', label: t('page.mask') },\n { value: 'regex', label: t('page.regex') },\n])\n\nconst columns = computed<TableColumn<SeoUrlRule>[]>(() => [\n { key: 'pattern', label: t('page.address') },\n { key: 'match_type', label: t('page.kind'), hideBelow: 560 },\n { key: 'title', label: t('page.title'), hideBelow: 900 },\n { key: 'priority', label: t('page.priority'), align: 'center', hideBelow: 760 },\n { key: 'is_active', label: t('page.state'), align: 'center', hideBelow: 660 },\n { key: 'actions', label: '', width: 56, align: 'right', hidden: !canManage, hideOnCards: true },\n])\n\n/**\n * What a rule offers: one line, and it is the destructive one — everything else about a rule is\n * done by opening it. Still a menu, because a red bin standing in every row shouts louder than\n * deleting a rule deserves, and because a reader should find a record's actions in the same\n * place in every list of the panel (§20).\n */\nfunction actionsFor(rule: SeoUrlRule): RowAction[] {\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('page.delete'),\n danger: true,\n run: () => remove(rule),\n },\n ]\n}\n\nasync function load(state: TableState): Promise<void> {\n last = state\n loading.value = true\n\n try {\n page.value = await api.urls({\n q: state.search,\n match_type: kind.value === 'any' ? null : (kind.value as 'exact' | 'mask' | 'regex'),\n page: state.page,\n per_page: state.perPage,\n })\n } finally {\n loading.value = false\n }\n}\n\n/**\n * Opening a rule is editing it, so a reader who may not manage these has nowhere to go: the\n * table is told so (`:clickable=\"canManage\"`) and withholds the pointer, the highlight and the\n * click together, rather than promising a dialog that would not open (§13).\n */\nasync function open(rule: SeoUrlRule | null): Promise<void> {\n const saved = await edit({ rule, mediaField: props.mediaField })\n\n if (saved) void load(last)\n}\n\nasync function remove(rule: SeoUrlRule): Promise<void> {\n const agreed = await confirm({\n title: t('page.delete-title', { pattern: rule.pattern }),\n message: t('page.delete-text'),\n confirmText: t('page.delete'),\n cancelText: t('page.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeUrl(rule.id)\n toast.success(t('page.deleted'))\n void load(last)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n</script>\n\n<template>\n <seo-layout :base=\"props.base\" current=\"rules\" @test=\"test({})\">\n <template v-if=\"canManage\" #actions>\n <wx-button type=\"primary\" icon=\"add\" @click=\"open(null)\">\n {{ t('page.new-rule') }}\n </wx-button>\n </template>\n\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"canManage\"\n :hover=\"canManage\"\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('page.search-rules')\"\n :empty-text=\"t('page.empty')\"\n @row-click=\"open\"\n @state-change=\"load\"\n >\n <template #actions>\n <wx-select v-model=\"kind\" :options=\"kindOptions\" class=\"wx-seo-urls__kind\" />\n </template>\n\n <template #cell-pattern=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.pattern }}</wx-text>\n </template>\n\n <template #cell-match_type=\"{ row }\">\n <wx-badge>{{ t(`page.${row.match_type}`) }}</wx-badge>\n </template>\n\n <template #cell-title=\"{ row }\">\n <wx-text v-if=\"ruleTitle(row)\" size=\"sm\">{{ ruleTitle(row) }}</wx-text>\n <wx-text v-else size=\"sm\" tone=\"muted\">—</wx-text>\n </template>\n\n <template #cell-is_active=\"{ row }\">\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot>\n {{ row.is_active ? t('page.active') : t('page.inactive') }}\n </wx-badge>\n </template>\n\n <template #card-actions=\"{ row }\">\n <wx-row-menu v-if=\"canManage\" :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n </wx-table>\n </seo-layout>\n</template>\n\n<style scoped>\n.wx-seo-urls__kind {\n width: 200px;\n}\n\n/* Once the rows are cards the filters are a column, and a fixed width leaves this one short. */\n.wx-table--cards .wx-seo-urls__kind {\n width: 100%;\n}\n</style>\n","import { markRaw, type Component } from 'vue'\nimport type { AdminModule } from '@webx-ui/module-admin'\nimport SeoAliasesPage from './SeoAliasesPage.vue'\nimport SeoCard from './SeoCard.vue'\nimport SeoRedirectsPage from './SeoRedirectsPage.vue'\nimport SeoUrlsPage from './SeoUrlsPage.vue'\n\nexport interface SeoOptions {\n /** Where the section lives inside the panel. */\n path?: string\n /**\n * The field that picks a share image — `WxMediaField` from `@webx-ui/module-media`.\n *\n * Handed in rather than imported, so this package does not depend on the library being\n * installed. Without it every other SEO field still works; only the picture is missing.\n */\n mediaField?: Component\n}\n\n/**\n * SEO as a section of the panel, and `wx-seo` as a field any screen can use.\n *\n * The id matches the module the server reports, which is what makes the entry appear in the\n * navigation: a section shows up when both halves are installed, and stays out of the way when\n * only one is.\n */\nexport function seo(options: SeoOptions = {}): AdminModule {\n const path = options.path ?? '/seo'\n /* A component kept in an options object arrives as a reactive proxy; Vue warns about one and\n `h()` wants the raw thing anyway. */\n const mediaField = options.mediaField ? markRaw(options.mediaField) : undefined\n\n return {\n id: 'seo',\n path,\n routes: [\n // The section's own path travels as a prop, so a panel that mounted it somewhere else\n // still links between its two screens correctly.\n {\n path,\n name: 'webx.seo',\n component: SeoUrlsPage,\n props: { base: path, mediaField },\n },\n {\n path: `${path}/redirects`,\n name: 'webx.seo.redirects',\n component: SeoRedirectsPage,\n props: { base: path },\n },\n // The trail of renames, from `webx-ui/routing`. The screen is here whether or not any\n // content module has registered a type yet: without one the table is simply empty, and an\n // empty table is a better answer than a tab that appears one day without explanation.\n {\n path: `${path}/aliases`,\n name: 'webx.seo.aliases',\n component: SeoAliasesPage,\n props: { base: path },\n },\n ],\n // What a screen means by `wx-seo`: everything a page says about itself, as one value. The\n // server registers the same name for what it stores.\n types: {\n 'wx-seo': {\n component: SeoCard,\n kind: 'field',\n bind: () => ({ mediaField }),\n },\n },\n }\n}\n"],"names":["seoMessages","seeded","useSeoMessages","i18n","useAdmin","props","__props","emit","__emit","context","router","useRouter","route","useRoute","t","useTranslate","title","computed","module","views","where","next","path","_createBlock","_unref","WxListScreen","$event","_renderSlot","_ctx","_createVNode","WxButton","createSeoApi","admin","base","data","body","page","listQuery","query","id","input","url","locale","open","useModal","api","ref","running","result","fields","seo","one","run","WxDialog","_createElementVNode","_hoisted_1","WxSpace","WxInput","_createElementBlock","_Fragment","WxAlert","_openBlock","_hoisted_2","WxText","WxBadge","_createTextVNode","_toDisplayString","_hoisted_3","WxDescriptions","_renderList","WxDescriptionsItem","_hoisted_4","step","WxEmpty","loading","test","createModal","TestUrlDialog","multilingual","columns","load","state","SeoLayout","WxTable","_withCtx","row","WxLink","WxDate","robotsDirectives","value","_useModel","locales","useLocales","jsonLdText","jsonLdBroken","field","key","h1","description","keywords","ogTitle","ogDescription","canonical","image","directives","kept","known","merged","robotsOptions","jsonLd","stored","inLocale","map","localizedValue","counted","preview","hasPreview","keptHelp","tone","count","limit","WxTabs","WxTab","WxFormItem","WxTextarea","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_resolveDynamicComponent","_hoisted_11","WxCheckboxGroup","WxCodeEditor","resolve","dismiss","message","useErrorText","saving","errors","occupant","form","editing","kindOptions","statusOptions","watch","redirect","shadowed","pattern","errorOf","save","saved","toast","error","WxSelect","_cache","WxSwitch","last","canManage","edit","SeoRedirectDialog","actionsFor","remove","confirm","WxRowMenu","SEO_FIELDS","ruleTitle","rule","seoOf","ruleInput","own","WxInputNumber","SeoCard","kind","SeoUrlDialog","options","mediaField","markRaw","SeoUrlsPage","SeoRedirectsPage","SeoAliasesPage"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/messages.ts","../src/i18n.ts","../src/SeoLayout.vue","../src/api.ts","../src/TestUrlDialog.vue","../src/SeoAliasesPage.vue","../src/types.ts","../src/SeoCard.vue","../src/SeoRedirectDialog.vue","../src/SeoRedirectsPage.vue","../src/rule.ts","../src/SeoUrlDialog.vue","../src/SeoUrlsPage.vue","../src/module.ts"],"sourcesContent":["import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * What this package says, in English — the same keys `webx-ui/module-seo` ships as\n * `lang/en/*.php`, so a key never reaches the screen when a translation has not arrived yet.\n *\n * The server's dictionary wins over these. There is no second dictionary inside this package:\n * a module translated into ten languages with an English dialog in the middle of it is worse\n * than one that is not translated at all.\n *\n * Placeholders are Laravel's, `:like-this` — the same line is read from a PHP file and from\n * here, and only one of the two spellings can be right.\n */\nexport const seoMessages: Record<string, Messages> = {\n module: {\n title: 'SEO',\n },\n\n page: {\n rules: 'Rules',\n redirects: 'Redirects',\n test: 'Check an address',\n\n 'new-rule': 'New rule',\n 'new-redirect': 'New redirect',\n 'search-rules': 'Search by address',\n 'search-redirects': 'Search by address or destination',\n empty: 'Nothing here yet.',\n\n address: 'Address',\n kind: 'Kind',\n priority: 'Priority',\n state: 'State',\n active: 'Active',\n inactive: 'Off',\n title: 'Title',\n target: 'Destination',\n status: 'Code',\n hits: 'Hits',\n 'last-hit': 'Last used',\n loop: 'Points at itself',\n\n 'any-kind': 'Any kind',\n exact: 'Exact',\n mask: 'Mask',\n regex: 'Regular expression',\n\n rule: 'Rule',\n redirect: 'Redirect',\n save: 'Save',\n cancel: 'Cancel',\n delete: 'Delete',\n saved: 'Saved.',\n deleted: 'Deleted.',\n failed: 'Some values were not accepted. Check the highlighted fields.',\n 'delete-title': 'Delete :pattern?',\n 'delete-text': 'This cannot be undone.',\n\n 'address-help':\n 'A path with its query string. In a mask, * is one segment and ** is any number of them; a regular expression is stored as written, delimiters and all.',\n 'target-help':\n 'A path on this site, or a full address elsewhere. $1 puts back what a mask caught.',\n 'priority-help': 'Among rules of the same kind, the higher number wins.',\n\n 'test-placeholder': '/catalog/shoes?page=2',\n 'test-run': 'Check',\n 'test-empty': 'Type an address to see what the site will say about it.',\n 'test-matched': 'Matched rule',\n 'test-none': 'No rule matches this address.',\n 'test-redirected': 'This address is redirected to :target with a :status.',\n 'test-result': 'What the page will say',\n 'test-chain': 'Where each part came from',\n\n automatic: 'Automatic',\n 'search-aliases': 'Search by address or destination',\n 'aliases-empty': 'Nothing has moved yet.',\n 'aliases-help':\n 'The site writes these itself: renaming or moving a page leaves its old address behind answering 301, so nothing that linked to it dies. They cannot be edited here — a redirect of your own is tried first and wins.',\n language: 'Language',\n 'moved-at': 'Moved',\n gone: 'Leads nowhere',\n occupied:\n 'A page of the site answers at :path. A redirect is tried before it, so the page stops being reachable there.',\n 'occupied-alias':\n 'This address already leads to :target — what a move left behind. A redirect written here is tried first.',\n },\n\n card: {\n 'section-page': 'The page',\n 'section-share': 'Sharing',\n 'section-advanced': 'Advanced',\n\n title: 'Title',\n h1: 'Heading',\n description: 'Description',\n keywords: 'Keywords',\n canonical: 'Canonical address',\n 'canonical-help': 'The address this page should be indexed under, when it is not its own.',\n\n robots: 'Indexing',\n 'robots-kept': 'Also kept, as written: :directives',\n noindex: 'Keep out of the index',\n nofollow: 'Do not follow the links',\n noarchive: 'No cached copy',\n nosnippet: 'No snippet',\n noimageindex: 'Do not index the images',\n\n 'og-title': 'Share title',\n 'og-description': 'Share description',\n 'og-image': 'Share image',\n 'og-help': 'Left empty, a share preview uses the title and description above.',\n\n 'json-ld': 'Structured data',\n 'json-ld-help':\n 'A JSON-LD object, or a list of them. What the site says about itself comes from the settings; this is for a page that needs markup of its own.',\n 'json-ld-invalid': 'This is not valid JSON.',\n\n preview: 'In search results',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { seoMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/** Puts this package's English under the panel's dictionary, once per panel. */\nexport function useSeoMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-seo', seoMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { useAdmin, useTranslate, WxListScreen } from '@webx-ui/module-admin'\nimport { WxButton, type TabItem, type TabValue } from '@webx-ui/core'\nimport { useSeoMessages } from './i18n'\n\n/**\n * The head of the section: its name, the views of what it holds, and the one tool that belongs\n * to the whole of it.\n *\n * Rules, redirects and the trail of renames are three routes rather than three panels of one,\n * because they are three tables with their own paging and their own search — and a panel that\n * quietly resets both when you come back to it is worse than a second address. What they share\n * is the frame: one heading, one strip of tabs, one card under it (§19).\n */\nconst props = defineProps<{ base: string; current: 'rules' | 'redirects' | 'aliases' }>()\n\nconst emit = defineEmits<{ test: [] }>()\n\ndefineSlots<{\n /** The table. */\n default?: () => unknown\n /** The view's own main action — `New rule` — before the section's `Check an address`. */\n actions?: () => unknown\n}>()\n\nconst context = useAdmin()\nconst router = useRouter()\nconst route = useRoute()\n\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n\n/** The section's name as the server translated it; the built-in English until it arrives. */\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'seo')?.title ??\n t('module.title'),\n)\n\nconst views = computed<TabItem[]>(() => [\n { value: 'rules', label: t('page.rules') },\n { value: 'redirects', label: t('page.redirects') },\n { value: 'aliases', label: t('page.automatic') },\n])\n\nconst where = computed<TabValue>({\n get: () => props.current,\n set: (next) => {\n const path = next === 'rules' ? props.base : `${props.base}/${String(next)}`\n\n if (path !== route.path) void router.push(path)\n },\n})\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"where\" :title=\"title\" :views=\"views\">\n <template #actions>\n <slot name=\"actions\" />\n\n <wx-button variant=\"outline\" icon=\"search\" @click=\"emit('test')\">\n {{ t('page.test') }}\n </wx-button>\n </template>\n\n <slot />\n </wx-list-screen>\n</template>\n","import type { AdminContext } from '@webx-ui/module-admin'\nimport type {\n SeoAlias,\n SeoAliasQuery,\n SeoPage,\n SeoRedirect,\n SeoRedirectInput,\n SeoRedirectQuery,\n SeoTestResult,\n SeoUrlInput,\n SeoUrlQuery,\n SeoUrlRule,\n} from './types'\n\nexport interface SeoApi {\n /** The rules, in the order the site tries them. */\n urls(query?: SeoUrlQuery): Promise<SeoPage<SeoUrlRule>>\n url(id: number): Promise<SeoUrlRule>\n createUrl(input: SeoUrlInput): Promise<SeoUrlRule>\n updateUrl(id: number, input: SeoUrlInput): Promise<SeoUrlRule>\n removeUrl(id: number): Promise<void>\n\n redirects(query?: SeoRedirectQuery): Promise<SeoPage<SeoRedirect>>\n createRedirect(input: SeoRedirectInput): Promise<SeoRedirect>\n updateRedirect(id: number, input: SeoRedirectInput): Promise<SeoRedirect>\n removeRedirect(id: number): Promise<void>\n\n /** The redirects nobody wrote: what the address registry kept after a rename. Read only. */\n aliases(query?: SeoAliasQuery): Promise<SeoPage<SeoAlias>>\n\n /** What an address ends up saying, and where every part of it came from. */\n test(url: string, locale?: string | null): Promise<SeoTestResult>\n}\n\n/** Everything under `/seo`, below the panel's API path. */\nexport function createSeoApi(admin: AdminContext): SeoApi {\n const base = `${admin.apiPath}/seo`\n const data = <T>(body: { data: T }): T => body.data\n\n /* A paginated list arrives as a resource collection — rows under `data`, numbers under\n `meta`. The table wants them on one object, so they are joined here once rather than in\n every screen. */\n const page = <T>(body: { data: T[]; meta: Omit<SeoPage<T>, 'data'> }): SeoPage<T> => ({\n ...body.meta,\n data: body.data,\n })\n\n /* `undefined` drops the parameter; `null` and `''` would be sent as words and read as a\n filter nobody asked for. */\n const listQuery = (\n query: SeoRedirectQuery,\n ): Record<string, string | number | boolean | null | undefined> => ({\n q: query.q || undefined,\n match_type: query.match_type ?? undefined,\n is_active: query.is_active ?? undefined,\n sort: query.sort || undefined,\n page: query.page,\n per_page: query.per_page,\n })\n\n return {\n urls: (query = {}) =>\n admin.http\n .get<{ data: SeoUrlRule[]; meta: Omit<SeoPage<SeoUrlRule>, 'data'> }>(`${base}/urls`, {\n query: listQuery(query),\n })\n .then(page),\n\n url: (id) => admin.http.get<{ data: SeoUrlRule }>(`${base}/urls/${id}`).then(data),\n\n createUrl: (input) => admin.http.post<{ data: SeoUrlRule }>(`${base}/urls`, input).then(data),\n\n updateUrl: (id, input) =>\n admin.http.put<{ data: SeoUrlRule }>(`${base}/urls/${id}`, input).then(data),\n\n removeUrl: (id) => admin.http.delete<void>(`${base}/urls/${id}`),\n\n redirects: (query = {}) =>\n admin.http\n .get<{ data: SeoRedirect[]; meta: Omit<SeoPage<SeoRedirect>, 'data'> }>(\n `${base}/redirects`,\n { query: listQuery(query) },\n )\n .then(page),\n\n createRedirect: (input) =>\n admin.http.post<{ data: SeoRedirect }>(`${base}/redirects`, input).then(data),\n\n updateRedirect: (id, input) =>\n admin.http.put<{ data: SeoRedirect }>(`${base}/redirects/${id}`, input).then(data),\n\n removeRedirect: (id) => admin.http.delete<void>(`${base}/redirects/${id}`),\n\n aliases: (query = {}) =>\n admin.http\n .get<{ data: SeoAlias[]; meta: Omit<SeoPage<SeoAlias>, 'data'> }>(`${base}/aliases`, {\n query: {\n q: query.q || undefined,\n locale: query.locale ?? undefined,\n page: query.page,\n per_page: query.per_page,\n },\n })\n .then(page),\n\n // A POST because it carries an address in its body, and an address in a query string is an\n // address somebody has to escape twice.\n test: (url, locale = null) =>\n admin.http\n .post<{ data: SeoTestResult }>(`${base}/test-url`, { url, locale: locale ?? undefined })\n .then(data),\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport {\n WxAlert,\n WxBadge,\n WxButton,\n WxDescriptions,\n WxDescriptionsItem,\n WxDialog,\n WxEmpty,\n WxInput,\n WxSpace,\n WxText,\n useModal,\n} from '@webx-ui/core'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { SeoTestResult } from './types'\n\n/**\n * \"Why does this page have the wrong title?\"\n *\n * The most common question this section gets, and the reason it should take one call to answer\n * rather than a reading of the code: which redirect catches the address, which rule matches it,\n * what every source contributed, and what the page ends up saying.\n */\nconst { open } = useModal<void>()\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n\nconst url = ref('')\nconst running = ref(false)\nconst result = ref<SeoTestResult | null>(null)\n\nconst fields = computed(() => {\n const seo = result.value?.seo\n\n if (!seo) return []\n\n return [\n { label: t('card.title'), value: seo.title },\n { label: t('card.h1'), value: seo.h1 },\n { label: t('card.description'), value: seo.description },\n { label: t('card.keywords'), value: seo.keywords },\n { label: t('card.canonical'), value: seo.canonical },\n { label: t('card.robots'), value: seo.robots },\n ].filter((one) => one.value)\n})\n\nasync function run(): Promise<void> {\n if (url.value.trim() === '') return\n\n running.value = true\n\n try {\n result.value = await api.test(url.value)\n } finally {\n running.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('page.test')\" :width=\"720\">\n <div class=\"wx-seo-test\">\n <wx-space size=\"sm\" class=\"wx-seo-test__ask\">\n <wx-input\n v-model=\"url\"\n :placeholder=\"t('page.test-placeholder')\"\n class=\"wx-seo-test__input\"\n @keyup.enter=\"run\"\n />\n <wx-button type=\"primary\" :loading=\"running\" @click=\"run\">\n {{ t('page.test-run') }}\n </wx-button>\n </wx-space>\n\n <template v-if=\"result\">\n <!-- First, because it happens first: a redirected address never reaches the rules. -->\n <wx-alert\n v-if=\"result.redirect\"\n type=\"warning\"\n :description=\"\n t('page.test-redirected', {\n target: result.redirect.target,\n status: result.redirect.status,\n })\n \"\n />\n\n <!-- Who answers at this address at all. A rule of the site's own comes before any of\n this, so an address can be redirected away from a page that still exists. -->\n <wx-alert\n v-if=\"result.route\"\n type=\"info\"\n :description=\"\n result.route.kind === 'alias'\n ? t('page.occupied-alias', { target: result.route.target ?? result.route.path })\n : t('page.occupied', { path: result.route.path })\n \"\n />\n\n <wx-alert v-if=\"!result.matched\" type=\"info\" :description=\"t('page.test-none')\" />\n <div v-else class=\"wx-seo-test__matched\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('page.test-matched') }}</wx-text>\n <wx-space size=\"sm\" align=\"center\">\n <wx-badge>{{ t(`page.${result.matched.match_type}`) }}</wx-badge>\n <wx-text mono size=\"sm\">{{ result.matched.pattern }}</wx-text>\n </wx-space>\n </div>\n\n <div v-if=\"fields.length > 0\" class=\"wx-seo-test__result\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('page.test-result') }}</wx-text>\n <wx-descriptions :columns=\"1\" bordered size=\"sm\">\n <wx-descriptions-item v-for=\"one in fields\" :key=\"one.label\" :label=\"one.label\">\n {{ one.value }}\n </wx-descriptions-item>\n </wx-descriptions>\n </div>\n\n <div v-if=\"result.chain.length > 0\" class=\"wx-seo-test__chain\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('page.test-chain') }}</wx-text>\n <wx-space size=\"xs\" wrap>\n <wx-badge v-for=\"step in result.chain\" :key=\"step.source\">\n {{ step.source }} · {{ step.priority }}\n </wx-badge>\n </wx-space>\n </div>\n </template>\n\n <wx-empty v-else-if=\"!running\" icon=\"search\" :description=\"t('page.test-empty')\" />\n </div>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-seo-test {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n}\n\n.wx-seo-test__ask {\n width: 100%;\n}\n\n.wx-seo-test__input {\n flex: 1 1 auto;\n}\n\n.wx-seo-test__matched,\n.wx-seo-test__result,\n.wx-seo-test__chain {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-6);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useTranslate, useAdmin, WxDate } from '@webx-ui/module-admin'\nimport {\n createModal,\n WxAlert,\n WxBadge,\n WxLink,\n WxTable,\n WxText,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport SeoLayout from './SeoLayout.vue'\nimport TestUrlDialog from './TestUrlDialog.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { SeoAlias, SeoPage } from './types'\n\n/**\n * The redirects nobody wrote.\n *\n * Renaming a page or moving a branch leaves its old address behind, answering 301 — which is\n * the only reason an external link, a bookmark or a search result survives an edit in the panel.\n * They are shown here because a reader who lands on a dead address does not care which half of\n * the system made the redirect, and an editor chasing one should not have to.\n *\n * Nothing on this screen writes. These rows belong to the entity that moved: it makes them, and\n * deleting it takes them with it. An editor who wants a different answer for an old address\n * writes a rule of their own on the next tab — those are tried first, and this one stops\n * mattering.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/seo' })\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n\nconst page = ref<SeoPage<SeoAlias> | null>(null)\nconst loading = ref(false)\n\nconst test = createModal<void, Record<string, never>>(TestUrlDialog)\n\n/** More than one language on the site is the only reason to say which one a row is in. */\nconst multilingual = computed(() => (context.state.manifest?.locales?.length ?? 1) > 1)\n\nconst columns = computed<TableColumn<SeoAlias>[]>(() => [\n { key: 'pattern', label: t('page.address'), sortable: false },\n { key: 'target', label: t('page.target') },\n {\n key: 'locale',\n label: t('page.language'),\n align: 'center',\n hidden: !multilingual.value,\n hideBelow: 760,\n },\n { key: 'created_at', label: t('page.moved-at'), hideBelow: 900, hideOnCards: true },\n])\n\nasync function load(state: TableState): Promise<void> {\n loading.value = true\n\n try {\n page.value = await api.aliases({\n q: state.search,\n page: state.page,\n per_page: state.perPage,\n })\n } finally {\n loading.value = false\n }\n}\n</script>\n\n<template>\n <seo-layout :base=\"props.base\" current=\"aliases\" @test=\"test({})\">\n <wx-alert\n type=\"info\"\n variant=\"soft\"\n :description=\"t('page.aliases-help')\"\n class=\"wx-seo-aliases__note\"\n />\n\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('page.search-aliases')\"\n :empty-text=\"t('page.aliases-empty')\"\n @state-change=\"load\"\n >\n <template #cell-pattern=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.pattern }}</wx-text>\n </template>\n\n <!-- The address it leads to now, as a link: this is the one place in the panel where an\n editor can walk from a dead address to the live page without knowing the section it\n lives in. -->\n <template #cell-target=\"{ row }\">\n <wx-link v-if=\"row.target && row.target_url\" :href=\"row.target_url\" external size=\"sm\">\n <wx-text mono size=\"sm\">{{ row.target }}</wx-text>\n </wx-link>\n <wx-badge v-else type=\"warning\">{{ t('page.gone') }}</wx-badge>\n </template>\n\n <template #cell-locale=\"{ row }\">\n <wx-badge>{{ row.locale }}</wx-badge>\n </template>\n\n <template #cell-created_at=\"{ row }\">\n <wx-date v-if=\"row.created_at\" :value=\"row.created_at\" />\n </template>\n </wx-table>\n </seo-layout>\n</template>\n\n<style scoped>\n.wx-seo-aliases__note {\n margin-bottom: var(--wx-space-12);\n}\n</style>\n","import type { LocalizedValue } from '@webx-ui/core'\n\n/** The shapes `webx-ui/module-seo` answers with, and what its forms send back. */\n\n/** How a pattern is compared with an address. */\nexport type MatchType = 'exact' | 'mask' | 'regex'\n\n/** What `wx-media` stores, plus the address the server works out on every read. */\nexport interface SeoImage {\n path: string\n alt?: string\n title?: string\n url?: string | null\n}\n\n/**\n * What a page says about itself.\n *\n * The text fields are language maps — the same `{ en: '…', ru: '…' }` every localized field in\n * the panel uses. The picture is not: there are no per-language images anywhere yet, and a\n * column that already holds an object would read `path` as a language code.\n */\nexport interface SeoFields {\n title: LocalizedValue\n h1: LocalizedValue\n description: LocalizedValue\n keywords: LocalizedValue\n og_title: LocalizedValue\n og_description: LocalizedValue\n og_image: SeoImage | null\n canonical: string | null\n robots: string | null\n /** A JSON-LD object, a list of them, or null. */\n json_ld: unknown\n}\n\n/**\n * The value of a `wx-seo` field: the same fields, none of them required.\n *\n * A record that has never been given any SEO stores nothing rather than a shape full of nulls.\n */\nexport type SeoValue = Partial<SeoFields>\n\nexport interface SeoUrlRule extends SeoFields {\n id: number\n match_type: MatchType\n pattern: string\n priority: number\n is_active: boolean\n created_at: string | null\n updated_at: string | null\n /**\n * What a table row is, as far as `WxTable` is concerned. Without it this type does not\n * extend `TableRow` and every cell slot hands back `unknown`.\n */\n [key: string]: unknown\n}\n\n/** A `PUT` replaces the whole rule: the form edits every field at once. */\nexport interface SeoUrlInput extends SeoValue {\n match_type: MatchType\n pattern: string\n priority?: number\n is_active?: boolean\n}\n\nexport interface SeoRedirect {\n id: number\n match_type: MatchType\n pattern: string\n target: string\n status: number\n is_active: boolean\n hits: number\n last_hit_at: string | null\n /** The server says so rather than refusing the row: a loop is skipped, not rejected. */\n is_loop: boolean\n created_at: string | null\n updated_at: string | null\n [key: string]: unknown\n}\n\n/**\n * An address a rename left behind, from `webx-ui/routing`.\n *\n * The same words a manual redirect uses where the two overlap, so one screen can show both\n * without learning two vocabularies. What it has instead of the rest is an entity: these are\n * written by whatever moved, always exact, always 301, and never edited here.\n */\nexport interface SeoAlias {\n id: number\n locale: string\n /** The old address. */\n pattern: string\n /** The address it leads to now; null when the row it pointed at is gone. */\n target: string | null\n url: string\n target_url: string | null\n /** `page`, `article`, `product` — what the content module called itself. */\n entity_type: string\n entity_id: number\n created_at: string | null\n [key: string]: unknown\n}\n\nexport interface SeoAliasQuery {\n q?: string\n locale?: string | null\n page?: number\n per_page?: number\n}\n\n/**\n * What the address registry holds at an address, when something does.\n *\n * A rule written by hand is tried before any of it — that is deliberate — so this is a warning\n * and never a refusal: the page at this address is about to stop being reachable.\n */\nexport interface SeoRoute {\n path: string\n url: string\n kind: 'canonical' | 'alias'\n /** Where an old address leads now; null on a live page, which has nowhere to lead. */\n target: string | null\n entity_type: string\n entity_id: number\n /** False when a shorter address matched: the page at `/parts` answering for `/parts/bobcat`. */\n exact: boolean\n}\n\nexport interface SeoRedirectInput {\n match_type: MatchType\n pattern: string\n target: string\n status?: number\n is_active?: boolean\n}\n\n/**\n * A page, flat — what `->paginate()` serialises and `WxTable` reads as it arrives. A resource\n * collection nests the same numbers under `meta`; the client flattens them once, here.\n */\nexport interface SeoPage<T> {\n data: T[]\n current_page: number\n last_page: number\n per_page: number\n total: number\n from: number | null\n to: number | null\n}\n\nexport interface SeoUrlQuery {\n q?: string\n match_type?: MatchType | null\n is_active?: boolean | null\n page?: number\n per_page?: number\n}\n\nexport interface SeoRedirectQuery extends SeoUrlQuery {\n sort?: string\n}\n\n/** What a page ends up saying, every source merged. */\nexport interface SeoResolved {\n title: string | null\n h1: string | null\n description: string | null\n keywords: string | null\n canonical: string | null\n robots: string | null\n og: Record<string, string>\n json_ld: Record<string, unknown>[]\n}\n\n/** One source's contribution, before the merge. */\nexport interface SeoChainStep {\n source: string\n priority: number\n data: SeoResolved\n}\n\n/** The answer to \"why does this page say that\". */\nexport interface SeoTestResult {\n url: string\n /** Said first because it happens first: a redirected address never reaches the rules. */\n redirect: SeoRedirect | null\n /** What the address registry has here — a live page, or the trail of one that moved. */\n route: SeoRoute | null\n matched: SeoUrlRule | null\n chain: SeoChainStep[]\n seo: SeoResolved\n}\n\n/** The meta directives the card offers as checkboxes. Anything else is kept as written. */\nexport const robotsDirectives = [\n 'noindex',\n 'nofollow',\n 'noarchive',\n 'nosnippet',\n 'noimageindex',\n] as const\n\nexport type RobotsDirective = (typeof robotsDirectives)[number]\n","<script setup lang=\"ts\">\nimport { computed, ref, type Component } from 'vue'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport {\n localizedValue,\n useLocales,\n WxCheckboxGroup,\n WxCodeEditor,\n WxFormItem,\n WxInput,\n WxTabs,\n WxTab,\n WxText,\n WxTextarea,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { useSeoMessages } from './i18n'\nimport { robotsDirectives, type SeoImage, type SeoValue } from './types'\n\n/**\n * What a page says about itself, as a field.\n *\n * Registered as `wx-seo`, so it can be dropped into any screen by a patch, and used directly by\n * the rule form in this package. The value is one object — the same shape the server stores —\n * rather than a dozen loose keys, because SEO is edited and reasoned about as a unit.\n *\n * The text fields are language maps and say so: the same chip every other localized field in the\n * panel grows, moving together with them.\n */\ndefineOptions({ name: 'WxSeo' })\n\nconst props = withDefaults(\n defineProps<{\n /**\n * The field that picks the share image — `WxMediaField` in a panel that has the library.\n *\n * Handed in rather than imported: this package does not depend on the media one, and a panel\n * without a library still edits everything else about a page.\n */\n mediaField?: Component\n disabled?: boolean\n /**\n * Soft limits, for the counters only. Long is not wrong — search engines shorten what they\n * shorten — so nothing here refuses a longer line, it only says how long it is.\n */\n titleLimit?: number\n descriptionLimit?: number\n /** What the snippet preview shows above the title. */\n site?: string\n /** Hidden when a screen has no use for them. */\n structuredData?: boolean\n }>(),\n {\n mediaField: undefined,\n disabled: false,\n titleLimit: 60,\n descriptionLimit: 160,\n site: undefined,\n structuredData: true,\n },\n)\n\nconst value = defineModel<SeoValue | null>({ default: null })\n\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\nconst locales = useLocales()\n\nconst jsonLdText = ref<string | null>(null)\nconst jsonLdBroken = ref(false)\n\n/** One field of the value, without rebuilding the object in every handler. */\nfunction field<K extends keyof SeoValue>(key: K) {\n return computed({\n get: () => value.value?.[key],\n set: (next: SeoValue[K]) => {\n value.value = { ...(value.value ?? {}), [key]: next }\n },\n })\n}\n\nconst title = field('title')\nconst h1 = field('h1')\nconst description = field('description')\nconst keywords = field('keywords')\nconst ogTitle = field('og_title')\nconst ogDescription = field('og_description')\n\nconst canonical = computed<string>({\n get: () => value.value?.canonical ?? '',\n set: (next: string) => {\n value.value = { ...(value.value ?? {}), canonical: next.trim() === '' ? null : next }\n },\n})\n\nconst image = computed({\n get: () => value.value?.og_image ?? null,\n set: (next: SeoImage | null) => {\n value.value = { ...(value.value ?? {}), og_image: next }\n },\n})\n\n/* The directives the checkboxes cover. Anything else somebody wrote — `max-snippet:20` and its\n kind — is kept and shown, because silently dropping a line a person typed is worse than not\n offering a box for it. */\nconst directives = computed(() => (value.value?.robots ?? '').split(',').map((one) => one.trim()))\n\nconst kept = computed(() =>\n directives.value.filter((one) => one !== '' && !robotsDirectives.includes(one as never)),\n)\n\nconst known = computed({\n get: () => directives.value.filter((one) => robotsDirectives.includes(one as never)),\n set: (next: readonly string[]) => {\n const merged = [...next, ...kept.value].join(', ')\n value.value = { ...(value.value ?? {}), robots: merged === '' ? null : merged }\n },\n})\n\nconst robotsOptions = computed(() =>\n robotsDirectives.map((one) => ({ value: one, label: t(`card.${one}`) })),\n)\n\nconst jsonLd = computed<string>({\n get: () => {\n if (jsonLdText.value !== null) return jsonLdText.value\n\n const stored = value.value?.json_ld\n\n return stored === null || stored === undefined ? '' : JSON.stringify(stored, null, 2)\n },\n set: (next: string) => {\n /* The text is kept as typed while it is being typed: reformatting somebody's JSON the\n moment it happens to parse moves the caret out from under them. */\n jsonLdText.value = next\n\n if (next.trim() === '') {\n jsonLdBroken.value = false\n value.value = { ...(value.value ?? {}), json_ld: null }\n\n return\n }\n\n try {\n value.value = { ...(value.value ?? {}), json_ld: JSON.parse(next) }\n jsonLdBroken.value = false\n } catch {\n jsonLdBroken.value = true\n }\n },\n})\n\n/** What the counters and the preview read: the language being edited. */\nfunction inLocale(map: LocalizedValue | undefined): string {\n return localizedValue(map, locales.active.value)\n}\n\nconst counted = computed(() => ({\n title: inLocale(title.value as LocalizedValue | undefined).length,\n description: inLocale(description.value as LocalizedValue | undefined).length,\n}))\n\nconst preview = computed(() => ({\n title: inLocale(title.value as LocalizedValue | undefined),\n description: inLocale(description.value as LocalizedValue | undefined),\n address: canonical.value || (props.site ?? ''),\n}))\n\nconst hasPreview = computed(() => preview.value.title !== '' || preview.value.description !== '')\n\nconst keptHelp = computed(() =>\n kept.value.length === 0\n ? undefined\n : t('card.robots-kept', { directives: kept.value.join(', ') }),\n)\n\nfunction tone(count: number, limit: number): 'muted' | 'warning' {\n return count > limit ? 'warning' : 'muted'\n}\n</script>\n\n<template>\n <div class=\"wx-seo\">\n <wx-tabs keep-alive>\n <wx-tab :label=\"t('card.section-page')\" value=\"page\">\n <div class=\"wx-seo__fields\">\n <wx-form-item :label=\"t('card.title')\" :disabled=\"disabled\">\n <wx-input v-model=\"title\" localized />\n <div class=\"wx-seo__count\">\n <wx-text size=\"sm\" :tone=\"tone(counted.title, titleLimit)\">\n {{ counted.title }} / {{ titleLimit }}\n </wx-text>\n </div>\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.h1')\" :disabled=\"disabled\">\n <wx-input v-model=\"h1\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.description')\" :disabled=\"disabled\">\n <wx-textarea v-model=\"description\" localized :rows=\"3\" />\n <div class=\"wx-seo__count\">\n <wx-text size=\"sm\" :tone=\"tone(counted.description, descriptionLimit)\">\n {{ counted.description }} / {{ descriptionLimit }}\n </wx-text>\n </div>\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.keywords')\" :disabled=\"disabled\">\n <wx-input v-model=\"keywords\" localized />\n </wx-form-item>\n\n <div v-if=\"hasPreview\" class=\"wx-seo__preview\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('card.preview') }}</wx-text>\n <div class=\"wx-seo__snippet\">\n <div v-if=\"preview.address\" class=\"wx-seo__snippet-address\">\n {{ preview.address }}\n </div>\n <div class=\"wx-seo__snippet-title\">{{ preview.title }}</div>\n <div class=\"wx-seo__snippet-text\">{{ preview.description }}</div>\n </div>\n </div>\n </div>\n </wx-tab>\n\n <wx-tab :label=\"t('card.section-share')\" value=\"share\">\n <div class=\"wx-seo__fields\">\n <component\n :is=\"mediaField\"\n v-if=\"mediaField\"\n v-model=\"image\"\n :label=\"t('card.og-image')\"\n aspect=\"16/9\"\n accept=\"image\"\n :disabled=\"disabled\"\n />\n\n <wx-form-item :label=\"t('card.og-title')\" :help=\"t('card.og-help')\" :disabled=\"disabled\">\n <wx-input v-model=\"ogTitle\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.og-description')\" :disabled=\"disabled\">\n <wx-textarea v-model=\"ogDescription\" localized :rows=\"2\" />\n </wx-form-item>\n </div>\n </wx-tab>\n\n <wx-tab :label=\"t('card.section-advanced')\" value=\"advanced\">\n <div class=\"wx-seo__fields\">\n <wx-form-item\n :label=\"t('card.canonical')\"\n :help=\"t('card.canonical-help')\"\n :disabled=\"disabled\"\n >\n <wx-input v-model=\"canonical\" placeholder=\"https://example.com/page\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('card.robots')\" :help=\"keptHelp\" :disabled=\"disabled\">\n <wx-checkbox-group v-model=\"known\" :options=\"robotsOptions\" />\n </wx-form-item>\n\n <wx-form-item\n v-if=\"structuredData\"\n :label=\"t('card.json-ld')\"\n :help=\"t('card.json-ld-help')\"\n :error=\"jsonLdBroken ? t('card.json-ld-invalid') : undefined\"\n :disabled=\"disabled\"\n >\n <wx-code-editor\n v-model=\"jsonLd\"\n language=\"json\"\n lint\n min-height=\"120px\"\n max-height=\"420px\"\n />\n </wx-form-item>\n </div>\n </wx-tab>\n </wx-tabs>\n </div>\n</template>\n\n<style scoped>\n.wx-seo {\n container-type: inline-size;\n}\n\n.wx-seo__fields {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n padding-block-start: var(--wx-space-12);\n}\n\n.wx-seo__count {\n display: flex;\n justify-content: flex-end;\n}\n\n.wx-seo__preview {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n}\n\n/* Not a copy of any one search engine's styling — a reminder of how much of a line survives. */\n.wx-seo__snippet {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-2);\n padding: var(--wx-space-12);\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-md);\n background: var(--wx-bg-subtle);\n}\n\n.wx-seo__snippet-address {\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n overflow-wrap: anywhere;\n}\n\n.wx-seo__snippet-title {\n color: var(--wx-color-primary);\n font-size: var(--wx-font-size-lg);\n overflow-wrap: anywhere;\n}\n\n.wx-seo__snippet-text {\n color: var(--wx-text-default);\n font-size: var(--wx-font-size-sm);\n overflow-wrap: anywhere;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxAlert,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n WxSwitch,\n} from '@webx-ui/core'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { MatchType, SeoRedirect, SeoRoute } from './types'\n\n/** One address that has moved, and where to. Four fields, so four fields. */\nconst props = withDefaults(defineProps<{ redirect?: SeoRedirect | null }>(), { redirect: null })\n\nconst { resolve, dismiss, open } = useModal<SeoRedirect>()\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\nconst occupant = ref<SeoRoute | null>(null)\n\nconst form = ref({\n match_type: 'exact' as MatchType,\n pattern: '',\n target: '',\n status: 301,\n is_active: true,\n})\n\nconst editing = computed(() => props.redirect !== null)\n\nconst kindOptions = computed(() => [\n { value: 'exact', label: t('page.exact') },\n { value: 'mask', label: t('page.mask') },\n { value: 'regex', label: t('page.regex') },\n])\n\n/* Numbers, not words: 301 and 302 are what a person types into a search when they want to know\n what one is, and a label would only stand between them and that. */\nconst statusOptions = [\n { value: 301, label: '301' },\n { value: 302, label: '302' },\n]\n\nwatch(\n () => props.redirect,\n (redirect) => {\n form.value = {\n match_type: redirect?.match_type ?? 'exact',\n pattern: redirect?.pattern ?? '',\n target: redirect?.target ?? '',\n status: redirect?.status ?? 301,\n is_active: redirect?.is_active ?? true,\n }\n errors.value = {}\n occupant.value = null\n void shadowed()\n },\n { immediate: true },\n)\n\n/**\n * Whether a live page is standing at the address this rule is about to take over.\n *\n * A rule wins: `RedirectRequests` runs before routing, deliberately, so that an editor can\n * redirect an address the site still answers at. Which is exactly why this has to be said out\n * loud — the page does not disappear from the panel, it only stops being reachable, and the\n * person who finds out otherwise is a reader.\n *\n * Only for an exact address. A mask covers addresses that do not exist yet and a regular\n * expression covers ones nobody can enumerate; guessing at either would produce a warning that\n * is wrong often enough to be ignored, and a warning that is ignored is worse than none.\n */\nasync function shadowed(): Promise<void> {\n const pattern = form.value.pattern.trim()\n\n if (form.value.match_type !== 'exact' || pattern === '') {\n occupant.value = null\n\n return\n }\n\n try {\n occupant.value = (await api.test(pattern)).route\n } catch {\n // The check is a courtesy; a rule is not held up because it could not be made.\n occupant.value = null\n }\n}\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n try {\n const saved = props.redirect\n ? await api.updateRedirect(props.redirect.id, form.value)\n : await api.createRedirect(form.value)\n\n toast.success(t('page.saved'))\n resolve(saved)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]>; message?: string } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('page.failed'))\n } else {\n toast.danger(message(error, t('page.failed')))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('page.redirect')\" :width=\"640\">\n <div class=\"wx-seo-redirect\">\n <wx-form-item :label=\"t('page.kind')\">\n <wx-select v-model=\"form.match_type\" :options=\"kindOptions\" @change=\"shadowed\" />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('page.address')\"\n :help=\"t('page.address-help')\"\n :error=\"errorOf('pattern')\"\n >\n <wx-input v-model=\"form.pattern\" placeholder=\"/old-address\" @blur=\"shadowed\" />\n </wx-form-item>\n\n <!-- Said, not refused: the rule is allowed to shadow the page, and whoever writes it\n should know that is what they are doing. -->\n <wx-alert\n v-if=\"occupant\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"\n occupant.kind === 'alias'\n ? t('page.occupied-alias', { target: occupant.target ?? occupant.path })\n : t('page.occupied', { path: occupant.path })\n \"\n />\n\n <wx-form-item\n :label=\"t('page.target')\"\n :help=\"t('page.target-help')\"\n :error=\"errorOf('target')\"\n >\n <wx-input v-model=\"form.target\" placeholder=\"/new-address\" />\n </wx-form-item>\n\n <div class=\"wx-seo-redirect__row\">\n <wx-form-item :label=\"t('page.status')\">\n <wx-select v-model=\"form.status\" :options=\"statusOptions\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('page.state')\">\n <wx-switch v-model=\"form.is_active\" />\n </wx-form-item>\n </div>\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('page.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">\n {{ editing ? t('page.save') : t('page.new-redirect') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-seo-redirect {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n\n.wx-seo-redirect__row {\n display: grid;\n grid-template-columns: 140px 1fr;\n gap: var(--wx-space-12);\n align-items: start;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n WxBadge,\n WxButton,\n WxTable,\n WxText,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport SeoLayout from './SeoLayout.vue'\nimport SeoRedirectDialog from './SeoRedirectDialog.vue'\nimport TestUrlDialog from './TestUrlDialog.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport type { SeoPage, SeoRedirect } from './types'\n\n/**\n * The addresses that have moved.\n *\n * Busiest first by default, which is the order this list is worked on: a redirect nobody has\n * followed since the site was rebuilt is the one worth deleting, and it sits at the bottom.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/seo' })\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst page = ref<SeoPage<SeoRedirect> | null>(null)\nconst loading = ref(false)\n\nlet last: TableState = { page: 1, perPage: 20, sort: null, search: '' }\n\nconst canManage = context.can('seo.manage')\n\nconst edit = createModal<SeoRedirect, { redirect: SeoRedirect | null }>(SeoRedirectDialog)\nconst test = createModal<void, Record<string, never>>(TestUrlDialog)\n\nconst columns = computed<TableColumn<SeoRedirect>[]>(() => [\n { key: 'pattern', label: t('page.address'), sortable: true },\n { key: 'target', label: t('page.target') },\n { key: 'status', label: t('page.status'), align: 'center', hideBelow: 560 },\n { key: 'hits', label: t('page.hits'), align: 'center', sortable: true, hideBelow: 760 },\n {\n key: 'last_hit_at',\n label: t('page.last-hit'),\n sortable: true,\n hideBelow: 900,\n hideOnCards: true,\n },\n { key: 'is_active', label: t('page.state'), align: 'center', hideBelow: 660 },\n { key: 'actions', label: '', width: 56, align: 'right', hidden: !canManage, hideOnCards: true },\n])\n\n/** One line, and the same menu every other list of the panel puts a record's actions in. */\nfunction actionsFor(redirect: SeoRedirect): RowAction[] {\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('page.delete'),\n danger: true,\n run: () => remove(redirect),\n },\n ]\n}\n\nasync function load(state: TableState): Promise<void> {\n last = state\n loading.value = true\n\n try {\n page.value = await api.redirects({\n q: state.search,\n sort: state.sort ? `${state.sort.order === 'desc' ? '-' : ''}${state.sort.key}` : '-hits',\n page: state.page,\n per_page: state.perPage,\n })\n } finally {\n loading.value = false\n }\n}\n\n/**\n * Opening a redirect is editing it, so a reader who may not manage these has nowhere to go: the\n * table is told so (`:clickable=\"canManage\"`) and withholds the pointer, the highlight and the\n * click together, rather than promising a dialog that would not open (§13).\n */\nasync function open(redirect: SeoRedirect | null): Promise<void> {\n const saved = await edit({ redirect })\n\n if (saved) void load(last)\n}\n\nasync function remove(redirect: SeoRedirect): Promise<void> {\n const agreed = await confirm({\n title: t('page.delete-title', { pattern: redirect.pattern }),\n message: t('page.delete-text'),\n confirmText: t('page.delete'),\n cancelText: t('page.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeRedirect(redirect.id)\n toast.success(t('page.deleted'))\n void load(last)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n</script>\n\n<template>\n <seo-layout :base=\"props.base\" current=\"redirects\" @test=\"test({})\">\n <template v-if=\"canManage\" #actions>\n <wx-button type=\"primary\" icon=\"add\" @click=\"open(null)\">\n {{ t('page.new-redirect') }}\n </wx-button>\n </template>\n\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"canManage\"\n :hover=\"canManage\"\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('page.search-redirects')\"\n :empty-text=\"t('page.empty')\"\n @row-click=\"open\"\n @state-change=\"load\"\n >\n <template #cell-pattern=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.pattern }}</wx-text>\n <!-- Said out loud rather than refused on save: the middleware steps over it, and a\n row that quietly does nothing is a row nobody ever fixes. -->\n <wx-badge v-if=\"row.is_loop\" type=\"warning\">{{ t('page.loop') }}</wx-badge>\n </template>\n\n <template #cell-target=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.target }}</wx-text>\n </template>\n\n <template #cell-last_hit_at=\"{ row }\">\n <wx-date :value=\"row.last_hit_at\" :tone=\"row.last_hit_at ? 'default' : 'muted'\" />\n </template>\n\n <template #cell-is_active=\"{ row }\">\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot>\n {{ row.is_active ? t('page.active') : t('page.inactive') }}\n </wx-badge>\n </template>\n\n <template #card-actions=\"{ row }\">\n <wx-row-menu v-if=\"canManage\" :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n </wx-table>\n </seo-layout>\n</template>\n","import { localizedValue, type LocalizedValue } from '@webx-ui/core'\nimport type { SeoUrlInput, SeoUrlRule, SeoValue } from './types'\n\n/** The fields the card owns, as opposed to the ones the rule form owns. */\nconst SEO_FIELDS = [\n 'title',\n 'h1',\n 'description',\n 'keywords',\n 'og_title',\n 'og_description',\n 'og_image',\n 'canonical',\n 'robots',\n 'json_ld',\n] as const\n\n/**\n * What a rule is called in a table.\n *\n * Any language rather than the current one: a record half translated is the normal state of a\n * site being worked on, and a blank cell says less than the language that does have the words.\n */\nexport function ruleTitle(rule: SeoUrlRule, locale?: string): string {\n return localizedValue(rule.title as LocalizedValue | undefined, locale)\n}\n\n/** The SEO half of a rule, for the card to edit. */\nexport function seoOf(rule: SeoUrlRule | null): SeoValue {\n if (!rule) return {}\n\n const value: SeoValue = {}\n\n for (const key of SEO_FIELDS) {\n const one = rule[key]\n\n if (one !== null && one !== undefined) {\n Object.assign(value, { [key]: one })\n }\n }\n\n return value\n}\n\n/**\n * What the form sends: the rule's own fields, with the card's value spread back over them.\n *\n * Every field of the card goes, present or not — a `PUT` replaces the whole rule, so a\n * description somebody cleared has to arrive as nothing rather than not arrive at all.\n */\nexport function ruleInput(\n own: Pick<SeoUrlInput, 'match_type' | 'pattern' | 'priority' | 'is_active'>,\n seo: SeoValue,\n): SeoUrlInput {\n const input: SeoUrlInput = { ...own }\n\n for (const key of SEO_FIELDS) {\n Object.assign(input, { [key]: seo[key] ?? null })\n }\n\n return input\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, type Component } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxInputNumber,\n WxSelect,\n WxSpace,\n WxSwitch,\n} from '@webx-ui/core'\nimport SeoCard from './SeoCard.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport { ruleInput, seoOf } from './rule'\nimport type { MatchType, SeoUrlRule, SeoValue } from './types'\n\n/**\n * One rule: which addresses it covers, and what it says about them.\n *\n * A dialog rather than a second route, and a template rather than a described screen — the same\n * decision `admins.form` is on, until the screens mechanism has been round the block a few more\n * times. What it edits below the address is `WxSeo`, the same card a content module will get by\n * patching it into its own form.\n */\nconst props = withDefaults(defineProps<{ rule?: SeoUrlRule | null; mediaField?: Component }>(), {\n rule: null,\n mediaField: undefined,\n})\n\nconst { resolve, dismiss, open } = useModal<SeoUrlRule>()\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst form = ref({\n match_type: 'exact' as MatchType,\n pattern: '',\n priority: 0,\n is_active: true,\n})\n\nconst seo = ref<SeoValue>({})\n\nconst editing = computed(() => props.rule !== null)\n\nconst kindOptions = computed(() => [\n { value: 'exact', label: t('page.exact') },\n { value: 'mask', label: t('page.mask') },\n { value: 'regex', label: t('page.regex') },\n])\n\nwatch(\n () => props.rule,\n (rule) => {\n form.value = {\n match_type: rule?.match_type ?? 'exact',\n pattern: rule?.pattern ?? '',\n priority: rule?.priority ?? 0,\n is_active: rule?.is_active ?? true,\n }\n seo.value = seoOf(rule)\n errors.value = {}\n },\n { immediate: true },\n)\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n const input = ruleInput(form.value, seo.value)\n\n try {\n const saved = props.rule\n ? await api.updateUrl(props.rule.id, input)\n : await api.createUrl(input)\n\n toast.success(t('page.saved'))\n resolve(saved)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]>; message?: string } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('page.failed'))\n } else {\n toast.danger(message(error, t('page.failed')))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('page.rule')\" :width=\"860\">\n <div class=\"wx-seo-rule\">\n <div class=\"wx-seo-rule__where\">\n <wx-form-item :label=\"t('page.kind')\">\n <wx-select v-model=\"form.match_type\" :options=\"kindOptions\" />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('page.address')\"\n :help=\"t('page.address-help')\"\n :error=\"errorOf('pattern')\"\n class=\"wx-seo-rule__address\"\n >\n <wx-input v-model=\"form.pattern\" placeholder=\"/catalog/shoes\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('page.priority')\" :help=\"t('page.priority-help')\">\n <wx-input-number v-model=\"form.priority\" :step=\"10\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('page.state')\">\n <wx-switch v-model=\"form.is_active\" />\n </wx-form-item>\n </div>\n\n <seo-card v-model=\"seo\" :media-field=\"props.mediaField\" />\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('page.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">\n {{ editing ? t('page.save') : t('page.new-rule') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-seo-rule {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n container-type: inline-size;\n}\n\n/* The address and its kind on one line: they are one decision, and reading them apart is\n reading half of it. */\n.wx-seo-rule__where {\n display: grid;\n grid-template-columns: 180px 1fr 140px 100px;\n gap: var(--wx-space-12);\n align-items: start;\n}\n\n@container (max-width: 720px) {\n .wx-seo-rule__where {\n grid-template-columns: 1fr 1fr;\n }\n\n .wx-seo-rule__address {\n grid-column: 1 / -1;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, type Component } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n WxBadge,\n WxButton,\n WxSelect,\n WxTable,\n WxText,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport SeoUrlDialog from './SeoUrlDialog.vue'\nimport TestUrlDialog from './TestUrlDialog.vue'\nimport SeoLayout from './SeoLayout.vue'\nimport { createSeoApi } from './api'\nimport { useSeoMessages } from './i18n'\nimport { ruleTitle } from './rule'\nimport type { SeoPage, SeoUrlRule } from './types'\n\n/**\n * The rules written for addresses.\n *\n * Listed in the order the site tries them — exact, then mask, then regular expression, by\n * priority inside each group — so that reading the table top to bottom is reading what will\n * happen. Sorting it any other way would hide the one thing a person comes here to work out.\n */\nconst props = withDefaults(defineProps<{ base?: string; mediaField?: Component }>(), {\n base: '/seo',\n mediaField: undefined,\n})\n\nconst context = useAdmin()\nconst api = createSeoApi(context)\nuseSeoMessages()\n\nconst t = useTranslate('webx-seo')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst page = ref<SeoPage<SeoUrlRule> | null>(null)\nconst loading = ref(false)\n/* A word rather than an empty string: a select reads \"\" as nothing chosen and shows a blank\n control where the option said \"any kind\". */\nconst kind = ref('any')\n\nlet last: TableState = { page: 1, perPage: 20, sort: null, search: '' }\n\nconst canManage = context.can('seo.manage')\n\nconst edit = createModal<SeoUrlRule, { rule: SeoUrlRule | null; mediaField?: Component }>(\n SeoUrlDialog,\n)\nconst test = createModal<void, Record<string, never>>(TestUrlDialog)\n\nconst kindOptions = computed(() => [\n { value: 'any', label: t('page.any-kind') },\n { value: 'exact', label: t('page.exact') },\n { value: 'mask', label: t('page.mask') },\n { value: 'regex', label: t('page.regex') },\n])\n\nconst columns = computed<TableColumn<SeoUrlRule>[]>(() => [\n { key: 'pattern', label: t('page.address') },\n { key: 'match_type', label: t('page.kind'), hideBelow: 560 },\n { key: 'title', label: t('page.title'), hideBelow: 900 },\n { key: 'priority', label: t('page.priority'), align: 'center', hideBelow: 760 },\n { key: 'is_active', label: t('page.state'), align: 'center', hideBelow: 660 },\n { key: 'actions', label: '', width: 56, align: 'right', hidden: !canManage, hideOnCards: true },\n])\n\n/**\n * What a rule offers: one line, and it is the destructive one — everything else about a rule is\n * done by opening it. Still a menu, because a red bin standing in every row shouts louder than\n * deleting a rule deserves, and because a reader should find a record's actions in the same\n * place in every list of the panel (§20).\n */\nfunction actionsFor(rule: SeoUrlRule): RowAction[] {\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('page.delete'),\n danger: true,\n run: () => remove(rule),\n },\n ]\n}\n\nasync function load(state: TableState): Promise<void> {\n last = state\n loading.value = true\n\n try {\n page.value = await api.urls({\n q: state.search,\n match_type: kind.value === 'any' ? null : (kind.value as 'exact' | 'mask' | 'regex'),\n page: state.page,\n per_page: state.perPage,\n })\n } finally {\n loading.value = false\n }\n}\n\n/**\n * Opening a rule is editing it, so a reader who may not manage these has nowhere to go: the\n * table is told so (`:clickable=\"canManage\"`) and withholds the pointer, the highlight and the\n * click together, rather than promising a dialog that would not open (§13).\n */\nasync function open(rule: SeoUrlRule | null): Promise<void> {\n const saved = await edit({ rule, mediaField: props.mediaField })\n\n if (saved) void load(last)\n}\n\nasync function remove(rule: SeoUrlRule): Promise<void> {\n const agreed = await confirm({\n title: t('page.delete-title', { pattern: rule.pattern }),\n message: t('page.delete-text'),\n confirmText: t('page.delete'),\n cancelText: t('page.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeUrl(rule.id)\n toast.success(t('page.deleted'))\n void load(last)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n</script>\n\n<template>\n <seo-layout :base=\"props.base\" current=\"rules\" @test=\"test({})\">\n <template v-if=\"canManage\" #actions>\n <wx-button type=\"primary\" icon=\"add\" @click=\"open(null)\">\n {{ t('page.new-rule') }}\n </wx-button>\n </template>\n\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"canManage\"\n :hover=\"canManage\"\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('page.search-rules')\"\n :empty-text=\"t('page.empty')\"\n @row-click=\"open\"\n @state-change=\"load\"\n >\n <template #actions>\n <wx-select v-model=\"kind\" :options=\"kindOptions\" class=\"wx-seo-urls__kind\" />\n </template>\n\n <template #cell-pattern=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.pattern }}</wx-text>\n </template>\n\n <template #cell-match_type=\"{ row }\">\n <wx-badge>{{ t(`page.${row.match_type}`) }}</wx-badge>\n </template>\n\n <template #cell-title=\"{ row }\">\n <wx-text v-if=\"ruleTitle(row)\" size=\"sm\">{{ ruleTitle(row) }}</wx-text>\n <wx-text v-else size=\"sm\" tone=\"muted\">—</wx-text>\n </template>\n\n <template #cell-is_active=\"{ row }\">\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot>\n {{ row.is_active ? t('page.active') : t('page.inactive') }}\n </wx-badge>\n </template>\n\n <template #card-actions=\"{ row }\">\n <wx-row-menu v-if=\"canManage\" :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.pattern\" />\n </template>\n </wx-table>\n </seo-layout>\n</template>\n\n<style scoped>\n.wx-seo-urls__kind {\n width: 200px;\n}\n\n/* Once the rows are cards the filters are a column, and a fixed width leaves this one short. */\n.wx-table--cards .wx-seo-urls__kind {\n width: 100%;\n}\n</style>\n","import { markRaw, type Component } from 'vue'\nimport type { AdminModule } from '@webx-ui/module-admin'\nimport SeoAliasesPage from './SeoAliasesPage.vue'\nimport SeoCard from './SeoCard.vue'\nimport SeoRedirectsPage from './SeoRedirectsPage.vue'\nimport SeoUrlsPage from './SeoUrlsPage.vue'\n\nexport interface SeoOptions {\n /** Where the section lives inside the panel. */\n path?: string\n /**\n * The field that picks a share image — `WxMediaField` from `@webx-ui/module-media`.\n *\n * Handed in rather than imported, so this package does not depend on the library being\n * installed. Without it every other SEO field still works; only the picture is missing.\n */\n mediaField?: Component\n}\n\n/**\n * SEO as a section of the panel, and `wx-seo` as a field any screen can use.\n *\n * The id matches the module the server reports, which is what makes the entry appear in the\n * navigation: a section shows up when both halves are installed, and stays out of the way when\n * only one is.\n */\nexport function seo(options: SeoOptions = {}): AdminModule {\n const path = options.path ?? '/seo'\n /* A component kept in an options object arrives as a reactive proxy; Vue warns about one and\n `h()` wants the raw thing anyway. */\n const mediaField = options.mediaField ? markRaw(options.mediaField) : undefined\n\n return {\n id: 'seo',\n path,\n routes: [\n // The section's own path travels as a prop, so a panel that mounted it somewhere else\n // still links between its two screens correctly.\n {\n path,\n name: 'webx.seo',\n component: SeoUrlsPage,\n props: { base: path, mediaField },\n },\n {\n path: `${path}/redirects`,\n name: 'webx.seo.redirects',\n component: SeoRedirectsPage,\n props: { base: path },\n },\n // The trail of renames, from `webx-ui/routing`. The screen is here whether or not any\n // content module has registered a type yet: without one the table is simply empty, and an\n // empty table is a better answer than a tab that appears one day without explanation.\n {\n path: `${path}/aliases`,\n name: 'webx.seo.aliases',\n component: SeoAliasesPage,\n props: { base: path },\n },\n ],\n // What a screen means by `wx-seo`: everything a page says about itself, as one value. The\n // server registers the same name for what it stores.\n types: {\n 'wx-seo': {\n component: SeoCard,\n kind: 'field',\n wide: true,\n bind: () => ({ mediaField }),\n },\n },\n }\n}\n"],"names":["seoMessages","seeded","useSeoMessages","i18n","useAdmin","props","__props","emit","__emit","context","router","useRouter","route","useRoute","t","useTranslate","title","computed","module","views","where","next","path","_createBlock","_unref","WxListScreen","$event","_renderSlot","_ctx","_createVNode","WxButton","createSeoApi","admin","base","data","body","page","listQuery","query","id","input","url","locale","open","useModal","api","ref","running","result","fields","seo","one","run","WxDialog","_createElementVNode","_hoisted_1","WxSpace","WxInput","_createElementBlock","_Fragment","WxAlert","_openBlock","_hoisted_2","WxText","WxBadge","_createTextVNode","_toDisplayString","_hoisted_3","WxDescriptions","_renderList","WxDescriptionsItem","_hoisted_4","step","WxEmpty","loading","test","createModal","TestUrlDialog","multilingual","columns","load","state","SeoLayout","WxTable","_withCtx","row","WxLink","WxDate","robotsDirectives","value","_useModel","locales","useLocales","jsonLdText","jsonLdBroken","field","key","h1","description","keywords","ogTitle","ogDescription","canonical","image","directives","kept","known","merged","robotsOptions","jsonLd","stored","inLocale","map","localizedValue","counted","preview","hasPreview","keptHelp","tone","count","limit","WxTabs","WxTab","WxFormItem","WxTextarea","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_resolveDynamicComponent","_hoisted_11","WxCheckboxGroup","WxCodeEditor","resolve","dismiss","message","useErrorText","saving","errors","occupant","form","editing","kindOptions","statusOptions","watch","redirect","shadowed","pattern","errorOf","save","saved","toast","error","WxSelect","_cache","WxSwitch","last","canManage","edit","SeoRedirectDialog","actionsFor","remove","confirm","WxRowMenu","SEO_FIELDS","ruleTitle","rule","seoOf","ruleInput","own","WxInputNumber","SeoCard","kind","SeoUrlDialog","options","mediaField","markRaw","SeoUrlsPage","SeoRedirectsPage","SeoAliasesPage"],"mappings":";;;;AAaO,MAAMA,KAAwC;AAAA,EACnD,QAAQ;AAAA,IACN,OAAO;AAAA,EAAA;AAAA,EAGT,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,WAAW;AAAA,IACX,MAAM;AAAA,IAEN,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,OAAO;AAAA,IAEP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,MAAM;AAAA,IAEN,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,OAAO;AAAA,IAEP,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,eAAe;AAAA,IAEf,gBACE;AAAA,IACF,eACE;AAAA,IACF,iBAAiB;AAAA,IAEjB,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,cAAc;AAAA,IAEd,WAAW;AAAA,IACX,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,gBACE;AAAA,IACF,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,UACE;AAAA,IACF,kBACE;AAAA,EAAA;AAAA,EAGJ,MAAM;AAAA,IACJ,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,oBAAoB;AAAA,IAEpB,OAAO;AAAA,IACP,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,WAAW;AAAA,IACX,kBAAkB;AAAA,IAElB,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,SAAS;AAAA,IACT,UAAU;AAAA,IACV,WAAW;AAAA,IACX,WAAW;AAAA,IACX,cAAc;AAAA,IAEd,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,YAAY;AAAA,IACZ,WAAW;AAAA,IAEX,WAAW;AAAA,IACX,gBACE;AAAA,IACF,mBAAmB;AAAA,IAEnB,SAAS;AAAA,EAAA;AAEb,GCpHMC,yBAAa,QAAA;AAGZ,SAASC,IAAuB;AACrC,MAAI;AACF,UAAM,EAAE,MAAAC,EAAA,IAASC,EAAA;AAEjB,IAAKH,GAAO,IAAIE,CAAI,MAClBA,EAAK,SAAS,YAAYH,EAAW,GACrCC,GAAO,IAAIE,CAAI;AAAA,EAEnB,QAAQ;AAAA,EAER;AACF;;;;;;;;;ACDA,UAAME,IAAQC,GAERC,IAAOC,GASPC,IAAUL,EAAA,GACVM,IAASC,GAAA,GACTC,IAAQC,GAAA;AAEd,IAAAX,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAG3BC,IAAQC;AAAA,MACZ,MACER,EAAQ,MAAM,UAAU,QAAQ,KAAK,CAACS,MAAWA,EAAO,OAAO,KAAK,GAAG,SACvEJ,EAAE,cAAc;AAAA,IAAA,GAGdK,IAAQF,EAAoB,MAAM;AAAA,MACtC,EAAE,OAAO,SAAS,OAAOH,EAAE,YAAY,EAAA;AAAA,MACvC,EAAE,OAAO,aAAa,OAAOA,EAAE,gBAAgB,EAAA;AAAA,MAC/C,EAAE,OAAO,WAAW,OAAOA,EAAE,gBAAgB,EAAA;AAAA,IAAE,CAChD,GAEKM,IAAQH,EAAmB;AAAA,MAC/B,KAAK,MAAMZ,EAAM;AAAA,MACjB,KAAK,CAACgB,MAAS;AACb,cAAMC,IAAOD,MAAS,UAAUhB,EAAM,OAAO,GAAGA,EAAM,IAAI,IAAI,OAAOgB,CAAI,CAAC;AAE1E,QAAIC,MAASV,EAAM,QAAWF,EAAO,KAAKY,CAAI;AAAA,MAChD;AAAA,IAAA,CACD;2BAICC,EAUiBC,EAAAC,EAAA,GAAA;AAAA,MAVO,MAAML,EAAA;AAAA,8CAAAA,EAAK,QAAAM;AAAA,MAAG,OAAOV,EAAA;AAAA,MAAQ,OAAOG,EAAA;AAAA,IAAA;MAC/C,WACT,MAAuB;AAAA,QAAvBQ,GAAuBC,EAAA,QAAA,SAAA;AAAA,QAEvBC,EAEYL,EAAAM,CAAA,GAAA;AAAA,UAFD,SAAQ;AAAA,UAAU,MAAK;AAAA,UAAU,gCAAOvB,EAAI,MAAA;AAAA,QAAA;qBACrD,MAAoB;AAAA,gBAAjBiB,EAAAV,CAAA,EAAC,WAAA,CAAA,GAAA,CAAA;AAAA,UAAA;;;;iBAIR,MAAQ;AAAA,QAARa,GAAQC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;;ACjCL,SAASG,EAAaC,GAA6B;AACxD,QAAMC,IAAO,GAAGD,EAAM,OAAO,QACvBE,IAAO,CAAIC,MAAyBA,EAAK,MAKzCC,IAAO,CAAID,OAAqE;AAAA,IACpF,GAAGA,EAAK;AAAA,IACR,MAAMA,EAAK;AAAA,EAAA,IAKPE,IAAY,CAChBC,OACkE;AAAA,IAClE,GAAGA,EAAM,KAAK;AAAA,IACd,YAAYA,EAAM,cAAc;AAAA,IAChC,WAAWA,EAAM,aAAa;AAAA,IAC9B,MAAMA,EAAM,QAAQ;AAAA,IACpB,MAAMA,EAAM;AAAA,IACZ,UAAUA,EAAM;AAAA,EAAA;AAGlB,SAAO;AAAA,IACL,MAAM,CAACA,IAAQ,CAAA,MACbN,EAAM,KACH,IAAqE,GAAGC,CAAI,SAAS;AAAA,MACpF,OAAOI,EAAUC,CAAK;AAAA,IAAA,CACvB,EACA,KAAKF,CAAI;AAAA,IAEd,KAAK,CAACG,MAAOP,EAAM,KAAK,IAA0B,GAAGC,CAAI,SAASM,CAAE,EAAE,EAAE,KAAKL,CAAI;AAAA,IAEjF,WAAW,CAACM,MAAUR,EAAM,KAAK,KAA2B,GAAGC,CAAI,SAASO,CAAK,EAAE,KAAKN,CAAI;AAAA,IAE5F,WAAW,CAACK,GAAIC,MACdR,EAAM,KAAK,IAA0B,GAAGC,CAAI,SAASM,CAAE,IAAIC,CAAK,EAAE,KAAKN,CAAI;AAAA,IAE7E,WAAW,CAACK,MAAOP,EAAM,KAAK,OAAa,GAAGC,CAAI,SAASM,CAAE,EAAE;AAAA,IAE/D,WAAW,CAACD,IAAQ,CAAA,MAClBN,EAAM,KACH;AAAA,MACC,GAAGC,CAAI;AAAA,MACP,EAAE,OAAOI,EAAUC,CAAK,EAAA;AAAA,IAAE,EAE3B,KAAKF,CAAI;AAAA,IAEd,gBAAgB,CAACI,MACfR,EAAM,KAAK,KAA4B,GAAGC,CAAI,cAAcO,CAAK,EAAE,KAAKN,CAAI;AAAA,IAE9E,gBAAgB,CAACK,GAAIC,MACnBR,EAAM,KAAK,IAA2B,GAAGC,CAAI,cAAcM,CAAE,IAAIC,CAAK,EAAE,KAAKN,CAAI;AAAA,IAEnF,gBAAgB,CAACK,MAAOP,EAAM,KAAK,OAAa,GAAGC,CAAI,cAAcM,CAAE,EAAE;AAAA,IAEzE,SAAS,CAACD,IAAQ,CAAA,MAChBN,EAAM,KACH,IAAiE,GAAGC,CAAI,YAAY;AAAA,MACnF,OAAO;AAAA,QACL,GAAGK,EAAM,KAAK;AAAA,QACd,QAAQA,EAAM,UAAU;AAAA,QACxB,MAAMA,EAAM;AAAA,QACZ,UAAUA,EAAM;AAAA,MAAA;AAAA,IAClB,CACD,EACA,KAAKF,CAAI;AAAA;AAAA;AAAA,IAId,MAAM,CAACK,GAAKC,IAAS,SACnBV,EAAM,KACH,KAA8B,GAAGC,CAAI,aAAa,EAAE,KAAAQ,GAAK,QAAQC,KAAU,QAAW,EACtF,KAAKR,CAAI;AAAA,EAAA;AAElB;;;;;;;;;;;;;ACrFA,UAAM,EAAE,MAAAS,EAAA,IAASC,GAAA,GAEXnC,IAAUL,EAAA,GACVyC,IAAMd,EAAatB,CAAO;AAChC,IAAAP,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAE3B0B,IAAMK,EAAI,EAAE,GACZC,IAAUD,EAAI,EAAK,GACnBE,IAASF,EAA0B,IAAI,GAEvCG,IAAShC,EAAS,MAAM;AAC5B,YAAMiC,IAAMF,EAAO,OAAO;AAE1B,aAAKE,IAEE;AAAA,QACL,EAAE,OAAOpC,EAAE,YAAY,GAAG,OAAOoC,EAAI,MAAA;AAAA,QACrC,EAAE,OAAOpC,EAAE,SAAS,GAAG,OAAOoC,EAAI,GAAA;AAAA,QAClC,EAAE,OAAOpC,EAAE,kBAAkB,GAAG,OAAOoC,EAAI,YAAA;AAAA,QAC3C,EAAE,OAAOpC,EAAE,eAAe,GAAG,OAAOoC,EAAI,SAAA;AAAA,QACxC,EAAE,OAAOpC,EAAE,gBAAgB,GAAG,OAAOoC,EAAI,UAAA;AAAA,QACzC,EAAE,OAAOpC,EAAE,aAAa,GAAG,OAAOoC,EAAI,OAAA;AAAA,MAAO,EAC7C,OAAO,CAACC,MAAQA,EAAI,KAAK,IATV,CAAA;AAAA,IAUnB,CAAC;AAED,mBAAeC,IAAqB;AAClC,UAAIX,EAAI,MAAM,KAAA,MAAW,IAEzB;AAAA,QAAAM,EAAQ,QAAQ;AAEhB,YAAI;AACF,UAAAC,EAAO,QAAQ,MAAMH,EAAI,KAAKJ,EAAI,KAAK;AAAA,QACzC,UAAA;AACE,UAAAM,EAAQ,QAAQ;AAAA,QAClB;AAAA;AAAA,IACF;2BAIExB,EAqEYC,EAAA6B,EAAA,GAAA;AAAA,MArEO,MAAM7B,EAAAmB,CAAA;AAAA,qDAAAA,EAAI,QAAAjB,IAAA;AAAA,MAAG,OAAOF,EAAAV,CAAA,EAAC,WAAA;AAAA,MAAgB,OAAO;AAAA,IAAA;iBAC7D,MAmEM;AAAA,QAnENwC,EAmEM,OAnENC,IAmEM;AAAA,UAlEJ1B,EAUWL,EAAAgC,CAAA,GAAA;AAAA,YAVD,MAAK;AAAA,YAAK,OAAM;AAAA,UAAA;uBACxB,MAKE;AAAA,cALF3B,EAKEL,EAAAiC,CAAA,GAAA;AAAA,4BAJShB,EAAA;AAAA,8DAAAA,EAAG,QAAAf;AAAA,gBACX,aAAaF,EAAAV,CAAA,EAAC,uBAAA;AAAA,gBACf,OAAM;AAAA,gBACL,YAAasC,GAAG,CAAA,OAAA,CAAA;AAAA,cAAA;cAEnBvB,EAEYL,EAAAM,CAAA,GAAA;AAAA,gBAFD,MAAK;AAAA,gBAAW,SAASiB,EAAA;AAAA,gBAAU,SAAOK;AAAA,cAAA;2BACnD,MAAwB;AAAA,sBAArB5B,EAAAV,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;;;;UAIQkC,EAAA,cAAhBU,EAmDWC,IAAA,EAAA,KAAA,KAAA;AAAA,YAhDDX,EAAA,MAAO,iBADfzB,EASEC,EAAAoC,CAAA,GAAA;AAAA;cAPA,MAAK;AAAA,cACJ,aAA0BpC,EAAAV,CAAA,EAAC,wBAAA;AAAA,wBAAiDkC,EAAA,MAAO,SAAS;AAAA,wBAA8BA,EAAA,MAAO,SAAS;AAAA,cAAA;;YAWrIA,EAAA,MAAO,cADfzB,EAQEC,EAAAoC,CAAA,GAAA;AAAA;cANA,MAAK;AAAA,cACJ,aAA0BZ,EAAA,MAAO,MAAM,SAAI,UAA6BxB,EAAAV,CAAA,EAAC,uBAAA,EAAA,QAAkCkC,EAAA,MAAO,MAAM,UAAUA,EAAA,MAAO,MAAM,KAAA,CAAI,IAAoBxB,EAAAV,CAAA,EAAC,iBAAA,EAAA,MAA0BkC,EAAA,MAAO,MAAM,MAAI;AAAA,YAAA;YAOrMA,EAAA,MAAO,WACxBa,KAAAH,EAMM,OANNI,IAMM;AAAA,cALJjC,EAAsEL,EAAAuC,CAAA,GAAA;AAAA,gBAA7D,MAAK;AAAA,gBAAK,MAAK;AAAA,cAAA;2BAAQ,MAA4B;AAAA,sBAAzBvC,EAAAV,CAAA,EAAC,mBAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;cACpCe,EAGWL,EAAAgC,CAAA,GAAA;AAAA,gBAHD,MAAK;AAAA,gBAAK,OAAM;AAAA,cAAA;2BACxB,MAAiE;AAAA,kBAAjE3B,EAAiEL,EAAAwC,CAAA,GAAA,MAAA;AAAA,+BAAvD,MAA4C;AAAA,sBAAzCC,EAAAC,EAAA1C,EAAAV,CAAA,EAAC,QAASkC,EAAA,MAAO,QAAQ,UAAU,EAAA,CAAA,GAAA,CAAA;AAAA,oBAAA;;;kBAChDnB,EAA8DL,EAAAuC,CAAA,GAAA;AAAA,oBAArD,MAAA;AAAA,oBAAK,MAAK;AAAA,kBAAA;+BAAK,MAA4B;AAAA,0BAAzBf,EAAA,MAAO,QAAQ,OAAO,GAAA,CAAA;AAAA,oBAAA;;;;;;wBALrDzB,EAAkFC,EAAAoC,CAAA,GAAA;AAAA;cAAjD,MAAK;AAAA,cAAQ,aAAapC,EAAAV,CAAA,EAAC,gBAAA;AAAA,YAAA;YASjDmC,EAAA,MAAO,SAAM,KAAxBY,KAAAH,EAOM,OAPNS,IAOM;AAAA,cANJtC,EAAqEL,EAAAuC,CAAA,GAAA;AAAA,gBAA5D,MAAK;AAAA,gBAAK,MAAK;AAAA,cAAA;2BAAQ,MAA2B;AAAA,sBAAxBvC,EAAAV,CAAA,EAAC,kBAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;cACpCe,EAIkBL,EAAA4C,EAAA,GAAA;AAAA,gBAJA,SAAS;AAAA,gBAAG,UAAA;AAAA,gBAAS,MAAK;AAAA,cAAA;2BACpB,MAAqB;AAAA,0BAA3CV,EAEuBC,IAAA,MAAAU,GAFapB,EAAA,OAAM,CAAbE,YAA7B5B,EAEuBC,EAAA8C,EAAA,GAAA;AAAA,oBAFsB,KAAKnB,EAAI;AAAA,oBAAQ,OAAOA,EAAI;AAAA,kBAAA;+BACvE,MAAe;AAAA,sBAAZc,EAAAC,EAAAf,EAAI,KAAK,GAAA,CAAA;AAAA,oBAAA;;;;;;;YAKPH,EAAA,MAAO,MAAM,SAAM,KAA9Ba,KAAAH,EAOM,OAPNa,IAOM;AAAA,cANJ1C,EAAoEL,EAAAuC,CAAA,GAAA;AAAA,gBAA3D,MAAK;AAAA,gBAAK,MAAK;AAAA,cAAA;2BAAQ,MAA0B;AAAA,sBAAvBvC,EAAAV,CAAA,EAAC,iBAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;cACpCe,EAIWL,EAAAgC,CAAA,GAAA;AAAA,gBAJD,MAAK;AAAA,gBAAK,MAAA;AAAA,cAAA;2BACR,MAA4B;AAAA,mBAAtCK,EAAA,EAAA,GAAAH,EAEWC,IAAA,MAAAU,GAFcrB,EAAA,MAAO,QAAfwB,YAAjBjD,EAEWC,EAAAwC,CAAA,GAAA;AAAA,oBAF6B,KAAKQ,EAAK;AAAA,kBAAA;+BAChD,MAAiB;AAAA,sBAAdP,EAAAC,EAAAM,EAAK,MAAM,IAAG,QAAGN,EAAGM,EAAK,QAAQ,GAAA,CAAA;AAAA,oBAAA;;;;;;;oBAMtBzB,EAAA,0BAAtBxB,EAAmFC,EAAAiD,EAAA,GAAA;AAAA;YAApD,MAAK;AAAA,YAAU,aAAajD,EAAAV,CAAA,EAAC,iBAAA;AAAA,UAAA;;;;;;;;;;;;;;;;;ACvGlE,UAAMT,IAAQC,GAERG,IAAUL,EAAA,GACVyC,IAAMd,EAAatB,CAAO;AAChC,IAAAP,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAE3BqB,IAAOU,EAA8B,IAAI,GACzC4B,IAAU5B,EAAI,EAAK,GAEnB6B,IAAOC,EAAyCC,EAAa,GAG7DC,IAAe7D,EAAS,OAAOR,EAAQ,MAAM,UAAU,SAAS,UAAU,KAAK,CAAC,GAEhFsE,IAAU9D,EAAkC,MAAM;AAAA,MACtD,EAAE,KAAK,WAAW,OAAOH,EAAE,cAAc,GAAG,UAAU,GAAA;AAAA,MACtD,EAAE,KAAK,UAAU,OAAOA,EAAE,aAAa,EAAA;AAAA,MACvC;AAAA,QACE,KAAK;AAAA,QACL,OAAOA,EAAE,eAAe;AAAA,QACxB,OAAO;AAAA,QACP,QAAQ,CAACgE,EAAa;AAAA,QACtB,WAAW;AAAA,MAAA;AAAA,MAEb,EAAE,KAAK,cAAc,OAAOhE,EAAE,eAAe,GAAG,WAAW,KAAK,aAAa,GAAA;AAAA,IAAK,CACnF;AAED,mBAAekE,EAAKC,GAAkC;AACpD,MAAAP,EAAQ,QAAQ;AAEhB,UAAI;AACF,QAAAtC,EAAK,QAAQ,MAAMS,EAAI,QAAQ;AAAA,UAC7B,GAAGoC,EAAM;AAAA,UACT,MAAMA,EAAM;AAAA,UACZ,UAAUA,EAAM;AAAA,QAAA,CACjB;AAAA,MACH,UAAA;AACE,QAAAP,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;2BAIEnD,EAyCa2D,IAAA;AAAA,MAzCA,MAAM7E,EAAM;AAAA,MAAM,SAAQ;AAAA,MAAW,+BAAMmB,EAAAmD,CAAA,EAAI,CAAA,CAAA;AAAA,IAAA;iBAC1D,MAKE;AAAA,QALF9C,EAKEL,EAAAoC,CAAA,GAAA;AAAA,UAJA,MAAK;AAAA,UACL,SAAQ;AAAA,UACP,aAAapC,EAAAV,CAAA,EAAC,mBAAA;AAAA,UACf,OAAM;AAAA,QAAA;QAGRe,EAgCWL,EAAA2D,EAAA,GAAA;AAAA,UA/BR,MAAM/C,EAAA;AAAA,UACN,SAAS2C,EAAA;AAAA,UACV,WAAQ;AAAA,UACR,YAAA;AAAA,UACA,OAAA;AAAA,UACC,SAASL,EAAA;AAAA,UACT,sBAAoBlD,EAAAV,CAAA,EAAC,qBAAA;AAAA,UACrB,cAAYU,EAAAV,CAAA,EAAC,oBAAA;AAAA,UACb,eAAckE;AAAA,QAAA;UAEJ,gBAAYI,EACrB,CAAmD,EAD1B,KAAAC,QAAG;AAAA,YAC5BxD,EAAmDL,EAAAuC,CAAA,GAAA;AAAA,cAA1C,MAAA;AAAA,cAAK,MAAK;AAAA,YAAA;yBAAK,MAAiB;AAAA,gBAAdE,EAAAC,EAAAmB,EAAI,OAAO,GAAA,CAAA;AAAA,cAAA;;;;UAM7B,eAAWD,EACpB,CAEU,EAHc,KAAAC,QAAG;AAAA,YACZA,EAAI,UAAUA,EAAI,mBAAjC9D,EAEUC,EAAA8D,EAAA,GAAA;AAAA;cAFoC,MAAMD,EAAI;AAAA,cAAY,UAAA;AAAA,cAAS,MAAK;AAAA,YAAA;yBAChF,MAAkD;AAAA,gBAAlDxD,EAAkDL,EAAAuC,CAAA,GAAA;AAAA,kBAAzC,MAAA;AAAA,kBAAK,MAAK;AAAA,gBAAA;6BAAK,MAAgB;AAAA,oBAAbE,EAAAC,EAAAmB,EAAI,MAAM,GAAA,CAAA;AAAA,kBAAA;;;;;wCAEvC9D,EAA+DC,EAAAwC,CAAA,GAAA;AAAA;cAA9C,MAAK;AAAA,YAAA;yBAAU,MAAoB;AAAA,oBAAjBxC,EAAAV,CAAA,EAAC,WAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;UAG3B,eAAWsE,EACpB,CAAqC,EADb,KAAAC,QAAG;AAAA,YAC3BxD,EAAqCL,EAAAwC,CAAA,GAAA,MAAA;AAAA,yBAA3B,MAAgB;AAAA,gBAAbC,EAAAC,EAAAmB,EAAI,MAAM,GAAA,CAAA;AAAA,cAAA;;;;UAGd,mBAAeD,EACxB,CAAyD,EAD7B,KAAAC,QAAG;AAAA,YAChBA,EAAI,mBAAnB9D,EAAyDC,EAAA+D,EAAA,GAAA;AAAA;cAAzB,OAAOF,EAAI;AAAA,YAAA;;;;;;;;oECiFtCG,KAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;;;;;;;;;;;;;;;;;;;;AC3KA,UAAMnF,IAAQC,GA+BRmF,IAAQC,GAA4BpF,GAAA,YAAkB;AAE5D,IAAAJ,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAC3B4E,IAAUC,GAAA,GAEVC,IAAa/C,EAAmB,IAAI,GACpCgD,IAAehD,EAAI,EAAK;AAG9B,aAASiD,EAAgCC,GAAQ;AAC/C,aAAO/E,EAAS;AAAA,QACd,KAAK,MAAMwE,EAAM,QAAQO,CAAG;AAAA,QAC5B,KAAK,CAAC3E,MAAsB;AAC1B,UAAAoE,EAAM,QAAQ,EAAE,GAAIA,EAAM,SAAS,IAAK,CAACO,CAAG,GAAG3E,EAAA;AAAA,QACjD;AAAA,MAAA,CACD;AAAA,IACH;AAEA,UAAML,IAAQ+E,EAAM,OAAO,GACrBE,IAAKF,EAAM,IAAI,GACfG,IAAcH,EAAM,aAAa,GACjCI,IAAWJ,EAAM,UAAU,GAC3BK,IAAUL,EAAM,UAAU,GAC1BM,IAAgBN,EAAM,gBAAgB,GAEtCO,IAAYrF,EAAiB;AAAA,MACjC,KAAK,MAAMwE,EAAM,OAAO,aAAa;AAAA,MACrC,KAAK,CAACpE,MAAiB;AACrB,QAAAoE,EAAM,QAAQ,EAAE,GAAIA,EAAM,SAAS,CAAA,GAAK,WAAWpE,EAAK,KAAA,MAAW,KAAK,OAAOA,EAAA;AAAA,MACjF;AAAA,IAAA,CACD,GAEKkF,IAAQtF,EAAS;AAAA,MACrB,KAAK,MAAMwE,EAAM,OAAO,YAAY;AAAA,MACpC,KAAK,CAACpE,MAA0B;AAC9B,QAAAoE,EAAM,QAAQ,EAAE,GAAIA,EAAM,SAAS,CAAA,GAAK,UAAUpE,EAAA;AAAA,MACpD;AAAA,IAAA,CACD,GAKKmF,IAAavF,EAAS,OAAOwE,EAAM,OAAO,UAAU,IAAI,MAAM,GAAG,EAAE,IAAI,CAACtC,MAAQA,EAAI,KAAA,CAAM,CAAC,GAE3FsD,IAAOxF;AAAA,MAAS,MACpBuF,EAAW,MAAM,OAAO,CAACrD,MAAQA,MAAQ,MAAM,CAACqC,GAAiB,SAASrC,CAAY,CAAC;AAAA,IAAA,GAGnFuD,IAAQzF,EAAS;AAAA,MACrB,KAAK,MAAMuF,EAAW,MAAM,OAAO,CAACrD,MAAQqC,GAAiB,SAASrC,CAAY,CAAC;AAAA,MACnF,KAAK,CAAC9B,MAA4B;AAChC,cAAMsF,IAAS,CAAC,GAAGtF,GAAM,GAAGoF,EAAK,KAAK,EAAE,KAAK,IAAI;AACjD,QAAAhB,EAAM,QAAQ,EAAE,GAAIA,EAAM,SAAS,IAAK,QAAQkB,MAAW,KAAK,OAAOA,EAAA;AAAA,MACzE;AAAA,IAAA,CACD,GAEKC,IAAgB3F;AAAA,MAAS,MAC7BuE,GAAiB,IAAI,CAACrC,OAAS,EAAE,OAAOA,GAAK,OAAOrC,EAAE,QAAQqC,CAAG,EAAE,IAAI;AAAA,IAAA,GAGnE0D,IAAS5F,EAAiB;AAAA,MAC9B,KAAK,MAAM;AACT,YAAI4E,EAAW,UAAU,KAAM,QAAOA,EAAW;AAEjD,cAAMiB,IAASrB,EAAM,OAAO;AAE5B,eAAOqB,KAAW,OAA+B,KAAK,KAAK,UAAUA,GAAQ,MAAM,CAAC;AAAA,MACtF;AAAA,MACA,KAAK,CAACzF,MAAiB;AAKrB,YAFAwE,EAAW,QAAQxE,GAEfA,EAAK,KAAA,MAAW,IAAI;AACtB,UAAAyE,EAAa,QAAQ,IACrBL,EAAM,QAAQ,EAAE,GAAIA,EAAM,SAAS,CAAA,GAAK,SAAS,KAAA;AAEjD;AAAA,QACF;AAEA,YAAI;AACF,UAAAA,EAAM,QAAQ,EAAE,GAAIA,EAAM,SAAS,IAAK,SAAS,KAAK,MAAMpE,CAAI,EAAA,GAChEyE,EAAa,QAAQ;AAAA,QACvB,QAAQ;AACN,UAAAA,EAAa,QAAQ;AAAA,QACvB;AAAA,MACF;AAAA,IAAA,CACD;AAGD,aAASiB,EAASC,GAAyC;AACzD,aAAOC,GAAeD,GAAKrB,EAAQ,OAAO,KAAK;AAAA,IACjD;AAEA,UAAMuB,KAAUjG,EAAS,OAAO;AAAA,MAC9B,OAAO8F,EAAS/F,EAAM,KAAmC,EAAE;AAAA,MAC3D,aAAa+F,EAASb,EAAY,KAAmC,EAAE;AAAA,IAAA,EACvE,GAEIiB,IAAUlG,EAAS,OAAO;AAAA,MAC9B,OAAO8F,EAAS/F,EAAM,KAAmC;AAAA,MACzD,aAAa+F,EAASb,EAAY,KAAmC;AAAA,MACrE,SAASI,EAAU,UAAUjG,EAAM,QAAQ;AAAA,IAAA,EAC3C,GAEI+G,KAAanG,EAAS,MAAMkG,EAAQ,MAAM,UAAU,MAAMA,EAAQ,MAAM,gBAAgB,EAAE,GAE1FE,KAAWpG;AAAA,MAAS,MACxBwF,EAAK,MAAM,WAAW,IAClB,SACA3F,EAAE,oBAAoB,EAAE,YAAY2F,EAAK,MAAM,KAAK,IAAI,GAAG;AAAA,IAAA;AAGjE,aAASa,GAAKC,GAAeC,GAAoC;AAC/D,aAAOD,IAAQC,IAAQ,YAAY;AAAA,IACrC;sBAIE3D,EAAA,GAAAH,EAiGM,OAjGNH,IAiGM;AAAA,MAhGJ1B,EA+FUL,EAAAiG,EAAA,GAAA,EA/FD,cAAA,MAAU;AAAA,mBACjB,MAuCS;AAAA,UAvCT5F,EAuCSL,EAAAkG,EAAA,GAAA;AAAA,YAvCA,OAAOlG,EAAAV,CAAA,EAAC,mBAAA;AAAA,YAAuB,OAAM;AAAA,UAAA;uBAC5C,MAqCM;AAAA,cArCNwC,EAqCM,OArCNQ,IAqCM;AAAA,gBApCJjC,EAOeL,EAAAmG,CAAA,GAAA;AAAA,kBAPA,OAAOnG,EAAAV,CAAA,EAAC,YAAA;AAAA,kBAAiB,UAAUR,EAAA;AAAA,gBAAA;6BAChD,MAAsC;AAAA,oBAAtCuB,EAAsCL,EAAAiC,CAAA,GAAA;AAAA,kCAAnBjC,EAAAR,CAAA;AAAA,2EAAAA,EAAK,QAAAU,IAAA;AAAA,sBAAE,WAAA;AAAA,oBAAA;oBAC1B4B,EAIM,OAJNa,IAIM;AAAA,sBAHJtC,EAEUL,EAAAuC,CAAA,GAAA;AAAA,wBAFD,MAAK;AAAA,wBAAM,MAAMuD,GAAKJ,SAAQ,OAAO5G,EAAA,UAAU;AAAA,sBAAA;mCACtD,MAAmB;AAAA,0BAAhB2D,EAAAC,EAAAgD,GAAA,MAAQ,KAAK,IAAG,UAAM5G,EAAA,UAAU,GAAA,CAAA;AAAA,wBAAA;;;;;;;gBAKzCuB,EAEeL,EAAAmG,CAAA,GAAA;AAAA,kBAFA,OAAOnG,EAAAV,CAAA,EAAC,SAAA;AAAA,kBAAc,UAAUR,EAAA;AAAA,gBAAA;6BAC7C,MAAmC;AAAA,oBAAnCuB,EAAmCL,EAAAiC,CAAA,GAAA;AAAA,kCAAhBjC,EAAAyE,CAAA;AAAA,2EAAAA,EAAE,QAAAvE,IAAA;AAAA,sBAAE,WAAA;AAAA,oBAAA;;;;gBAGzBG,EAOeL,EAAAmG,CAAA,GAAA;AAAA,kBAPA,OAAOnG,EAAAV,CAAA,EAAC,kBAAA;AAAA,kBAAuB,UAAUR,EAAA;AAAA,gBAAA;6BACtD,MAAyD;AAAA,oBAAzDuB,EAAyDL,EAAAoG,EAAA,GAAA;AAAA,kCAAnCpG,EAAA0E,CAAA;AAAA,2EAAAA,EAAW,QAAAxE,IAAA;AAAA,sBAAE,WAAA;AAAA,sBAAW,MAAM;AAAA,oBAAA;oBACpD4B,EAIM,OAJNiB,IAIM;AAAA,sBAHJ1C,EAEUL,EAAAuC,CAAA,GAAA;AAAA,wBAFD,MAAK;AAAA,wBAAM,MAAMuD,GAAKJ,SAAQ,aAAa5G,EAAA,gBAAgB;AAAA,sBAAA;mCAClE,MAAyB;AAAA,0BAAtB2D,EAAAC,EAAAgD,GAAA,MAAQ,WAAW,IAAG,UAAM5G,EAAA,gBAAgB,GAAA,CAAA;AAAA,wBAAA;;;;;;;gBAKrDuB,EAEeL,EAAAmG,CAAA,GAAA;AAAA,kBAFA,OAAOnG,EAAAV,CAAA,EAAC,eAAA;AAAA,kBAAoB,UAAUR,EAAA;AAAA,gBAAA;6BACnD,MAAyC;AAAA,oBAAzCuB,EAAyCL,EAAAiC,CAAA,GAAA;AAAA,kCAAtBjC,EAAA2E,CAAA;AAAA,2EAAAA,EAAQ,QAAAzE,IAAA;AAAA,sBAAE,WAAA;AAAA,oBAAA;;;;gBAGpB0F,GAAA,SAAXvD,EAAA,GAAAH,EASM,OATNmE,IASM;AAAA,kBARJhG,EAAiEL,EAAAuC,CAAA,GAAA;AAAA,oBAAxD,MAAK;AAAA,oBAAK,MAAK;AAAA,kBAAA;+BAAQ,MAAuB;AAAA,0BAApBvC,EAAAV,CAAA,EAAC,cAAA,CAAA,GAAA,CAAA;AAAA,oBAAA;;;kBACpCwC,EAMM,OANNwE,IAMM;AAAA,oBALOX,EAAA,MAAQ,WAAnBtD,EAAA,GAAAH,EAEM,OAFNqE,IAEM7D,EADDiD,EAAA,MAAQ,OAAO,GAAA,CAAA;oBAEpB7D,EAA4D,OAA5D0E,IAA4D9D,EAAtBiD,EAAA,MAAQ,KAAK,GAAA,CAAA;AAAA,oBACnD7D,EAAiE,OAAjE2E,IAAiE/D,EAA5BiD,EAAA,MAAQ,WAAW,GAAA,CAAA;AAAA,kBAAA;;;;;;UAMhEtF,EAoBSL,EAAAkG,EAAA,GAAA;AAAA,YApBA,OAAOlG,EAAAV,CAAA,EAAC,oBAAA;AAAA,YAAwB,OAAM;AAAA,UAAA;uBAC7C,MAkBM;AAAA,cAlBNwC,EAkBM,OAlBN4E,IAkBM;AAAA,gBAfI5H,EAAA,cAFRuD,EAAA,GAAAtC,EAQE4G,GAPK7H,EAAA,UAAU,GAAA;AAAA;8BAENiG,EAAA;AAAA,gEAAAA,EAAK,QAAA7E;AAAA,kBACb,OAAOF,EAAAV,CAAA,EAAC,eAAA;AAAA,kBACT,QAAO;AAAA,kBACP,QAAO;AAAA,kBACN,UAAUR,EAAA;AAAA,gBAAA;gBAGbuB,EAEeL,EAAAmG,CAAA,GAAA;AAAA,kBAFA,OAAOnG,EAAAV,CAAA,EAAC,eAAA;AAAA,kBAAoB,MAAMU,EAAAV,CAAA,EAAC,cAAA;AAAA,kBAAmB,UAAUR,EAAA;AAAA,gBAAA;6BAC7E,MAAwC;AAAA,oBAAxCuB,EAAwCL,EAAAiC,CAAA,GAAA;AAAA,kCAArBjC,EAAA4E,CAAA;AAAA,2EAAAA,EAAO,QAAA1E,IAAA;AAAA,sBAAE,WAAA;AAAA,oBAAA;;;;gBAG9BG,EAEeL,EAAAmG,CAAA,GAAA;AAAA,kBAFA,OAAOnG,EAAAV,CAAA,EAAC,qBAAA;AAAA,kBAA0B,UAAUR,EAAA;AAAA,gBAAA;6BACzD,MAA2D;AAAA,oBAA3DuB,EAA2DL,EAAAoG,EAAA,GAAA;AAAA,kCAArCpG,EAAA6E,CAAA;AAAA,2EAAAA,EAAa,QAAA3E,IAAA;AAAA,sBAAE,WAAA;AAAA,sBAAW,MAAM;AAAA,oBAAA;;;;;;;;UAK5DG,EA8BSL,EAAAkG,EAAA,GAAA;AAAA,YA9BA,OAAOlG,EAAAV,CAAA,EAAC,uBAAA;AAAA,YAA2B,OAAM;AAAA,UAAA;uBAChD,MA4BM;AAAA,cA5BNwC,EA4BM,OA5BN8E,IA4BM;AAAA,gBA3BJvG,EAMeL,EAAAmG,CAAA,GAAA;AAAA,kBALZ,OAAOnG,EAAAV,CAAA,EAAC,gBAAA;AAAA,kBACR,MAAMU,EAAAV,CAAA,EAAC,qBAAA;AAAA,kBACP,UAAUR,EAAA;AAAA,gBAAA;6BAEX,MAAuE;AAAA,oBAAvEuB,EAAuEL,EAAAiC,CAAA,GAAA;AAAA,kCAApD6C,EAAA;AAAA,oEAAAA,EAAS,QAAA5E;AAAA,sBAAE,aAAY;AAAA,oBAAA;;;;gBAG5CG,EAEeL,EAAAmG,CAAA,GAAA;AAAA,kBAFA,OAAOnG,EAAAV,CAAA,EAAC,aAAA;AAAA,kBAAkB,MAAMuG,GAAA;AAAA,kBAAW,UAAU/G,EAAA;AAAA,gBAAA;6BAClE,MAA8D;AAAA,oBAA9DuB,EAA8DL,EAAA6G,EAAA,GAAA;AAAA,kCAAlC3B,EAAA;AAAA,oEAAAA,EAAK,QAAAhF;AAAA,sBAAG,SAASkF,EAAA;AAAA,oBAAA;;;;gBAIvCtG,EAAA,uBADRiB,EAceC,EAAAmG,CAAA,GAAA;AAAA;kBAZZ,OAAOnG,EAAAV,CAAA,EAAC,cAAA;AAAA,kBACR,MAAMU,EAAAV,CAAA,EAAC,mBAAA;AAAA,kBACP,OAAOgF,EAAA,QAAetE,EAAAV,CAAA,4BAA4B;AAAA,kBAClD,UAAUR,EAAA;AAAA,gBAAA;6BAEX,MAME;AAAA,oBANFuB,EAMEL,EAAA8G,EAAA,GAAA;AAAA,kCALSzB,EAAA;AAAA,oEAAAA,EAAM,QAAAnF;AAAA,sBACf,UAAS;AAAA,sBACT,MAAA;AAAA,sBACA,cAAW;AAAA,sBACX,cAAW;AAAA,oBAAA;;;;;;;;;;;;;;;;;;;AC9PzB,UAAMrB,IAAQC,GAER,EAAE,SAAAiI,GAAS,SAAAC,GAAS,MAAA7F,EAAA,IAASC,GAAA,GAE7BnC,IAAUL,EAAA,GACVyC,IAAMd,EAAatB,CAAO;AAChC,IAAAP,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAE3B0H,IAAUC,GAAA,GAEVC,IAAS7F,EAAI,EAAK,GAClB8F,IAAS9F,EAA8B,EAAE,GACzC+F,IAAW/F,EAAqB,IAAI,GAEpCgG,IAAOhG,EAAI;AAAA,MACf,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,WAAW;AAAA,IAAA,CACZ,GAEKiG,IAAU9H,EAAS,MAAMZ,EAAM,aAAa,IAAI,GAEhD2I,IAAc/H,EAAS,MAAM;AAAA,MACjC,EAAE,OAAO,SAAS,OAAOH,EAAE,YAAY,EAAA;AAAA,MACvC,EAAE,OAAO,QAAQ,OAAOA,EAAE,WAAW,EAAA;AAAA,MACrC,EAAE,OAAO,SAAS,OAAOA,EAAE,YAAY,EAAA;AAAA,IAAE,CAC1C,GAIKmI,IAAgB;AAAA,MACpB,EAAE,OAAO,KAAK,OAAO,MAAA;AAAA,MACrB,EAAE,OAAO,KAAK,OAAO,MAAA;AAAA,IAAM;AAG7B,IAAAC;AAAA,MACE,MAAM7I,EAAM;AAAA,MACZ,CAAC8I,MAAa;AACZ,QAAAL,EAAK,QAAQ;AAAA,UACX,YAAYK,GAAU,cAAc;AAAA,UACpC,SAASA,GAAU,WAAW;AAAA,UAC9B,QAAQA,GAAU,UAAU;AAAA,UAC5B,QAAQA,GAAU,UAAU;AAAA,UAC5B,WAAWA,GAAU,aAAa;AAAA,QAAA,GAEpCP,EAAO,QAAQ,CAAA,GACfC,EAAS,QAAQ,MACZO,EAAA;AAAA,MACP;AAAA,MACA,EAAE,WAAW,GAAA;AAAA,IAAK;AAepB,mBAAeA,IAA0B;AACvC,YAAMC,IAAUP,EAAK,MAAM,QAAQ,KAAA;AAEnC,UAAIA,EAAK,MAAM,eAAe,WAAWO,MAAY,IAAI;AACvD,QAAAR,EAAS,QAAQ;AAEjB;AAAA,MACF;AAEA,UAAI;AACF,QAAAA,EAAS,SAAS,MAAMhG,EAAI,KAAKwG,CAAO,GAAG;AAAA,MAC7C,QAAQ;AAEN,QAAAR,EAAS,QAAQ;AAAA,MACnB;AAAA,IACF;AAEA,aAASS,EAAQvD,GAAmC;AAClD,aAAO6C,EAAO,MAAM7C,CAAK,IAAI,CAAC;AAAA,IAChC;AAEA,mBAAewD,IAAsB;AACnC,MAAAZ,EAAO,QAAQ,IACfC,EAAO,QAAQ,CAAA;AAEf,UAAI;AACF,cAAMY,IAAQnJ,EAAM,WAChB,MAAMwC,EAAI,eAAexC,EAAM,SAAS,IAAIyI,EAAK,KAAK,IACtD,MAAMjG,EAAI,eAAeiG,EAAK,KAAK;AAEvC,QAAAW,EAAM,QAAQ3I,EAAE,YAAY,CAAC,GAC7ByH,EAAQiB,CAAK;AAAA,MACf,SAASE,GAAO;AACd,cAAMvH,IAAQuH,EAA6E;AAE3F,QAAIvH,GAAM,UACRyG,EAAO,QAAQzG,EAAK,QACpBsH,EAAM,OAAO3I,EAAE,aAAa,CAAC,KAE7B2I,EAAM,OAAOhB,EAAQiB,GAAO5I,EAAE,aAAa,CAAC,CAAC;AAAA,MAEjD,UAAA;AACE,QAAA6H,EAAO,QAAQ;AAAA,MACjB;AAAA,IACF;2BAIEpH,EAsDYC,EAAA6B,EAAA,GAAA;AAAA,MAtDO,MAAM7B,EAAAmB,CAAA;AAAA,qDAAAA,EAAI,QAAAjB,IAAA;AAAA,MAAG,OAAOF,EAAAV,CAAA,EAAC,eAAA;AAAA,MAAoB,OAAO;AAAA,IAAA;MA8CtD,UACT,MAKW;AAAA,QALXe,EAKWL,EAAAgC,CAAA,GAAA,EALD,MAAK,QAAI;AAAA,qBACjB,MAAkF;AAAA,YAAlF3B,EAAkFL,EAAAM,CAAA,GAAA;AAAA,cAAvE,SAAQ;AAAA,cAAW,gCAAON,EAAAgH,CAAA,EAAA;AAAA,YAAO;yBAAI,MAAsB;AAAA,oBAAnBhH,EAAAV,CAAA,EAAC,aAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;YACpDe,EAEYL,EAAAM,CAAA,GAAA;AAAA,cAFD,MAAK;AAAA,cAAW,SAAS6G,EAAA;AAAA,cAAS,SAAOY;AAAA,YAAA;yBAClD,MAAuD;AAAA,oBAApDR,EAAA,QAAUvH,EAAAV,CAAA,EAAC,WAAA,IAAgBU,EAAAV,CAAA,EAAC,mBAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;;;;iBAjDrC,MA2CM;AAAA,QA3CNwC,EA2CM,OA3CNC,IA2CM;AAAA,UA1CJ1B,EAEeL,EAAAmG,CAAA,GAAA;AAAA,YAFA,OAAOnG,EAAAV,CAAA,EAAC,WAAA;AAAA,UAAA;uBACrB,MAAiF;AAAA,cAAjFe,EAAiFL,EAAAmI,EAAA,GAAA;AAAA,gBAA7D,YAAAb,EAAA,MAAK;AAAA,gBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,aAAUpH;AAAA,gBAAG,SAASsH,EAAA;AAAA,gBAAc,UAAQI;AAAA,cAAA;;;;UAGvEvH,EAMeL,EAAAmG,CAAA,GAAA;AAAA,YALZ,OAAOnG,EAAAV,CAAA,EAAC,cAAA;AAAA,YACR,MAAMU,EAAAV,CAAA,EAAC,mBAAA;AAAA,YACP,OAAOwI,EAAO,SAAA;AAAA,UAAA;uBAEf,MAA+E;AAAA,cAA/EzH,EAA+EL,EAAAiC,CAAA,GAAA;AAAA,gBAA5D,YAAAqF,EAAA,MAAK;AAAA,gBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,UAAOpH;AAAA,gBAAE,aAAY;AAAA,gBAAgB,QAAM0H;AAAA,cAAA;;;;UAM7DP,EAAA,cADRtH,EASEC,EAAAoC,CAAA,GAAA;AAAA;YAPA,MAAK;AAAA,YACL,SAAQ;AAAA,YACP,aAAwBiF,EAAA,MAAS,SAAI,UAA2BrH,EAAAV,CAAA,mCAAmC+H,EAAA,MAAS,UAAUA,EAAA,MAAS,KAAA,CAAI,IAAkBrH,EAAAV,CAAA,EAAC,iBAAA,EAAA,MAA0B+H,EAAA,MAAS,KAAA,CAAI;AAAA,UAAA;UAOhMhH,EAMeL,EAAAmG,CAAA,GAAA;AAAA,YALZ,OAAOnG,EAAAV,CAAA,EAAC,aAAA;AAAA,YACR,MAAMU,EAAAV,CAAA,EAAC,kBAAA;AAAA,YACP,OAAOwI,EAAO,QAAA;AAAA,UAAA;uBAEf,MAA6D;AAAA,cAA7DzH,EAA6DL,EAAAiC,CAAA,GAAA;AAAA,gBAA1C,YAAAqF,EAAA,MAAK;AAAA,gBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,SAAMpH;AAAA,gBAAE,aAAY;AAAA,cAAA;;;;UAG9C4B,EAQM,OARNQ,IAQM;AAAA,YAPJjC,EAEeL,EAAAmG,CAAA,GAAA;AAAA,cAFA,OAAOnG,EAAAV,CAAA,EAAC,aAAA;AAAA,YAAA;yBACrB,MAA4D;AAAA,gBAA5De,EAA4DL,EAAAmI,EAAA,GAAA;AAAA,kBAAxC,YAAAb,EAAA,MAAK;AAAA,kBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,SAAMpH;AAAA,kBAAG,SAASuH;AAAA,gBAAA;;;;YAG7CpH,EAEeL,EAAAmG,CAAA,GAAA;AAAA,cAFA,OAAOnG,EAAAV,CAAA,EAAC,YAAA;AAAA,YAAA;yBACrB,MAAsC;AAAA,gBAAtCe,EAAsCL,EAAAqI,EAAA,GAAA;AAAA,kBAAlB,YAAAf,EAAA,MAAK;AAAA,kBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,YAASpH;AAAA,gBAAA;;;;;;;;;;;;;;;;AC/I5C,UAAMrB,IAAQC,GAERG,IAAUL,EAAA,GACVyC,IAAMd,EAAatB,CAAO;AAChC,IAAAP,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAE3B0H,IAAUC,GAAA,GAEVtG,IAAOU,EAAiC,IAAI,GAC5C4B,IAAU5B,EAAI,EAAK;AAEzB,QAAIgH,IAAmB,EAAE,MAAM,GAAG,SAAS,IAAI,MAAM,MAAM,QAAQ,GAAA;AAEnE,UAAMC,IAAYtJ,EAAQ,IAAI,YAAY,GAEpCuJ,IAAOpF,EAA2DqF,EAAiB,GACnFtF,IAAOC,EAAyCC,EAAa,GAE7DE,IAAU9D,EAAqC,MAAM;AAAA,MACzD,EAAE,KAAK,WAAW,OAAOH,EAAE,cAAc,GAAG,UAAU,GAAA;AAAA,MACtD,EAAE,KAAK,UAAU,OAAOA,EAAE,aAAa,EAAA;AAAA,MACvC,EAAE,KAAK,UAAU,OAAOA,EAAE,aAAa,GAAG,OAAO,UAAU,WAAW,IAAA;AAAA,MACtE,EAAE,KAAK,QAAQ,OAAOA,EAAE,WAAW,GAAG,OAAO,UAAU,UAAU,IAAM,WAAW,IAAA;AAAA,MAClF;AAAA,QACE,KAAK;AAAA,QACL,OAAOA,EAAE,eAAe;AAAA,QACxB,UAAU;AAAA,QACV,WAAW;AAAA,QACX,aAAa;AAAA,MAAA;AAAA,MAEf,EAAE,KAAK,aAAa,OAAOA,EAAE,YAAY,GAAG,OAAO,UAAU,WAAW,IAAA;AAAA,MACxE,EAAE,KAAK,WAAW,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,QAAQ,CAACiJ,GAAW,aAAa,GAAA;AAAA,IAAK,CAC/F;AAGD,aAASG,EAAWf,GAAoC;AACtD,aAAO;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAOrI,EAAE,aAAa;AAAA,UACtB,QAAQ;AAAA,UACR,KAAK,MAAMqJ,EAAOhB,CAAQ;AAAA,QAAA;AAAA,MAC5B;AAAA,IAEJ;AAEA,mBAAenE,EAAKC,GAAkC;AACpD,MAAA6E,IAAO7E,GACPP,EAAQ,QAAQ;AAEhB,UAAI;AACF,QAAAtC,EAAK,QAAQ,MAAMS,EAAI,UAAU;AAAA,UAC/B,GAAGoC,EAAM;AAAA,UACT,MAAMA,EAAM,OAAO,GAAGA,EAAM,KAAK,UAAU,SAAS,MAAM,EAAE,GAAGA,EAAM,KAAK,GAAG,KAAK;AAAA,UAClF,MAAMA,EAAM;AAAA,UACZ,UAAUA,EAAM;AAAA,QAAA,CACjB;AAAA,MACH,UAAA;AACE,QAAAP,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAOA,mBAAe/B,EAAKwG,GAA6C;AAG/D,MAFc,MAAMa,EAAK,EAAE,UAAAb,GAAU,KAErBnE,EAAK8E,CAAI;AAAA,IAC3B;AAEA,mBAAeK,EAAOhB,GAAsC;AAS1D,UARe,MAAMiB,GAAQ;AAAA,QAC3B,OAAOtJ,EAAE,qBAAqB,EAAE,SAASqI,EAAS,SAAS;AAAA,QAC3D,SAASrI,EAAE,kBAAkB;AAAA,QAC7B,aAAaA,EAAE,aAAa;AAAA,QAC5B,YAAYA,EAAE,aAAa;AAAA,QAC3B,MAAM;AAAA,MAAA,CACP;AAID,YAAI;AACF,gBAAM+B,EAAI,eAAesG,EAAS,EAAE,GACpCM,EAAM,QAAQ3I,EAAE,cAAc,CAAC,GAC1BkE,EAAK8E,CAAI;AAAA,QAChB,SAASJ,GAAO;AACd,UAAAD,EAAM,OAAOhB,EAAQiB,CAAK,CAAC;AAAA,QAC7B;AAAA,IACF;2BAIEnI,EAkDa2D,IAAA;AAAA,MAlDA,MAAM7E,EAAM;AAAA,MAAM,SAAQ;AAAA,MAAa,+BAAMmB,EAAAmD,CAAA,EAAI,CAAA,CAAA;AAAA,IAAA;iBAO5D,MA0CW;AAAA,QA1CX9C,EA0CWL,EAAA2D,EAAA,GAAA;AAAA,UAzCR,MAAM/C,EAAA;AAAA,UACN,SAAS2C,EAAA;AAAA,UACV,WAAQ;AAAA,UACR,YAAA;AAAA,UACC,WAAWvD,EAAAuI,CAAA;AAAA,UACX,OAAOvI,EAAAuI,CAAA;AAAA,UACR,OAAA;AAAA,UACC,SAASrF,EAAA;AAAA,UACT,sBAAoBlD,EAAAV,CAAA,EAAC,uBAAA;AAAA,UACrB,cAAYU,EAAAV,CAAA,EAAC,YAAA;AAAA,UACb,YAAW6B;AAAA,UACX,eAAcqC;AAAA,QAAA;UAEJ,gBAAYI,EACrB,CAAmD,EAD1B,KAAAC,QAAG;AAAA,YAC5BxD,EAAmDL,EAAAuC,CAAA,GAAA;AAAA,cAA1C,MAAA;AAAA,cAAK,MAAK;AAAA,YAAA;yBAAK,MAAiB;AAAA,gBAAdE,EAAAC,EAAAmB,EAAI,OAAO,GAAA,CAAA;AAAA,cAAA;;;YAGtBA,EAAI,gBAApB9D,EAA2EC,EAAAwC,CAAA,GAAA;AAAA;cAA9C,MAAK;AAAA,YAAA;yBAAU,MAAoB;AAAA,oBAAjBxC,EAAAV,CAAA,EAAC,WAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;UAGvC,eAAWsE,EACpB,CAAkD,EAD1B,KAAAC,QAAG;AAAA,YAC3BxD,EAAkDL,EAAAuC,CAAA,GAAA;AAAA,cAAzC,MAAA;AAAA,cAAK,MAAK;AAAA,YAAA;yBAAK,MAAgB;AAAA,gBAAbE,EAAAC,EAAAmB,EAAI,MAAM,GAAA,CAAA;AAAA,cAAA;;;;UAG5B,oBAAgBD,EACzB,CAAkF,EADrD,KAAAC,QAAG;AAAA,YAChCxD,EAAkFL,EAAA+D,EAAA,GAAA;AAAA,cAAxE,OAAOF,EAAI;AAAA,cAAc,MAAMA,EAAI,cAAW,YAAA;AAAA,YAAA;;UAG/C,kBAAcD,EACvB,CAEW,EAHgB,KAAAC,QAAG;AAAA,YAC9BxD,EAEWL,EAAAwC,CAAA,GAAA;AAAA,cAFA,MAAMqB,EAAI,YAAS,YAAA;AAAA,cAA0B,KAAA;AAAA,YAAA;yBACtD,MAA2D;AAAA,gBAAxDpB,EAAAC,EAAAmB,EAAI,YAAY7D,EAAAV,CAAA,mBAAmBU,EAAAV,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;UAIhC,gBAAYsE,EACrB,CAAgF,EADvD,KAAAC,QAAG;AAAA,YACT7D,EAAAuI,CAAA,UAAnBxI,EAAgFC,EAAA6I,EAAA,GAAA;AAAA;cAAjD,SAASH,EAAW7E,CAAG;AAAA,cAAI,OAAOA,EAAI;AAAA,YAAA;;UAG5D,gBAAYD,EACrB,CAA+D,EADtC,KAAAC,QAAG;AAAA,YAC5BxD,EAA+DL,EAAA6I,EAAA,GAAA;AAAA,cAAjD,SAASH,EAAW7E,CAAG;AAAA,cAAI,OAAOA,EAAI;AAAA,YAAA;;;;;;;MA9CxC7D,EAAAuI,CAAA;cAAY;AAAA,cAC1B,MAEY;AAAA,UAFZlI,EAEYL,EAAAM,CAAA,GAAA;AAAA,YAFD,MAAK;AAAA,YAAU,MAAK;AAAA,YAAO,gCAAOa,EAAI,IAAA;AAAA,UAAA;uBAC/C,MAA4B;AAAA,kBAAzBnB,EAAAV,CAAA,EAAC,mBAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;;;;;;ICnINwJ,KAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQO,SAASC,GAAUC,GAAkB9H,GAAyB;AACnE,SAAOuE,GAAeuD,EAAK,OAAqC9H,CAAM;AACxE;AAGO,SAAS+H,GAAMD,GAAmC;AACvD,MAAI,CAACA,EAAM,QAAO,CAAA;AAElB,QAAM/E,IAAkB,CAAA;AAExB,aAAWO,KAAOsE,IAAY;AAC5B,UAAMnH,IAAMqH,EAAKxE,CAAG;AAEpB,IAAI7C,KAAQ,QACV,OAAO,OAAOsC,GAAO,EAAE,CAACO,CAAG,GAAG7C,GAAK;AAAA,EAEvC;AAEA,SAAOsC;AACT;AAQO,SAASiF,GACdC,GACAzH,GACa;AACb,QAAMV,IAAqB,EAAE,GAAGmI,EAAA;AAEhC,aAAW3E,KAAOsE;AAChB,WAAO,OAAO9H,GAAO,EAAE,CAACwD,CAAG,GAAG9C,EAAI8C,CAAG,KAAK,MAAM;AAGlD,SAAOxD;AACT;;;;;;;;AChCA,UAAMnC,IAAQC,GAKR,EAAE,SAAAiI,GAAS,SAAAC,GAAS,MAAA7F,EAAA,IAASC,GAAA,GAE7BnC,IAAUL,EAAA,GACVyC,IAAMd,EAAatB,CAAO;AAChC,IAAAP,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAE3B0H,IAAUC,GAAA,GAEVC,IAAS7F,EAAI,EAAK,GAClB8F,IAAS9F,EAA8B,EAAE,GAEzCgG,IAAOhG,EAAI;AAAA,MACf,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,IAAA,CACZ,GAEKI,IAAMJ,EAAc,EAAE,GAEtBiG,IAAU9H,EAAS,MAAMZ,EAAM,SAAS,IAAI,GAE5C2I,IAAc/H,EAAS,MAAM;AAAA,MACjC,EAAE,OAAO,SAAS,OAAOH,EAAE,YAAY,EAAA;AAAA,MACvC,EAAE,OAAO,QAAQ,OAAOA,EAAE,WAAW,EAAA;AAAA,MACrC,EAAE,OAAO,SAAS,OAAOA,EAAE,YAAY,EAAA;AAAA,IAAE,CAC1C;AAED,IAAAoI;AAAA,MACE,MAAM7I,EAAM;AAAA,MACZ,CAACmK,MAAS;AACR,QAAA1B,EAAK,QAAQ;AAAA,UACX,YAAY0B,GAAM,cAAc;AAAA,UAChC,SAASA,GAAM,WAAW;AAAA,UAC1B,UAAUA,GAAM,YAAY;AAAA,UAC5B,WAAWA,GAAM,aAAa;AAAA,QAAA,GAEhCtH,EAAI,QAAQuH,GAAMD,CAAI,GACtB5B,EAAO,QAAQ,CAAA;AAAA,MACjB;AAAA,MACA,EAAE,WAAW,GAAA;AAAA,IAAK;AAGpB,aAASU,EAAQvD,GAAmC;AAClD,aAAO6C,EAAO,MAAM7C,CAAK,IAAI,CAAC;AAAA,IAChC;AAEA,mBAAewD,IAAsB;AACnC,MAAAZ,EAAO,QAAQ,IACfC,EAAO,QAAQ,CAAA;AAEf,YAAMpG,IAAQkI,GAAU5B,EAAK,OAAO5F,EAAI,KAAK;AAE7C,UAAI;AACF,cAAMsG,IAAQnJ,EAAM,OAChB,MAAMwC,EAAI,UAAUxC,EAAM,KAAK,IAAImC,CAAK,IACxC,MAAMK,EAAI,UAAUL,CAAK;AAE7B,QAAAiH,EAAM,QAAQ3I,EAAE,YAAY,CAAC,GAC7ByH,EAAQiB,CAAK;AAAA,MACf,SAASE,GAAO;AACd,cAAMvH,IAAQuH,EAA6E;AAE3F,QAAIvH,GAAM,UACRyG,EAAO,QAAQzG,EAAK,QACpBsH,EAAM,OAAO3I,EAAE,aAAa,CAAC,KAE7B2I,EAAM,OAAOhB,EAAQiB,GAAO5I,EAAE,aAAa,CAAC,CAAC;AAAA,MAEjD,UAAA;AACE,QAAA6H,EAAO,QAAQ;AAAA,MACjB;AAAA,IACF;2BAIEpH,EAoCYC,EAAA6B,EAAA,GAAA;AAAA,MApCO,MAAM7B,EAAAmB,CAAA;AAAA,qDAAAA,EAAI,QAAAjB,IAAA;AAAA,MAAG,OAAOF,EAAAV,CAAA,EAAC,WAAA;AAAA,MAAgB,OAAO;AAAA,IAAA;MA4BlD,UACT,MAKW;AAAA,QALXe,EAKWL,EAAAgC,CAAA,GAAA,EALD,MAAK,QAAI;AAAA,qBACjB,MAAkF;AAAA,YAAlF3B,EAAkFL,EAAAM,CAAA,GAAA;AAAA,cAAvE,SAAQ;AAAA,cAAW,gCAAON,EAAAgH,CAAA,EAAA;AAAA,YAAO;yBAAI,MAAsB;AAAA,oBAAnBhH,EAAAV,CAAA,EAAC,aAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;YACpDe,EAEYL,EAAAM,CAAA,GAAA;AAAA,cAFD,MAAK;AAAA,cAAW,SAAS6G,EAAA;AAAA,cAAS,SAAOY;AAAA,YAAA;yBAClD,MAAmD;AAAA,oBAAhDR,EAAA,QAAUvH,EAAAV,CAAA,EAAC,WAAA,IAAgBU,EAAAV,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;;;;iBA/BrC,MAyBM;AAAA,QAzBNwC,EAyBM,OAzBNC,IAyBM;AAAA,UAxBJD,EAqBM,OArBNQ,IAqBM;AAAA,YApBJjC,EAEeL,EAAAmG,CAAA,GAAA;AAAA,cAFA,OAAOnG,EAAAV,CAAA,EAAC,WAAA;AAAA,YAAA;yBACrB,MAA8D;AAAA,gBAA9De,EAA8DL,EAAAmI,EAAA,GAAA;AAAA,kBAA1C,YAAAb,EAAA,MAAK;AAAA,kBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,aAAUpH;AAAA,kBAAG,SAASsH,EAAA;AAAA,gBAAA;;;;YAGjDnH,EAOeL,EAAAmG,CAAA,GAAA;AAAA,cANZ,OAAOnG,EAAAV,CAAA,EAAC,cAAA;AAAA,cACR,MAAMU,EAAAV,CAAA,EAAC,mBAAA;AAAA,cACP,OAAOwI,EAAO,SAAA;AAAA,cACf,OAAM;AAAA,YAAA;yBAEN,MAAgE;AAAA,gBAAhEzH,EAAgEL,EAAAiC,CAAA,GAAA;AAAA,kBAA7C,YAAAqF,EAAA,MAAK;AAAA,kBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,UAAOpH;AAAA,kBAAE,aAAY;AAAA,gBAAA;;;;YAG/CG,EAEeL,EAAAmG,CAAA,GAAA;AAAA,cAFA,OAAOnG,EAAAV,CAAA,EAAC,eAAA;AAAA,cAAoB,MAAMU,EAAAV,CAAA,EAAC,oBAAA;AAAA,YAAA;yBAChD,MAAsD;AAAA,gBAAtDe,EAAsDL,EAAAoJ,EAAA,GAAA;AAAA,kBAA5B,YAAA9B,EAAA,MAAK;AAAA,kBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,WAAQpH;AAAA,kBAAG,MAAM;AAAA,gBAAA;;;;YAGlDG,EAEeL,EAAAmG,CAAA,GAAA;AAAA,cAFA,OAAOnG,EAAAV,CAAA,EAAC,YAAA;AAAA,YAAA;yBACrB,MAAsC;AAAA,gBAAtCe,EAAsCL,EAAAqI,EAAA,GAAA;AAAA,kBAAlB,YAAAf,EAAA,MAAK;AAAA,kBAAL,uBAAAc,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAlI,MAAAoH,EAAA,MAAK,YAASpH;AAAA,gBAAA;;;;;UAItCG,EAA0DgJ,IAAA;AAAA,wBAAvC3H,EAAA;AAAA,0DAAAA,EAAG,QAAAxB;AAAA,YAAG,eAAarB,EAAM;AAAA,UAAA;;;;;;;;;;;;;ACrGlD,UAAMA,IAAQC,GAKRG,IAAUL,EAAA,GACVyC,IAAMd,EAAatB,CAAO;AAChC,IAAAP,EAAA;AAEA,UAAMY,IAAIC,EAAa,UAAU,GAE3B0H,IAAUC,GAAA,GAEVtG,IAAOU,EAAgC,IAAI,GAC3C4B,IAAU5B,EAAI,EAAK,GAGnBgI,IAAOhI,EAAI,KAAK;AAEtB,QAAIgH,IAAmB,EAAE,MAAM,GAAG,SAAS,IAAI,MAAM,MAAM,QAAQ,GAAA;AAEnE,UAAMC,IAAYtJ,EAAQ,IAAI,YAAY,GAEpCuJ,IAAOpF;AAAA,MACXmG;AAAA,IAAA,GAEIpG,IAAOC,EAAyCC,EAAa,GAE7DmE,IAAc/H,EAAS,MAAM;AAAA,MACjC,EAAE,OAAO,OAAO,OAAOH,EAAE,eAAe,EAAA;AAAA,MACxC,EAAE,OAAO,SAAS,OAAOA,EAAE,YAAY,EAAA;AAAA,MACvC,EAAE,OAAO,QAAQ,OAAOA,EAAE,WAAW,EAAA;AAAA,MACrC,EAAE,OAAO,SAAS,OAAOA,EAAE,YAAY,EAAA;AAAA,IAAE,CAC1C,GAEKiE,IAAU9D,EAAoC,MAAM;AAAA,MACxD,EAAE,KAAK,WAAW,OAAOH,EAAE,cAAc,EAAA;AAAA,MACzC,EAAE,KAAK,cAAc,OAAOA,EAAE,WAAW,GAAG,WAAW,IAAA;AAAA,MACvD,EAAE,KAAK,SAAS,OAAOA,EAAE,YAAY,GAAG,WAAW,IAAA;AAAA,MACnD,EAAE,KAAK,YAAY,OAAOA,EAAE,eAAe,GAAG,OAAO,UAAU,WAAW,IAAA;AAAA,MAC1E,EAAE,KAAK,aAAa,OAAOA,EAAE,YAAY,GAAG,OAAO,UAAU,WAAW,IAAA;AAAA,MACxE,EAAE,KAAK,WAAW,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,QAAQ,CAACiJ,GAAW,aAAa,GAAA;AAAA,IAAK,CAC/F;AAQD,aAASG,EAAWM,GAA+B;AACjD,aAAO;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO1J,EAAE,aAAa;AAAA,UACtB,QAAQ;AAAA,UACR,KAAK,MAAMqJ,EAAOK,CAAI;AAAA,QAAA;AAAA,MACxB;AAAA,IAEJ;AAEA,mBAAexF,EAAKC,GAAkC;AACpD,MAAA6E,IAAO7E,GACPP,EAAQ,QAAQ;AAEhB,UAAI;AACF,QAAAtC,EAAK,QAAQ,MAAMS,EAAI,KAAK;AAAA,UAC1B,GAAGoC,EAAM;AAAA,UACT,YAAY6F,EAAK,UAAU,QAAQ,OAAQA,EAAK;AAAA,UAChD,MAAM7F,EAAM;AAAA,UACZ,UAAUA,EAAM;AAAA,QAAA,CACjB;AAAA,MACH,UAAA;AACE,QAAAP,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAOA,mBAAe/B,EAAK6H,GAAwC;AAG1D,MAFc,MAAMR,EAAK,EAAE,MAAAQ,GAAM,YAAYnK,EAAM,YAAY,KAE/C2E,EAAK8E,CAAI;AAAA,IAC3B;AAEA,mBAAeK,EAAOK,GAAiC;AASrD,UARe,MAAMJ,GAAQ;AAAA,QAC3B,OAAOtJ,EAAE,qBAAqB,EAAE,SAAS0J,EAAK,SAAS;AAAA,QACvD,SAAS1J,EAAE,kBAAkB;AAAA,QAC7B,aAAaA,EAAE,aAAa;AAAA,QAC5B,YAAYA,EAAE,aAAa;AAAA,QAC3B,MAAM;AAAA,MAAA,CACP;AAID,YAAI;AACF,gBAAM+B,EAAI,UAAU2H,EAAK,EAAE,GAC3Bf,EAAM,QAAQ3I,EAAE,cAAc,CAAC,GAC1BkE,EAAK8E,CAAI;AAAA,QAChB,SAASJ,GAAO;AACd,UAAAD,EAAM,OAAOhB,EAAQiB,CAAK,CAAC;AAAA,QAC7B;AAAA,IACF;2BAIEnI,EAoDa2D,IAAA;AAAA,MApDA,MAAM7E,EAAM;AAAA,MAAM,SAAQ;AAAA,MAAS,+BAAMmB,EAAAmD,CAAA,EAAI,CAAA,CAAA;AAAA,IAAA;iBAOxD,MA4CW;AAAA,QA5CX9C,EA4CWL,EAAA2D,EAAA,GAAA;AAAA,UA3CR,MAAM/C,EAAA;AAAA,UACN,SAAS2C,EAAA;AAAA,UACV,WAAQ;AAAA,UACR,YAAA;AAAA,UACC,WAAWvD,EAAAuI,CAAA;AAAA,UACX,OAAOvI,EAAAuI,CAAA;AAAA,UACR,OAAA;AAAA,UACC,SAASrF,EAAA;AAAA,UACT,sBAAoBlD,EAAAV,CAAA,EAAC,mBAAA;AAAA,UACrB,cAAYU,EAAAV,CAAA,EAAC,YAAA;AAAA,UACb,YAAW6B;AAAA,UACX,eAAcqC;AAAA,QAAA;UAEJ,WACT,MAA6E;AAAA,YAA7EnD,EAA6EL,EAAAmI,EAAA,GAAA;AAAA,0BAAzDmB,EAAA;AAAA,4DAAAA,EAAI,QAAApJ;AAAA,cAAG,SAASsH,EAAA;AAAA,cAAa,OAAM;AAAA,YAAA;;UAG9C,gBAAY5D,EACrB,CAAmD,EAD1B,KAAAC,QAAG;AAAA,YAC5BxD,EAAmDL,EAAAuC,CAAA,GAAA;AAAA,cAA1C,MAAA;AAAA,cAAK,MAAK;AAAA,YAAA;yBAAK,MAAiB;AAAA,gBAAdE,EAAAC,EAAAmB,EAAI,OAAO,GAAA,CAAA;AAAA,cAAA;;;;UAG7B,mBAAeD,EACxB,CAAsD,EAD1B,KAAAC,QAAG;AAAA,YAC/BxD,EAAsDL,EAAAwC,CAAA,GAAA,MAAA;AAAA,yBAA5C,MAAiC;AAAA,oBAA9BxC,EAAAV,CAAA,EAAC,QAASuE,EAAI,UAAU,EAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;UAG5B,cAAUD,EACnB,CAAuE,EADhD,KAAAC,QAAG;AAAA,YACX7D,EAAA+I,EAAA,EAAUlF,CAAG,UAA5B9D,EAAuEC,EAAAuC,CAAA,GAAA;AAAA;cAAxC,MAAK;AAAA,YAAA;yBAAK,MAAoB;AAAA,gBAAjBE,EAAAC,EAAA1C,EAAA+I,EAAA,EAAUlF,CAAG,CAAA,GAAA,CAAA;AAAA,cAAA;;8BACzD9D,EAAkDC,EAAAuC,CAAA,GAAA;AAAA;cAAlC,MAAK;AAAA,cAAK,MAAK;AAAA,YAAA;yBAAQ,MAAC,CAAA,GAAA6F,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA;AAAA,kBAAD,KAAC,EAAA;AAAA,cAAA;;;;UAG/B,kBAAcxE,EACvB,CAEW,EAHgB,KAAAC,QAAG;AAAA,YAC9BxD,EAEWL,EAAAwC,CAAA,GAAA;AAAA,cAFA,MAAMqB,EAAI,YAAS,YAAA;AAAA,cAA0B,KAAA;AAAA,YAAA;yBACtD,MAA2D;AAAA,gBAAxDpB,EAAAC,EAAAmB,EAAI,YAAY7D,EAAAV,CAAA,mBAAmBU,EAAAV,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;UAIhC,gBAAYsE,EACrB,CAAgF,EADvD,KAAAC,QAAG;AAAA,YACT7D,EAAAuI,CAAA,UAAnBxI,EAAgFC,EAAA6I,EAAA,GAAA;AAAA;cAAjD,SAASH,EAAW7E,CAAG;AAAA,cAAI,OAAOA,EAAI;AAAA,YAAA;;UAG5D,gBAAYD,EACrB,CAA+D,EADtC,KAAAC,QAAG;AAAA,YAC5BxD,EAA+DL,EAAA6I,EAAA,GAAA;AAAA,cAAjD,SAASH,EAAW7E,CAAG;AAAA,cAAI,OAAOA,EAAI;AAAA,YAAA;;;;;;;MAhDxC7D,EAAAuI,CAAA;cAAY;AAAA,cAC1B,MAEY;AAAA,UAFZlI,EAEYL,EAAAM,CAAA,GAAA;AAAA,YAFD,MAAK;AAAA,YAAU,MAAK;AAAA,YAAO,gCAAOa,EAAI,IAAA;AAAA,UAAA;uBAC/C,MAAwB;AAAA,kBAArBnB,EAAAV,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;;;;;;;AC5HL,SAASoC,GAAI8H,IAAsB,IAAiB;AACzD,QAAM1J,IAAO0J,EAAQ,QAAQ,QAGvBC,IAAaD,EAAQ,aAAaE,GAAQF,EAAQ,UAAU,IAAI;AAEtE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAA1J;AAAA,IACA,QAAQ;AAAA;AAAA;AAAA,MAGN;AAAA,QACE,MAAAA;AAAA,QACA,MAAM;AAAA,QACN,WAAW6J;AAAA,QACX,OAAO,EAAE,MAAM7J,GAAM,YAAA2J,EAAA;AAAA,MAAW;AAAA,MAElC;AAAA,QACE,MAAM,GAAG3J,CAAI;AAAA,QACb,MAAM;AAAA,QACN,WAAW8J;AAAAA,QACX,OAAO,EAAE,MAAM9J,EAAA;AAAA,MAAK;AAAA;AAAA;AAAA;AAAA,MAKtB;AAAA,QACE,MAAM,GAAGA,CAAI;AAAA,QACb,MAAM;AAAA,QACN,WAAW+J;AAAA,QACX,OAAO,EAAE,MAAM/J,EAAA;AAAA,MAAK;AAAA,IACtB;AAAA;AAAA;AAAA,IAIF,OAAO;AAAA,MACL,UAAU;AAAA,QACR,WAAWuJ;AAAA,QACX,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM,OAAO,EAAE,YAAAI,EAAA;AAAA,MAAW;AAAA,IAC5B;AAAA,EACF;AAEJ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webx-ui/module-seo",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "SEO for the WebX UI admin panel: rules for addresses, redirects, and the card that edits what a page says about itself.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,9 +43,9 @@
43
43
  "vue-router": "^4.5.0"
44
44
  },
45
45
  "dependencies": {
46
- "@webx-ui/core": "^0.22.0",
47
- "@webx-ui/module-admin": "^0.7.0",
48
- "@webx-ui/schema": "^0.2.3"
46
+ "@webx-ui/core": "^0.24.0",
47
+ "@webx-ui/module-admin": "^0.9.0",
48
+ "@webx-ui/schema": "^0.3.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^24.10.1",