@webx-ui/module-press 0.1.0 → 0.1.1
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 +51 -51
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent as se, ref as W, computed as h, onMounted as he, onBeforeUnmount as ne, openBlock as v, createElementBlock as D, createBlock as b, unref as t, Fragment as re, createVNode as w, createSlots as fe, withCtx as p, createTextVNode as z, toDisplayString as $, createCommentVNode as O, watch as te, createElementVNode as N, normalizeClass as H, resolveDynamicComponent as me } from "vue";
|
|
2
2
|
import { onBeforeRouteUpdate as we, onBeforeRouteLeave as ye, useRoute as ge, useRouter as be } from "vue-router";
|
|
3
3
|
import { useLocales as _e, localizedValue as xe, WxSkeleton as ie, WxBadge as Q, WxActionBar as ke, WxButton as Se, toast as _, confirm as X, WxCard as We, WxListDetail as Te, WxEmpty as le, WxInput as Pe, WxIcon as Y, WxSortableList as Ae, WxAvatar as qe, WxTooltip as ae, WxAlert as Be } from "@webx-ui/core";
|
|
4
|
-
import { reorderItems as $e, useAdmin as ee, useTranslate as ue, useErrorText as ce, provideRecordAddress as Le,
|
|
4
|
+
import { reorderItems as $e, useAdmin as ee, useTranslate as ue, useErrorText as ce, provideRecordAddress as Le, useBodyKeys as Ne, WxScreenHead as Oe, WxScreen as Ce, WxSaveState as Ve, useItemOrder as De, WxListScreen as Ie, WxRowMenu as Re } from "@webx-ui/module-admin";
|
|
5
5
|
const Z = "press";
|
|
6
6
|
function de(u) {
|
|
7
7
|
const d = `${u.apiPath}/${Z}`, i = (s) => s.data;
|
|
@@ -20,7 +20,7 @@ function de(u) {
|
|
|
20
20
|
reorder: (s) => $e(u, Z, s, null)
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
const
|
|
23
|
+
const ze = {
|
|
24
24
|
module: {
|
|
25
25
|
press: "Press"
|
|
26
26
|
},
|
|
@@ -114,11 +114,11 @@ const Re = {
|
|
|
114
114
|
function ve() {
|
|
115
115
|
try {
|
|
116
116
|
const { i18n: u } = ee();
|
|
117
|
-
oe.has(u) || (u.defaults("webx-press",
|
|
117
|
+
oe.has(u) || (u.defaults("webx-press", ze), oe.add(u));
|
|
118
118
|
} catch {
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
const
|
|
121
|
+
const Ee = /* @__PURE__ */ se({
|
|
122
122
|
name: "WxOutletPane",
|
|
123
123
|
__name: "OutletPane",
|
|
124
124
|
props: {
|
|
@@ -129,7 +129,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
129
129
|
setup(u, { emit: d }) {
|
|
130
130
|
const i = u, s = d, f = ee(), a = de(f), A = _e();
|
|
131
131
|
ve();
|
|
132
|
-
const n = ue("webx-press"), q = ce(), y = W(null), x = W({}), L = W(null), m = W(""), k = W(i.id !== null), S = W(!1), g = W({}), B = h(() => f.can("press.manage")), E = h(() => JSON.stringify(x.value)), T = h(() => m.value !== "" && E.value !== m.value), U = h(() => S.value ? "saving" : T.value || i.id === null ? "unsaved" : "saved"),
|
|
132
|
+
const n = ue("webx-press"), q = ce(), y = W(null), x = W({}), L = W(null), m = W(""), k = W(i.id !== null), S = W(!1), g = W({}), B = h(() => f.can("press.manage")), E = h(() => JSON.stringify(x.value)), T = h(() => m.value !== "" && E.value !== m.value), U = h(() => S.value ? "saving" : T.value || i.id === null ? "unsaved" : "saved"), J = h(() => {
|
|
133
133
|
const l = x.value.title, r = i.id === null ? n("outlet.new") : y.value?.title ?? "";
|
|
134
134
|
return xe(l, A.active.value, r) || n("outlet.untitled");
|
|
135
135
|
});
|
|
@@ -190,7 +190,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
-
async function
|
|
193
|
+
async function G() {
|
|
194
194
|
const l = y.value;
|
|
195
195
|
if (!(!l || !await X({
|
|
196
196
|
title: n("outlet.delete-title", { name: l.title }),
|
|
@@ -208,15 +208,15 @@ const ze = /* @__PURE__ */ se({
|
|
|
208
208
|
function M(l) {
|
|
209
209
|
T.value && l.preventDefault();
|
|
210
210
|
}
|
|
211
|
-
function
|
|
211
|
+
function K(l) {
|
|
212
212
|
(l.ctrlKey || l.metaKey) && l.key.toLowerCase() === "s" && (l.preventDefault(), (T.value || i.id === null) && R());
|
|
213
213
|
}
|
|
214
|
-
he(() => {
|
|
214
|
+
Ne(K), he(() => {
|
|
215
215
|
I(), window.addEventListener("beforeunload", M);
|
|
216
216
|
}), ne(() => {
|
|
217
217
|
window.removeEventListener("beforeunload", M);
|
|
218
218
|
});
|
|
219
|
-
async function
|
|
219
|
+
async function F() {
|
|
220
220
|
return !T.value || !B.value ? !0 : await X({
|
|
221
221
|
title: n("outlet.leave-title"),
|
|
222
222
|
message: n("outlet.leave-text"),
|
|
@@ -226,9 +226,9 @@ const ze = /* @__PURE__ */ se({
|
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
228
|
we(
|
|
229
|
-
async (l, r) => l.query.outlet === r.query.outlet ? !0 : await
|
|
230
|
-
), ye(
|
|
231
|
-
const
|
|
229
|
+
async (l, r) => l.query.outlet === r.query.outlet ? !0 : await F()
|
|
230
|
+
), ye(F);
|
|
231
|
+
const j = h(() => {
|
|
232
232
|
const l = y.value, r = [];
|
|
233
233
|
if (l?.url) {
|
|
234
234
|
const e = l.url;
|
|
@@ -248,13 +248,13 @@ const ze = /* @__PURE__ */ se({
|
|
|
248
248
|
danger: !0,
|
|
249
249
|
menu: !0,
|
|
250
250
|
run: () => {
|
|
251
|
-
|
|
251
|
+
G();
|
|
252
252
|
}
|
|
253
253
|
}), r;
|
|
254
254
|
});
|
|
255
255
|
return (l, r) => (v(), D("div", {
|
|
256
256
|
class: "wx-outlet",
|
|
257
|
-
onKeydown:
|
|
257
|
+
onKeydown: K
|
|
258
258
|
}, [
|
|
259
259
|
k.value ? (v(), b(t(ie), {
|
|
260
260
|
key: 0,
|
|
@@ -262,12 +262,12 @@ const ze = /* @__PURE__ */ se({
|
|
|
262
262
|
title: "",
|
|
263
263
|
rows: 6
|
|
264
264
|
})) : (v(), D(re, { key: 1 }, [
|
|
265
|
-
w(t(
|
|
265
|
+
w(t(Oe), {
|
|
266
266
|
level: 3,
|
|
267
267
|
back: !i.inline,
|
|
268
268
|
"back-label": t(n)("outlet.back"),
|
|
269
|
-
title:
|
|
270
|
-
actions:
|
|
269
|
+
title: J.value,
|
|
270
|
+
actions: j.value,
|
|
271
271
|
"collapse-below": 480,
|
|
272
272
|
onBack: r[0] || (r[0] = (e) => s("back"))
|
|
273
273
|
}, fe({ _: 2 }, [
|
|
@@ -284,7 +284,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
284
284
|
key: "0"
|
|
285
285
|
} : void 0
|
|
286
286
|
]), 1032, ["back", "back-label", "title", "actions"]),
|
|
287
|
-
w(t(
|
|
287
|
+
w(t(Ce), {
|
|
288
288
|
modelValue: x.value,
|
|
289
289
|
"onUpdate:modelValue": r[1] || (r[1] = (e) => x.value = e),
|
|
290
290
|
name: "press.outlet-form",
|
|
@@ -293,7 +293,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
293
293
|
}, null, 8, ["modelValue", "errors", "disabled"]),
|
|
294
294
|
B.value ? (v(), b(t(ke), { key: 0 }, {
|
|
295
295
|
state: p(() => [
|
|
296
|
-
w(t(
|
|
296
|
+
w(t(Ve), { state: U.value }, null, 8, ["state"])
|
|
297
297
|
]),
|
|
298
298
|
default: p(() => [
|
|
299
299
|
w(t(Se), {
|
|
@@ -318,10 +318,10 @@ const ze = /* @__PURE__ */ se({
|
|
|
318
318
|
for (const [s, f] of d)
|
|
319
319
|
i[s] = f;
|
|
320
320
|
return i;
|
|
321
|
-
},
|
|
321
|
+
}, Ue = /* @__PURE__ */ pe(Ee, [["__scopeId", "data-v-b6ea9c8f"]]), Me = { class: "wx-press__bar" }, Ke = {
|
|
322
322
|
key: 0,
|
|
323
323
|
class: "wx-outlet-row__logo"
|
|
324
|
-
},
|
|
324
|
+
}, Fe = ["src"], je = { class: "wx-outlet-row__body" }, Je = { class: "wx-outlet-row__head" }, Ge = { class: "wx-outlet-row__title" }, He = { class: "wx-outlet-row__facts" }, Ye = { class: "wx-outlet-row__count" }, Qe = ["aria-label"], Xe = /* @__PURE__ */ se({
|
|
325
325
|
__name: "PressPage",
|
|
326
326
|
props: {
|
|
327
327
|
base: { default: "/press" }
|
|
@@ -351,7 +351,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
351
351
|
}), E = h(() => [
|
|
352
352
|
{ value: "", label: a("outlet.all") },
|
|
353
353
|
{ value: "trashed", label: a("outlet.trashed"), icon: "trash" }
|
|
354
|
-
]), T =
|
|
354
|
+
]), T = De(Z, () => ({ q: k.value, filtered: m.value })), U = h(() => y.value && T.sortable.value), J = h(() => k.value !== "" ? a("outlet.empty-search") : m.value ? a("outlet.empty-bin") : a("outlet.empty"));
|
|
355
355
|
async function P() {
|
|
356
356
|
q.value = !0;
|
|
357
357
|
try {
|
|
@@ -382,7 +382,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
382
382
|
const e = { ...s.query };
|
|
383
383
|
delete e.outlet, f.replace({ query: e });
|
|
384
384
|
}
|
|
385
|
-
function
|
|
385
|
+
function G(e) {
|
|
386
386
|
I(e.id), P();
|
|
387
387
|
}
|
|
388
388
|
async function M() {
|
|
@@ -392,7 +392,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
392
392
|
_.danger(A(e, a("outlet.reorder-failed"))), await P();
|
|
393
393
|
}
|
|
394
394
|
}
|
|
395
|
-
function
|
|
395
|
+
function K(e) {
|
|
396
396
|
return y.value ? m.value ? [
|
|
397
397
|
{
|
|
398
398
|
key: "restore",
|
|
@@ -409,12 +409,12 @@ const ze = /* @__PURE__ */ se({
|
|
|
409
409
|
label: a("outlet.delete"),
|
|
410
410
|
danger: !0,
|
|
411
411
|
run: () => {
|
|
412
|
-
|
|
412
|
+
F(e);
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
] : [];
|
|
416
416
|
}
|
|
417
|
-
async function
|
|
417
|
+
async function F(e) {
|
|
418
418
|
if (await X({
|
|
419
419
|
title: a("outlet.delete-title", { name: e.title }),
|
|
420
420
|
message: a("outlet.delete-text"),
|
|
@@ -423,12 +423,12 @@ const ze = /* @__PURE__ */ se({
|
|
|
423
423
|
tone: "danger"
|
|
424
424
|
}))
|
|
425
425
|
try {
|
|
426
|
-
await i.remove(e.id), _.success(a("outlet.deleted")),
|
|
426
|
+
await i.remove(e.id), _.success(a("outlet.deleted")), j(e.id);
|
|
427
427
|
} catch (o) {
|
|
428
428
|
_.danger(A(o));
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
|
-
function
|
|
431
|
+
function j(e) {
|
|
432
432
|
g.value === e && R(), P();
|
|
433
433
|
}
|
|
434
434
|
async function l(e) {
|
|
@@ -443,7 +443,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
443
443
|
function r(e) {
|
|
444
444
|
return e.published && e.locales.length === 0;
|
|
445
445
|
}
|
|
446
|
-
return (e, c) => (v(), b(t(
|
|
446
|
+
return (e, c) => (v(), b(t(Ie), {
|
|
447
447
|
view: L.value,
|
|
448
448
|
"onUpdate:view": c[4] || (c[4] = (o) => L.value = o),
|
|
449
449
|
title: x.value,
|
|
@@ -465,7 +465,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
465
465
|
"detail-label": t(a)("module.press")
|
|
466
466
|
}, {
|
|
467
467
|
list: p(() => [
|
|
468
|
-
N("div",
|
|
468
|
+
N("div", Me, [
|
|
469
469
|
w(t(Pe), {
|
|
470
470
|
modelValue: S.value,
|
|
471
471
|
"onUpdate:modelValue": c[0] || (c[0] = (o) => S.value = o),
|
|
@@ -502,7 +502,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
502
502
|
rows: 5
|
|
503
503
|
})) : n.value.length === 0 ? (v(), b(t(le), {
|
|
504
504
|
key: 2,
|
|
505
|
-
title:
|
|
505
|
+
title: J.value,
|
|
506
506
|
description: !m.value && k.value === "" ? t(a)("outlet.empty-help") : void 0
|
|
507
507
|
}, null, 8, ["title", "description"])) : (v(), D(re, { key: 3 }, [
|
|
508
508
|
w(t(Ae), {
|
|
@@ -523,11 +523,11 @@ const ze = /* @__PURE__ */ se({
|
|
|
523
523
|
onClick: (V) => m.value ? void 0 : I(o.id)
|
|
524
524
|
}, {
|
|
525
525
|
default: p(() => [
|
|
526
|
-
o.logo?.thumb ? (v(), D("span",
|
|
526
|
+
o.logo?.thumb ? (v(), D("span", Ke, [
|
|
527
527
|
N("img", {
|
|
528
528
|
src: o.logo.thumb,
|
|
529
529
|
alt: ""
|
|
530
|
-
}, null, 8,
|
|
530
|
+
}, null, 8, Fe)
|
|
531
531
|
])) : (v(), b(t(qe), {
|
|
532
532
|
key: 1,
|
|
533
533
|
class: "wx-outlet-row__initials",
|
|
@@ -535,9 +535,9 @@ const ze = /* @__PURE__ */ se({
|
|
|
535
535
|
name: o.title,
|
|
536
536
|
size: "md"
|
|
537
537
|
}, null, 8, ["name"])),
|
|
538
|
-
N("span",
|
|
539
|
-
N("span",
|
|
540
|
-
N("span",
|
|
538
|
+
N("span", je, [
|
|
539
|
+
N("span", Je, [
|
|
540
|
+
N("span", Ge, $(o.title), 1),
|
|
541
541
|
o.featured ? (v(), b(t(ae), {
|
|
542
542
|
key: 0,
|
|
543
543
|
content: t(a)("outlet.featured")
|
|
@@ -553,7 +553,7 @@ const ze = /* @__PURE__ */ se({
|
|
|
553
553
|
_: 1
|
|
554
554
|
}, 8, ["content"])) : O("", !0)
|
|
555
555
|
]),
|
|
556
|
-
N("span",
|
|
556
|
+
N("span", He, [
|
|
557
557
|
o.published ? r(o) ? (v(), b(t(ae), {
|
|
558
558
|
key: 1,
|
|
559
559
|
content: t(a)("outlet.visible-nowhere")
|
|
@@ -581,12 +581,12 @@ const ze = /* @__PURE__ */ se({
|
|
|
581
581
|
]),
|
|
582
582
|
_: 1
|
|
583
583
|
})),
|
|
584
|
-
N("span",
|
|
584
|
+
N("span", Ye, $(o.articles_count > 0 ? t(a)("outlet.articles-count", { count: o.articles_count }) : t(a)("outlet.no-articles")), 1),
|
|
585
585
|
o.locales.length > 0 ? (v(), D("span", {
|
|
586
586
|
key: 2,
|
|
587
587
|
class: "wx-outlet-row__locales",
|
|
588
588
|
"aria-label": t(a)("outlet.seen-in", { locales: o.locales.join(", ") })
|
|
589
|
-
}, $(o.locales.join(" · ")), 9,
|
|
589
|
+
}, $(o.locales.join(" · ")), 9, Qe)) : O("", !0)
|
|
590
590
|
])
|
|
591
591
|
])
|
|
592
592
|
]),
|
|
@@ -594,8 +594,8 @@ const ze = /* @__PURE__ */ se({
|
|
|
594
594
|
}, 1032, ["type", "class", "onClick"]))
|
|
595
595
|
]),
|
|
596
596
|
actions: p(({ item: o }) => [
|
|
597
|
-
w(t(
|
|
598
|
-
actions:
|
|
597
|
+
w(t(Re), {
|
|
598
|
+
actions: K(o),
|
|
599
599
|
label: o.title
|
|
600
600
|
}, null, 8, ["actions", "label"])
|
|
601
601
|
]),
|
|
@@ -610,14 +610,14 @@ const ze = /* @__PURE__ */ se({
|
|
|
610
610
|
], 64))
|
|
611
611
|
]),
|
|
612
612
|
detail: p(({ inline: o, back: V }) => [
|
|
613
|
-
g.value !== null ? (v(), b(
|
|
613
|
+
g.value !== null ? (v(), b(Ue, {
|
|
614
614
|
id: g.value === "new" ? null : g.value,
|
|
615
615
|
key: String(g.value),
|
|
616
616
|
inline: o,
|
|
617
617
|
onBack: V,
|
|
618
618
|
onSaved: P,
|
|
619
|
-
onCreated:
|
|
620
|
-
onRemoved:
|
|
619
|
+
onCreated: G,
|
|
620
|
+
onRemoved: j
|
|
621
621
|
}, null, 8, ["id", "inline", "onBack"])) : O("", !0)
|
|
622
622
|
]),
|
|
623
623
|
empty: p(() => [
|
|
@@ -635,23 +635,23 @@ const ze = /* @__PURE__ */ se({
|
|
|
635
635
|
_: 1
|
|
636
636
|
}, 8, ["view", "title", "views"]));
|
|
637
637
|
}
|
|
638
|
-
}),
|
|
639
|
-
function
|
|
638
|
+
}), Ze = /* @__PURE__ */ pe(Xe, [["__scopeId", "data-v-7ef16f83"]]);
|
|
639
|
+
function ot(u = {}) {
|
|
640
640
|
const d = u.path ?? "/press";
|
|
641
641
|
return {
|
|
642
642
|
id: "press",
|
|
643
643
|
path: d,
|
|
644
644
|
// One route: the open outlet is in the address (`?outlet=`), beside the list it was opened
|
|
645
645
|
// from, so a link to it is a link to both.
|
|
646
|
-
routes: [{ path: d, name: "webx.press", component:
|
|
646
|
+
routes: [{ path: d, name: "webx.press", component: Ze, props: { base: d } }]
|
|
647
647
|
};
|
|
648
648
|
}
|
|
649
649
|
export {
|
|
650
650
|
Z as PRESS_API,
|
|
651
|
-
|
|
652
|
-
|
|
651
|
+
Ue as WxOutletPane,
|
|
652
|
+
Ze as WxPressPage,
|
|
653
653
|
de as createPressApi,
|
|
654
|
-
|
|
655
|
-
|
|
654
|
+
ot as press,
|
|
655
|
+
ze as pressMessages
|
|
656
656
|
};
|
|
657
657
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/api.ts","../src/messages.ts","../src/i18n.ts","../src/OutletPane.vue","../src/PressPage.vue","../src/module.ts"],"sourcesContent":["import { reorderItems, type AdminContext } from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { OutletDetail, OutletQuery, OutletRow, OutletsList } from './types'\n\nexport interface PressApi {\n list(query?: OutletQuery): Promise<OutletsList>\n get(id: number): Promise<OutletDetail>\n /**\n * A new outlet out of the values of its form, articles included, in one request: the form is\n * filled in first and the record made on the first save, so nobody leaves an empty outlet behind.\n */\n create(values: ScreenModel): Promise<OutletDetail>\n /** Refused with a 422 under the name of the field — `articles.<n>.<field>` for an article. */\n save(id: number, values: ScreenModel): Promise<OutletDetail>\n remove(id: number): Promise<void>\n restore(id: number): Promise<OutletRow>\n /** The order of the whole list. The order of the articles is the order of the form's rows. */\n reorder(ids: number[]): Promise<void>\n}\n\n/** The path of the section under the panel's API, and of its order. */\nexport const PRESS_API = 'press'\n\n/** Everything under `/press`, below the panel's API path (§4.10). */\nexport function createPressApi(admin: AdminContext): PressApi {\n const base = `${admin.apiPath}/${PRESS_API}`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n list: (query = {}) => {\n const search = new URLSearchParams()\n\n if (query.search) search.set('search', query.search)\n if (query.trashed) search.set('trashed', '1')\n\n return admin.http.get<OutletsList>(search.size > 0 ? `${base}?${search}` : base)\n },\n get: (id) => admin.http.get<{ data: OutletDetail }>(`${base}/${id}`).then(data),\n create: (values) => admin.http.post<{ data: OutletDetail }>(base, { values }).then(data),\n save: (id, values) =>\n admin.http.put<{ data: OutletDetail }>(`${base}/${id}`, { values }).then(data),\n remove: (id) => admin.http.delete<void>(`${base}/${id}`).then(() => undefined),\n // The resource of the list line, and not the form's answer: a restored outlet stays closed.\n restore: (id) => admin.http.post<{ data: OutletRow }>(`${base}/${id}/restore`, {}).then(data),\n reorder: (ids) => reorderItems(admin, PRESS_API, ids, null),\n }\n}\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * The English this package speaks on its own, before the server's dictionary arrives, or without\n * one. The server ships the same lines in ten languages under `webx-press::` and overrides these;\n * `messages.test.ts` keeps the two sets of keys equal.\n *\n * `screen` is what the described screen `press.outlet-form` is labelled with: the server sends the\n * labels as `trans::webx-press::screen.*`, and these are what they say when no dictionary came.\n *\n * `kinds` are the four of the default config (decision 3). A kind a site adds brings its word in\n * `lang/vendor/webx-press` — the server labels the options of the field itself, so a kind missing\n * here is only missing from a panel that runs with no server at all.\n */\nexport const pressMessages: Record<string, Messages> = {\n module: {\n press: 'Press',\n },\n outlet: {\n new: 'New outlet',\n untitled: 'Untitled',\n search: 'Search the outlets',\n empty: 'No outlets yet.',\n 'empty-help':\n 'An outlet is a magazine, a paper or a site that wrote about you; its articles are added on its form.',\n 'empty-search': 'Nothing matches that.',\n 'empty-bin': 'The bin is empty.',\n all: 'All',\n trashed: 'Bin',\n 'not-published': 'Not published',\n 'visible-nowhere': 'Published, but seen in no language: none of its articles has a title yet.',\n 'articles-count': 'Articles: :count',\n 'no-articles': 'No articles yet',\n featured: 'In the logo strip',\n 'seen-in': 'Seen in: :locales',\n choose: 'Choose an outlet',\n 'choose-help': 'Or add a new one: the first line of the list.',\n save: 'Save',\n saved: 'The outlet is saved.',\n 'save-failed': 'The outlet was not saved.',\n cancel: 'Cancel',\n delete: 'Delete',\n 'delete-title': 'Delete “:name”?',\n 'delete-text':\n 'It goes to the bin with its articles and leaves the site and every block. Restored, it comes back with them.',\n deleted: 'The outlet is in the bin.',\n restore: 'Restore',\n restored: 'The outlet is back.',\n 'reorder-failed': 'The new order was not saved.',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'What you wrote in this outlet is not on the server.',\n leave: 'Leave',\n back: 'Back to the list',\n open: 'Open on the site',\n 'address-moving': 'The page of the outlet moves to the new address when you save.',\n },\n screen: {\n general: 'General',\n outlet: 'Outlet',\n logo: 'Logo',\n 'logo-help': 'Without a logo the site prints the name.',\n title: 'Name',\n 'title-help': 'Where it is not written in a language, the name from another one is shown.',\n slug: 'Address',\n 'slug-help':\n \"The outlet's page. A language with articles and no address of its own takes this one.\",\n 'website-url': 'Website',\n 'website-url-help': 'An address that starts with http:// or https://.',\n summary: 'About the outlet',\n 'summary-help':\n 'A few words under the name on its page, and its description for search engines.',\n settings: 'Settings',\n published: 'Published',\n 'published-help': 'On the site at once — in the languages it has articles in.',\n featured: 'In the strip of logos',\n 'featured-help': 'Shown by the logos block set to the marked outlets only.',\n articles: 'Articles',\n 'articles-help': \"In the order they are shown on the outlet's page. Saved with the outlet.\",\n 'article-label': '#:number · :title',\n 'article-untitled': 'Untitled',\n 'article-title': 'Title',\n 'article-title-help': 'An article is shown in a language only when it has a title in it.',\n excerpt: 'Excerpt',\n kind: 'Kind',\n 'kind-none': 'No kind',\n 'published-on': 'Date',\n 'date-precision': 'Known to',\n 'date-precision-help': 'How much of the date is printed: the day, the month, or the year only.',\n precision: {\n day: 'Day',\n month: 'Month',\n year: 'Year',\n },\n url: 'Link',\n 'url-help': \"The article on the outlet's site. Without it the title leads to the PDF.\",\n file: 'PDF',\n 'file-help': 'A PDF from the library — a scan of the page, say.',\n hidden: 'Do not show',\n 'hidden-help': 'Kept here, and shown nowhere on the site.',\n seo: 'SEO',\n 'seo-empty': 'The SEO card appears here when module-seo is installed.',\n },\n kinds: {\n mention: 'Mention',\n interview: 'Interview',\n expert_comment: 'Expert comment',\n authored: 'Authored article',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { pressMessages } 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 usePressMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-press', pressMessages)\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, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { onBeforeRouteLeave, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxBadge,\n WxButton,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport {\n provideRecordAddress,\n useAdmin,\n useErrorText,\n useTranslate,\n WxSaveState,\n WxScreen,\n WxScreenHead,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport { createPressApi } from './api'\nimport { usePressMessages } from './i18n'\nimport type { OutletDetail, OutletSummary } from './types'\n\n/**\n * One outlet with its articles: the pane beside the list, and the whole screen on a phone.\n *\n * The described screen `press.outlet-form` — General · Articles · SEO — with a head over it and\n * one button under it. One explicit save for the whole outlet, articles included (decision 15):\n * the articles are rows of its form, and the server writes them into their own table in the same\n * transaction (§4.6). No autosave: there is no draft (decision 9), so what is saved is on the site\n * at once — the button is the moment somebody decides it is ready.\n *\n * A new outlet is a form first and a record on the first save: an empty row made by pressing\n * \"New outlet\" and walking away would be an outlet with nothing in it on the list for ever.\n *\n * The way back is drawn here and not by the pane around it: the drawer a narrow screen opens\n * this in has no close of its own on purpose (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxOutletPane' })\n\nconst props = withDefaults(\n defineProps<{\n /** `null` — an outlet not written yet. */\n id: number | null\n /** False in the drawer a narrow screen opens this in. */\n inline?: boolean\n }>(),\n { inline: true },\n)\n\nconst emit = defineEmits<{\n back: []\n /** The outlet was written; the list redraws its row. */\n saved: [outlet: OutletSummary]\n /** The first save of a new outlet: it is a record now, with an id to be opened by. */\n created: [outlet: OutletSummary]\n removed: [id: number]\n}>()\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst locales = useLocales()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst outlet = ref<OutletSummary | null>(null)\nconst values = ref<ScreenModel>({})\nconst prefix = ref<string | null>(null)\nconst snapshot = ref('')\n\nconst loading = ref(props.id !== null)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst current = computed(() => JSON.stringify(values.value))\nconst dirty = computed(() => snapshot.value !== '' && current.value !== snapshot.value)\n\nconst state = computed<'saving' | 'unsaved' | 'saved'>(() => {\n if (saving.value) return 'saving'\n\n return dirty.value || props.id === null ? 'unsaved' : 'saved'\n})\n\n/** The name follows the field rather than the answer: an edit shows at the top as it is typed. */\nconst title = computed(() => {\n const written = values.value.title as LocalizedValue | string | null | undefined\n const fallback = props.id === null ? t('outlet.new') : (outlet.value?.title ?? '')\n\n return localizedValue(written, locales.active.value, fallback) || t('outlet.untitled')\n})\n\n/*\n * The address field is the panel's shared one (`wx-slug`), and this is what it prints. The form\n * answers with the page's address (`url`) in the panel's language — `/en/press/…` on a site whose\n * default is another — while the field compares it with the slug in the language being edited.\n * So the address is handed over without the host, the slash and the language, and only in its own\n * language: in any other the field has nothing to compare with, and says nothing rather than\n * warning that a page moves which does not.\n */\nprovideRecordAddress({\n values,\n prefix,\n path: computed(() => pathIn(outlet.value?.url ?? null, locales.active.value)),\n moving: () => t('outlet.address-moving'),\n})\n\nfunction pathIn(url: string | null, locale: string): string | null | undefined {\n if (url === null) return null\n\n let segments: string[]\n\n try {\n segments = new URL(url, 'http://site.invalid').pathname.split('/').filter((one) => one !== '')\n } catch {\n return null\n }\n\n const codes = locales.list.value.map((one) => one.code)\n const own =\n codes.includes(segments[0] ?? '') && segments[0] !== codes[0] ? segments.shift()! : codes[0]\n\n return own === undefined || own === locale ? segments.join('/') : undefined\n}\n\nfunction take(detail: OutletDetail): void {\n outlet.value = detail.outlet\n values.value = detail.values\n prefix.value = detail.prefix\n snapshot.value = JSON.stringify(detail.values)\n}\n\nif (props.id === null) {\n // What the server would answer for an outlet with nothing in it yet.\n const blank: ScreenModel = {\n title: {},\n slug: {},\n summary: {},\n published: false,\n featured: false,\n articles: [],\n }\n\n values.value = blank\n snapshot.value = JSON.stringify(blank)\n}\n\nasync function load(): Promise<void> {\n if (props.id === null) return\n\n loading.value = true\n\n try {\n take(await api.get(props.id))\n } catch (error) {\n toast.danger(message(error))\n emit('back')\n } finally {\n loading.value = false\n }\n}\n\nasync function save(): Promise<void> {\n if (saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const made = props.id === null\n const detail = made\n ? await api.create(values.value)\n : await api.save(props.id as number, values.value)\n\n take(detail)\n toast.success(t('outlet.saved'))\n if (made) emit('created', detail.outlet)\n else emit('saved', detail.outlet)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n // `articles.2.url` as the server names it: the repeater finds its row and field by that.\n errors.value = body.errors\n toast.danger(t('outlet.save-failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n\nasync function remove(): Promise<void> {\n const record = outlet.value\n\n if (!record) return\n\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: record.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(record.id)\n toast.success(t('outlet.deleted'))\n // Nothing left to protect: the record is in the bin.\n snapshot.value = current.value\n emit('removed', record.id)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/* The browser's own guard: it cannot wait for a request, so all it can do is ask. */\nfunction leaveGuard(event: BeforeUnloadEvent): void {\n if (dirty.value) event.preventDefault()\n}\n\n/* Ctrl+S saves, as it does everywhere else that has a save button. */\nfunction onKeydown(event: KeyboardEvent): void {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {\n event.preventDefault()\n if (dirty.value || props.id === null) void save()\n }\n}\n\nonMounted(() => {\n void load()\n window.addEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('beforeunload', leaveGuard)\n})\n\nasync function mayLeave(): Promise<boolean> {\n if (!dirty.value || !canManage.value) return true\n\n return await confirm({\n title: t('outlet.leave-title'),\n message: t('outlet.leave-text'),\n confirmText: t('outlet.leave'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n}\n\n/*\n * The list and this form are one route, so another outlet is an update of it rather than a way\n * out — and so is every keystroke in the search above the list, which goes into the address too.\n * Only a change of the outlet asks.\n */\nonBeforeRouteUpdate(async (to: RouteLocationNormalized, from: RouteLocationNormalized) =>\n to.query.outlet === from.query.outlet ? true : await mayLeave(),\n)\n\nonBeforeRouteLeave(mayLeave)\n\n/* What leads away from the outlet. On a narrow pane the head folds them into the ···. */\nconst actions = computed<ScreenAction[]>(() => {\n const record = outlet.value\n const list: ScreenAction[] = []\n\n // Only a page that answers: an unpublished outlet, or one seen nowhere, has no address.\n if (record?.url) {\n const url = record.url\n\n list.push({\n key: 'open',\n label: t('outlet.open'),\n icon: 'external-link',\n run: () => void window.open(url, '_blank', 'noopener'),\n })\n }\n\n if (canManage.value && record !== null) {\n list.push({\n key: 'delete',\n label: t('outlet.delete'),\n icon: 'trash',\n danger: true,\n menu: true,\n run: () => void remove(),\n })\n }\n\n return list\n})\n</script>\n\n<template>\n <div class=\"wx-outlet\" @keydown=\"onKeydown\">\n <wx-skeleton v-if=\"loading\" class=\"wx-outlet__ghost\" title :rows=\"6\" />\n\n <template v-else>\n <wx-screen-head\n :level=\"3\"\n :back=\"!props.inline\"\n :back-label=\"t('outlet.back')\"\n :title=\"title\"\n :actions=\"actions\"\n :collapse-below=\"480\"\n @back=\"emit('back')\"\n >\n <template v-if=\"outlet && !outlet.published\" #title-after>\n <wx-badge dot>{{ t('outlet.not-published') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-screen\n v-model=\"values\"\n name=\"press.outlet-form\"\n :errors=\"errors\"\n :disabled=\"!canManage || saving\"\n />\n\n <wx-action-bar v-if=\"canManage\">\n <template #state>\n <wx-save-state :state=\"state\" />\n </template>\n\n <wx-button\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"!dirty && props.id !== null\"\n @click=\"save\"\n >\n {{ t('outlet.save') }}\n </wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-outlet {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n min-width: 0;\n padding: var(--wx-gap, var(--wx-space-16));\n container-type: inline-size;\n}\n\n.wx-outlet__ghost {\n max-width: 480px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n confirm,\n toast,\n WxAlert,\n WxAvatar,\n WxBadge,\n WxCard,\n WxEmpty,\n WxIcon,\n WxInput,\n WxListDetail,\n WxSkeleton,\n WxSortableList,\n WxTooltip,\n type TabItem,\n type TabValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useItemOrder,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport { createPressApi, PRESS_API } from './api'\nimport { usePressMessages } from './i18n'\nimport OutletPane from './OutletPane.vue'\nimport type { OutletRow, OutletSummary } from './types'\n\n/**\n * The outlets: all of them on the left, the one being written on the right (§4.9, decision 15).\n *\n * The shape of the reviews, for the same reason: an outlet is typed in from a clipping in a\n * minute, and the next one is usually under it. On a phone the form is the whole screen, in the\n * drawer `WxListDetail` raises, and draws its own way back.\n *\n * One order, the general one (decision 6): a drag moves it; a search or the bin shows a\n * selection, so the grips go away and the line under the list says why. The order of the\n * articles is not here — it is the order of the rows in the outlet's form.\n *\n * Everything the screen is looking at lives in the address — the view, the search and the open\n * outlet — so a link to an outlet is a link to it in its list.\n */\nwithDefaults(defineProps<{ base?: string }>(), { base: '/press' })\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst route = useRoute()\nconst router = useRouter()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst rows = ref<OutletRow[]>([])\nconst loading = ref(true)\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'press')?.title ??\n t('module.press'),\n)\n\nconst view = computed<TabValue>({\n get: () => (route.query.view === 'trashed' ? 'trashed' : ''),\n set: (value) => {\n const query: LocationQueryRaw = {\n ...route.query,\n view: value === '' ? undefined : String(value),\n }\n\n // An outlet from the list is not an outlet from the bin: the form closes with the view.\n delete query.outlet\n void router.replace({ query })\n },\n})\n\nconst inBin = computed(() => view.value === 'trashed')\n\nconst q = computed(() => (typeof route.query.q === 'string' ? route.query.q : ''))\n\n/* What is typed, ahead of the address: the address follows it after a pause. */\nconst typed = ref(q.value)\n\n/** The open outlet: a number, `'new'` for one not yet saved, or nothing. */\nconst selected = computed<number | 'new' | null>(() => {\n const raw = route.query.outlet\n\n if (raw === 'new') return canManage.value ? 'new' : null\n\n return typeof raw === 'string' && raw !== '' ? Number(raw) || null : null\n})\n\n/*\n * Open is \"an outlet is chosen\", and closing is un-choosing it — so the address, the list's\n * highlight and the drawer on a phone can never disagree about it.\n */\nconst open = computed({\n get: () => selected.value !== null,\n set: (value) => {\n if (!value) close()\n },\n})\n\nconst views = computed<TabItem[]>(() => [\n { value: '', label: t('outlet.all') },\n { value: 'trashed', label: t('outlet.trashed'), icon: 'trash' },\n])\n\nconst order = useItemOrder(PRESS_API, () => ({ q: q.value, filtered: inBin.value }))\n\n/* Dragged only by somebody who may write, and only in a list that is the whole order. */\nconst sortable = computed(() => canManage.value && order.sortable.value)\n\nconst emptyText = computed(() => {\n if (q.value !== '') return t('outlet.empty-search')\n\n return inBin.value ? t('outlet.empty-bin') : t('outlet.empty')\n})\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const answer = await api.list({ search: q.value, trashed: inBin.value })\n\n rows.value = answer.data\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => [view.value, q.value] as const,\n () => void load(),\n)\nvoid load()\n\nlet pause: ReturnType<typeof setTimeout> | undefined\n\nwatch(typed, (value) => {\n clearTimeout(pause)\n pause = setTimeout(() => {\n void router.replace({ query: { ...route.query, q: value.trim() === '' ? undefined : value } })\n }, 300)\n})\n\nonBeforeUnmount(() => clearTimeout(pause))\n\n/* `replace` and not `push`: going through twenty outlets is not twenty steps of history. */\nfunction choose(id: number | 'new'): void {\n void router.replace({ query: { ...route.query, outlet: String(id) } })\n}\n\nfunction close(): void {\n const query = { ...route.query }\n\n delete query.outlet\n void router.replace({ query })\n}\n\n/** A new outlet was saved: it is a record now, and the list has it. */\nfunction created(outlet: OutletSummary): void {\n choose(outlet.id)\n void load()\n}\n\n/**\n * The order on screen, written as a whole. The list is already in its new order\n * (`WxSortableList` reorders the model before it says so), so a failure has to put it back\n * rather than leave the screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await order.move(rows.value.map((row) => row.id))\n } catch (error) {\n toast.danger(message(error, t('outlet.reorder-failed')))\n await load()\n }\n}\n\nfunction actionsFor(row: OutletRow): RowAction[] {\n if (!canManage.value) return []\n\n if (inBin.value) {\n return [\n {\n key: 'restore',\n icon: 'refresh',\n label: t('outlet.restore'),\n run: () => void restore(row),\n },\n ]\n }\n\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('outlet.delete'),\n danger: true,\n run: () => void remove(row),\n },\n ]\n}\n\nasync function remove(row: OutletRow): Promise<void> {\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: row.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(row.id)\n toast.success(t('outlet.deleted'))\n removed(row.id)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/** Gone from the list; and if it was the open one, the form goes with it. */\nfunction removed(id: number): void {\n if (selected.value === id) close()\n\n void load()\n}\n\nasync function restore(row: OutletRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('outlet.restored'))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\n/* Published and yet shown in no language: the one state the list has to point out. */\nfunction unseen(row: OutletRow): boolean {\n return row.published && row.locales.length === 0\n}\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"view\" :title=\"title\" :views=\"views\" :card=\"false\">\n <wx-card class=\"wx-press\" padding=\"none\">\n <wx-list-detail\n v-model:open=\"open\"\n class=\"wx-press__panes\"\n :list-width=\"360\"\n :detail-min=\"520\"\n :detail-label=\"t('module.press')\"\n >\n <template #list>\n <div class=\"wx-press__bar\">\n <wx-input\n v-model=\"typed\"\n class=\"wx-press__search\"\n size=\"sm\"\n clearable\n :placeholder=\"t('outlet.search')\"\n :aria-label=\"t('outlet.search')\"\n >\n <template #prefix><wx-icon name=\"search\" size=\"sm\" /></template>\n </wx-input>\n </div>\n\n <!--\n A line of the list rather than a button in the head: the new outlet is written where\n it will stand, beside the ones it joins — and the form opens the way any other does.\n -->\n <button\n v-if=\"canManage && !inBin\"\n type=\"button\"\n class=\"wx-press__new\"\n :class=\"{ 'is-current': selected === 'new' }\"\n @click=\"choose('new')\"\n >\n <wx-icon name=\"plus\" size=\"sm\" />\n {{ t('outlet.new') }}\n </button>\n\n <wx-skeleton v-if=\"loading && rows.length === 0\" class=\"wx-press__loading\" :rows=\"5\" />\n\n <wx-empty\n v-else-if=\"rows.length === 0\"\n :title=\"emptyText\"\n :description=\"!inBin && q === '' ? t('outlet.empty-help') : undefined\"\n />\n\n <template v-else>\n <!-- A grip and not the whole row: the row opens an outlet, and a row that both opens\n and drags is one where one of the two happens by accident. -->\n <wx-sortable-list\n v-model=\"rows\"\n class=\"wx-press__list\"\n :class=\"{ 'is-loading': loading }\"\n plain\n item-key=\"id\"\n :handle=\"sortable ? 'grip' : 'row'\"\n :item-label=\"(item: OutletRow) => item.title\"\n :disabled=\"!sortable\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <component\n :is=\"inBin ? 'div' : 'button'\"\n :type=\"inBin ? undefined : 'button'\"\n class=\"wx-outlet-row\"\n :class=\"{ 'is-current': selected === item.id }\"\n @click=\"inBin ? undefined : choose(item.id)\"\n >\n <!-- A logo is wide as often as not: in a square it shrinks to a sliver, so it gets a\n box of its own shape, and only an outlet without one gets initials. -->\n <span v-if=\"item.logo?.thumb\" class=\"wx-outlet-row__logo\">\n <img :src=\"item.logo.thumb\" alt=\"\" />\n </span>\n <wx-avatar\n v-else\n class=\"wx-outlet-row__initials\"\n shape=\"square\"\n :name=\"item.title\"\n size=\"md\"\n />\n\n <span class=\"wx-outlet-row__body\">\n <span class=\"wx-outlet-row__head\">\n <span class=\"wx-outlet-row__title\">{{ item.title }}</span>\n <wx-tooltip v-if=\"item.featured\" :content=\"t('outlet.featured')\">\n <wx-icon\n class=\"wx-outlet-row__featured\"\n name=\"star\"\n size=\"sm\"\n :aria-label=\"t('outlet.featured')\"\n />\n </wx-tooltip>\n </span>\n\n <span class=\"wx-outlet-row__facts\">\n <wx-badge v-if=\"!item.published\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n <wx-tooltip v-else-if=\"unseen(item)\" :content=\"t('outlet.visible-nowhere')\">\n <wx-badge type=\"warning\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n </wx-tooltip>\n\n <span class=\"wx-outlet-row__count\">\n {{\n item.articles_count > 0\n ? t('outlet.articles-count', { count: item.articles_count })\n : t('outlet.no-articles')\n }}\n </span>\n\n <span\n v-if=\"item.locales.length > 0\"\n class=\"wx-outlet-row__locales\"\n :aria-label=\"t('outlet.seen-in', { locales: item.locales.join(', ') })\"\n >\n {{ item.locales.join(' · ') }}\n </span>\n </span>\n </span>\n </component>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu :actions=\"actionsFor(item)\" :label=\"item.title\" />\n </template>\n </wx-sortable-list>\n\n <!-- What a drag does here, or why there is nothing to drag. -->\n <wx-alert\n v-if=\"canManage && !inBin\"\n class=\"wx-press__note\"\n type=\"info\"\n :description=\"order.hint.value\"\n />\n </template>\n </template>\n\n <template #detail=\"{ inline, back }\">\n <outlet-pane\n v-if=\"selected !== null\"\n :id=\"selected === 'new' ? null : selected\"\n :key=\"String(selected)\"\n :inline=\"inline\"\n @back=\"back\"\n @saved=\"load\"\n @created=\"created\"\n @removed=\"removed\"\n />\n </template>\n\n <template #empty>\n <wx-empty\n :title=\"t('outlet.choose')\"\n :description=\"canManage ? t('outlet.choose-help') : undefined\"\n />\n </template>\n </wx-list-detail>\n </wx-card>\n </wx-list-screen>\n</template>\n\n<style>\n/*\n * What scrolls here is the page, the way it does on every other list of the panel. The card's\n * corners are the screen's, and the panes paint their own background to the edge: `clip`, not\n * `hidden`, which would make this a scroll container and pin everything sticky inside to a box\n * that never moves (CLAUDE.md §4).\n */\n.wx-press > .wx-card__body {\n display: flex;\n flex-direction: column;\n overflow: clip;\n border-radius: inherit;\n}\n</style>\n\n<style scoped>\n.wx-press__bar {\n display: flex;\n gap: var(--wx-space-8);\n padding: var(--wx-space-12) var(--wx-space-12) var(--wx-space-8);\n}\n\n.wx-press__search {\n flex: 1 1 180px;\n min-width: 0;\n}\n\n.wx-press__new {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n width: calc(100% - 2 * var(--wx-space-8));\n margin: 0 var(--wx-space-8) var(--wx-space-4);\n padding: var(--wx-space-8);\n border: 1px dashed var(--wx-border-default);\n border-radius: var(--wx-radius-sm);\n background: none;\n font: inherit;\n color: var(--wx-color-primary);\n text-align: start;\n cursor: pointer;\n}\n\n.wx-press__new:hover,\n.wx-press__new.is-current {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__loading {\n padding: var(--wx-space-12);\n}\n\n.wx-press__list {\n padding-inline: var(--wx-space-8);\n}\n\n.wx-press__list.is-loading {\n opacity: 0.6;\n}\n\n.wx-press__list :deep(.wx-sortable-list__row) {\n gap: var(--wx-space-4);\n padding-inline: var(--wx-space-4);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-press__list :deep(.wx-sortable-list__row:hover),\n.wx-press__list :deep(.wx-sortable-list__row:has(.is-current)) {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__note {\n margin: var(--wx-space-8);\n}\n\n.wx-outlet-row {\n display: flex;\n align-items: flex-start;\n gap: var(--wx-space-10);\n /* The row is the target: a short name should not leave half the line dead — and a button,\n unlike a link, is only as wide as what is in it until it is told otherwise. */\n flex: 1 1 auto;\n width: 100%;\n min-width: 0;\n padding-block: var(--wx-space-8);\n padding-inline: 0;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n}\n\nbutton.wx-outlet-row {\n cursor: pointer;\n}\n\n.wx-outlet-row__initials {\n flex: none;\n}\n\n/* Twice as wide as the initials beside it, and the logo drawn whole inside: cropped, it loses\n its letters. */\n.wx-outlet-row__logo {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 64px;\n height: 32px;\n overflow: hidden;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n}\n\n.wx-outlet-row__logo img {\n display: block;\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}\n\n.wx-outlet-row__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--wx-space-2);\n min-width: 0;\n}\n\n.wx-outlet-row__head {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n}\n\n.wx-outlet-row__title {\n min-width: 0;\n overflow: hidden;\n font-weight: var(--wx-font-weight-medium);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-outlet-row.is-current .wx-outlet-row__title {\n color: var(--wx-color-primary);\n}\n\n.wx-outlet-row__featured {\n flex: none;\n color: var(--wx-color-warning);\n}\n\n.wx-outlet-row__facts {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-outlet-row__locales {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-xs);\n text-transform: uppercase;\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport PressPage from './PressPage.vue'\n\nexport interface PressOptions {\n /** Where the outlets live inside the panel. */\n path?: string\n}\n\n/**\n * The press as a section of the panel (§4.9): one module and one entry, without a group of its\n * own — the reviews have one because they have two entries, and a group around a single item is\n * a heading over itself. A section whose server half is not installed never appears — the entry\n * is built from the manifest.\n */\nexport function press(options: PressOptions = {}): AdminModule {\n const path = options.path ?? '/press'\n\n return {\n id: 'press',\n path,\n // One route: the open outlet is in the address (`?outlet=`), beside the list it was opened\n // from, so a link to it is a link to both.\n routes: [{ path, name: 'webx.press', component: PressPage, props: { base: path } }],\n }\n}\n"],"names":["PRESS_API","createPressApi","admin","base","data","body","query","search","id","values","ids","reorderItems","pressMessages","seeded","usePressMessages","i18n","useAdmin","props","__props","emit","__emit","context","api","locales","useLocales","t","useTranslate","message","useErrorText","outlet","ref","prefix","snapshot","loading","saving","errors","canManage","computed","current","dirty","state","title","written","fallback","localizedValue","provideRecordAddress","pathIn","url","locale","segments","one","codes","own","take","detail","blank","load","error","toast","save","made","remove","record","confirm","leaveGuard","event","onKeydown","onMounted","onBeforeUnmount","mayLeave","onBeforeRouteUpdate","to","from","onBeforeRouteLeave","actions","list","_createElementBlock","_createBlock","_unref","WxSkeleton","_Fragment","_createVNode","WxScreenHead","WxBadge","WxScreen","$event","WxActionBar","WxSaveState","WxButton","route","useRoute","router","useRouter","rows","module","view","value","inBin","q","typed","selected","raw","open","close","views","order","useItemOrder","sortable","emptyText","answer","watch","pause","choose","created","reorder","row","actionsFor","restore","removed","unseen","WxListScreen","WxCard","WxListDetail","_createElementVNode","_hoisted_1","WxInput","WxIcon","_normalizeClass","_createTextVNode","WxEmpty","WxSortableList","item","_withCtx","_openBlock","_resolveDynamicComponent","_hoisted_2","WxAvatar","_hoisted_4","_hoisted_5","_hoisted_6","_toDisplayString","WxTooltip","_hoisted_7","_hoisted_8","_hoisted_9","WxRowMenu","WxAlert","inline","back","OutletPane","press","options","path","PressPage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/api.ts","../src/messages.ts","../src/i18n.ts","../src/OutletPane.vue","../src/PressPage.vue","../src/module.ts"],"sourcesContent":["import { reorderItems, type AdminContext } from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport type { OutletDetail, OutletQuery, OutletRow, OutletsList } from './types'\n\nexport interface PressApi {\n list(query?: OutletQuery): Promise<OutletsList>\n get(id: number): Promise<OutletDetail>\n /**\n * A new outlet out of the values of its form, articles included, in one request: the form is\n * filled in first and the record made on the first save, so nobody leaves an empty outlet behind.\n */\n create(values: ScreenModel): Promise<OutletDetail>\n /** Refused with a 422 under the name of the field — `articles.<n>.<field>` for an article. */\n save(id: number, values: ScreenModel): Promise<OutletDetail>\n remove(id: number): Promise<void>\n restore(id: number): Promise<OutletRow>\n /** The order of the whole list. The order of the articles is the order of the form's rows. */\n reorder(ids: number[]): Promise<void>\n}\n\n/** The path of the section under the panel's API, and of its order. */\nexport const PRESS_API = 'press'\n\n/** Everything under `/press`, below the panel's API path (§4.10). */\nexport function createPressApi(admin: AdminContext): PressApi {\n const base = `${admin.apiPath}/${PRESS_API}`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n list: (query = {}) => {\n const search = new URLSearchParams()\n\n if (query.search) search.set('search', query.search)\n if (query.trashed) search.set('trashed', '1')\n\n return admin.http.get<OutletsList>(search.size > 0 ? `${base}?${search}` : base)\n },\n get: (id) => admin.http.get<{ data: OutletDetail }>(`${base}/${id}`).then(data),\n create: (values) => admin.http.post<{ data: OutletDetail }>(base, { values }).then(data),\n save: (id, values) =>\n admin.http.put<{ data: OutletDetail }>(`${base}/${id}`, { values }).then(data),\n remove: (id) => admin.http.delete<void>(`${base}/${id}`).then(() => undefined),\n // The resource of the list line, and not the form's answer: a restored outlet stays closed.\n restore: (id) => admin.http.post<{ data: OutletRow }>(`${base}/${id}/restore`, {}).then(data),\n reorder: (ids) => reorderItems(admin, PRESS_API, ids, null),\n }\n}\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * The English this package speaks on its own, before the server's dictionary arrives, or without\n * one. The server ships the same lines in ten languages under `webx-press::` and overrides these;\n * `messages.test.ts` keeps the two sets of keys equal.\n *\n * `screen` is what the described screen `press.outlet-form` is labelled with: the server sends the\n * labels as `trans::webx-press::screen.*`, and these are what they say when no dictionary came.\n *\n * `kinds` are the four of the default config (decision 3). A kind a site adds brings its word in\n * `lang/vendor/webx-press` — the server labels the options of the field itself, so a kind missing\n * here is only missing from a panel that runs with no server at all.\n */\nexport const pressMessages: Record<string, Messages> = {\n module: {\n press: 'Press',\n },\n outlet: {\n new: 'New outlet',\n untitled: 'Untitled',\n search: 'Search the outlets',\n empty: 'No outlets yet.',\n 'empty-help':\n 'An outlet is a magazine, a paper or a site that wrote about you; its articles are added on its form.',\n 'empty-search': 'Nothing matches that.',\n 'empty-bin': 'The bin is empty.',\n all: 'All',\n trashed: 'Bin',\n 'not-published': 'Not published',\n 'visible-nowhere': 'Published, but seen in no language: none of its articles has a title yet.',\n 'articles-count': 'Articles: :count',\n 'no-articles': 'No articles yet',\n featured: 'In the logo strip',\n 'seen-in': 'Seen in: :locales',\n choose: 'Choose an outlet',\n 'choose-help': 'Or add a new one: the first line of the list.',\n save: 'Save',\n saved: 'The outlet is saved.',\n 'save-failed': 'The outlet was not saved.',\n cancel: 'Cancel',\n delete: 'Delete',\n 'delete-title': 'Delete “:name”?',\n 'delete-text':\n 'It goes to the bin with its articles and leaves the site and every block. Restored, it comes back with them.',\n deleted: 'The outlet is in the bin.',\n restore: 'Restore',\n restored: 'The outlet is back.',\n 'reorder-failed': 'The new order was not saved.',\n 'leave-title': 'Leave without saving?',\n 'leave-text': 'What you wrote in this outlet is not on the server.',\n leave: 'Leave',\n back: 'Back to the list',\n open: 'Open on the site',\n 'address-moving': 'The page of the outlet moves to the new address when you save.',\n },\n screen: {\n general: 'General',\n outlet: 'Outlet',\n logo: 'Logo',\n 'logo-help': 'Without a logo the site prints the name.',\n title: 'Name',\n 'title-help': 'Where it is not written in a language, the name from another one is shown.',\n slug: 'Address',\n 'slug-help':\n \"The outlet's page. A language with articles and no address of its own takes this one.\",\n 'website-url': 'Website',\n 'website-url-help': 'An address that starts with http:// or https://.',\n summary: 'About the outlet',\n 'summary-help':\n 'A few words under the name on its page, and its description for search engines.',\n settings: 'Settings',\n published: 'Published',\n 'published-help': 'On the site at once — in the languages it has articles in.',\n featured: 'In the strip of logos',\n 'featured-help': 'Shown by the logos block set to the marked outlets only.',\n articles: 'Articles',\n 'articles-help': \"In the order they are shown on the outlet's page. Saved with the outlet.\",\n 'article-label': '#:number · :title',\n 'article-untitled': 'Untitled',\n 'article-title': 'Title',\n 'article-title-help': 'An article is shown in a language only when it has a title in it.',\n excerpt: 'Excerpt',\n kind: 'Kind',\n 'kind-none': 'No kind',\n 'published-on': 'Date',\n 'date-precision': 'Known to',\n 'date-precision-help': 'How much of the date is printed: the day, the month, or the year only.',\n precision: {\n day: 'Day',\n month: 'Month',\n year: 'Year',\n },\n url: 'Link',\n 'url-help': \"The article on the outlet's site. Without it the title leads to the PDF.\",\n file: 'PDF',\n 'file-help': 'A PDF from the library — a scan of the page, say.',\n hidden: 'Do not show',\n 'hidden-help': 'Kept here, and shown nowhere on the site.',\n seo: 'SEO',\n 'seo-empty': 'The SEO card appears here when module-seo is installed.',\n },\n kinds: {\n mention: 'Mention',\n interview: 'Interview',\n expert_comment: 'Expert comment',\n authored: 'Authored article',\n },\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { pressMessages } 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 usePressMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-press', pressMessages)\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, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { onBeforeRouteLeave, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'\nimport {\n confirm,\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxBadge,\n WxButton,\n WxSkeleton,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport {\n provideRecordAddress,\n useAdmin,\n useErrorText,\n useTranslate,\n WxSaveState,\n WxScreen,\n WxScreenHead,\n type ScreenAction,\n useBodyKeys,\n} from '@webx-ui/module-admin'\nimport type { ScreenModel } from '@webx-ui/schema'\nimport { createPressApi } from './api'\nimport { usePressMessages } from './i18n'\nimport type { OutletDetail, OutletSummary } from './types'\n\n/**\n * One outlet with its articles: the pane beside the list, and the whole screen on a phone.\n *\n * The described screen `press.outlet-form` — General · Articles · SEO — with a head over it and\n * one button under it. One explicit save for the whole outlet, articles included (decision 15):\n * the articles are rows of its form, and the server writes them into their own table in the same\n * transaction (§4.6). No autosave: there is no draft (decision 9), so what is saved is on the site\n * at once — the button is the moment somebody decides it is ready.\n *\n * A new outlet is a form first and a record on the first save: an empty row made by pressing\n * \"New outlet\" and walking away would be an outlet with nothing in it on the list for ever.\n *\n * The way back is drawn here and not by the pane around it: the drawer a narrow screen opens\n * this in has no close of its own on purpose (CLAUDE.md §4).\n */\ndefineOptions({ name: 'WxOutletPane' })\n\nconst props = withDefaults(\n defineProps<{\n /** `null` — an outlet not written yet. */\n id: number | null\n /** False in the drawer a narrow screen opens this in. */\n inline?: boolean\n }>(),\n { inline: true },\n)\n\nconst emit = defineEmits<{\n back: []\n /** The outlet was written; the list redraws its row. */\n saved: [outlet: OutletSummary]\n /** The first save of a new outlet: it is a record now, with an id to be opened by. */\n created: [outlet: OutletSummary]\n removed: [id: number]\n}>()\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst locales = useLocales()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst outlet = ref<OutletSummary | null>(null)\nconst values = ref<ScreenModel>({})\nconst prefix = ref<string | null>(null)\nconst snapshot = ref('')\n\nconst loading = ref(props.id !== null)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst current = computed(() => JSON.stringify(values.value))\nconst dirty = computed(() => snapshot.value !== '' && current.value !== snapshot.value)\n\nconst state = computed<'saving' | 'unsaved' | 'saved'>(() => {\n if (saving.value) return 'saving'\n\n return dirty.value || props.id === null ? 'unsaved' : 'saved'\n})\n\n/** The name follows the field rather than the answer: an edit shows at the top as it is typed. */\nconst title = computed(() => {\n const written = values.value.title as LocalizedValue | string | null | undefined\n const fallback = props.id === null ? t('outlet.new') : (outlet.value?.title ?? '')\n\n return localizedValue(written, locales.active.value, fallback) || t('outlet.untitled')\n})\n\n/*\n * The address field is the panel's shared one (`wx-slug`), and this is what it prints. The form\n * answers with the page's address (`url`) in the panel's language — `/en/press/…` on a site whose\n * default is another — while the field compares it with the slug in the language being edited.\n * So the address is handed over without the host, the slash and the language, and only in its own\n * language: in any other the field has nothing to compare with, and says nothing rather than\n * warning that a page moves which does not.\n */\nprovideRecordAddress({\n values,\n prefix,\n path: computed(() => pathIn(outlet.value?.url ?? null, locales.active.value)),\n moving: () => t('outlet.address-moving'),\n})\n\nfunction pathIn(url: string | null, locale: string): string | null | undefined {\n if (url === null) return null\n\n let segments: string[]\n\n try {\n segments = new URL(url, 'http://site.invalid').pathname.split('/').filter((one) => one !== '')\n } catch {\n return null\n }\n\n const codes = locales.list.value.map((one) => one.code)\n const own =\n codes.includes(segments[0] ?? '') && segments[0] !== codes[0] ? segments.shift()! : codes[0]\n\n return own === undefined || own === locale ? segments.join('/') : undefined\n}\n\nfunction take(detail: OutletDetail): void {\n outlet.value = detail.outlet\n values.value = detail.values\n prefix.value = detail.prefix\n snapshot.value = JSON.stringify(detail.values)\n}\n\nif (props.id === null) {\n // What the server would answer for an outlet with nothing in it yet.\n const blank: ScreenModel = {\n title: {},\n slug: {},\n summary: {},\n published: false,\n featured: false,\n articles: [],\n }\n\n values.value = blank\n snapshot.value = JSON.stringify(blank)\n}\n\nasync function load(): Promise<void> {\n if (props.id === null) return\n\n loading.value = true\n\n try {\n take(await api.get(props.id))\n } catch (error) {\n toast.danger(message(error))\n emit('back')\n } finally {\n loading.value = false\n }\n}\n\nasync function save(): Promise<void> {\n if (saving.value || !canManage.value) return\n\n saving.value = true\n errors.value = {}\n\n try {\n const made = props.id === null\n const detail = made\n ? await api.create(values.value)\n : await api.save(props.id as number, values.value)\n\n take(detail)\n toast.success(t('outlet.saved'))\n if (made) emit('created', detail.outlet)\n else emit('saved', detail.outlet)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n // `articles.2.url` as the server names it: the repeater finds its row and field by that.\n errors.value = body.errors\n toast.danger(t('outlet.save-failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n\nasync function remove(): Promise<void> {\n const record = outlet.value\n\n if (!record) return\n\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: record.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(record.id)\n toast.success(t('outlet.deleted'))\n // Nothing left to protect: the record is in the bin.\n snapshot.value = current.value\n emit('removed', record.id)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/* The browser's own guard: it cannot wait for a request, so all it can do is ask. */\nfunction leaveGuard(event: BeforeUnloadEvent): void {\n if (dirty.value) event.preventDefault()\n}\n\n/* Ctrl+S saves, as it does everywhere else that has a save button. */\nfunction onKeydown(event: KeyboardEvent): void {\n if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {\n event.preventDefault()\n if (dirty.value || props.id === null) void save()\n }\n}\n\n// And from the page around the form, where a click on empty space leaves the focus.\nuseBodyKeys(onKeydown)\n\nonMounted(() => {\n void load()\n window.addEventListener('beforeunload', leaveGuard)\n})\n\nonBeforeUnmount(() => {\n window.removeEventListener('beforeunload', leaveGuard)\n})\n\nasync function mayLeave(): Promise<boolean> {\n if (!dirty.value || !canManage.value) return true\n\n return await confirm({\n title: t('outlet.leave-title'),\n message: t('outlet.leave-text'),\n confirmText: t('outlet.leave'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n}\n\n/*\n * The list and this form are one route, so another outlet is an update of it rather than a way\n * out — and so is every keystroke in the search above the list, which goes into the address too.\n * Only a change of the outlet asks.\n */\nonBeforeRouteUpdate(async (to: RouteLocationNormalized, from: RouteLocationNormalized) =>\n to.query.outlet === from.query.outlet ? true : await mayLeave(),\n)\n\nonBeforeRouteLeave(mayLeave)\n\n/* What leads away from the outlet. On a narrow pane the head folds them into the ···. */\nconst actions = computed<ScreenAction[]>(() => {\n const record = outlet.value\n const list: ScreenAction[] = []\n\n // Only a page that answers: an unpublished outlet, or one seen nowhere, has no address.\n if (record?.url) {\n const url = record.url\n\n list.push({\n key: 'open',\n label: t('outlet.open'),\n icon: 'external-link',\n run: () => void window.open(url, '_blank', 'noopener'),\n })\n }\n\n if (canManage.value && record !== null) {\n list.push({\n key: 'delete',\n label: t('outlet.delete'),\n icon: 'trash',\n danger: true,\n menu: true,\n run: () => void remove(),\n })\n }\n\n return list\n})\n</script>\n\n<template>\n <div class=\"wx-outlet\" @keydown=\"onKeydown\">\n <wx-skeleton v-if=\"loading\" class=\"wx-outlet__ghost\" title :rows=\"6\" />\n\n <template v-else>\n <wx-screen-head\n :level=\"3\"\n :back=\"!props.inline\"\n :back-label=\"t('outlet.back')\"\n :title=\"title\"\n :actions=\"actions\"\n :collapse-below=\"480\"\n @back=\"emit('back')\"\n >\n <template v-if=\"outlet && !outlet.published\" #title-after>\n <wx-badge dot>{{ t('outlet.not-published') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <wx-screen\n v-model=\"values\"\n name=\"press.outlet-form\"\n :errors=\"errors\"\n :disabled=\"!canManage || saving\"\n />\n\n <wx-action-bar v-if=\"canManage\">\n <template #state>\n <wx-save-state :state=\"state\" />\n </template>\n\n <wx-button\n type=\"primary\"\n :loading=\"saving\"\n :disabled=\"!dirty && props.id !== null\"\n @click=\"save\"\n >\n {{ t('outlet.save') }}\n </wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-outlet {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n min-width: 0;\n padding: var(--wx-gap, var(--wx-space-16));\n container-type: inline-size;\n}\n\n.wx-outlet__ghost {\n max-width: 480px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n confirm,\n toast,\n WxAlert,\n WxAvatar,\n WxBadge,\n WxCard,\n WxEmpty,\n WxIcon,\n WxInput,\n WxListDetail,\n WxSkeleton,\n WxSortableList,\n WxTooltip,\n type TabItem,\n type TabValue,\n} from '@webx-ui/core'\nimport {\n useAdmin,\n useErrorText,\n useItemOrder,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport { createPressApi, PRESS_API } from './api'\nimport { usePressMessages } from './i18n'\nimport OutletPane from './OutletPane.vue'\nimport type { OutletRow, OutletSummary } from './types'\n\n/**\n * The outlets: all of them on the left, the one being written on the right (§4.9, decision 15).\n *\n * The shape of the reviews, for the same reason: an outlet is typed in from a clipping in a\n * minute, and the next one is usually under it. On a phone the form is the whole screen, in the\n * drawer `WxListDetail` raises, and draws its own way back.\n *\n * One order, the general one (decision 6): a drag moves it; a search or the bin shows a\n * selection, so the grips go away and the line under the list says why. The order of the\n * articles is not here — it is the order of the rows in the outlet's form.\n *\n * Everything the screen is looking at lives in the address — the view, the search and the open\n * outlet — so a link to an outlet is a link to it in its list.\n */\nwithDefaults(defineProps<{ base?: string }>(), { base: '/press' })\n\nconst context = useAdmin()\nconst api = createPressApi(context)\nconst route = useRoute()\nconst router = useRouter()\nusePressMessages()\n\nconst t = useTranslate('webx-press')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst rows = ref<OutletRow[]>([])\nconst loading = ref(true)\n\nconst canManage = computed(() => context.can('press.manage'))\n\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'press')?.title ??\n t('module.press'),\n)\n\nconst view = computed<TabValue>({\n get: () => (route.query.view === 'trashed' ? 'trashed' : ''),\n set: (value) => {\n const query: LocationQueryRaw = {\n ...route.query,\n view: value === '' ? undefined : String(value),\n }\n\n // An outlet from the list is not an outlet from the bin: the form closes with the view.\n delete query.outlet\n void router.replace({ query })\n },\n})\n\nconst inBin = computed(() => view.value === 'trashed')\n\nconst q = computed(() => (typeof route.query.q === 'string' ? route.query.q : ''))\n\n/* What is typed, ahead of the address: the address follows it after a pause. */\nconst typed = ref(q.value)\n\n/** The open outlet: a number, `'new'` for one not yet saved, or nothing. */\nconst selected = computed<number | 'new' | null>(() => {\n const raw = route.query.outlet\n\n if (raw === 'new') return canManage.value ? 'new' : null\n\n return typeof raw === 'string' && raw !== '' ? Number(raw) || null : null\n})\n\n/*\n * Open is \"an outlet is chosen\", and closing is un-choosing it — so the address, the list's\n * highlight and the drawer on a phone can never disagree about it.\n */\nconst open = computed({\n get: () => selected.value !== null,\n set: (value) => {\n if (!value) close()\n },\n})\n\nconst views = computed<TabItem[]>(() => [\n { value: '', label: t('outlet.all') },\n { value: 'trashed', label: t('outlet.trashed'), icon: 'trash' },\n])\n\nconst order = useItemOrder(PRESS_API, () => ({ q: q.value, filtered: inBin.value }))\n\n/* Dragged only by somebody who may write, and only in a list that is the whole order. */\nconst sortable = computed(() => canManage.value && order.sortable.value)\n\nconst emptyText = computed(() => {\n if (q.value !== '') return t('outlet.empty-search')\n\n return inBin.value ? t('outlet.empty-bin') : t('outlet.empty')\n})\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const answer = await api.list({ search: q.value, trashed: inBin.value })\n\n rows.value = answer.data\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nwatch(\n () => [view.value, q.value] as const,\n () => void load(),\n)\nvoid load()\n\nlet pause: ReturnType<typeof setTimeout> | undefined\n\nwatch(typed, (value) => {\n clearTimeout(pause)\n pause = setTimeout(() => {\n void router.replace({ query: { ...route.query, q: value.trim() === '' ? undefined : value } })\n }, 300)\n})\n\nonBeforeUnmount(() => clearTimeout(pause))\n\n/* `replace` and not `push`: going through twenty outlets is not twenty steps of history. */\nfunction choose(id: number | 'new'): void {\n void router.replace({ query: { ...route.query, outlet: String(id) } })\n}\n\nfunction close(): void {\n const query = { ...route.query }\n\n delete query.outlet\n void router.replace({ query })\n}\n\n/** A new outlet was saved: it is a record now, and the list has it. */\nfunction created(outlet: OutletSummary): void {\n choose(outlet.id)\n void load()\n}\n\n/**\n * The order on screen, written as a whole. The list is already in its new order\n * (`WxSortableList` reorders the model before it says so), so a failure has to put it back\n * rather than leave the screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await order.move(rows.value.map((row) => row.id))\n } catch (error) {\n toast.danger(message(error, t('outlet.reorder-failed')))\n await load()\n }\n}\n\nfunction actionsFor(row: OutletRow): RowAction[] {\n if (!canManage.value) return []\n\n if (inBin.value) {\n return [\n {\n key: 'restore',\n icon: 'refresh',\n label: t('outlet.restore'),\n run: () => void restore(row),\n },\n ]\n }\n\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('outlet.delete'),\n danger: true,\n run: () => void remove(row),\n },\n ]\n}\n\nasync function remove(row: OutletRow): Promise<void> {\n const agreed = await confirm({\n title: t('outlet.delete-title', { name: row.title }),\n message: t('outlet.delete-text'),\n confirmText: t('outlet.delete'),\n cancelText: t('outlet.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(row.id)\n toast.success(t('outlet.deleted'))\n removed(row.id)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/** Gone from the list; and if it was the open one, the form goes with it. */\nfunction removed(id: number): void {\n if (selected.value === id) close()\n\n void load()\n}\n\nasync function restore(row: OutletRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('outlet.restored'))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n await load()\n }\n}\n\n/* Published and yet shown in no language: the one state the list has to point out. */\nfunction unseen(row: OutletRow): boolean {\n return row.published && row.locales.length === 0\n}\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"view\" :title=\"title\" :views=\"views\" :card=\"false\">\n <wx-card class=\"wx-press\" padding=\"none\">\n <wx-list-detail\n v-model:open=\"open\"\n class=\"wx-press__panes\"\n :list-width=\"360\"\n :detail-min=\"520\"\n :detail-label=\"t('module.press')\"\n >\n <template #list>\n <div class=\"wx-press__bar\">\n <wx-input\n v-model=\"typed\"\n class=\"wx-press__search\"\n size=\"sm\"\n clearable\n :placeholder=\"t('outlet.search')\"\n :aria-label=\"t('outlet.search')\"\n >\n <template #prefix><wx-icon name=\"search\" size=\"sm\" /></template>\n </wx-input>\n </div>\n\n <!--\n A line of the list rather than a button in the head: the new outlet is written where\n it will stand, beside the ones it joins — and the form opens the way any other does.\n -->\n <button\n v-if=\"canManage && !inBin\"\n type=\"button\"\n class=\"wx-press__new\"\n :class=\"{ 'is-current': selected === 'new' }\"\n @click=\"choose('new')\"\n >\n <wx-icon name=\"plus\" size=\"sm\" />\n {{ t('outlet.new') }}\n </button>\n\n <wx-skeleton v-if=\"loading && rows.length === 0\" class=\"wx-press__loading\" :rows=\"5\" />\n\n <wx-empty\n v-else-if=\"rows.length === 0\"\n :title=\"emptyText\"\n :description=\"!inBin && q === '' ? t('outlet.empty-help') : undefined\"\n />\n\n <template v-else>\n <!-- A grip and not the whole row: the row opens an outlet, and a row that both opens\n and drags is one where one of the two happens by accident. -->\n <wx-sortable-list\n v-model=\"rows\"\n class=\"wx-press__list\"\n :class=\"{ 'is-loading': loading }\"\n plain\n item-key=\"id\"\n :handle=\"sortable ? 'grip' : 'row'\"\n :item-label=\"(item: OutletRow) => item.title\"\n :disabled=\"!sortable\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <component\n :is=\"inBin ? 'div' : 'button'\"\n :type=\"inBin ? undefined : 'button'\"\n class=\"wx-outlet-row\"\n :class=\"{ 'is-current': selected === item.id }\"\n @click=\"inBin ? undefined : choose(item.id)\"\n >\n <!-- A logo is wide as often as not: in a square it shrinks to a sliver, so it gets a\n box of its own shape, and only an outlet without one gets initials. -->\n <span v-if=\"item.logo?.thumb\" class=\"wx-outlet-row__logo\">\n <img :src=\"item.logo.thumb\" alt=\"\" />\n </span>\n <wx-avatar\n v-else\n class=\"wx-outlet-row__initials\"\n shape=\"square\"\n :name=\"item.title\"\n size=\"md\"\n />\n\n <span class=\"wx-outlet-row__body\">\n <span class=\"wx-outlet-row__head\">\n <span class=\"wx-outlet-row__title\">{{ item.title }}</span>\n <wx-tooltip v-if=\"item.featured\" :content=\"t('outlet.featured')\">\n <wx-icon\n class=\"wx-outlet-row__featured\"\n name=\"star\"\n size=\"sm\"\n :aria-label=\"t('outlet.featured')\"\n />\n </wx-tooltip>\n </span>\n\n <span class=\"wx-outlet-row__facts\">\n <wx-badge v-if=\"!item.published\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n <wx-tooltip v-else-if=\"unseen(item)\" :content=\"t('outlet.visible-nowhere')\">\n <wx-badge type=\"warning\" dot size=\"sm\">\n {{ t('outlet.not-published') }}\n </wx-badge>\n </wx-tooltip>\n\n <span class=\"wx-outlet-row__count\">\n {{\n item.articles_count > 0\n ? t('outlet.articles-count', { count: item.articles_count })\n : t('outlet.no-articles')\n }}\n </span>\n\n <span\n v-if=\"item.locales.length > 0\"\n class=\"wx-outlet-row__locales\"\n :aria-label=\"t('outlet.seen-in', { locales: item.locales.join(', ') })\"\n >\n {{ item.locales.join(' · ') }}\n </span>\n </span>\n </span>\n </component>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu :actions=\"actionsFor(item)\" :label=\"item.title\" />\n </template>\n </wx-sortable-list>\n\n <!-- What a drag does here, or why there is nothing to drag. -->\n <wx-alert\n v-if=\"canManage && !inBin\"\n class=\"wx-press__note\"\n type=\"info\"\n :description=\"order.hint.value\"\n />\n </template>\n </template>\n\n <template #detail=\"{ inline, back }\">\n <outlet-pane\n v-if=\"selected !== null\"\n :id=\"selected === 'new' ? null : selected\"\n :key=\"String(selected)\"\n :inline=\"inline\"\n @back=\"back\"\n @saved=\"load\"\n @created=\"created\"\n @removed=\"removed\"\n />\n </template>\n\n <template #empty>\n <wx-empty\n :title=\"t('outlet.choose')\"\n :description=\"canManage ? t('outlet.choose-help') : undefined\"\n />\n </template>\n </wx-list-detail>\n </wx-card>\n </wx-list-screen>\n</template>\n\n<style>\n/*\n * What scrolls here is the page, the way it does on every other list of the panel. The card's\n * corners are the screen's, and the panes paint their own background to the edge: `clip`, not\n * `hidden`, which would make this a scroll container and pin everything sticky inside to a box\n * that never moves (CLAUDE.md §4).\n */\n.wx-press > .wx-card__body {\n display: flex;\n flex-direction: column;\n overflow: clip;\n border-radius: inherit;\n}\n</style>\n\n<style scoped>\n.wx-press__bar {\n display: flex;\n gap: var(--wx-space-8);\n padding: var(--wx-space-12) var(--wx-space-12) var(--wx-space-8);\n}\n\n.wx-press__search {\n flex: 1 1 180px;\n min-width: 0;\n}\n\n.wx-press__new {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n width: calc(100% - 2 * var(--wx-space-8));\n margin: 0 var(--wx-space-8) var(--wx-space-4);\n padding: var(--wx-space-8);\n border: 1px dashed var(--wx-border-default);\n border-radius: var(--wx-radius-sm);\n background: none;\n font: inherit;\n color: var(--wx-color-primary);\n text-align: start;\n cursor: pointer;\n}\n\n.wx-press__new:hover,\n.wx-press__new.is-current {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__loading {\n padding: var(--wx-space-12);\n}\n\n.wx-press__list {\n padding-inline: var(--wx-space-8);\n}\n\n.wx-press__list.is-loading {\n opacity: 0.6;\n}\n\n.wx-press__list :deep(.wx-sortable-list__row) {\n gap: var(--wx-space-4);\n padding-inline: var(--wx-space-4);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-press__list :deep(.wx-sortable-list__row:hover),\n.wx-press__list :deep(.wx-sortable-list__row:has(.is-current)) {\n background: var(--wx-bg-subtle);\n}\n\n.wx-press__note {\n margin: var(--wx-space-8);\n}\n\n.wx-outlet-row {\n display: flex;\n align-items: flex-start;\n gap: var(--wx-space-10);\n /* The row is the target: a short name should not leave half the line dead — and a button,\n unlike a link, is only as wide as what is in it until it is told otherwise. */\n flex: 1 1 auto;\n width: 100%;\n min-width: 0;\n padding-block: var(--wx-space-8);\n padding-inline: 0;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n}\n\nbutton.wx-outlet-row {\n cursor: pointer;\n}\n\n.wx-outlet-row__initials {\n flex: none;\n}\n\n/* Twice as wide as the initials beside it, and the logo drawn whole inside: cropped, it loses\n its letters. */\n.wx-outlet-row__logo {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: 64px;\n height: 32px;\n overflow: hidden;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n}\n\n.wx-outlet-row__logo img {\n display: block;\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n}\n\n.wx-outlet-row__body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--wx-space-2);\n min-width: 0;\n}\n\n.wx-outlet-row__head {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n}\n\n.wx-outlet-row__title {\n min-width: 0;\n overflow: hidden;\n font-weight: var(--wx-font-weight-medium);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-outlet-row.is-current .wx-outlet-row__title {\n color: var(--wx-color-primary);\n}\n\n.wx-outlet-row__featured {\n flex: none;\n color: var(--wx-color-warning);\n}\n\n.wx-outlet-row__facts {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-4) var(--wx-space-8);\n min-width: 0;\n color: var(--wx-text-muted);\n font-size: var(--wx-font-size-sm);\n}\n\n.wx-outlet-row__locales {\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-xs);\n text-transform: uppercase;\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport PressPage from './PressPage.vue'\n\nexport interface PressOptions {\n /** Where the outlets live inside the panel. */\n path?: string\n}\n\n/**\n * The press as a section of the panel (§4.9): one module and one entry, without a group of its\n * own — the reviews have one because they have two entries, and a group around a single item is\n * a heading over itself. A section whose server half is not installed never appears — the entry\n * is built from the manifest.\n */\nexport function press(options: PressOptions = {}): AdminModule {\n const path = options.path ?? '/press'\n\n return {\n id: 'press',\n path,\n // One route: the open outlet is in the address (`?outlet=`), beside the list it was opened\n // from, so a link to it is a link to both.\n routes: [{ path, name: 'webx.press', component: PressPage, props: { base: path } }],\n }\n}\n"],"names":["PRESS_API","createPressApi","admin","base","data","body","query","search","id","values","ids","reorderItems","pressMessages","seeded","usePressMessages","i18n","useAdmin","props","__props","emit","__emit","context","api","locales","useLocales","t","useTranslate","message","useErrorText","outlet","ref","prefix","snapshot","loading","saving","errors","canManage","computed","current","dirty","state","title","written","fallback","localizedValue","provideRecordAddress","pathIn","url","locale","segments","one","codes","own","take","detail","blank","load","error","toast","save","made","remove","record","confirm","leaveGuard","event","onKeydown","useBodyKeys","onMounted","onBeforeUnmount","mayLeave","onBeforeRouteUpdate","to","from","onBeforeRouteLeave","actions","list","_createElementBlock","_createBlock","_unref","WxSkeleton","_Fragment","_createVNode","WxScreenHead","WxBadge","WxScreen","$event","WxActionBar","WxSaveState","WxButton","route","useRoute","router","useRouter","rows","module","view","value","inBin","q","typed","selected","raw","open","close","views","order","useItemOrder","sortable","emptyText","answer","watch","pause","choose","created","reorder","row","actionsFor","restore","removed","unseen","WxListScreen","WxCard","WxListDetail","_createElementVNode","_hoisted_1","WxInput","WxIcon","_normalizeClass","_createTextVNode","WxEmpty","WxSortableList","item","_withCtx","_openBlock","_resolveDynamicComponent","_hoisted_2","WxAvatar","_hoisted_4","_hoisted_5","_hoisted_6","_toDisplayString","WxTooltip","_hoisted_7","_hoisted_8","_hoisted_9","WxRowMenu","WxAlert","inline","back","OutletPane","press","options","path","PressPage"],"mappings":";;;;AAqBO,MAAMA,IAAY;AAGlB,SAASC,GAAeC,GAA+B;AAC5D,QAAMC,IAAO,GAAGD,EAAM,OAAO,IAAIF,CAAS,IACpCI,IAAO,CAAIC,MAAyBA,EAAK;AAE/C,SAAO;AAAA,IACL,MAAM,CAACC,IAAQ,OAAO;AACpB,YAAMC,IAAS,IAAI,gBAAA;AAEnB,aAAID,EAAM,UAAQC,EAAO,IAAI,UAAUD,EAAM,MAAM,GAC/CA,EAAM,WAASC,EAAO,IAAI,WAAW,GAAG,GAErCL,EAAM,KAAK,IAAiBK,EAAO,OAAO,IAAI,GAAGJ,CAAI,IAAII,CAAM,KAAKJ,CAAI;AAAA,IACjF;AAAA,IACA,KAAK,CAACK,MAAON,EAAM,KAAK,IAA4B,GAAGC,CAAI,IAAIK,CAAE,EAAE,EAAE,KAAKJ,CAAI;AAAA,IAC9E,QAAQ,CAACK,MAAWP,EAAM,KAAK,KAA6BC,GAAM,EAAE,QAAAM,EAAA,CAAQ,EAAE,KAAKL,CAAI;AAAA,IACvF,MAAM,CAACI,GAAIC,MACTP,EAAM,KAAK,IAA4B,GAAGC,CAAI,IAAIK,CAAE,IAAI,EAAE,QAAAC,GAAQ,EAAE,KAAKL,CAAI;AAAA,IAC/E,QAAQ,CAACI,MAAON,EAAM,KAAK,OAAa,GAAGC,CAAI,IAAIK,CAAE,EAAE,EAAE,KAAK,MAAA;AAAA,KAAe;AAAA;AAAA,IAE7E,SAAS,CAACA,MAAON,EAAM,KAAK,KAA0B,GAAGC,CAAI,IAAIK,CAAE,YAAY,CAAA,CAAE,EAAE,KAAKJ,CAAI;AAAA,IAC5F,SAAS,CAACM,MAAQC,GAAaT,GAAOF,GAAWU,GAAK,IAAI;AAAA,EAAA;AAE9D;AChCO,MAAME,KAA0C;AAAA,EACrD,QAAQ;AAAA,IACN,OAAO;AAAA,EAAA;AAAA,EAET,QAAQ;AAAA,IACN,KAAK;AAAA,IACL,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cACE;AAAA,IACF,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,KAAK;AAAA,IACL,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,UAAU;AAAA,IACV,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,eACE;AAAA,IACF,SAAS;AAAA,IACT,SAAS;AAAA,IACT,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,kBAAkB;AAAA,EAAA;AAAA,EAEpB,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,IACP,cAAc;AAAA,IACd,MAAM;AAAA,IACN,aACE;AAAA,IACF,eAAe;AAAA,IACf,oBAAoB;AAAA,IACpB,SAAS;AAAA,IACT,gBACE;AAAA,IACF,UAAU;AAAA,IACV,WAAW;AAAA,IACX,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,IACtB,SAAS;AAAA,IACT,MAAM;AAAA,IACN,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,IAClB,uBAAuB;AAAA,IACvB,WAAW;AAAA,MACT,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,IAAA;AAAA,IAER,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,KAAK;AAAA,IACL,aAAa;AAAA,EAAA;AAAA,EAEf,OAAO;AAAA,IACL,SAAS;AAAA,IACT,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,UAAU;AAAA,EAAA;AAEd,GCzGMC,yBAAa,QAAA;AAGZ,SAASC,KAAyB;AACvC,MAAI;AACF,UAAM,EAAE,MAAAC,EAAA,IAASC,GAAA;AAEjB,IAAKH,GAAO,IAAIE,CAAI,MAClBA,EAAK,SAAS,cAAcH,EAAa,GACzCC,GAAO,IAAIE,CAAI;AAAA,EAEnB,QAAQ;AAAA,EAER;AACF;;;;;;;;;;AC8BA,UAAME,IAAQC,GAURC,IAAOC,GASPC,IAAUL,GAAA,GACVM,IAAMrB,GAAeoB,CAAO,GAC5BE,IAAUC,GAAA;AAChB,IAAAV,GAAA;AAEA,UAAMW,IAAIC,GAAa,YAAY,GAE7BC,IAAUC,GAAA,GAEVC,IAASC,EAA0B,IAAI,GACvCrB,IAASqB,EAAiB,EAAE,GAC5BC,IAASD,EAAmB,IAAI,GAChCE,IAAWF,EAAI,EAAE,GAEjBG,IAAUH,EAAIb,EAAM,OAAO,IAAI,GAC/BiB,IAASJ,EAAI,EAAK,GAClBK,IAASL,EAA8B,EAAE,GAEzCM,IAAYC,EAAS,MAAMhB,EAAQ,IAAI,cAAc,CAAC,GAEtDiB,IAAUD,EAAS,MAAM,KAAK,UAAU5B,EAAO,KAAK,CAAC,GACrD8B,IAAQF,EAAS,MAAML,EAAS,UAAU,MAAMM,EAAQ,UAAUN,EAAS,KAAK,GAEhFQ,IAAQH,EAAyC,MACjDH,EAAO,QAAc,WAElBK,EAAM,SAAStB,EAAM,OAAO,OAAO,YAAY,OACvD,GAGKwB,IAAQJ,EAAS,MAAM;AAC3B,YAAMK,IAAUjC,EAAO,MAAM,OACvBkC,IAAW1B,EAAM,OAAO,OAAOQ,EAAE,YAAY,IAAKI,EAAO,OAAO,SAAS;AAE/E,aAAOe,GAAeF,GAASnB,EAAQ,OAAO,OAAOoB,CAAQ,KAAKlB,EAAE,iBAAiB;AAAA,IACvF,CAAC;AAUD,IAAAoB,GAAqB;AAAA,MACnB,QAAApC;AAAA,MACA,QAAAsB;AAAA,MACA,MAAMM,EAAS,MAAMS,EAAOjB,EAAO,OAAO,OAAO,MAAMN,EAAQ,OAAO,KAAK,CAAC;AAAA,MAC5E,QAAQ,MAAME,EAAE,uBAAuB;AAAA,IAAA,CACxC;AAED,aAASqB,EAAOC,GAAoBC,GAA2C;AAC7E,UAAID,MAAQ,KAAM,QAAO;AAEzB,UAAIE;AAEJ,UAAI;AACF,QAAAA,IAAW,IAAI,IAAIF,GAAK,qBAAqB,EAAE,SAAS,MAAM,GAAG,EAAE,OAAO,CAACG,MAAQA,MAAQ,EAAE;AAAA,MAC/F,QAAQ;AACN,eAAO;AAAA,MACT;AAEA,YAAMC,IAAQ5B,EAAQ,KAAK,MAAM,IAAI,CAAC2B,MAAQA,EAAI,IAAI,GAChDE,IACJD,EAAM,SAASF,EAAS,CAAC,KAAK,EAAE,KAAKA,EAAS,CAAC,MAAME,EAAM,CAAC,IAAIF,EAAS,MAAA,IAAWE,EAAM,CAAC;AAE7F,aAAOC,MAAQ,UAAaA,MAAQJ,IAASC,EAAS,KAAK,GAAG,IAAI;AAAA,IACpE;AAEA,aAASI,EAAKC,GAA4B;AACxC,MAAAzB,EAAO,QAAQyB,EAAO,QACtB7C,EAAO,QAAQ6C,EAAO,QACtBvB,EAAO,QAAQuB,EAAO,QACtBtB,EAAS,QAAQ,KAAK,UAAUsB,EAAO,MAAM;AAAA,IAC/C;AAEA,QAAIrC,EAAM,OAAO,MAAM;AAErB,YAAMsC,IAAqB;AAAA,QACzB,OAAO,CAAA;AAAA,QACP,MAAM,CAAA;AAAA,QACN,SAAS,CAAA;AAAA,QACT,WAAW;AAAA,QACX,UAAU;AAAA,QACV,UAAU,CAAA;AAAA,MAAC;AAGb,MAAA9C,EAAO,QAAQ8C,GACfvB,EAAS,QAAQ,KAAK,UAAUuB,CAAK;AAAA,IACvC;AAEA,mBAAeC,IAAsB;AACnC,UAAIvC,EAAM,OAAO,MAEjB;AAAA,QAAAgB,EAAQ,QAAQ;AAEhB,YAAI;AACF,UAAAoB,EAAK,MAAM/B,EAAI,IAAIL,EAAM,EAAE,CAAC;AAAA,QAC9B,SAASwC,GAAO;AACd,UAAAC,EAAM,OAAO/B,EAAQ8B,CAAK,CAAC,GAC3BtC,EAAK,MAAM;AAAA,QACb,UAAA;AACE,UAAAc,EAAQ,QAAQ;AAAA,QAClB;AAAA;AAAA,IACF;AAEA,mBAAe0B,IAAsB;AACnC,UAAI,EAAAzB,EAAO,SAAS,CAACE,EAAU,QAE/B;AAAA,QAAAF,EAAO,QAAQ,IACfC,EAAO,QAAQ,CAAA;AAEf,YAAI;AACF,gBAAMyB,IAAO3C,EAAM,OAAO,MACpBqC,IAASM,IACX,MAAMtC,EAAI,OAAOb,EAAO,KAAK,IAC7B,MAAMa,EAAI,KAAKL,EAAM,IAAcR,EAAO,KAAK;AAEnD,UAAA4C,EAAKC,CAAM,GACXI,EAAM,QAAQjC,EAAE,cAAc,CAAC,GACrBN,EAANyC,IAAW,YACL,SADgBN,EAAO,MAAM;AAAA,QAEzC,SAASG,GAAO;AACd,gBAAMpD,IAAQoD,EAA2D;AAEzE,UAAIpD,GAAM,UAER8B,EAAO,QAAQ9B,EAAK,QACpBqD,EAAM,OAAOjC,EAAE,oBAAoB,CAAC,KAEpCiC,EAAM,OAAO/B,EAAQ8B,CAAK,CAAC;AAAA,QAE/B,UAAA;AACE,UAAAvB,EAAO,QAAQ;AAAA,QACjB;AAAA;AAAA,IACF;AAEA,mBAAe2B,IAAwB;AACrC,YAAMC,IAASjC,EAAO;AAYtB,UAVI,GAACiC,KAUD,CARW,MAAMC,EAAQ;AAAA,QAC3B,OAAOtC,EAAE,uBAAuB,EAAE,MAAMqC,EAAO,OAAO;AAAA,QACtD,SAASrC,EAAE,oBAAoB;AAAA,QAC/B,aAAaA,EAAE,eAAe;AAAA,QAC9B,YAAYA,EAAE,eAAe;AAAA,QAC7B,MAAM;AAAA,MAAA,CACP;AAID,YAAI;AACF,gBAAMH,EAAI,OAAOwC,EAAO,EAAE,GAC1BJ,EAAM,QAAQjC,EAAE,gBAAgB,CAAC,GAEjCO,EAAS,QAAQM,EAAQ,OACzBnB,EAAK,WAAW2C,EAAO,EAAE;AAAA,QAC3B,SAASL,GAAO;AACd,UAAAC,EAAM,OAAO/B,EAAQ8B,CAAK,CAAC;AAAA,QAC7B;AAAA,IACF;AAGA,aAASO,EAAWC,GAAgC;AAClD,MAAI1B,EAAM,SAAO0B,EAAM,eAAA;AAAA,IACzB;AAGA,aAASC,EAAUD,GAA4B;AAC7C,OAAKA,EAAM,WAAWA,EAAM,YAAYA,EAAM,IAAI,YAAA,MAAkB,QAClEA,EAAM,eAAA,IACF1B,EAAM,SAAStB,EAAM,OAAO,SAAW0C,EAAA;AAAA,IAE/C;AAGA,IAAAQ,GAAYD,CAAS,GAErBE,GAAU,MAAM;AACd,MAAKZ,EAAA,GACL,OAAO,iBAAiB,gBAAgBQ,CAAU;AAAA,IACpD,CAAC,GAEDK,GAAgB,MAAM;AACpB,aAAO,oBAAoB,gBAAgBL,CAAU;AAAA,IACvD,CAAC;AAED,mBAAeM,IAA6B;AAC1C,aAAI,CAAC/B,EAAM,SAAS,CAACH,EAAU,QAAc,KAEtC,MAAM2B,EAAQ;AAAA,QACnB,OAAOtC,EAAE,oBAAoB;AAAA,QAC7B,SAASA,EAAE,mBAAmB;AAAA,QAC9B,aAAaA,EAAE,cAAc;AAAA,QAC7B,YAAYA,EAAE,eAAe;AAAA,QAC7B,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAOA,IAAA8C;AAAA,MAAoB,OAAOC,GAA6BC,MACtDD,EAAG,MAAM,WAAWC,EAAK,MAAM,SAAS,KAAO,MAAMH,EAAA;AAAA,IAAS,GAGhEI,GAAmBJ,CAAQ;AAG3B,UAAMK,IAAUtC,EAAyB,MAAM;AAC7C,YAAMyB,IAASjC,EAAO,OAChB+C,IAAuB,CAAA;AAG7B,UAAId,GAAQ,KAAK;AACf,cAAMf,IAAMe,EAAO;AAEnB,QAAAc,EAAK,KAAK;AAAA,UACR,KAAK;AAAA,UACL,OAAOnD,EAAE,aAAa;AAAA,UACtB,MAAM;AAAA,UACN,KAAK,MAAA;AAAM,YAAK,OAAO,KAAKsB,GAAK,UAAU,UAAU;AAAA;AAAA,QAAA,CACtD;AAAA,MACH;AAEA,aAAIX,EAAU,SAAS0B,MAAW,QAChCc,EAAK,KAAK;AAAA,QACR,KAAK;AAAA,QACL,OAAOnD,EAAE,eAAe;AAAA,QACxB,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,KAAK,MAAA;AAAM,UAAKoC,EAAA;AAAA;AAAA,MAAO,CACxB,GAGIe;AAAA,IACT,CAAC;2BAICC,EAwCM,OAAA;AAAA,MAxCD,OAAM;AAAA,MAAa,WAAAX;AAAA,IAAA;MACHjC,EAAA,cAAnB6C,EAAuEC,EAAAC,EAAA,GAAA;AAAA;QAA3C,OAAM;AAAA,QAAmB,OAAA;AAAA,QAAO,MAAM;AAAA,MAAA,YAElEH,EAoCWI,IAAA,EAAA,KAAA,KAAA;AAAA,QAnCTC,EAYiBH,EAAAI,EAAA,GAAA;AAAA,UAXd,OAAO;AAAA,UACP,MAAI,CAAGlE,EAAM;AAAA,UACb,cAAY8D,EAAAtD,CAAA,EAAC,aAAA;AAAA,UACb,OAAOgB,EAAA;AAAA,UACP,SAASkC,EAAA;AAAA,UACT,kBAAgB;AAAA,UAChB,+BAAMxD,EAAI,MAAA;AAAA,QAAA;UAEKU,EAAA,SAAM,CAAKA,EAAA,MAAO;kBAAY;AAAA,kBAC5C,MAAwD;AAAA,cAAxDqD,EAAwDH,EAAAK,CAAA,GAAA,EAA9C,KAAA,MAAG;AAAA,2BAAC,MAA+B;AAAA,sBAA5BL,EAAAtD,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;;;;;QAItByD,EAKEH,EAAAM,EAAA,GAAA;AAAA,sBAJS5E,EAAA;AAAA,wDAAAA,EAAM,QAAA6E;AAAA,UACf,MAAK;AAAA,UACJ,QAAQnD,EAAA;AAAA,UACR,UAAQ,CAAGC,EAAA,SAAaF,EAAA;AAAA,QAAA;QAGNE,EAAA,cAArB0C,EAagBC,EAAAQ,EAAA,GAAA,EAAA,KAAA,KAAA;AAAA,UAZH,SACT,MAAgC;AAAA,YAAhCL,EAAgCH,EAAAS,EAAA,GAAA,EAAhB,OAAOhD,EAAA,SAAK,MAAA,GAAA,CAAA,OAAA,CAAA;AAAA,UAAA;qBAG9B,MAOY;AAAA,YAPZ0C,EAOYH,EAAAU,EAAA,GAAA;AAAA,cANV,MAAK;AAAA,cACJ,SAASvD,EAAA;AAAA,cACT,UAAQ,CAAGK,EAAA,SAAStB,EAAM,OAAE;AAAA,cAC5B,SAAO0C;AAAA,YAAA;yBAER,MAAsB;AAAA,oBAAnBoB,EAAAtD,CAAA,EAAC,aAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;ACzSd,UAAMJ,IAAUL,GAAA,GACVM,IAAMrB,GAAeoB,CAAO,GAC5BqE,IAAQC,GAAA,GACRC,IAASC,GAAA;AACf,IAAA/E,GAAA;AAEA,UAAMW,IAAIC,GAAa,YAAY,GAE7BC,IAAUC,GAAA,GAEVkE,IAAOhE,EAAiB,EAAE,GAC1BG,IAAUH,EAAI,EAAI,GAElBM,IAAYC,EAAS,MAAMhB,EAAQ,IAAI,cAAc,CAAC,GAEtDoB,IAAQJ;AAAA,MACZ,MACEhB,EAAQ,MAAM,UAAU,QAAQ,KAAK,CAAC0E,MAAWA,EAAO,OAAO,OAAO,GAAG,SACzEtE,EAAE,cAAc;AAAA,IAAA,GAGduE,IAAO3D,EAAmB;AAAA,MAC9B,KAAK,MAAOqD,EAAM,MAAM,SAAS,YAAY,YAAY;AAAA,MACzD,KAAK,CAACO,MAAU;AACd,cAAM3F,IAA0B;AAAA,UAC9B,GAAGoF,EAAM;AAAA,UACT,MAAMO,MAAU,KAAK,SAAY,OAAOA,CAAK;AAAA,QAAA;AAI/C,eAAO3F,EAAM,QACRsF,EAAO,QAAQ,EAAE,OAAAtF,GAAO;AAAA,MAC/B;AAAA,IAAA,CACD,GAEK4F,IAAQ7D,EAAS,MAAM2D,EAAK,UAAU,SAAS,GAE/CG,IAAI9D,EAAS,MAAO,OAAOqD,EAAM,MAAM,KAAM,WAAWA,EAAM,MAAM,IAAI,EAAG,GAG3EU,IAAQtE,EAAIqE,EAAE,KAAK,GAGnBE,IAAWhE,EAAgC,MAAM;AACrD,YAAMiE,IAAMZ,EAAM,MAAM;AAExB,aAAIY,MAAQ,QAAclE,EAAU,QAAQ,QAAQ,OAE7C,OAAOkE,KAAQ,YAAYA,MAAQ,MAAK,OAAOA,CAAG,KAAK;AAAA,IAChE,CAAC,GAMKC,IAAOlE,EAAS;AAAA,MACpB,KAAK,MAAMgE,EAAS,UAAU;AAAA,MAC9B,KAAK,CAACJ,MAAU;AACd,QAAKA,KAAOO,EAAA;AAAA,MACd;AAAA,IAAA,CACD,GAEKC,IAAQpE,EAAoB,MAAM;AAAA,MACtC,EAAE,OAAO,IAAI,OAAOZ,EAAE,YAAY,EAAA;AAAA,MAClC,EAAE,OAAO,WAAW,OAAOA,EAAE,gBAAgB,GAAG,MAAM,QAAA;AAAA,IAAQ,CAC/D,GAEKiF,IAAQC,GAAa3G,GAAW,OAAO,EAAE,GAAGmG,EAAE,OAAO,UAAUD,EAAM,MAAA,EAAQ,GAG7EU,IAAWvE,EAAS,MAAMD,EAAU,SAASsE,EAAM,SAAS,KAAK,GAEjEG,IAAYxE,EAAS,MACrB8D,EAAE,UAAU,KAAW1E,EAAE,qBAAqB,IAE3CyE,EAAM,QAAQzE,EAAE,kBAAkB,IAAIA,EAAE,cAAc,CAC9D;AAED,mBAAe+B,IAAsB;AACnC,MAAAvB,EAAQ,QAAQ;AAEhB,UAAI;AACF,cAAM6E,IAAS,MAAMxF,EAAI,KAAK,EAAE,QAAQ6E,EAAE,OAAO,SAASD,EAAM,MAAA,CAAO;AAEvE,QAAAJ,EAAK,QAAQgB,EAAO;AAAA,MACtB,SAASrD,GAAO;AACd,QAAAC,EAAM,OAAO/B,EAAQ8B,CAAK,CAAC;AAAA,MAC7B,UAAA;AACE,QAAAxB,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAEA,IAAA8E;AAAA,MACE,MAAM,CAACf,EAAK,OAAOG,EAAE,KAAK;AAAA,MAC1B,MAAA;AAAM,QAAK3C,EAAA;AAAA;AAAA,IAAK,GAEbA,EAAA;AAEL,QAAIwD;AAEJ,IAAAD,GAAMX,GAAO,CAACH,MAAU;AACtB,mBAAae,CAAK,GAClBA,IAAQ,WAAW,MAAM;AACvB,QAAKpB,EAAO,QAAQ,EAAE,OAAO,EAAE,GAAGF,EAAM,OAAO,GAAGO,EAAM,WAAW,KAAK,SAAYA,EAAA,GAAS;AAAA,MAC/F,GAAG,GAAG;AAAA,IACR,CAAC,GAED5B,GAAgB,MAAM,aAAa2C,CAAK,CAAC;AAGzC,aAASC,EAAOzG,GAA0B;AACxC,MAAKoF,EAAO,QAAQ,EAAE,OAAO,EAAE,GAAGF,EAAM,OAAO,QAAQ,OAAOlF,CAAE,EAAA,GAAK;AAAA,IACvE;AAEA,aAASgG,IAAc;AACrB,YAAMlG,IAAQ,EAAE,GAAGoF,EAAM,MAAA;AAEzB,aAAOpF,EAAM,QACRsF,EAAO,QAAQ,EAAE,OAAAtF,GAAO;AAAA,IAC/B;AAGA,aAAS4G,EAAQrF,GAA6B;AAC5C,MAAAoF,EAAOpF,EAAO,EAAE,GACX2B,EAAA;AAAA,IACP;AAOA,mBAAe2D,IAAyB;AACtC,UAAI;AACF,cAAMT,EAAM,KAAKZ,EAAK,MAAM,IAAI,CAACsB,MAAQA,EAAI,EAAE,CAAC;AAAA,MAClD,SAAS3D,GAAO;AACd,QAAAC,EAAM,OAAO/B,EAAQ8B,GAAOhC,EAAE,uBAAuB,CAAC,CAAC,GACvD,MAAM+B,EAAA;AAAA,MACR;AAAA,IACF;AAEA,aAAS6D,EAAWD,GAA6B;AAC/C,aAAKhF,EAAU,QAEX8D,EAAM,QACD;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAOzE,EAAE,gBAAgB;AAAA,UACzB,KAAK,MAAA;AAAM,YAAK6F,EAAQF,CAAG;AAAA;AAAA,QAAA;AAAA,MAC7B,IAIG;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO3F,EAAE,eAAe;AAAA,UACxB,QAAQ;AAAA,UACR,KAAK,MAAA;AAAM,YAAKoC,EAAOuD,CAAG;AAAA;AAAA,QAAA;AAAA,MAC5B,IApB2B,CAAA;AAAA,IAsB/B;AAEA,mBAAevD,EAAOuD,GAA+B;AASnD,UARe,MAAMrD,EAAQ;AAAA,QAC3B,OAAOtC,EAAE,uBAAuB,EAAE,MAAM2F,EAAI,OAAO;AAAA,QACnD,SAAS3F,EAAE,oBAAoB;AAAA,QAC/B,aAAaA,EAAE,eAAe;AAAA,QAC9B,YAAYA,EAAE,eAAe;AAAA,QAC7B,MAAM;AAAA,MAAA,CACP;AAID,YAAI;AACF,gBAAMH,EAAI,OAAO8F,EAAI,EAAE,GACvB1D,EAAM,QAAQjC,EAAE,gBAAgB,CAAC,GACjC8F,EAAQH,EAAI,EAAE;AAAA,QAChB,SAAS3D,GAAO;AACd,UAAAC,EAAM,OAAO/B,EAAQ8B,CAAK,CAAC;AAAA,QAC7B;AAAA,IACF;AAGA,aAAS8D,EAAQ/G,GAAkB;AACjC,MAAI6F,EAAS,UAAU7F,KAAIgG,EAAA,GAEtBhD,EAAA;AAAA,IACP;AAEA,mBAAe8D,EAAQF,GAA+B;AACpD,UAAI;AACF,cAAM9F,EAAI,QAAQ8F,EAAI,EAAE,GACxB1D,EAAM,QAAQjC,EAAE,iBAAiB,CAAC;AAAA,MACpC,SAASgC,GAAO;AACd,QAAAC,EAAM,OAAO/B,EAAQ8B,CAAK,CAAC;AAAA,MAC7B,UAAA;AACE,cAAMD,EAAA;AAAA,MACR;AAAA,IACF;AAGA,aAASgE,EAAOJ,GAAyB;AACvC,aAAOA,EAAI,aAAaA,EAAI,QAAQ,WAAW;AAAA,IACjD;2BAIEtC,EAgKiBC,EAAA0C,EAAA,GAAA;AAAA,MAhKO,MAAMzB,EAAA;AAAA,8CAAAA,EAAI,QAAAV;AAAA,MAAG,OAAO7C,EAAA;AAAA,MAAQ,OAAOgE,EAAA;AAAA,MAAQ,MAAM;AAAA,IAAA;iBACvE,MA8JU;AAAA,QA9JVvB,EA8JUH,EAAA2C,EAAA,GAAA;AAAA,UA9JD,OAAM;AAAA,UAAW,SAAQ;AAAA,QAAA;qBAChC,MA4JiB;AAAA,YA5JjBxC,EA4JiBH,EAAA4C,EAAA,GAAA;AAAA,cA3JP,MAAMpB,EAAA;AAAA,sDAAAA,EAAI,QAAAjB;AAAA,cAClB,OAAM;AAAA,cACL,cAAY;AAAA,cACZ,cAAY;AAAA,cACZ,gBAAcP,EAAAtD,CAAA,EAAC,cAAA;AAAA,YAAA;cAEL,QACT,MAWM;AAAA,gBAXNmG,EAWM,OAXNC,IAWM;AAAA,kBAVJ3C,EASWH,EAAA+C,EAAA,GAAA;AAAA,gCARA1B,EAAA;AAAA,kEAAAA,EAAK,QAAAd;AAAA,oBACd,OAAM;AAAA,oBACN,MAAK;AAAA,oBACL,WAAA;AAAA,oBACC,aAAaP,EAAAtD,CAAA,EAAC,eAAA;AAAA,oBACd,cAAYsD,EAAAtD,CAAA,EAAC,eAAA;AAAA,kBAAA;oBAEH,UAAO,MAAmC;AAAA,sBAAnCyD,EAAmCH,EAAAgD,CAAA,GAAA;AAAA,wBAA1B,MAAK;AAAA,wBAAS,MAAK;AAAA,sBAAA;;;;;gBAS1C3F,EAAA,UAAc8D,EAAA,cADtBrB,EASS,UAAA;AAAA;kBAPP,MAAK;AAAA,kBACL,OAAKmD,EAAA,CAAC,iBAAe,EAAA,cACG3B,EAAA,UAAQ,MAAA,CAAA,CAAA;AAAA,kBAC/B,gCAAOY,EAAM,KAAA;AAAA,gBAAA;kBAEd/B,EAAiCH,EAAAgD,CAAA,GAAA;AAAA,oBAAxB,MAAK;AAAA,oBAAO,MAAK;AAAA,kBAAA;kBAAOE,EAAA,QAC9BlD,EAAAtD,CAAA,EAAC,YAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;gBAGaQ,EAAA,SAAW6D,EAAA,MAAK,WAAM,UAAzChB,EAAuFC,EAAAC,EAAA,GAAA;AAAA;kBAAtC,OAAM;AAAA,kBAAqB,MAAM;AAAA,gBAAA,MAGrEc,EAAA,MAAK,WAAM,UADxBhB,EAIEC,EAAAmD,EAAA,GAAA;AAAA;kBAFC,OAAOrB,EAAA;AAAA,kBACP,cAAcX,EAAA,SAASC,YAAC,KAAUpB,EAAAtD,CAAA,yBAAyB;AAAA,gBAAA,+CAG9DoD,EA0FWI,IAAA,EAAA,KAAA,KAAA;AAAA,kBAvFTC,EA8EmBH,EAAAoD,EAAA,GAAA;AAAA,gCA7ERrC,EAAA;AAAA,kEAAAA,EAAI,QAAAR;AAAA,oBACb,OAAK0C,EAAA,CAAC,kBAAgB,EAAA,cACE/F,EAAA,MAAA,CAAO,CAAA;AAAA,oBAC/B,OAAA;AAAA,oBACA,YAAS;AAAA,oBACR,QAAQ2E,EAAA,QAAQ,SAAA;AAAA,oBAChB,cAAU,CAAGwB,MAAoBA,EAAK;AAAA,oBACtC,WAAWxB,EAAA;AAAA,oBACX,QAAMO;AAAA,kBAAA;oBAEI,SAAOkB,EAChB,CA4DY,EA7DQ,MAAAD,QAAI;AAAA,uBACxBE,EAAA,GAAAxD,EA4DYyD,GA3DLrC,EAAA,QAAK,QAAA,QAAA,GAAA;AAAA,wBACT,MAAMA,EAAA,QAAQ,SAAS;AAAA,wBACxB,UAAM,iBAAe,EAAA,cACGG,YAAa+B,EAAK,GAAA,CAAE,CAAA;AAAA,wBAC3C,SAAK,CAAA9C,MAAEY,UAAQ,SAAYe,EAAOmB,EAAK,EAAE;AAAA,sBAAA;mCAI1C,MAEO;AAAA,0BAFKA,EAAK,MAAM,SAAvBE,KAAAzD,EAEO,QAFP2D,IAEO;AAAA,4BADLZ,EAAqC,OAAA;AAAA,8BAA/B,KAAKQ,EAAK,KAAK;AAAA,8BAAO,KAAI;AAAA,4BAAA;sCAElCtD,EAMEC,EAAA0D,EAAA,GAAA;AAAA;4BAJA,OAAM;AAAA,4BACN,OAAM;AAAA,4BACL,MAAML,EAAK;AAAA,4BACZ,MAAK;AAAA,0BAAA;0BAGPR,EAuCO,QAvCPc,IAuCO;AAAA,4BAtCLd,EAUO,QAVPe,IAUO;AAAA,8BATLf,EAA0D,QAA1DgB,IAA0DC,EAApBT,EAAK,KAAK,GAAA,CAAA;AAAA,8BAC9BA,EAAK,iBAAvBtD,EAOaC,EAAA+D,EAAA,GAAA;AAAA;gCAPqB,SAAS/D,EAAAtD,CAAA,EAAC,iBAAA;AAAA,8BAAA;2CAC1C,MAKE;AAAA,kCALFyD,EAKEH,EAAAgD,CAAA,GAAA;AAAA,oCAJA,OAAM;AAAA,oCACN,MAAK;AAAA,oCACL,MAAK;AAAA,oCACJ,cAAYhD,EAAAtD,CAAA,EAAC,iBAAA;AAAA,kCAAA;;;;;4BAKpBmG,EAyBO,QAzBPmB,IAyBO;AAAA,8BAxBYX,EAAK,YAGCZ,EAAOY,CAAI,UAAlCtD,EAIaC,EAAA+D,EAAA,GAAA;AAAA;gCAJyB,SAAS/D,EAAAtD,CAAA,EAAC,wBAAA;AAAA,8BAAA;2CAC9C,MAEW;AAAA,kCAFXyD,EAEWH,EAAAK,CAAA,GAAA;AAAA,oCAFD,MAAK;AAAA,oCAAU,KAAA;AAAA,oCAAI,MAAK;AAAA,kCAAA;+CAChC,MAA+B;AAAA,0CAA5BL,EAAAtD,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,oCAAA;;;;;sEALRqD,EAEWC,EAAAK,CAAA,GAAA;AAAA;gCAFsB,KAAA;AAAA,gCAAI,MAAK;AAAA,8BAAA;2CACxC,MAA+B;AAAA,sCAA5BL,EAAAtD,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,gCAAA;;;8BAQNmG,EAMO,QANPoB,IAMOH,EAJHT,EAAK,iBAAc,IAAmCrD,EAAAtD,CAAA,EAAC,yBAAA,EAAA,OAAmC2G,EAAK,eAAA,CAAc,IAAkCrD,EAAAtD,CAAA,EAAC,oBAAA,CAAA,GAAA,CAAA;AAAA,8BAO5I2G,EAAK,QAAQ,SAAM,UAD3BvD,EAMO,QAAA;AAAA;gCAJL,OAAM;AAAA,gCACL,cAAYE,EAAAtD,CAAA,EAAC,kBAAA,EAAA,SAA8B2G,EAAK,QAAQ,KAAI,IAAA,EAAA,CAAA;AAAA,8BAAA,KAE1DA,EAAK,QAAQ,KAAI,KAAA,CAAA,GAAA,GAAAa,EAAA;;;;;;;oBAOnB,SAAOZ,EAChB,CAA+D,EAD3C,MAAAD,QAAI;AAAA,sBACxBlD,EAA+DH,EAAAmE,EAAA,GAAA;AAAA,wBAAjD,SAAS7B,EAAWe,CAAI;AAAA,wBAAI,OAAOA,EAAK;AAAA,sBAAA;;;;kBAMlDhG,EAAA,UAAc8D,EAAA,cADtBpB,EAKEC,EAAAoE,EAAA,GAAA;AAAA;oBAHA,OAAM;AAAA,oBACN,MAAK;AAAA,oBACJ,aAAapE,EAAA2B,CAAA,EAAM,KAAK;AAAA,kBAAA;;;cAKpB,QAAM2B,EACf,CASE,EAViB,QAAAe,GAAQ,MAAAC,QAAI;AAAA,gBAEvBhD,EAAA,UAAQ,aADhBvB,EASEwE,IAAA;AAAA,kBAPC,IAAIjD,EAAA,UAAQ,QAAA,OAAoBA,EAAA;AAAA,kBAChC,KAAK,OAAOA,EAAA,KAAQ;AAAA,kBACpB,QAAA+C;AAAA,kBACA,QAAMC;AAAA,kBACN,SAAO7F;AAAA,kBACP,WAAS0D;AAAA,kBACT,WAASK;AAAA,gBAAA;;cAIH,SACT,MAGE;AAAA,gBAHFrC,EAGEH,EAAAmD,EAAA,GAAA;AAAA,kBAFC,OAAOnD,EAAAtD,CAAA,EAAC,eAAA;AAAA,kBACR,aAAaW,EAAA,QAAY2C,EAAAtD,CAAA,0BAA0B;AAAA,gBAAA;;;;;;;;;;;;AClZzD,SAAS8H,GAAMC,IAAwB,IAAiB;AAC7D,QAAMC,IAAOD,EAAQ,QAAQ;AAE7B,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAAC;AAAA;AAAA;AAAA,IAGA,QAAQ,CAAC,EAAE,MAAAA,GAAM,MAAM,cAAc,WAAWC,IAAW,OAAO,EAAE,MAAMD,EAAA,GAAQ;AAAA,EAAA;AAEtF;"}
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.wx-outlet[data-v-
|
|
1
|
+
.wx-outlet[data-v-b6ea9c8f]{display:flex;flex-direction:column;gap:var(--wx-gap, var(--wx-space-16));min-width:0;padding:var(--wx-gap, var(--wx-space-16));container-type:inline-size}.wx-outlet__ghost[data-v-b6ea9c8f]{max-width:480px}.wx-press>.wx-card__body{display:flex;flex-direction:column;overflow:clip;border-radius:inherit}.wx-press__bar[data-v-7ef16f83]{display:flex;gap:var(--wx-space-8);padding:var(--wx-space-12) var(--wx-space-12) var(--wx-space-8)}.wx-press__search[data-v-7ef16f83]{flex:1 1 180px;min-width:0}.wx-press__new[data-v-7ef16f83]{display:flex;align-items:center;gap:var(--wx-space-8);width:calc(100% - 2 * var(--wx-space-8));margin:0 var(--wx-space-8) var(--wx-space-4);padding:var(--wx-space-8);border:1px dashed var(--wx-border-default);border-radius:var(--wx-radius-sm);background:none;font:inherit;color:var(--wx-color-primary);text-align:start;cursor:pointer}.wx-press__new[data-v-7ef16f83]:hover,.wx-press__new.is-current[data-v-7ef16f83]{background:var(--wx-bg-subtle)}.wx-press__loading[data-v-7ef16f83]{padding:var(--wx-space-12)}.wx-press__list[data-v-7ef16f83]{padding-inline:var(--wx-space-8)}.wx-press__list.is-loading[data-v-7ef16f83]{opacity:.6}.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row{gap:var(--wx-space-4);padding-inline:var(--wx-space-4);border-radius:var(--wx-radius-sm)}.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row:hover,.wx-press__list[data-v-7ef16f83] .wx-sortable-list__row:has(.is-current){background:var(--wx-bg-subtle)}.wx-press__note[data-v-7ef16f83]{margin:var(--wx-space-8)}.wx-outlet-row[data-v-7ef16f83]{display:flex;align-items:flex-start;gap:var(--wx-space-10);flex:1 1 auto;width:100%;min-width:0;padding-block:var(--wx-space-8);padding-inline:0;border:0;background:none;font:inherit;color:inherit;text-align:start}button.wx-outlet-row[data-v-7ef16f83]{cursor:pointer}.wx-outlet-row__initials[data-v-7ef16f83]{flex:none}.wx-outlet-row__logo[data-v-7ef16f83]{display:flex;flex:none;align-items:center;justify-content:center;width:64px;height:32px;overflow:hidden;border-radius:var(--wx-radius-sm);background:var(--wx-bg-subtle)}.wx-outlet-row__logo img[data-v-7ef16f83]{display:block;max-width:100%;max-height:100%;object-fit:contain}.wx-outlet-row__body[data-v-7ef16f83]{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--wx-space-2);min-width:0}.wx-outlet-row__head[data-v-7ef16f83]{display:flex;align-items:center;gap:var(--wx-space-6);min-width:0}.wx-outlet-row__title[data-v-7ef16f83]{min-width:0;overflow:hidden;font-weight:var(--wx-font-weight-medium);text-overflow:ellipsis;white-space:nowrap}.wx-outlet-row.is-current .wx-outlet-row__title[data-v-7ef16f83]{color:var(--wx-color-primary)}.wx-outlet-row__featured[data-v-7ef16f83]{flex:none;color:var(--wx-color-warning)}.wx-outlet-row__facts[data-v-7ef16f83]{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wx-space-4) var(--wx-space-8);min-width:0;color:var(--wx-text-muted);font-size:var(--wx-font-size-sm)}.wx-outlet-row__locales[data-v-7ef16f83]{font-family:var(--wx-font-family-mono);font-size:var(--wx-font-size-xs);text-transform:uppercase}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webx-ui/module-press",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Press for the WebX UI admin panel: the outlets that wrote about a site and their articles — a link or a PDF — ordered by hand, shown on the site by blocks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"vue-router": "^4.5.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@webx-ui/core": "^0.34.
|
|
48
|
-
"@webx-ui/module-admin": "^0.18.
|
|
49
|
-
"@webx-ui/schema": "^0.6.
|
|
47
|
+
"@webx-ui/core": "^0.34.1",
|
|
48
|
+
"@webx-ui/module-admin": "^0.18.2",
|
|
49
|
+
"@webx-ui/schema": "^0.6.4"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^24.10.1",
|