@openrails/contentkit-ui 0.83.0 → 0.85.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/README.md +11 -3
- package/dist/client/beacons.d.ts +8 -5
- package/dist/client/beacons.js +15 -7
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/content/moderation.d.ts +14 -2
- package/dist/client/content/moderation.js +34 -0
- package/dist/client/content/moderation.js.map +1 -1
- package/dist/client/content/types.d.ts +13 -0
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/discovery.d.ts +6 -0
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +8 -0
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +77 -12
- package/dist/client/generated/routes.js +106 -1
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +52 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/reader-meta.d.ts +27 -0
- package/dist/client/reader-meta.js +99 -0
- package/dist/client/reader-meta.js.map +1 -0
- package/dist/client/versions.d.ts +1 -1
- package/dist/client/versions.js +7 -2
- package/dist/client/versions.js.map +1 -1
- package/dist/client.js +2 -1
- package/dist/components/admin/reader-manifest.d.ts +18 -0
- package/dist/components/admin/reader-manifest.js +490 -0
- package/dist/components/admin/reader-manifest.js.map +1 -0
- package/dist/components/content/card.js +1 -1
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/editors.d.ts +2 -1
- package/dist/components/content/editors.js +291 -256
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/groups/banner.d.ts +1 -1
- package/dist/components/groups/banner.js +1 -1
- package/dist/components/groups/banner.js.map +1 -1
- package/dist/components/groups/editor.js +3 -3
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/parts.d.ts +2 -2
- package/dist/components/groups/parts.js +71 -72
- package/dist/components/groups/parts.js.map +1 -1
- package/dist/components/social/comments.js +59 -45
- package/dist/components/social/comments.js.map +1 -1
- package/dist/components/social/index.d.ts +1 -0
- package/dist/components/social/moderation.d.ts +5 -4
- package/dist/components/social/moderation.js +143 -130
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/social/parts.js +22 -14
- package/dist/components/social/parts.js.map +1 -1
- package/dist/components/social/rules.d.ts +23 -0
- package/dist/components/social/rules.js +261 -0
- package/dist/components/social/rules.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +26 -24
- package/dist/locales/admin/de.js +78 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +78 -0
- package/dist/locales/admin/en.js +78 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +78 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +78 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +78 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +78 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/de.js +3 -1
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +1 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +1 -0
- package/dist/locales/discovery/en.js +1 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +1 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +1 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +1 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +1 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +84 -0
- package/dist/locales/en.js +3 -1
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +3 -1
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +3 -1
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +3 -1
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/social/de.js +3 -0
- package/dist/locales/social/de.js.map +1 -1
- package/dist/locales/social/en.d.ts +3 -0
- package/dist/locales/social/en.js +3 -0
- package/dist/locales/social/en.js.map +1 -1
- package/dist/locales/social/es.js +3 -0
- package/dist/locales/social/es.js.map +1 -1
- package/dist/locales/social/ja.js +3 -0
- package/dist/locales/social/ja.js.map +1 -1
- package/dist/locales/social/ko.js +3 -0
- package/dist/locales/social/ko.js.map +1 -1
- package/dist/locales/social/zh.js +3 -0
- package/dist/locales/social/zh.js.map +1 -1
- package/dist/locales/zh.js +3 -1
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/admin.d.ts +1 -1
- package/dist/react/admin.js +2 -0
- package/dist/react/admin.js.map +1 -1
- package/dist/react/comments.d.ts +29 -1
- package/dist/react/comments.js +30 -2
- package/dist/react/comments.js.map +1 -1
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/discovery.js +1 -1
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/resources.js +6 -0
- package/dist/react/resources.js.map +1 -1
- package/dist/react.js +14 -14
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import { useMessages as e } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as t } from "../../scope.js";
|
|
3
|
+
import { useContentKitClient as n } from "../../react/context.js";
|
|
4
|
+
import { Button as r } from "../../ui/button.js";
|
|
5
|
+
import { Input as i } from "../../ui/input.js";
|
|
6
|
+
import { Label as a } from "../../ui/label.js";
|
|
7
|
+
import { Select as o, SelectContent as s, SelectItem as c, SelectTrigger as l, SelectValue as u } from "../../ui/select.js";
|
|
8
|
+
import { ErrorLine as d, useAction as f } from "../social/parts.js";
|
|
9
|
+
import { LoadFailed as p, Rows as m } from "./shell.js";
|
|
10
|
+
import { Switch as h } from "../../ui/switch.js";
|
|
11
|
+
import { emptyReaderMeta as g, matchAudio as _, readerMetaProblems as v, readerNames as y } from "../../client/reader-meta.js";
|
|
12
|
+
import { useReaderPlan as b } from "../../react/versions.js";
|
|
13
|
+
import { useId as x, useMemo as S, useState as C } from "react";
|
|
14
|
+
import { jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
15
|
+
import { cn as E } from "cn";
|
|
16
|
+
//#region src/components/admin/reader-manifest.tsx
|
|
17
|
+
var D = "__none__", O = (e, t) => t === void 0 ? e === void 0 ? D : `page:${e}` : `anchor:${t}`, k = (e) => e.startsWith("anchor:") ? { anchor: e.slice(7) } : e.startsWith("page:") ? { page: e.slice(5) } : {};
|
|
18
|
+
function A(e) {
|
|
19
|
+
let t = e.defaults?.auto_advance_seconds;
|
|
20
|
+
return {
|
|
21
|
+
schema_version: 1,
|
|
22
|
+
...t === void 0 ? {} : { defaults: { auto_advance_seconds: t } },
|
|
23
|
+
anchors: (e.anchors ?? []).map((e) => ({
|
|
24
|
+
id: e.id.trim(),
|
|
25
|
+
page: e.page,
|
|
26
|
+
offset_percent: e.offset_percent,
|
|
27
|
+
...e.duration_seconds === void 0 ? {} : { duration_seconds: e.duration_seconds }
|
|
28
|
+
})),
|
|
29
|
+
background: (e.background ?? []).map((e) => {
|
|
30
|
+
let t = e.layer?.trim();
|
|
31
|
+
return {
|
|
32
|
+
...e,
|
|
33
|
+
...t ? { layer: t } : { layer: void 0 }
|
|
34
|
+
};
|
|
35
|
+
}),
|
|
36
|
+
foreground: e.foreground ?? []
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
var j = (e) => e.trim() === "" ? NaN : Number(e), M = (e) => e === void 0 || Number.isNaN(e) ? "" : String(e);
|
|
40
|
+
function N({ content: i, version: a, client: o, onError: s, className: c, appearance: l }) {
|
|
41
|
+
let { t: u } = e(), h = n(o), x = b(i, a, { client: h }), D = S(() => x.plan ? y(x.plan) : {
|
|
42
|
+
pages: [],
|
|
43
|
+
files: []
|
|
44
|
+
}, [x.plan]), [O, k] = C(null), [j, M] = C(null), [N, z] = C(!1), [B, V] = C(!1), [q, J] = C(null), Y = f("reader.save", s);
|
|
45
|
+
x.plan && x.plan !== O && !N && (k(x.plan), M(x.plan.reader ? structuredClone(x.plan.reader) : g()));
|
|
46
|
+
let X = S(() => j ? v(j, D) : [], [j, D]), Z = (e) => {
|
|
47
|
+
M((t) => t && e(t)), z(!0), V(!1);
|
|
48
|
+
}, Q = (() => {
|
|
49
|
+
if (x.loading && !x.plan && !x.pending) return /* @__PURE__ */ w(m, { count: 2 });
|
|
50
|
+
if (x.pending) return /* @__PURE__ */ w("p", {
|
|
51
|
+
className: "text-muted-foreground",
|
|
52
|
+
children: u("readerManifest.pending")
|
|
53
|
+
});
|
|
54
|
+
if (x.error && !x.plan) return /* @__PURE__ */ w(p, {
|
|
55
|
+
error: x.error,
|
|
56
|
+
message: u("readerManifest.loadFailed"),
|
|
57
|
+
retry: x.reload
|
|
58
|
+
});
|
|
59
|
+
if (!j) return null;
|
|
60
|
+
let e = j.anchors ?? [], t = e.map((e) => e.id).filter(Boolean);
|
|
61
|
+
return /* @__PURE__ */ T("form", {
|
|
62
|
+
className: "grid gap-4",
|
|
63
|
+
onSubmit: (e) => {
|
|
64
|
+
e.preventDefault(), !X.length && (V(!1), Y.run(async () => (await h.versions.setReader(i, a, A(j)), !0)).then((e) => {
|
|
65
|
+
e && (z(!1), V(!0));
|
|
66
|
+
}));
|
|
67
|
+
},
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ w("p", {
|
|
70
|
+
className: "text-muted-foreground",
|
|
71
|
+
children: u("readerManifest.summary", {
|
|
72
|
+
pages: D.pages.length,
|
|
73
|
+
files: D.files.length
|
|
74
|
+
})
|
|
75
|
+
}),
|
|
76
|
+
/* @__PURE__ */ w(R, {
|
|
77
|
+
meta: j,
|
|
78
|
+
edit: Z
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ w(L, {
|
|
81
|
+
title: u("readerManifest.anchors"),
|
|
82
|
+
add: u("readerManifest.addAnchor"),
|
|
83
|
+
disabled: !D.pages.length,
|
|
84
|
+
onAdd: () => Z((e) => ({
|
|
85
|
+
...e,
|
|
86
|
+
anchors: [...e.anchors ?? [], {
|
|
87
|
+
id: P(e.anchors ?? []),
|
|
88
|
+
page: D.pages[0],
|
|
89
|
+
offset_percent: 0
|
|
90
|
+
}]
|
|
91
|
+
})),
|
|
92
|
+
children: e.map((e, t) => /* @__PURE__ */ w(H, {
|
|
93
|
+
index: t,
|
|
94
|
+
anchor: e,
|
|
95
|
+
pages: D.pages,
|
|
96
|
+
onChange: (e) => Z((n) => ({
|
|
97
|
+
...n,
|
|
98
|
+
anchors: F(n.anchors, t, e)
|
|
99
|
+
})),
|
|
100
|
+
onRemove: () => Z((e) => ({
|
|
101
|
+
...e,
|
|
102
|
+
anchors: I(e.anchors, t)
|
|
103
|
+
}))
|
|
104
|
+
}, t))
|
|
105
|
+
}),
|
|
106
|
+
/* @__PURE__ */ T(L, {
|
|
107
|
+
title: u("readerManifest.foreground"),
|
|
108
|
+
add: u("readerManifest.addCue"),
|
|
109
|
+
disabled: !D.files.length || !D.pages.length,
|
|
110
|
+
onAdd: () => Z((e) => ({
|
|
111
|
+
...e,
|
|
112
|
+
foreground: [...e.foreground ?? [], {
|
|
113
|
+
file: D.files[0],
|
|
114
|
+
page: D.pages[0]
|
|
115
|
+
}]
|
|
116
|
+
})),
|
|
117
|
+
extra: /* @__PURE__ */ w(r, {
|
|
118
|
+
type: "button",
|
|
119
|
+
size: "sm",
|
|
120
|
+
variant: "outline",
|
|
121
|
+
disabled: !D.files.length,
|
|
122
|
+
onClick: () => {
|
|
123
|
+
let e = _(D, t);
|
|
124
|
+
J(e), e.foreground.length && Z((t) => ({
|
|
125
|
+
...t,
|
|
126
|
+
foreground: e.foreground
|
|
127
|
+
}));
|
|
128
|
+
},
|
|
129
|
+
children: u("readerManifest.match")
|
|
130
|
+
}),
|
|
131
|
+
children: [q && /* @__PURE__ */ w(G, { match: q }), (j.foreground ?? []).map((e, n) => /* @__PURE__ */ w(U, {
|
|
132
|
+
index: n,
|
|
133
|
+
cue: e,
|
|
134
|
+
names: D,
|
|
135
|
+
anchors: t,
|
|
136
|
+
onChange: (e) => Z((t) => ({
|
|
137
|
+
...t,
|
|
138
|
+
foreground: F(t.foreground, n, e)
|
|
139
|
+
})),
|
|
140
|
+
onRemove: () => Z((e) => ({
|
|
141
|
+
...e,
|
|
142
|
+
foreground: I(e.foreground, n)
|
|
143
|
+
}))
|
|
144
|
+
}, n))]
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ w(L, {
|
|
147
|
+
title: u("readerManifest.background"),
|
|
148
|
+
add: u("readerManifest.addTrack"),
|
|
149
|
+
disabled: !D.files.length,
|
|
150
|
+
onAdd: () => Z((e) => ({
|
|
151
|
+
...e,
|
|
152
|
+
background: [...e.background ?? [], { file: D.files[0] }]
|
|
153
|
+
})),
|
|
154
|
+
children: (j.background ?? []).map((e, n) => /* @__PURE__ */ w(W, {
|
|
155
|
+
index: n,
|
|
156
|
+
cue: e,
|
|
157
|
+
names: D,
|
|
158
|
+
anchors: t,
|
|
159
|
+
onChange: (e) => Z((t) => ({
|
|
160
|
+
...t,
|
|
161
|
+
background: F(t.background, n, e)
|
|
162
|
+
})),
|
|
163
|
+
onRemove: () => Z((e) => ({
|
|
164
|
+
...e,
|
|
165
|
+
background: I(e.background, n)
|
|
166
|
+
}))
|
|
167
|
+
}, n))
|
|
168
|
+
}),
|
|
169
|
+
X.length > 0 && /* @__PURE__ */ w(K, { problems: X }),
|
|
170
|
+
/* @__PURE__ */ w(d, { error: Y.error }),
|
|
171
|
+
/* @__PURE__ */ T("div", {
|
|
172
|
+
className: "flex flex-wrap items-center gap-3",
|
|
173
|
+
children: [
|
|
174
|
+
/* @__PURE__ */ w(r, {
|
|
175
|
+
type: "submit",
|
|
176
|
+
size: "sm",
|
|
177
|
+
disabled: Y.pending || !N || X.length > 0,
|
|
178
|
+
children: Y.pending ? u("common.saving") : u("common.save")
|
|
179
|
+
}),
|
|
180
|
+
N && /* @__PURE__ */ w(r, {
|
|
181
|
+
type: "button",
|
|
182
|
+
size: "sm",
|
|
183
|
+
variant: "ghost",
|
|
184
|
+
onClick: () => {
|
|
185
|
+
z(!1), k(null), J(null);
|
|
186
|
+
},
|
|
187
|
+
children: u("readerManifest.revert")
|
|
188
|
+
}),
|
|
189
|
+
B && /* @__PURE__ */ w("span", {
|
|
190
|
+
role: "status",
|
|
191
|
+
className: "text-muted-foreground",
|
|
192
|
+
children: u("readerManifest.saved")
|
|
193
|
+
})
|
|
194
|
+
]
|
|
195
|
+
})
|
|
196
|
+
]
|
|
197
|
+
});
|
|
198
|
+
})();
|
|
199
|
+
return /* @__PURE__ */ T(t, {
|
|
200
|
+
appearance: l,
|
|
201
|
+
className: E("grid gap-3 text-sm", c),
|
|
202
|
+
"data-ckui": "reader-manifest",
|
|
203
|
+
children: [/* @__PURE__ */ w("h4", {
|
|
204
|
+
className: "font-medium",
|
|
205
|
+
children: u("readerManifest.title")
|
|
206
|
+
}), Q]
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function P(e) {
|
|
210
|
+
let t = new Set(e.map((e) => e.id)), n = e.length + 1;
|
|
211
|
+
for (; t.has(`a${n}`);) n++;
|
|
212
|
+
return `a${n}`;
|
|
213
|
+
}
|
|
214
|
+
var F = (e, t, n) => (e ?? []).map((e, r) => r === t ? n : e), I = (e, t) => (e ?? []).filter((e, n) => n !== t);
|
|
215
|
+
function L({ title: e, add: t, disabled: n, onAdd: i, extra: a, children: o }) {
|
|
216
|
+
return /* @__PURE__ */ T("fieldset", {
|
|
217
|
+
className: "grid gap-2 rounded-md border border-border p-3",
|
|
218
|
+
children: [
|
|
219
|
+
/* @__PURE__ */ w("legend", {
|
|
220
|
+
className: "px-1 font-medium",
|
|
221
|
+
children: e
|
|
222
|
+
}),
|
|
223
|
+
o,
|
|
224
|
+
/* @__PURE__ */ T("div", {
|
|
225
|
+
className: "flex flex-wrap gap-2",
|
|
226
|
+
children: [/* @__PURE__ */ w(r, {
|
|
227
|
+
type: "button",
|
|
228
|
+
size: "sm",
|
|
229
|
+
variant: "outline",
|
|
230
|
+
disabled: n,
|
|
231
|
+
onClick: i,
|
|
232
|
+
children: t
|
|
233
|
+
}), a]
|
|
234
|
+
})
|
|
235
|
+
]
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function R({ meta: t, edit: n }) {
|
|
239
|
+
let { t: r } = e(), o = x(), s = t.defaults?.auto_advance_seconds;
|
|
240
|
+
return /* @__PURE__ */ T("div", {
|
|
241
|
+
className: "grid max-w-xs gap-1.5",
|
|
242
|
+
children: [/* @__PURE__ */ w(a, {
|
|
243
|
+
htmlFor: `${o}-auto`,
|
|
244
|
+
children: r("readerManifest.autoAdvance")
|
|
245
|
+
}), /* @__PURE__ */ w(i, {
|
|
246
|
+
id: `${o}-auto`,
|
|
247
|
+
className: "h-8",
|
|
248
|
+
type: "number",
|
|
249
|
+
min: 0,
|
|
250
|
+
step: "any",
|
|
251
|
+
value: M(s),
|
|
252
|
+
onChange: (e) => n((t) => ({
|
|
253
|
+
...t,
|
|
254
|
+
defaults: e.target.value.trim() === "" ? void 0 : { auto_advance_seconds: j(e.target.value) }
|
|
255
|
+
}))
|
|
256
|
+
})]
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function z({ label: e, value: t, names: n, onChange: r }) {
|
|
260
|
+
return /* @__PURE__ */ T(o, {
|
|
261
|
+
value: t,
|
|
262
|
+
onValueChange: (e) => r(String(e)),
|
|
263
|
+
children: [/* @__PURE__ */ w(l, {
|
|
264
|
+
size: "sm",
|
|
265
|
+
"aria-label": e,
|
|
266
|
+
children: /* @__PURE__ */ w(u, { children: (e) => e })
|
|
267
|
+
}), /* @__PURE__ */ w(s, { children: n.map((e) => /* @__PURE__ */ w(c, {
|
|
268
|
+
value: e,
|
|
269
|
+
children: e
|
|
270
|
+
}, e)) })]
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function B({ label: t, value: n, pages: r, anchors: i, none: a, onChange: d }) {
|
|
274
|
+
let { t: f } = e(), p = (e) => e === D ? a ?? "" : e.startsWith("anchor:") ? f("readerManifest.anchorNamed", { id: e.slice(7) }) : f("readerManifest.pageNamed", { name: e.slice(5) });
|
|
275
|
+
return /* @__PURE__ */ T(o, {
|
|
276
|
+
value: n,
|
|
277
|
+
onValueChange: (e) => d(String(e)),
|
|
278
|
+
children: [/* @__PURE__ */ w(l, {
|
|
279
|
+
size: "sm",
|
|
280
|
+
"aria-label": t,
|
|
281
|
+
children: /* @__PURE__ */ w(u, { children: p })
|
|
282
|
+
}), /* @__PURE__ */ T(s, { children: [
|
|
283
|
+
a !== void 0 && /* @__PURE__ */ w(c, {
|
|
284
|
+
value: D,
|
|
285
|
+
children: a
|
|
286
|
+
}),
|
|
287
|
+
i.map((e) => /* @__PURE__ */ w(c, {
|
|
288
|
+
value: `anchor:${e}`,
|
|
289
|
+
children: p(`anchor:${e}`)
|
|
290
|
+
}, `a-${e}`)),
|
|
291
|
+
r.map((e) => /* @__PURE__ */ w(c, {
|
|
292
|
+
value: `page:${e}`,
|
|
293
|
+
children: p(`page:${e}`)
|
|
294
|
+
}, `p-${e}`))
|
|
295
|
+
] })]
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function V({ label: t, onRemove: n }) {
|
|
299
|
+
let { t: i } = e();
|
|
300
|
+
return /* @__PURE__ */ w(r, {
|
|
301
|
+
type: "button",
|
|
302
|
+
size: "sm",
|
|
303
|
+
variant: "ghost",
|
|
304
|
+
"aria-label": t,
|
|
305
|
+
onClick: n,
|
|
306
|
+
children: i("common.remove")
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function H({ index: t, anchor: n, pages: r, onChange: a, onRemove: o }) {
|
|
310
|
+
let { t: s } = e(), c = t + 1;
|
|
311
|
+
return /* @__PURE__ */ T("div", {
|
|
312
|
+
className: "flex flex-wrap items-center gap-2",
|
|
313
|
+
"data-ckui": "reader-anchor",
|
|
314
|
+
children: [
|
|
315
|
+
/* @__PURE__ */ w(i, {
|
|
316
|
+
className: "h-8 w-28",
|
|
317
|
+
"aria-label": s("readerManifest.anchorId", { n: c }),
|
|
318
|
+
value: n.id,
|
|
319
|
+
onChange: (e) => a({
|
|
320
|
+
...n,
|
|
321
|
+
id: e.target.value
|
|
322
|
+
})
|
|
323
|
+
}),
|
|
324
|
+
/* @__PURE__ */ w(z, {
|
|
325
|
+
label: s("readerManifest.anchorPage", { n: c }),
|
|
326
|
+
value: n.page,
|
|
327
|
+
names: r,
|
|
328
|
+
onChange: (e) => a({
|
|
329
|
+
...n,
|
|
330
|
+
page: e
|
|
331
|
+
})
|
|
332
|
+
}),
|
|
333
|
+
/* @__PURE__ */ w(i, {
|
|
334
|
+
className: "h-8 w-24",
|
|
335
|
+
type: "number",
|
|
336
|
+
min: 0,
|
|
337
|
+
max: 100,
|
|
338
|
+
step: "any",
|
|
339
|
+
"aria-label": s("readerManifest.offset", { n: c }),
|
|
340
|
+
value: M(n.offset_percent),
|
|
341
|
+
onChange: (e) => a({
|
|
342
|
+
...n,
|
|
343
|
+
offset_percent: j(e.target.value)
|
|
344
|
+
})
|
|
345
|
+
}),
|
|
346
|
+
/* @__PURE__ */ w(i, {
|
|
347
|
+
className: "h-8 w-28",
|
|
348
|
+
type: "number",
|
|
349
|
+
min: 0,
|
|
350
|
+
step: "any",
|
|
351
|
+
"aria-label": s("readerManifest.duration", { n: c }),
|
|
352
|
+
placeholder: s("readerManifest.durationPlaceholder"),
|
|
353
|
+
value: M(n.duration_seconds),
|
|
354
|
+
onChange: (e) => a({
|
|
355
|
+
...n,
|
|
356
|
+
duration_seconds: e.target.value.trim() === "" ? void 0 : j(e.target.value)
|
|
357
|
+
})
|
|
358
|
+
}),
|
|
359
|
+
/* @__PURE__ */ w(V, {
|
|
360
|
+
label: s("readerManifest.removeAnchor", { n: c }),
|
|
361
|
+
onRemove: o
|
|
362
|
+
})
|
|
363
|
+
]
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
function U({ index: t, cue: n, names: r, anchors: i, onChange: a, onRemove: o }) {
|
|
367
|
+
let { t: s } = e(), c = t + 1;
|
|
368
|
+
return /* @__PURE__ */ T("div", {
|
|
369
|
+
className: "flex flex-wrap items-center gap-2",
|
|
370
|
+
"data-ckui": "reader-foreground",
|
|
371
|
+
children: [
|
|
372
|
+
/* @__PURE__ */ w(z, {
|
|
373
|
+
label: s("readerManifest.cueFile", { n: c }),
|
|
374
|
+
value: n.file,
|
|
375
|
+
names: r.files,
|
|
376
|
+
onChange: (e) => a({
|
|
377
|
+
...n,
|
|
378
|
+
file: e
|
|
379
|
+
})
|
|
380
|
+
}),
|
|
381
|
+
/* @__PURE__ */ w(B, {
|
|
382
|
+
label: s("readerManifest.cueAt", { n: c }),
|
|
383
|
+
value: O(n.page, n.anchor),
|
|
384
|
+
pages: r.pages,
|
|
385
|
+
anchors: i,
|
|
386
|
+
onChange: (e) => a({
|
|
387
|
+
file: n.file,
|
|
388
|
+
...k(e)
|
|
389
|
+
})
|
|
390
|
+
}),
|
|
391
|
+
/* @__PURE__ */ w(V, {
|
|
392
|
+
label: s("readerManifest.removeCue", { n: c }),
|
|
393
|
+
onRemove: o
|
|
394
|
+
})
|
|
395
|
+
]
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function W({ index: t, cue: n, names: r, anchors: a, onChange: o, onRemove: s }) {
|
|
399
|
+
let { t: c } = e(), l = t + 1, u = k(O(n.from_page, n.from_anchor)), d = k(O(n.to_page, n.to_anchor)), f = (e, t) => o({
|
|
400
|
+
file: n.file,
|
|
401
|
+
loop: n.loop,
|
|
402
|
+
layer: n.layer,
|
|
403
|
+
from_page: e.page,
|
|
404
|
+
from_anchor: e.anchor,
|
|
405
|
+
to_page: t.page,
|
|
406
|
+
to_anchor: t.anchor
|
|
407
|
+
});
|
|
408
|
+
return /* @__PURE__ */ T("div", {
|
|
409
|
+
className: "flex flex-wrap items-center gap-2",
|
|
410
|
+
"data-ckui": "reader-background",
|
|
411
|
+
children: [
|
|
412
|
+
/* @__PURE__ */ w(z, {
|
|
413
|
+
label: c("readerManifest.trackFile", { n: l }),
|
|
414
|
+
value: n.file,
|
|
415
|
+
names: r.files,
|
|
416
|
+
onChange: (e) => o({
|
|
417
|
+
...n,
|
|
418
|
+
file: e
|
|
419
|
+
})
|
|
420
|
+
}),
|
|
421
|
+
/* @__PURE__ */ w(B, {
|
|
422
|
+
label: c("readerManifest.trackFrom", { n: l }),
|
|
423
|
+
none: c("readerManifest.start"),
|
|
424
|
+
value: O(n.from_page, n.from_anchor),
|
|
425
|
+
pages: r.pages,
|
|
426
|
+
anchors: a,
|
|
427
|
+
onChange: (e) => f(k(e), d)
|
|
428
|
+
}),
|
|
429
|
+
/* @__PURE__ */ w(B, {
|
|
430
|
+
label: c("readerManifest.trackTo", { n: l }),
|
|
431
|
+
none: c("readerManifest.end"),
|
|
432
|
+
value: O(n.to_page, n.to_anchor),
|
|
433
|
+
pages: r.pages,
|
|
434
|
+
anchors: a,
|
|
435
|
+
onChange: (e) => f(u, k(e))
|
|
436
|
+
}),
|
|
437
|
+
/* @__PURE__ */ T("label", {
|
|
438
|
+
className: "flex items-center gap-1.5",
|
|
439
|
+
children: [/* @__PURE__ */ w(h, {
|
|
440
|
+
size: "sm",
|
|
441
|
+
checked: !!n.loop,
|
|
442
|
+
onCheckedChange: (e) => o({
|
|
443
|
+
...n,
|
|
444
|
+
loop: e || void 0
|
|
445
|
+
}),
|
|
446
|
+
"aria-label": c("readerManifest.loopN", { n: l })
|
|
447
|
+
}), c("readerManifest.loop")]
|
|
448
|
+
}),
|
|
449
|
+
/* @__PURE__ */ w(i, {
|
|
450
|
+
className: "h-8 w-28",
|
|
451
|
+
"aria-label": c("readerManifest.layer", { n: l }),
|
|
452
|
+
placeholder: c("readerManifest.layerPlaceholder"),
|
|
453
|
+
value: n.layer ?? "",
|
|
454
|
+
onChange: (e) => o({
|
|
455
|
+
...n,
|
|
456
|
+
layer: e.target.value || void 0
|
|
457
|
+
})
|
|
458
|
+
}),
|
|
459
|
+
/* @__PURE__ */ w(V, {
|
|
460
|
+
label: c("readerManifest.removeTrack", { n: l }),
|
|
461
|
+
onRemove: s
|
|
462
|
+
})
|
|
463
|
+
]
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
function G({ match: t }) {
|
|
467
|
+
let { t: n } = e();
|
|
468
|
+
return /* @__PURE__ */ T("div", {
|
|
469
|
+
className: "grid gap-1 rounded-md bg-muted p-2 text-xs",
|
|
470
|
+
role: "status",
|
|
471
|
+
children: [
|
|
472
|
+
/* @__PURE__ */ w("p", { children: t.foreground.length ? n("readerManifest.matched", { count: t.foreground.length }) : n("readerManifest.matchedNone") }),
|
|
473
|
+
t.unmatchedPages.length > 0 && /* @__PURE__ */ w("p", { children: n("readerManifest.pagesWithoutAudio", { names: t.unmatchedPages.join(", ") }) }),
|
|
474
|
+
t.unmatchedFiles.length > 0 && /* @__PURE__ */ w("p", { children: n("readerManifest.audioWithoutPlace", { names: t.unmatchedFiles.join(", ") }) })
|
|
475
|
+
]
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
function K({ problems: t }) {
|
|
479
|
+
let { t: n } = e(), r = (e) => n(`readerManifest.at_${e.at}`, { n: e.index + 1 });
|
|
480
|
+
return /* @__PURE__ */ w("ul", {
|
|
481
|
+
className: "grid gap-0.5 text-destructive",
|
|
482
|
+
role: "alert",
|
|
483
|
+
"data-ckui": "reader-problems",
|
|
484
|
+
children: t.map((e, t) => /* @__PURE__ */ w("li", { children: n(`readerManifest.problem_${e.problem}`, { where: r(e) }) }, t))
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
//#endregion
|
|
488
|
+
export { N as ReaderManifestEditor };
|
|
489
|
+
|
|
490
|
+
//# sourceMappingURL=reader-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader-manifest.js","names":[],"sources":["../../../src/components/admin/reader-manifest.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useId, useMemo, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Anchor, BackgroundCue, ForegroundCue, ReaderMeta, ReaderPlan } from \"../../client/generated/wire.js\";\nimport { emptyReaderMeta, matchAudio, readerMetaProblems, readerNames, type AudioMatch, type ReaderMetaProblem, type ReaderNames } from \"../../client/reader-meta.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useReaderPlan } from \"../../react/versions.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Switch } from \"#ckui/ui/switch\";\nimport { ErrorLine, useAction } from \"../social/parts.js\";\nimport { LoadFailed, Rows } from \"./shell.js\";\n\nexport interface ReaderManifestEditorProps {\n content: string;\n version: string;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\nconst NONE = \"__none__\";\n\n/** A page or an anchor as one select value. */\nconst placeOf = (page?: string, anchor?: string) => (anchor !== undefined ? `anchor:${anchor}` : page !== undefined ? `page:${page}` : NONE);\nconst fromPlace = (v: string): { page?: string; anchor?: string } =>\n v.startsWith(\"anchor:\") ? { anchor: v.slice(7) } : v.startsWith(\"page:\") ? { page: v.slice(5) } : {};\n\n/** Only what the server stores: no empty layers, defaults or lists. */\nfunction clean(meta: ReaderMeta): ReaderMeta {\n const auto = meta.defaults?.auto_advance_seconds;\n return {\n schema_version: 1,\n ...(auto !== undefined ? { defaults: { auto_advance_seconds: auto } } : {}),\n anchors: (meta.anchors ?? []).map((a) => ({ id: a.id.trim(), page: a.page, offset_percent: a.offset_percent, ...(a.duration_seconds !== undefined ? { duration_seconds: a.duration_seconds } : {}) })),\n background: (meta.background ?? []).map((c) => {\n const layer = c.layer?.trim();\n return { ...c, ...(layer ? { layer } : { layer: undefined }) };\n }),\n foreground: meta.foreground ?? [],\n };\n}\n\nconst num = (v: string): number => (v.trim() === \"\" ? Number.NaN : Number(v));\nconst shown = (n: number | undefined) => (n === undefined || Number.isNaN(n) ? \"\" : String(n));\n\n/**\n * Editors: a reader version's manifest over its published pages and audio:\n * scroll anchors, foreground cues (one track at a page or anchor, matched\n * from file names in one step), background tracks over a range, and the\n * auto-advance default. Page order is the version's media order.\n */\nexport function ReaderManifestEditor({ content, version, client: own, onError, className, appearance }: ReaderManifestEditorProps) {\n const { t } = useMessages();\n const client = useContentKitClient(own);\n const plan = useReaderPlan(content, version, { client });\n const names = useMemo<ReaderNames>(() => (plan.plan ? readerNames(plan.plan) : { pages: [], files: [] }), [plan.plan]);\n const [basis, setBasis] = useState<ReaderPlan | null>(null);\n const [meta, setMeta] = useState<ReaderMeta | null>(null);\n const [dirty, setDirty] = useState(false);\n const [saved, setSaved] = useState(false);\n const [matched, setMatched] = useState<AudioMatch | null>(null);\n const save = useAction(\"reader.save\", onError);\n // A fresh plan replaces the draft unless it has unsaved edits.\n if (plan.plan && plan.plan !== basis && !dirty) {\n setBasis(plan.plan);\n setMeta(plan.plan.reader ? structuredClone(plan.plan.reader) : emptyReaderMeta());\n }\n const problems = useMemo(() => (meta ? readerMetaProblems(meta, names) : []), [meta, names]);\n const edit = (fn: (m: ReaderMeta) => ReaderMeta) => {\n setMeta((m) => (m ? fn(m) : m));\n setDirty(true);\n setSaved(false);\n };\n const body = (() => {\n if (plan.loading && !plan.plan && !plan.pending) return <Rows count={2} />;\n if (plan.pending) return <p className=\"text-muted-foreground\">{t(\"readerManifest.pending\")}</p>;\n if (plan.error && !plan.plan) return <LoadFailed error={plan.error} message={t(\"readerManifest.loadFailed\")} retry={plan.reload} />;\n if (!meta) return null;\n const anchors = meta.anchors ?? [];\n const anchorIds = anchors.map((a) => a.id).filter(Boolean);\n return (\n <form\n className=\"grid gap-4\"\n onSubmit={(e) => {\n e.preventDefault();\n if (problems.length) return;\n setSaved(false);\n void save\n .run(async () => {\n await client.versions.setReader(content, version, clean(meta));\n return true;\n })\n .then((ok) => {\n if (!ok) return;\n setDirty(false);\n setSaved(true);\n });\n }}\n >\n <p className=\"text-muted-foreground\">{t(\"readerManifest.summary\", { pages: names.pages.length, files: names.files.length })}</p>\n <Defaults meta={meta} edit={edit} />\n <Group title={t(\"readerManifest.anchors\")} add={t(\"readerManifest.addAnchor\")} disabled={!names.pages.length}\n onAdd={() => edit((m) => ({ ...m, anchors: [...(m.anchors ?? []), { id: nextId(m.anchors ?? []), page: names.pages[0]!, offset_percent: 0 }] }))}>\n {anchors.map((a, i) => (\n <AnchorRow key={i} index={i} anchor={a} pages={names.pages} onChange={(next) => edit((m) => ({ ...m, anchors: replace(m.anchors, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, anchors: drop(m.anchors, i) }))} />\n ))}\n </Group>\n <Group title={t(\"readerManifest.foreground\")} add={t(\"readerManifest.addCue\")} disabled={!names.files.length || !names.pages.length}\n onAdd={() => edit((m) => ({ ...m, foreground: [...(m.foreground ?? []), { file: names.files[0]!, page: names.pages[0]! }] }))}\n extra={\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={!names.files.length} onClick={() => {\n const r = matchAudio(names, anchorIds);\n setMatched(r);\n if (r.foreground.length) edit((m) => ({ ...m, foreground: r.foreground }));\n }}>\n {t(\"readerManifest.match\")}\n </Button>\n }>\n {matched && <MatchReport match={matched} />}\n {(meta.foreground ?? []).map((c, i) => (\n <ForegroundRow key={i} index={i} cue={c} names={names} anchors={anchorIds} onChange={(next) => edit((m) => ({ ...m, foreground: replace(m.foreground, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, foreground: drop(m.foreground, i) }))} />\n ))}\n </Group>\n <Group title={t(\"readerManifest.background\")} add={t(\"readerManifest.addTrack\")} disabled={!names.files.length}\n onAdd={() => edit((m) => ({ ...m, background: [...(m.background ?? []), { file: names.files[0]! }] }))}>\n {(meta.background ?? []).map((c, i) => (\n <BackgroundRow key={i} index={i} cue={c} names={names} anchors={anchorIds} onChange={(next) => edit((m) => ({ ...m, background: replace(m.background, i, next) }))}\n onRemove={() => edit((m) => ({ ...m, background: drop(m.background, i) }))} />\n ))}\n </Group>\n {problems.length > 0 && <Problems problems={problems} />}\n <ErrorLine error={save.error} />\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button type=\"submit\" size=\"sm\" disabled={save.pending || !dirty || problems.length > 0}>\n {save.pending ? t(\"common.saving\") : t(\"common.save\")}\n </Button>\n {dirty && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => {\n setDirty(false);\n setBasis(null);\n setMatched(null);\n }}>\n {t(\"readerManifest.revert\")}\n </Button>\n )}\n {saved && <span role=\"status\" className=\"text-muted-foreground\">{t(\"readerManifest.saved\")}</span>}\n </div>\n </form>\n );\n })();\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 text-sm\", className)} data-ckui=\"reader-manifest\">\n <h4 className=\"font-medium\">{t(\"readerManifest.title\")}</h4>\n {body}\n </ContentKitUiRoot>\n );\n}\n\nfunction nextId(anchors: readonly Anchor[]): string {\n const taken = new Set(anchors.map((a) => a.id));\n let n = anchors.length + 1;\n while (taken.has(`a${n}`)) n++;\n return `a${n}`;\n}\n\nconst replace = <T,>(list: T[] | undefined, i: number, next: T): T[] => (list ?? []).map((x, j) => (j === i ? next : x));\nconst drop = <T,>(list: T[] | undefined, i: number): T[] => (list ?? []).filter((_, j) => j !== i);\n\nfunction Group({ title, add, disabled, onAdd, extra, children }: { title: string; add: string; disabled: boolean; onAdd: () => void; extra?: ReactNode; children: ReactNode }) {\n return (\n <fieldset className=\"grid gap-2 rounded-md border border-border p-3\">\n <legend className=\"px-1 font-medium\">{title}</legend>\n {children}\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" size=\"sm\" variant=\"outline\" disabled={disabled} onClick={onAdd}>\n {add}\n </Button>\n {extra}\n </div>\n </fieldset>\n );\n}\n\nfunction Defaults({ meta, edit }: { meta: ReaderMeta; edit: (fn: (m: ReaderMeta) => ReaderMeta) => void }) {\n const { t } = useMessages();\n const id = useId();\n const auto = meta.defaults?.auto_advance_seconds;\n return (\n <div className=\"grid max-w-xs gap-1.5\">\n <Label htmlFor={`${id}-auto`}>{t(\"readerManifest.autoAdvance\")}</Label>\n <Input id={`${id}-auto`} className=\"h-8\" type=\"number\" min={0} step=\"any\" value={shown(auto)}\n onChange={(e) => edit((m) => ({ ...m, defaults: e.target.value.trim() === \"\" ? undefined : { auto_advance_seconds: num(e.target.value) } }))} />\n </div>\n );\n}\n\nfunction NameSelect({ label, value, names, onChange }: { label: string; value: string; names: readonly string[]; onChange: (v: string) => void }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{(v: string) => v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {names.map((n) => (\n <SelectItem key={n} value={n}>\n {n}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction PlaceSelect({ label, value, pages, anchors, none, onChange }: { label: string; value: string; pages: readonly string[]; anchors: readonly string[]; none?: string; onChange: (v: string) => void }) {\n const { t } = useMessages();\n const text = (v: string) => (v === NONE ? (none ?? \"\") : v.startsWith(\"anchor:\") ? t(\"readerManifest.anchorNamed\", { id: v.slice(7) }) : t(\"readerManifest.pageNamed\", { name: v.slice(5) }));\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{text}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {none !== undefined && <SelectItem value={NONE}>{none}</SelectItem>}\n {anchors.map((a) => (\n <SelectItem key={`a-${a}`} value={`anchor:${a}`}>\n {text(`anchor:${a}`)}\n </SelectItem>\n ))}\n {pages.map((p) => (\n <SelectItem key={`p-${p}`} value={`page:${p}`}>\n {text(`page:${p}`)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction Remove({ label, onRemove }: { label: string; onRemove: () => void }) {\n const { t } = useMessages();\n return (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" aria-label={label} onClick={onRemove}>\n {t(\"common.remove\")}\n </Button>\n );\n}\n\nfunction AnchorRow({ index, anchor: a, pages, onChange, onRemove }: { index: number; anchor: Anchor; pages: readonly string[]; onChange: (a: Anchor) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-anchor\">\n <Input className=\"h-8 w-28\" aria-label={t(\"readerManifest.anchorId\", { n })} value={a.id} onChange={(e) => onChange({ ...a, id: e.target.value })} />\n <NameSelect label={t(\"readerManifest.anchorPage\", { n })} value={a.page} names={pages} onChange={(page) => onChange({ ...a, page })} />\n <Input className=\"h-8 w-24\" type=\"number\" min={0} max={100} step=\"any\" aria-label={t(\"readerManifest.offset\", { n })} value={shown(a.offset_percent)}\n onChange={(e) => onChange({ ...a, offset_percent: num(e.target.value) })} />\n <Input className=\"h-8 w-28\" type=\"number\" min={0} step=\"any\" aria-label={t(\"readerManifest.duration\", { n })} placeholder={t(\"readerManifest.durationPlaceholder\")}\n value={shown(a.duration_seconds)} onChange={(e) => onChange({ ...a, duration_seconds: e.target.value.trim() === \"\" ? undefined : num(e.target.value) })} />\n <Remove label={t(\"readerManifest.removeAnchor\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction ForegroundRow({ index, cue: c, names, anchors, onChange, onRemove }: { index: number; cue: ForegroundCue; names: ReaderNames; anchors: readonly string[]; onChange: (c: ForegroundCue) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-foreground\">\n <NameSelect label={t(\"readerManifest.cueFile\", { n })} value={c.file} names={names.files} onChange={(file) => onChange({ ...c, file })} />\n <PlaceSelect label={t(\"readerManifest.cueAt\", { n })} value={placeOf(c.page, c.anchor)} pages={names.pages} anchors={anchors} onChange={(v) => onChange({ file: c.file, ...fromPlace(v) })} />\n <Remove label={t(\"readerManifest.removeCue\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction BackgroundRow({ index, cue: c, names, anchors, onChange, onRemove }: { index: number; cue: BackgroundCue; names: ReaderNames; anchors: readonly string[]; onChange: (c: BackgroundCue) => void; onRemove: () => void }) {\n const { t } = useMessages();\n const n = index + 1;\n const from = fromPlace(placeOf(c.from_page, c.from_anchor));\n const to = fromPlace(placeOf(c.to_page, c.to_anchor));\n const set = (f: typeof from, tt: typeof to) => onChange({ file: c.file, loop: c.loop, layer: c.layer, from_page: f.page, from_anchor: f.anchor, to_page: tt.page, to_anchor: tt.anchor });\n return (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"reader-background\">\n <NameSelect label={t(\"readerManifest.trackFile\", { n })} value={c.file} names={names.files} onChange={(file) => onChange({ ...c, file })} />\n <PlaceSelect label={t(\"readerManifest.trackFrom\", { n })} none={t(\"readerManifest.start\")} value={placeOf(c.from_page, c.from_anchor)} pages={names.pages} anchors={anchors}\n onChange={(v) => set(fromPlace(v), to)} />\n <PlaceSelect label={t(\"readerManifest.trackTo\", { n })} none={t(\"readerManifest.end\")} value={placeOf(c.to_page, c.to_anchor)} pages={names.pages} anchors={anchors}\n onChange={(v) => set(from, fromPlace(v))} />\n <label className=\"flex items-center gap-1.5\">\n <Switch size=\"sm\" checked={!!c.loop} onCheckedChange={(on) => onChange({ ...c, loop: on || undefined })} aria-label={t(\"readerManifest.loopN\", { n })} />\n {t(\"readerManifest.loop\")}\n </label>\n <Input className=\"h-8 w-28\" aria-label={t(\"readerManifest.layer\", { n })} placeholder={t(\"readerManifest.layerPlaceholder\")} value={c.layer ?? \"\"}\n onChange={(e) => onChange({ ...c, layer: e.target.value || undefined })} />\n <Remove label={t(\"readerManifest.removeTrack\", { n })} onRemove={onRemove} />\n </div>\n );\n}\n\nfunction MatchReport({ match }: { match: AudioMatch }) {\n const { t } = useMessages();\n return (\n <div className=\"grid gap-1 rounded-md bg-muted p-2 text-xs\" role=\"status\">\n <p>{match.foreground.length ? t(\"readerManifest.matched\", { count: match.foreground.length }) : t(\"readerManifest.matchedNone\")}</p>\n {match.unmatchedPages.length > 0 && <p>{t(\"readerManifest.pagesWithoutAudio\", { names: match.unmatchedPages.join(\", \") })}</p>}\n {match.unmatchedFiles.length > 0 && <p>{t(\"readerManifest.audioWithoutPlace\", { names: match.unmatchedFiles.join(\", \") })}</p>}\n </div>\n );\n}\n\nfunction Problems({ problems }: { problems: ReaderMetaProblem[] }) {\n const { t } = useMessages();\n const where = (p: ReaderMetaProblem) => t(`readerManifest.at_${p.at}`, { n: p.index + 1 });\n return (\n <ul className=\"grid gap-0.5 text-destructive\" role=\"alert\" data-ckui=\"reader-problems\">\n {problems.map((p, i) => (\n <li key={i}>{t(`readerManifest.problem_${p.problem}`, { where: where(p) })}</li>\n ))}\n </ul>\n );\n}\n"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ function x(e, t, n = [], r) {
|
|
|
19
19
|
if (t.allowed) return null;
|
|
20
20
|
if (t.unknown) return e.t("contents.unknownAccess");
|
|
21
21
|
let i = t.offers?.[0];
|
|
22
|
-
if (i) return u(e, i.terms);
|
|
22
|
+
if (i) return i.target.kind === "group" && Number(i.terms.amount) === 0 ? e.t("contents.joinFree") : u(e, i.terms);
|
|
23
23
|
let a = t.sell?.find((e) => n.includes(e));
|
|
24
24
|
return a ? c(r, a) : t.sell?.some((e) => e.includes(":group:")) ? e.t("contents.members") : t.sell?.length ? e.t("contents.forSale") : e.t("contents.locked");
|
|
25
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","names":[],"sources":["../../../src/components/content/card.tsx"],"sourcesContent":["import { FavouriteIcon, LockIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, type MouseEvent, type ReactNode } from \"react\";\nimport type { Card, CardAccess } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { formatDate, RelativeTime } from \"../social/parts.js\";\nimport { GroupAvatar, GroupLink, labelOf, useLanguageName, type GroupLike, type Labels } from \"../groups/parts.js\";\nimport { priceLabel } from \"./paywall.js\";\n\nexport interface ContentCardProps {\n card: Card;\n /** The card's link; default its canonical path. */\n href?: (card: Card) => string | undefined;\n /** Before the card's link is followed: a click signal. */\n onOpen?: (card: Card) => void;\n /** Cover shape: \"portrait\" (3:4, readers) or \"landscape\" (16:9, players). Default portrait. */\n shape?: \"portrait\" | \"landscape\";\n /** The group kinds whose chips show, in order; default every kind the card names. */\n groupKinds?: readonly string[];\n /** Chips per group kind. Default 3. */\n chips?: number;\n /** Tiers the site sells, so a lock names them; others show as \"for sale\". */\n tiers?: readonly string[];\n /** Display names of kinds, traits and tiers by identifier. */\n labels?: Labels;\n /** Show the owner group (a channel) when the card has one; default true, false on that group's own feed. */\n owner?: boolean;\n /** The owner group's link; default its canonical path. */\n ownerHref?: (g: GroupLike) => string | undefined;\n /** Below the meta line: the host's own. */\n children?: ReactNode;\n className?: string;\n}\n\nconst compact = (n: number, language?: string) => new Intl.NumberFormat(language, { notation: \"compact\", maximumFractionDigits: 1 }).format(n);\n\n/** Why a card is locked, in words (its price when it is on sale); null when it opens. */\nexport function lockLabel(m: Translator, a: CardAccess, tiers: readonly string[] = [], labels?: Labels): string | null {\n if (a.allowed) return null;\n if (a.unknown) return m.t(\"contents.unknownAccess\");\n const offer = a.offers?.[0];\n if (offer) return priceLabel(m, offer.terms);\n const tier = a.sell?.find((k) => tiers.includes(k));\n if (tier) return labelOf(labels, tier);\n if (a.sell?.some((k) => k.includes(\":group:\"))) return m.t(\"contents.members\");\n if (a.sell?.length) return m.t(\"contents.forSale\");\n return m.t(\"contents.locked\");\n}\n\n/** A link followed in-app through the provider's navigate. */\nfunction useLink(url: string | undefined, before?: () => void) {\n const navigate = useContext(ContentKitContext)?.navigate;\n return useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n before?.();\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url, before],\n );\n}\n\n/** One content in a listing: cover, title, version, groups, counts, the reader's progress and why it is locked. */\nexport function ContentCard({ card, href, onOpen, shape = \"portrait\", groupKinds, chips = 3, tiers, labels, owner = true, ownerHref, children, className }: ContentCardProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const v = card.version;\n const progress = card.viewer && card.viewer.progress_max > 0 ? Math.min(1, card.viewer.progress / card.viewer.progress_max) : 0;\n const kinds = groupKinds ?? Object.keys(card.groups);\n const minutes = v.duration_ms ? Math.round(v.duration_ms / 60_000) : 0;\n const meta = [\n ...v.languages.map(languageName),\n ...v.traits.map((x) => labelOf(labels, x)),\n v.pages ? m.plural(\"contents.pages\", v.pages) : null,\n minutes ? m.plural(\"contents.minutes\", minutes) : null,\n ].filter(Boolean);\n const title = card.title || card.code;\n return (\n <article className={cn(\"group/card grid content-start gap-2\", className)} data-ckui=\"content-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n <a href={url} onClick={onClick} className=\"relative block overflow-hidden rounded-lg bg-muted\" tabIndex={-1} aria-hidden>\n <div className={shape === \"landscape\" ? \"aspect-video\" : \"aspect-[3/4]\"}>\n <RenditionImg\n outputs={card.cover}\n alt=\"\"\n fit=\"cover\"\n skeleton\n className=\"size-full transition-transform group-hover/card:scale-[1.02]\"\n fallback={<span className=\"grid size-full place-items-center text-2xl font-semibold text-muted-foreground\">{title.charAt(0).toUpperCase()}</span>}\n />\n </div>\n {lock && (\n <span className=\"absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n )}\n {progress > 0 && (\n <span className=\"absolute inset-x-0 bottom-0 h-1 bg-background/60\" data-ckui=\"card-progress\">\n <span className=\"block h-full bg-primary\" style={{ width: `${Math.round(progress * 100)}%` }} />\n </span>\n )}\n </a>\n <div className=\"grid gap-1\">\n <a href={url} onClick={onClick} className=\"line-clamp-2 font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n {lock && <span className=\"sr-only\"> ({lock})</span>}\n </a>\n {owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} />}\n {meta.length > 0 && <p className=\"text-xs text-muted-foreground\">{meta.join(\" · \")}</p>}\n {card.viewer?.completed && (\n <Badge variant=\"secondary\" className=\"justify-self-start\">\n {t(\"contents.completed\")}\n </Badge>\n )}\n {!card.access.allowed && card.access.free_after && <p className=\"text-xs text-muted-foreground\">{t(\"contents.freeFrom\", { date: formatDate(m, card.access.free_after, false) })}</p>}\n {kinds.some((k) => card.groups[k]?.length) && (\n <ul className=\"flex flex-wrap gap-1\" data-ckui=\"card-groups\">\n {kinds.flatMap((k) =>\n (card.groups[k] ?? []).slice(0, chips).map((g) => (\n <li key={`${k}:${g.id}`} data-kind={k}>\n <Chip url={g.path} name={g.name || g.slug} />\n </li>\n )),\n )}\n </ul>\n )}\n <p className=\"flex items-center gap-3 text-xs text-muted-foreground tabular-nums\" data-ckui=\"card-counts\">\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.favorites\")}>\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.likes\")}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n <span className=\"sr-only\">{t(\"contents.likes\")}</span>\n {compact(card.counts.likes, m.language)}\n </span>\n </p>\n {children}\n </div>\n </article>\n );\n}\n\n/** The group that owns a content (a channel): its avatar and name, linking to its page. */\nexport function OwnerChip({ group, href, className }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string }) {\n return (\n <span className={cn(\"flex min-w-0 text-xs text-muted-foreground\", className)} data-ckui=\"card-owner\" data-id={group.id}>\n <GroupLink group={group} href={href} className=\"inline-flex min-w-0 items-center gap-1.5 hover:text-foreground\">\n <GroupAvatar group={group} className=\"size-5 shrink-0 text-[0.625rem]\" />\n <span className=\"truncate\">{group.name || group.slug}</span>\n </GroupLink>\n </span>\n );\n}\n\nfunction Chip({ url, name }: { url?: string; name: string }) {\n const onClick = useLink(url);\n const cls = \"inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground\";\n return url ? (\n <a href={url} onClick={onClick} className={cn(cls, \"hover:bg-muted hover:text-foreground\")}>\n {name}\n </a>\n ) : (\n <span className={cls}>{name}</span>\n );\n}\n\nexport interface PostCardProps extends Omit<ContentCardProps, \"shape\" | \"chips\" | \"groupKinds\"> {\n /** Above the title: the post's channel or author; default its owner group's chip. */\n byline?: ReactNode;\n}\n\n/** A post in a feed: a wide cover, its title, when it was published and what unlocking it costs. */\nexport function PostCard({ card, href, onOpen, tiers, labels, byline, owner = true, ownerHref, children, className }: PostCardProps) {\n const m = useMessages();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const title = card.title || card.code;\n return (\n <article className={cn(\"grid gap-3 rounded-lg border border-border p-3\", className)} data-ckui=\"post-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n {byline ?? (owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} className=\"text-sm\" />)}\n <a href={url} onClick={onClick} className=\"grid gap-2\">\n <span className=\"font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n </span>\n {card.cover.length > 0 && (\n <span className=\"relative block aspect-video overflow-hidden rounded-md bg-muted\">\n <RenditionImg outputs={card.cover} alt=\"\" fit=\"cover\" skeleton className={cn(\"size-full\", lock && \"blur-md\")} />\n {lock && (\n <span className=\"absolute inset-0 grid place-items-center\">\n <span className=\"inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n </span>\n )}\n </span>\n )}\n </a>\n <p className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {card.published_at && <RelativeTime at={card.published_at} />}\n {!card.cover.length && lock && <span data-ckui=\"card-lock\">{lock}</span>}\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{m.t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n </p>\n {children}\n </article>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"card.js","names":[],"sources":["../../../src/components/content/card.tsx"],"sourcesContent":["import { FavouriteIcon, LockIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, type MouseEvent, type ReactNode } from \"react\";\nimport type { Card, CardAccess } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { formatDate, RelativeTime } from \"../social/parts.js\";\nimport { GroupAvatar, GroupLink, labelOf, useLanguageName, type GroupLike, type Labels } from \"../groups/parts.js\";\nimport { priceLabel } from \"./paywall.js\";\n\nexport interface ContentCardProps {\n card: Card;\n /** The card's link; default its canonical path. */\n href?: (card: Card) => string | undefined;\n /** Before the card's link is followed: a click signal. */\n onOpen?: (card: Card) => void;\n /** Cover shape: \"portrait\" (3:4, readers) or \"landscape\" (16:9, players). Default portrait. */\n shape?: \"portrait\" | \"landscape\";\n /** The group kinds whose chips show, in order; default every kind the card names. */\n groupKinds?: readonly string[];\n /** Chips per group kind. Default 3. */\n chips?: number;\n /** Tiers the site sells, so a lock names them; others show as \"for sale\". */\n tiers?: readonly string[];\n /** Display names of kinds, traits and tiers by identifier. */\n labels?: Labels;\n /** Show the owner group (a channel) when the card has one; default true, false on that group's own feed. */\n owner?: boolean;\n /** The owner group's link; default its canonical path. */\n ownerHref?: (g: GroupLike) => string | undefined;\n /** Below the meta line: the host's own. */\n children?: ReactNode;\n className?: string;\n}\n\nconst compact = (n: number, language?: string) => new Intl.NumberFormat(language, { notation: \"compact\", maximumFractionDigits: 1 }).format(n);\n\n/** Why a card is locked, in words (its price when it is on sale); null when it opens. */\nexport function lockLabel(m: Translator, a: CardAccess, tiers: readonly string[] = [], labels?: Labels): string | null {\n if (a.allowed) return null;\n if (a.unknown) return m.t(\"contents.unknownAccess\");\n const offer = a.offers?.[0];\n if (offer) return offer.target.kind === \"group\" && Number(offer.terms.amount) === 0 ? m.t(\"contents.joinFree\") : priceLabel(m, offer.terms);\n const tier = a.sell?.find((k) => tiers.includes(k));\n if (tier) return labelOf(labels, tier);\n if (a.sell?.some((k) => k.includes(\":group:\"))) return m.t(\"contents.members\");\n if (a.sell?.length) return m.t(\"contents.forSale\");\n return m.t(\"contents.locked\");\n}\n\n/** A link followed in-app through the provider's navigate. */\nfunction useLink(url: string | undefined, before?: () => void) {\n const navigate = useContext(ContentKitContext)?.navigate;\n return useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n before?.();\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url, before],\n );\n}\n\n/** One content in a listing: cover, title, version, groups, counts, the reader's progress and why it is locked. */\nexport function ContentCard({ card, href, onOpen, shape = \"portrait\", groupKinds, chips = 3, tiers, labels, owner = true, ownerHref, children, className }: ContentCardProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const v = card.version;\n const progress = card.viewer && card.viewer.progress_max > 0 ? Math.min(1, card.viewer.progress / card.viewer.progress_max) : 0;\n const kinds = groupKinds ?? Object.keys(card.groups);\n const minutes = v.duration_ms ? Math.round(v.duration_ms / 60_000) : 0;\n const meta = [\n ...v.languages.map(languageName),\n ...v.traits.map((x) => labelOf(labels, x)),\n v.pages ? m.plural(\"contents.pages\", v.pages) : null,\n minutes ? m.plural(\"contents.minutes\", minutes) : null,\n ].filter(Boolean);\n const title = card.title || card.code;\n return (\n <article className={cn(\"group/card grid content-start gap-2\", className)} data-ckui=\"content-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n <a href={url} onClick={onClick} className=\"relative block overflow-hidden rounded-lg bg-muted\" tabIndex={-1} aria-hidden>\n <div className={shape === \"landscape\" ? \"aspect-video\" : \"aspect-[3/4]\"}>\n <RenditionImg\n outputs={card.cover}\n alt=\"\"\n fit=\"cover\"\n skeleton\n className=\"size-full transition-transform group-hover/card:scale-[1.02]\"\n fallback={<span className=\"grid size-full place-items-center text-2xl font-semibold text-muted-foreground\">{title.charAt(0).toUpperCase()}</span>}\n />\n </div>\n {lock && (\n <span className=\"absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n )}\n {progress > 0 && (\n <span className=\"absolute inset-x-0 bottom-0 h-1 bg-background/60\" data-ckui=\"card-progress\">\n <span className=\"block h-full bg-primary\" style={{ width: `${Math.round(progress * 100)}%` }} />\n </span>\n )}\n </a>\n <div className=\"grid gap-1\">\n <a href={url} onClick={onClick} className=\"line-clamp-2 font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n {lock && <span className=\"sr-only\"> ({lock})</span>}\n </a>\n {owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} />}\n {meta.length > 0 && <p className=\"text-xs text-muted-foreground\">{meta.join(\" · \")}</p>}\n {card.viewer?.completed && (\n <Badge variant=\"secondary\" className=\"justify-self-start\">\n {t(\"contents.completed\")}\n </Badge>\n )}\n {!card.access.allowed && card.access.free_after && <p className=\"text-xs text-muted-foreground\">{t(\"contents.freeFrom\", { date: formatDate(m, card.access.free_after, false) })}</p>}\n {kinds.some((k) => card.groups[k]?.length) && (\n <ul className=\"flex flex-wrap gap-1\" data-ckui=\"card-groups\">\n {kinds.flatMap((k) =>\n (card.groups[k] ?? []).slice(0, chips).map((g) => (\n <li key={`${k}:${g.id}`} data-kind={k}>\n <Chip url={g.path} name={g.name || g.slug} />\n </li>\n )),\n )}\n </ul>\n )}\n <p className=\"flex items-center gap-3 text-xs text-muted-foreground tabular-nums\" data-ckui=\"card-counts\">\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.favorites\")}>\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.likes\")}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n <span className=\"sr-only\">{t(\"contents.likes\")}</span>\n {compact(card.counts.likes, m.language)}\n </span>\n </p>\n {children}\n </div>\n </article>\n );\n}\n\n/** The group that owns a content (a channel): its avatar and name, linking to its page. */\nexport function OwnerChip({ group, href, className }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string }) {\n return (\n <span className={cn(\"flex min-w-0 text-xs text-muted-foreground\", className)} data-ckui=\"card-owner\" data-id={group.id}>\n <GroupLink group={group} href={href} className=\"inline-flex min-w-0 items-center gap-1.5 hover:text-foreground\">\n <GroupAvatar group={group} className=\"size-5 shrink-0 text-[0.625rem]\" />\n <span className=\"truncate\">{group.name || group.slug}</span>\n </GroupLink>\n </span>\n );\n}\n\nfunction Chip({ url, name }: { url?: string; name: string }) {\n const onClick = useLink(url);\n const cls = \"inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground\";\n return url ? (\n <a href={url} onClick={onClick} className={cn(cls, \"hover:bg-muted hover:text-foreground\")}>\n {name}\n </a>\n ) : (\n <span className={cls}>{name}</span>\n );\n}\n\nexport interface PostCardProps extends Omit<ContentCardProps, \"shape\" | \"chips\" | \"groupKinds\"> {\n /** Above the title: the post's channel or author; default its owner group's chip. */\n byline?: ReactNode;\n}\n\n/** A post in a feed: a wide cover, its title, when it was published and what unlocking it costs. */\nexport function PostCard({ card, href, onOpen, tiers, labels, byline, owner = true, ownerHref, children, className }: PostCardProps) {\n const m = useMessages();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const title = card.title || card.code;\n return (\n <article className={cn(\"grid gap-3 rounded-lg border border-border p-3\", className)} data-ckui=\"post-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n {byline ?? (owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} className=\"text-sm\" />)}\n <a href={url} onClick={onClick} className=\"grid gap-2\">\n <span className=\"font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n </span>\n {card.cover.length > 0 && (\n <span className=\"relative block aspect-video overflow-hidden rounded-md bg-muted\">\n <RenditionImg outputs={card.cover} alt=\"\" fit=\"cover\" skeleton className={cn(\"size-full\", lock && \"blur-md\")} />\n {lock && (\n <span className=\"absolute inset-0 grid place-items-center\">\n <span className=\"inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n </span>\n )}\n </span>\n )}\n </a>\n <p className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {card.published_at && <RelativeTime at={card.published_at} />}\n {!card.cover.length && lock && <span data-ckui=\"card-lock\">{lock}</span>}\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{m.t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n </p>\n {children}\n </article>\n );\n}\n"],"mappings":"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"}
|