@openrails/contentkit-ui 0.99.3 → 0.100.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.
Files changed (49) hide show
  1. package/README.md +15 -2
  2. package/dist/client/discovery.d.ts +6 -1
  3. package/dist/client/discovery.js +10 -7
  4. package/dist/client/discovery.js.map +1 -1
  5. package/dist/client/generated/wire.d.ts +1 -0
  6. package/dist/client/generated/wire.js.map +1 -1
  7. package/dist/client/index.d.ts +1 -1
  8. package/dist/components/admin/group-manager.js +124 -117
  9. package/dist/components/admin/group-manager.js.map +1 -1
  10. package/dist/components/admin/user-comment-standing.js +8 -6
  11. package/dist/components/admin/user-comment-standing.js.map +1 -1
  12. package/dist/components/content/card.js +2 -2
  13. package/dist/components/content/card.js.map +1 -1
  14. package/dist/components/content/editors.d.ts +2 -2
  15. package/dist/components/content/editors.js +447 -328
  16. package/dist/components/content/editors.js.map +1 -1
  17. package/dist/components/content/filters.js +36 -18
  18. package/dist/components/content/filters.js.map +1 -1
  19. package/dist/components/content/grid.js +1 -1
  20. package/dist/components/content/grid.js.map +1 -1
  21. package/dist/components/content/popular.d.ts +3 -3
  22. package/dist/components/content/popular.js +15 -9
  23. package/dist/components/content/popular.js.map +1 -1
  24. package/dist/components/groups/directory.js +13 -7
  25. package/dist/components/groups/directory.js.map +1 -1
  26. package/dist/components/groups/memberships.js +13 -7
  27. package/dist/components/groups/memberships.js.map +1 -1
  28. package/dist/components/rendition-img.js +73 -55
  29. package/dist/components/rendition-img.js.map +1 -1
  30. package/dist/components/social/moderation.js +14 -5
  31. package/dist/components/social/moderation.js.map +1 -1
  32. package/dist/locales/discovery/de.js +1 -0
  33. package/dist/locales/discovery/de.js.map +1 -1
  34. package/dist/locales/discovery/en.d.ts +1 -0
  35. package/dist/locales/discovery/en.js +1 -0
  36. package/dist/locales/discovery/en.js.map +1 -1
  37. package/dist/locales/discovery/es.js +1 -0
  38. package/dist/locales/discovery/es.js.map +1 -1
  39. package/dist/locales/discovery/ja.js +1 -0
  40. package/dist/locales/discovery/ja.js.map +1 -1
  41. package/dist/locales/discovery/ko.js +1 -0
  42. package/dist/locales/discovery/ko.js.map +1 -1
  43. package/dist/locales/discovery/zh.js +1 -0
  44. package/dist/locales/discovery/zh.js.map +1 -1
  45. package/dist/locales/en.d.ts +1 -0
  46. package/dist/react/read.d.ts +3 -2
  47. package/dist/react/read.js +68 -61
  48. package/dist/react/read.js.map +1 -1
  49. package/package.json +1 -1
@@ -1,27 +1,29 @@
1
1
  import { itemRef as e } from "../client/media/ref.js";
2
2
  import { useOptionalContentKitClient as t, useReadScope as n } from "./context.js";
3
- import { chunkOffsets as r, mergeReads as i, processing as a } from "../client/media/windows.js";
4
- import { readKey as o, storeFor as s } from "./store.js";
5
- import { useCallback as c, useEffect as l, useMemo as u, useRef as d, useState as f, useSyncExternalStore as ee } from "react";
3
+ import { chunkOffsets as r, mergeReads as i, processing as ee } from "../client/media/windows.js";
4
+ import { readKey as a, storeFor as o } from "./store.js";
5
+ import { useCallback as s, useEffect as c, useMemo as l, useRef as u, useState as d, useSyncExternalStore as te } from "react";
6
6
  //#region src/react/read.ts
7
- var te = 2500, p = [];
7
+ var f = 2500, p = [];
8
8
  function m(m, h = {}) {
9
- let { read: g, client: _, prefix: v, offset: y, limit: b, download: x, editor: S, window: C, refresh: w = !0 } = h, T = h.poll ?? (S ? te : !1), E = t(_), D = n(), O = E ? s(E) : null, k = C?.chunk ?? 50, A = D + o(m, {
9
+ let { read: g, client: _, prefix: v, offset: y, limit: b, download: x, editor: S, window: C, refresh: w = !0 } = h, T = h.poll ?? (S ? f : !1), E = t(_), D = n(), O = E ? o(E) : null, k = C?.chunk ?? 50, A = D + a(m, {
10
10
  prefix: v,
11
11
  download: x,
12
12
  editor: S
13
- }) + (C ? `#${k}` : `#${y ?? 0}:${b ?? 0}`), [j, M] = f({ base: A }), N = j.base === A ? j.n : void 0, P = C ? r(C.start, C.end, N ?? C.start + 1, k) : [], [F, I] = f({
13
+ }) + (C ? `#${k}` : `#${y ?? 0}:${b ?? 0}`), [j, M] = d({
14
14
  base: A,
15
- offsets: []
16
- }), L = F.base === A ? F.offsets : [];
17
- C && (F.base !== A || P.some((e) => !L.includes(e))) && I({
15
+ store: O,
16
+ read: null
17
+ }), N = j.base === A && j.store === O;
18
+ N || M({
18
19
  base: A,
19
- offsets: [.../* @__PURE__ */ new Set([...L, ...P])].sort((e, t) => e - t)
20
+ store: O,
21
+ read: null
20
22
  });
21
- let R = C ? L.length ? L : P.length ? P : [0] : [y ?? 0], z = u(() => R.map((e) => ({
23
+ let P = N ? j.n : void 0, F = C ? r(C.start, C.end, P ?? C.start + 1, k) : [], I = C ? F.length ? F : [0] : [y ?? 0], L = l(() => I.map((e) => ({
22
24
  offset: e,
23
25
  limit: C ? k : b
24
- })), [R.join(","), C ? k : b]), B = u(() => z.map((e) => D + o(m, {
26
+ })), [I.join(","), C ? k : b]), R = l(() => L.map((e) => D + a(m, {
25
27
  prefix: v,
26
28
  download: x,
27
29
  editor: S,
@@ -29,20 +31,20 @@ function m(m, h = {}) {
29
31
  limit: e.limit
30
32
  })), [
31
33
  D,
32
- z,
34
+ L,
33
35
  m.kind,
34
36
  m.id,
35
37
  m.version,
36
38
  v,
37
39
  x,
38
40
  S
39
- ]), [V, H] = f(null), [U, W] = f(null), G = V?.base === A && V.given === g ? V : null, K = !!O && (g === void 0 || !!G), ne = c((t) => {
40
- if (!O || !K) return () => {};
41
- let n = B.map((n, r) => O.subscribe(n, e(m), {
41
+ ]), [z, B] = d(null), [V, H] = d(null), U = z?.base === A && z.given === g ? z : null, W = !!O && (g === void 0 || !!U), G = s((t) => {
42
+ if (!O || !W) return () => {};
43
+ let n = R.map((n, r) => O.subscribe(n, e(m), {
42
44
  prefix: v,
43
45
  download: x,
44
46
  editor: S,
45
- ...z[r]
47
+ ...L[r]
46
48
  }, t, {
47
49
  poll: T,
48
50
  refresh: w
@@ -50,91 +52,96 @@ function m(m, h = {}) {
50
52
  return () => n.forEach((e) => e());
51
53
  }, [
52
54
  O,
53
- K,
54
- B,
55
+ W,
56
+ R,
55
57
  T,
56
58
  w
57
- ]), q = d(p), J = c(() => {
58
- if (!O || !K) return p;
59
- let e = B.map((e) => O.snapshot(e)), t = q.current;
60
- return t.length === e.length && t.every((t, n) => t === e[n]) ? t : q.current = e;
59
+ ]), K = u(p), q = s(() => {
60
+ if (!O || !W) return p;
61
+ let e = R.map((e) => O.snapshot(e)), t = K.current;
62
+ return t.length === e.length && t.every((t, n) => t === e[n]) ? t : K.current = e;
61
63
  }, [
62
64
  O,
63
- K,
64
- B
65
- ]), Y = ee(ne, J, J);
66
- l(() => {
67
- if (O && K) for (let e of B) O.load(e, G ? "reload" : "initial");
65
+ W,
66
+ R
67
+ ]), J = te(G, q, q);
68
+ c(() => {
69
+ if (O && W) for (let e of R) O.load(e, U ? "reload" : "initial");
68
70
  }, [
69
71
  O,
70
- K,
71
- B,
72
- G
72
+ W,
73
+ R,
74
+ U
73
75
  ]);
74
- let X = u(() => i(Y.map((e) => e.read)), [Y]);
75
- l(() => {
76
- C && X && X.total !== N && M({
76
+ let Y = l(() => i(J.map((e) => e.read)), [J]), X = J.some((e) => !!e.error);
77
+ c(() => {
78
+ let e = X ? null : Y;
79
+ C && (X || Y || J.length && J.every((e) => e.loaded)) && M((t) => t.base === A && t.store === O && t.read === e ? t : {
77
80
  base: A,
78
- n: X.total
81
+ store: O,
82
+ read: e,
83
+ n: Y?.total ?? (t.base === A && t.store === O ? t.n : void 0)
79
84
  });
80
85
  }, [
81
- C,
86
+ !!C,
87
+ Y,
82
88
  X,
83
- N,
84
- A
89
+ J,
90
+ A,
91
+ O
85
92
  ]);
86
- let re = c(() => {
87
- if (g !== void 0) H({
93
+ let ne = s(() => {
94
+ if (g !== void 0) B({
88
95
  base: A,
89
96
  given: g
90
97
  });
91
- else if (O) for (let e of B) O.load(e, "reload");
92
- W(null);
98
+ else if (O) for (let e of R) O.load(e, "reload");
99
+ H(null);
93
100
  }, [
94
101
  O,
95
- B,
102
+ R,
96
103
  A,
97
104
  g
98
- ]), ie = c(() => {
99
- if (g !== void 0 && !G) H({
105
+ ]), re = s(() => {
106
+ if (g !== void 0 && !U) B({
100
107
  base: A,
101
108
  given: g
102
109
  });
103
- else if (O) for (let e of B) O.load(e, "refresh");
110
+ else if (O) for (let e of R) O.load(e, "refresh");
104
111
  }, [
105
112
  O,
106
- B,
113
+ R,
107
114
  A,
108
115
  g,
109
- G
110
- ]), ae = c((e) => {
111
- if (O && K) for (let t of B) O.set(t, e);
112
- else W({
116
+ U
117
+ ]), ie = s((e) => {
118
+ if (O && W) for (let t of R) O.set(t, e);
119
+ else H({
113
120
  base: A,
114
121
  given: g,
115
122
  read: e
116
123
  });
117
124
  }, [
118
125
  O,
119
- K,
120
- B,
126
+ W,
127
+ R,
121
128
  A,
122
129
  g
123
130
  ]), Z, Q = !1, $;
124
- if (U && U.base === A && U.given === g) Z = U.read;
125
- else if (!Y.length) Z = g ?? null;
131
+ if (V && V.base === A && V.given === g) Z = V.read;
132
+ else if (!J.length) Z = g ?? null;
126
133
  else {
127
- let e = Y.every((e) => e.loaded);
128
- Z = X ?? (e ? null : g ?? null), Q = Y.some((e) => e.loading || !e.loaded), $ = Y.find((e) => e.error)?.error;
134
+ let e = J.every((e) => e.loaded);
135
+ Z = Y ?? (e ? null : g ?? (C && N && !X ? j.read : null)), Q = J.some((e) => e.loading || !e.loaded), $ = J.find((e) => e.error)?.error;
129
136
  }
130
137
  return {
131
138
  read: Z,
132
139
  loading: Q,
133
140
  error: $,
134
- processing: a(Z),
135
- reload: re,
136
- refresh: ie,
137
- set: ae
141
+ processing: ee(Z),
142
+ reload: ne,
143
+ refresh: re,
144
+ set: ie
138
145
  };
139
146
  }
140
147
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","names":[],"sources":["../../src/react/read.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { ReadOptions } from \"../client/media/api.js\";\nimport { chunkOffsets, mergeReads, processing as isProcessing, READ_CHUNK } from \"../client/media/windows.js\";\nimport { itemRef } from \"../client/media/ref.js\";\nimport { useOptionalContentKitClient, useReadScope } from \"./context.js\";\nimport { readKey, storeFor, type ReadEntry } from \"./store.js\";\n\n/** Files [start, end) get URLs. */\nexport interface ReadWindow {\n start: number;\n end: number;\n /** Files per read. Default 50. */\n chunk?: number;\n}\n\nexport interface MediaReadOptions extends Omit<ReadOptions, \"offset\" | \"limit\"> {\n /**\n * Read in windows of `chunk` files and merge them: the files in\n * [start, end) get URLs, and windows once read stay loaded (and refreshed)\n * until the item or options change. Without it, one read of the server's\n * default range (or offset and limit).\n */\n window?: ReadWindow;\n offset?: number;\n limit?: number;\n /** Poll at this interval (ms) while uploads process. Default 2500 for editor reads, else off. */\n poll?: number | false;\n /** Read again shortly before the URLs expire. Default true. */\n refresh?: boolean;\n /** A read the host already has: shown until reload() or set(). */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UseMediaRead {\n /** The read (windows merged); null until it loads or when it failed. */\n read: ReadResult | null;\n loading: boolean;\n error?: ContentKitError;\n /** Uploads are still processing (editor reads). */\n processing: boolean;\n /** Reads again now, restarting a read in flight. */\n reload: () => void;\n /** Reads again unless a read is in flight: a player's grant refresh. */\n refresh: () => void;\n /** Replaces the read without refetching, for every hook showing it. */\n set: (r: ReadResult) => void;\n}\n\nconst POLL = 2500;\nconst NONE: readonly ReadEntry[] = [];\n\n/**\n * A read of the item, shared with every hook reading the same item and\n * options: refreshed before its URLs expire, retried after a read rate limit,\n * polled while uploads process, reloaded after a commit through the client.\n * A window splits it into merged reads. Without a client (none passed, no\n * provider) it only shows a supplied read.\n */\nexport function useMediaRead(ref: RefBody, o: MediaReadOptions = {}): UseMediaRead {\n const { read: given, client: own, prefix, offset, limit, download, editor, window, refresh: refreshing = true } = o;\n const poll = o.poll ?? (editor ? POLL : false);\n const client = useOptionalContentKitClient(own);\n const scope = useReadScope();\n const store = client ? storeFor(client) : null;\n const chunk = window?.chunk ?? READ_CHUNK;\n const base = scope + readKey(ref, { prefix, download, editor }) + (window ? `#${chunk}` : `#${offset ?? 0}:${limit ?? 0}`);\n\n // Windows once wanted stay loaded until the item or options change.\n const [total, setTotal] = useState<{ base: string; n?: number }>({ base });\n const known = total.base === base ? total.n : undefined;\n // Until the first read states the total, only the window's first chunk.\n const wanted = window ? chunkOffsets(window.start, window.end, known ?? window.start + 1, chunk) : [];\n const [kept, setKept] = useState<{ base: string; offsets: number[] }>({ base, offsets: [] });\n const prior = kept.base === base ? kept.offsets : [];\n if (window && (kept.base !== base || wanted.some((w) => !prior.includes(w)))) {\n setKept({ base, offsets: [...new Set([...prior, ...wanted])].sort((a, b) => a - b) });\n }\n const offsets = window ? (prior.length ? prior : wanted.length ? wanted : [0]) : [offset ?? 0];\n const windows = useMemo(\n () => offsets.map((off) => ({ offset: off, limit: window ? chunk : limit })),\n // offsets is derived from the stable kept list\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [offsets.join(\",\"), window ? chunk : limit],\n );\n const keys = useMemo(() => windows.map((w) => scope + readKey(ref, { prefix, download, editor, offset: w.offset, limit: w.limit })), [scope, windows, ref.kind, ref.id, ref.version, prefix, download, editor]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // A supplied read is shown until reload() asks the server or set() replaces it.\n const [requested, setRequested] = useState<{ base: string; given?: ReadResult | null } | null>(null);\n const [local, setLocal] = useState<{ base: string; given?: ReadResult | null; read: ReadResult } | null>(null);\n const asked = requested?.base === base && requested.given === given ? requested : null;\n const fetching = !!store && (given === undefined || !!asked);\n\n const subscribe = useCallback(\n (l: () => void) => {\n if (!store || !fetching) return () => {};\n const offs = keys.map((key, i) => store.subscribe(key, itemRef(ref), { prefix, download, editor, ...windows[i] }, l, { poll, refresh: refreshing }, scope));\n return () => offs.forEach((off) => off());\n },\n // ref and options are in keys.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, fetching, keys, poll, refreshing],\n );\n const last = useRef<readonly ReadEntry[]>(NONE);\n const snapshot = useCallback(() => {\n if (!store || !fetching) return NONE;\n const next = keys.map((k) => store.snapshot(k));\n const prev = last.current;\n if (prev.length === next.length && prev.every((e, i) => e === next[i])) return prev;\n return (last.current = next);\n }, [store, fetching, keys]);\n const entries = useSyncExternalStore(subscribe, snapshot, snapshot);\n useEffect(() => {\n if (store && fetching) for (const k of keys) store.load(k, asked ? \"reload\" : \"initial\");\n }, [store, fetching, keys, asked]);\n\n const merged = useMemo(() => mergeReads(entries.map((e) => e.read)), [entries]);\n useEffect(() => {\n if (window && merged && merged.total !== known) setTotal({ base, n: merged.total });\n }, [window, merged, known, base]);\n\n const reload = useCallback(() => {\n if (given !== undefined) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"reload\");\n setLocal(null);\n }, [store, keys, base, given]);\n const refresh = useCallback(() => {\n if (given !== undefined && !asked) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"refresh\");\n }, [store, keys, base, given, asked]);\n const set = useCallback(\n (read: ReadResult) => {\n if (store && fetching) for (const k of keys) store.set(k, read);\n else setLocal({ base, given, read });\n },\n [store, fetching, keys, base, given],\n );\n\n let read: ReadResult | null;\n let loading = false;\n let error: ContentKitError | undefined;\n if (local && local.base === base && local.given === given) read = local.read;\n else if (!entries.length) read = given ?? null;\n else {\n const loaded = entries.every((e) => e.loaded);\n read = merged ?? (loaded ? null : (given ?? null));\n loading = entries.some((e) => e.loading || !e.loaded);\n error = entries.find((e) => e.error)?.error;\n }\n return { read, loading, error, processing: isProcessing(read), reload, refresh, set };\n}\n"],"mappings":";;;;;;AAqDA,IAAM,KAAO,MACP,IAA6B,CAAC;AASpC,SAAgB,EAAa,GAAc,IAAsB,CAAC,GAAiB;CACjF,IAAM,EAAE,MAAM,GAAO,QAAQ,GAAK,WAAQ,WAAQ,UAAO,aAAU,WAAQ,WAAQ,SAAS,IAAa,OAAS,GAC5G,IAAO,EAAE,SAAS,IAAS,KAAO,KAClC,IAAS,EAA4B,CAAG,GACxC,IAAQ,EAAa,GACrB,IAAQ,IAAS,EAAS,CAAM,IAAI,MACpC,IAAQ,GAAQ,SAAA,IAChB,IAAO,IAAQ,EAAQ,GAAK;EAAE;EAAQ;EAAU;CAAO,CAAC,KAAK,IAAS,IAAI,MAAU,IAAI,KAAU,EAAE,GAAG,KAAS,MAGhH,CAAC,GAAO,KAAY,EAAuC,EAAE,QAAK,CAAC,GACnE,IAAQ,EAAM,SAAS,IAAO,EAAM,IAAI,KAAA,GAExC,IAAS,IAAS,EAAa,EAAO,OAAO,EAAO,KAAK,KAAS,EAAO,QAAQ,GAAG,CAAK,IAAI,CAAC,GAC9F,CAAC,GAAM,KAAW,EAA8C;EAAE;EAAM,SAAS,CAAC;CAAE,CAAC,GACrF,IAAQ,EAAK,SAAS,IAAO,EAAK,UAAU,CAAC;CACnD,AAAI,MAAW,EAAK,SAAS,KAAQ,EAAO,MAAM,MAAM,CAAC,EAAM,SAAS,CAAC,CAAC,MACxE,EAAQ;EAAE;EAAM,SAAS,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,GAAO,GAAG,CAAM,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAAE,CAAC;CAEtF,IAAM,IAAU,IAAU,EAAM,SAAS,IAAQ,EAAO,SAAS,IAAS,CAAC,CAAC,IAAK,CAAC,KAAU,CAAC,GACvF,IAAU,QACR,EAAQ,KAAK,OAAS;EAAE,QAAQ;EAAK,OAAO,IAAS,IAAQ;CAAM,EAAE,GAG3E,CAAC,EAAQ,KAAK,GAAG,GAAG,IAAS,IAAQ,CAAK,CAC5C,GACM,IAAO,QAAc,EAAQ,KAAK,MAAM,IAAQ,EAAQ,GAAK;EAAE;EAAQ;EAAU;EAAQ,QAAQ,EAAE;EAAQ,OAAO,EAAE;CAAM,CAAC,CAAC,GAAG;EAAC;EAAO;EAAS,EAAI;EAAM,EAAI;EAAI,EAAI;EAAS;EAAQ;EAAU;CAAM,CAAC,GAGxM,CAAC,GAAW,KAAgB,EAA6D,IAAI,GAC7F,CAAC,GAAO,KAAY,EAA+E,IAAI,GACvG,IAAQ,GAAW,SAAS,KAAQ,EAAU,UAAU,IAAQ,IAAY,MAC5E,IAAW,CAAC,CAAC,MAAU,MAAU,KAAA,KAAa,CAAC,CAAC,IAEhD,KAAY,GACf,MAAkB;EACjB,IAAI,CAAC,KAAS,CAAC,GAAU,aAAa,CAAC;EACvC,IAAM,IAAO,EAAK,KAAK,GAAK,MAAM,EAAM,UAAU,GAAK,EAAQ,CAAG,GAAG;GAAE;GAAQ;GAAU;GAAQ,GAAG,EAAQ;EAAG,GAAG,GAAG;GAAE;GAAM,SAAS;EAAW,GAAG,CAAK,CAAC;EAC1J,aAAa,EAAK,SAAS,MAAQ,EAAI,CAAC;CAC1C,GAGA;EAAC;EAAO;EAAU;EAAM;EAAM;CAAU,CAC1C,GACM,IAAO,EAA6B,CAAI,GACxC,IAAW,QAAkB;EACjC,IAAI,CAAC,KAAS,CAAC,GAAU,OAAO;EAChC,IAAM,IAAO,EAAK,KAAK,MAAM,EAAM,SAAS,CAAC,CAAC,GACxC,IAAO,EAAK;EAElB,OADI,EAAK,WAAW,EAAK,UAAU,EAAK,OAAO,GAAG,MAAM,MAAM,EAAK,EAAE,IAAU,IACvE,EAAK,UAAU;CACzB,GAAG;EAAC;EAAO;EAAU;CAAI,CAAC,GACpB,IAAU,GAAqB,IAAW,GAAU,CAAQ;CAClE,QAAgB;EACd,IAAI,KAAS,GAAU,KAAK,IAAM,KAAK,GAAM,EAAM,KAAK,GAAG,IAAQ,WAAW,SAAS;CACzF,GAAG;EAAC;EAAO;EAAU;EAAM;CAAK,CAAC;CAEjC,IAAM,IAAS,QAAc,EAAW,EAAQ,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAO,CAAC;CAC9E,QAAgB;EACd,AAAI,KAAU,KAAU,EAAO,UAAU,KAAO,EAAS;GAAE;GAAM,GAAG,EAAO;EAAM,CAAC;CACpF,GAAG;EAAC;EAAQ;EAAQ;EAAO;CAAI,CAAC;CAEhC,IAAM,KAAS,QAAkB;EAC/B,IAAI,MAAU,KAAA,GAAW,EAAa;GAAE;GAAM;EAAM,CAAC;OAChD,IAAI,GAAO,KAAK,IAAM,KAAK,GAAM,EAAM,KAAK,GAAG,QAAQ;EAC5D,EAAS,IAAI;CACf,GAAG;EAAC;EAAO;EAAM;EAAM;CAAK,CAAC,GACvB,KAAU,QAAkB;EAChC,IAAI,MAAU,KAAA,KAAa,CAAC,GAAO,EAAa;GAAE;GAAM;EAAM,CAAC;OAC1D,IAAI,GAAO,KAAK,IAAM,KAAK,GAAM,EAAM,KAAK,GAAG,SAAS;CAC/D,GAAG;EAAC;EAAO;EAAM;EAAM;EAAO;CAAK,CAAC,GAC9B,KAAM,GACT,MAAqB;EACpB,IAAI,KAAS,GAAU,KAAK,IAAM,KAAK,GAAM,EAAM,IAAI,GAAG,CAAI;OACzD,EAAS;GAAE;GAAM;GAAO;EAAK,CAAC;CACrC,GACA;EAAC;EAAO;EAAU;EAAM;EAAM;CAAK,CACrC,GAEI,GACA,IAAU,IACV;CACJ,IAAI,KAAS,EAAM,SAAS,KAAQ,EAAM,UAAU,GAAO,IAAO,EAAM;MACnE,IAAI,CAAC,EAAQ,QAAQ,IAAO,KAAS;MACrC;EACH,IAAM,IAAS,EAAQ,OAAO,MAAM,EAAE,MAAM;EAG5C,AAFA,IAAO,MAAW,IAAS,OAAQ,KAAS,OAC5C,IAAU,EAAQ,MAAM,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GACpD,IAAQ,EAAQ,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE;CACxC;CACA,OAAO;EAAE;EAAM;EAAS;EAAO,YAAY,EAAa,CAAI;EAAG;EAAQ;EAAS;CAAI;AACtF"}
1
+ {"version":3,"file":"read.js","names":[],"sources":["../../src/react/read.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { ReadOptions } from \"../client/media/api.js\";\nimport { chunkOffsets, mergeReads, processing as isProcessing, READ_CHUNK } from \"../client/media/windows.js\";\nimport { itemRef } from \"../client/media/ref.js\";\nimport { useOptionalContentKitClient, useReadScope } from \"./context.js\";\nimport { readKey, storeFor, type ReadEntry } from \"./store.js\";\n\n/** Files [start, end) get URLs. */\nexport interface ReadWindow {\n start: number;\n end: number;\n /** Files per read. Default 50. */\n chunk?: number;\n}\n\nexport interface MediaReadOptions extends Omit<ReadOptions, \"offset\" | \"limit\"> {\n /**\n * Read in windows of `chunk` files and merge them: the files in\n * [start, end) get URLs. Only the requested windows stay active and refresh;\n * a prior read supplies placeholder metadata while the new range loads.\n * Without it, one read of the server's\n * default range (or offset and limit).\n */\n window?: ReadWindow;\n offset?: number;\n limit?: number;\n /** Poll at this interval (ms) while uploads process. Default 2500 for editor reads, else off. */\n poll?: number | false;\n /** Read again shortly before the URLs expire. Default true. */\n refresh?: boolean;\n /** A read the host already has: shown until reload() or set(). */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UseMediaRead {\n /** The read (windows merged); null until it loads or when it failed. */\n read: ReadResult | null;\n loading: boolean;\n error?: ContentKitError;\n /** Uploads are still processing (editor reads). */\n processing: boolean;\n /** Reads again now, restarting a read in flight. */\n reload: () => void;\n /** Reads again unless a read is in flight: a player's grant refresh. */\n refresh: () => void;\n /** Replaces the read without refetching, for every hook showing it. */\n set: (r: ReadResult) => void;\n}\n\nconst POLL = 2500;\nconst NONE: readonly ReadEntry[] = [];\n\n/**\n * A read of the item, shared with every hook reading the same item and\n * options: refreshed before its URLs expire, retried after a read rate limit,\n * polled while uploads process, reloaded after a commit through the client.\n * A window splits it into merged reads. Without a client (none passed, no\n * provider) it only shows a supplied read.\n */\nexport function useMediaRead(ref: RefBody, o: MediaReadOptions = {}): UseMediaRead {\n const { read: given, client: own, prefix, offset, limit, download, editor, window, refresh: refreshing = true } = o;\n const poll = o.poll ?? (editor ? POLL : false);\n const client = useOptionalContentKitClient(own);\n const scope = useReadScope();\n const store = client ? storeFor(client) : null;\n const chunk = window?.chunk ?? READ_CHUNK;\n const base = scope + readKey(ref, { prefix, download, editor }) + (window ? `#${chunk}` : `#${offset ?? 0}:${limit ?? 0}`);\n\n // Keep one placeholder, never subscriptions for the reader's visit history.\n const [previous, setPrevious] = useState<{ base: string; store: typeof store; read: ReadResult | null; n?: number }>({ base, store, read: null });\n const same = previous.base === base && previous.store === store;\n if (!same) setPrevious({ base, store, read: null });\n const known = same ? previous.n : undefined;\n // Until the first read states the total, only the window's first chunk.\n const wanted = window ? chunkOffsets(window.start, window.end, known ?? window.start + 1, chunk) : [];\n const offsets = window ? (wanted.length ? wanted : [0]) : [offset ?? 0];\n const windows = useMemo(\n () => offsets.map((off) => ({ offset: off, limit: window ? chunk : limit })),\n // Window identity depends on its values, not the caller's array identity.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [offsets.join(\",\"), window ? chunk : limit],\n );\n const keys = useMemo(() => windows.map((w) => scope + readKey(ref, { prefix, download, editor, offset: w.offset, limit: w.limit })), [scope, windows, ref.kind, ref.id, ref.version, prefix, download, editor]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // A supplied read is shown until reload() asks the server or set() replaces it.\n const [requested, setRequested] = useState<{ base: string; given?: ReadResult | null } | null>(null);\n const [local, setLocal] = useState<{ base: string; given?: ReadResult | null; read: ReadResult } | null>(null);\n const asked = requested?.base === base && requested.given === given ? requested : null;\n const fetching = !!store && (given === undefined || !!asked);\n\n const subscribe = useCallback(\n (l: () => void) => {\n if (!store || !fetching) return () => {};\n const offs = keys.map((key, i) => store.subscribe(key, itemRef(ref), { prefix, download, editor, ...windows[i] }, l, { poll, refresh: refreshing }, scope));\n return () => offs.forEach((off) => off());\n },\n // ref and options are in keys.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, fetching, keys, poll, refreshing],\n );\n const last = useRef<readonly ReadEntry[]>(NONE);\n const snapshot = useCallback(() => {\n if (!store || !fetching) return NONE;\n const next = keys.map((k) => store.snapshot(k));\n const prev = last.current;\n if (prev.length === next.length && prev.every((e, i) => e === next[i])) return prev;\n return (last.current = next);\n }, [store, fetching, keys]);\n const entries = useSyncExternalStore(subscribe, snapshot, snapshot);\n useEffect(() => {\n if (store && fetching) for (const k of keys) store.load(k, asked ? \"reload\" : \"initial\");\n }, [store, fetching, keys, asked]);\n\n const merged = useMemo(() => mergeReads(entries.map((e) => e.read)), [entries]);\n const failed = entries.some((e) => !!e.error);\n useEffect(() => {\n const read = failed ? null : merged;\n if (window && (failed || merged || (entries.length && entries.every((e) => e.loaded)))) {\n setPrevious((prev) => prev.base === base && prev.store === store && prev.read === read ? prev : {\n base, store, read, n: merged?.total ?? (prev.base === base && prev.store === store ? prev.n : undefined),\n });\n }\n }, [!!window, merged, failed, entries, base, store]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const reload = useCallback(() => {\n if (given !== undefined) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"reload\");\n setLocal(null);\n }, [store, keys, base, given]);\n const refresh = useCallback(() => {\n if (given !== undefined && !asked) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"refresh\");\n }, [store, keys, base, given, asked]);\n const set = useCallback(\n (read: ReadResult) => {\n if (store && fetching) for (const k of keys) store.set(k, read);\n else setLocal({ base, given, read });\n },\n [store, fetching, keys, base, given],\n );\n\n let read: ReadResult | null;\n let loading = false;\n let error: ContentKitError | undefined;\n if (local && local.base === base && local.given === given) read = local.read;\n else if (!entries.length) read = given ?? null;\n else {\n const loaded = entries.every((e) => e.loaded);\n read = merged ?? (loaded ? null : (given ?? (window && same && !failed ? previous.read : null)));\n loading = entries.some((e) => e.loading || !e.loaded);\n error = entries.find((e) => e.error)?.error;\n }\n return { read, loading, error, processing: isProcessing(read), reload, refresh, set };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrails/contentkit-ui",
3
- "version": "0.99.3",
3
+ "version": "0.100.1",
4
4
  "description": "ContentKit in the browser: client, React hooks and styled components for media, uploads and content URLs.",
5
5
  "type": "module",
6
6
  "license": "MIT",