@openrails/contentkit-ui 0.89.0 → 0.89.2

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 (43) hide show
  1. package/README.md +29 -2
  2. package/dist/client/browser-search-history.d.ts +24 -0
  3. package/dist/client/browser-search-history.js +92 -0
  4. package/dist/client/browser-search-history.js.map +1 -0
  5. package/dist/client/discovery.d.ts +5 -2
  6. package/dist/client/discovery.js +14 -22
  7. package/dist/client/discovery.js.map +1 -1
  8. package/dist/client/generated/routes.d.ts +3 -3
  9. package/dist/client/generated/routes.js +3 -2
  10. package/dist/client/generated/routes.js.map +1 -1
  11. package/dist/client.js +14 -14
  12. package/dist/components/content/filters.d.ts +1 -1
  13. package/dist/components/content/filters.js +32 -52
  14. package/dist/components/content/filters.js.map +1 -1
  15. package/dist/components/content/index.d.ts +1 -0
  16. package/dist/components/content/reader-settings.d.ts +19 -0
  17. package/dist/components/content/reader-settings.js +95 -0
  18. package/dist/components/content/reader-settings.js.map +1 -0
  19. package/dist/components/content/search.d.ts +2 -0
  20. package/dist/components/content/search.js +334 -137
  21. package/dist/components/content/search.js.map +1 -1
  22. package/dist/index.js +17 -16
  23. package/dist/locales/discovery/de.js +9 -0
  24. package/dist/locales/discovery/de.js.map +1 -1
  25. package/dist/locales/discovery/en.d.ts +9 -0
  26. package/dist/locales/discovery/en.js +9 -0
  27. package/dist/locales/discovery/en.js.map +1 -1
  28. package/dist/locales/discovery/es.js +9 -0
  29. package/dist/locales/discovery/es.js.map +1 -1
  30. package/dist/locales/discovery/ja.js +9 -0
  31. package/dist/locales/discovery/ja.js.map +1 -1
  32. package/dist/locales/discovery/ko.js +9 -0
  33. package/dist/locales/discovery/ko.js.map +1 -1
  34. package/dist/locales/discovery/zh.js +9 -0
  35. package/dist/locales/discovery/zh.js.map +1 -1
  36. package/dist/locales/en.d.ts +9 -0
  37. package/dist/react/discovery.d.ts +1 -1
  38. package/dist/react/discovery.js +145 -92
  39. package/dist/react/discovery.js.map +1 -1
  40. package/dist/styles.css +1 -1
  41. package/dist/ui/dialog.js +16 -10
  42. package/dist/ui/dialog.js.map +1 -1
  43. package/package.json +1 -1
package/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  ContentKit in the browser: a framework-free client, content URLs, React hooks
4
4
  and styled components (uploads with cropping, video posters, galleries, HLS
5
5
  playback, comments, reactions, favorites, polls and their staff screens).
6
- `@openrails/contentkit-ui@X.Y.Z` speaks ContentKit `vX.Y.Z`.
6
+ UI releases share the backend's major/minor API version; UI-only patches can
7
+ advance independently (for example, UI `0.89.1` with ContentKit `v0.89.0`).
7
8
 
8
9
  ```sh
9
10
  pnpm add @openrails/contentkit-ui@X.Y.Z
@@ -648,7 +649,8 @@ generated from ContentKit's route catalog (`pnpm contract`; CI runs
648
649
  `pnpm contract:check`). UI primitives come from
649
650
  `pnpm dlx shadcn@4.21.0 add …` (`components.json`); local edits are marked
650
651
  `// Local:`. The `release` workflow publishes the package to npm for every
651
- published `v*` release.
652
+ published `v*` release. For a UI-only patch, publish an `sdk/ui/vX.Y.Z` release;
653
+ this uses the same checks and trusted npm publication without tagging the Go modules.
652
654
 
653
655
 
654
656
  ## Notifications
@@ -677,3 +679,28 @@ poll by default. Call `client.notifications.refresh()` when your application's
677
679
  event channel announces new data. `useNotifications` and `useNotificationUnread`
678
680
  are available from `/react` for a custom presentation. Keep provider viewer and
679
681
  access revision current; signed-out viewers do not fetch an inbox.
682
+
683
+ ### Reader header controls
684
+
685
+ `SearchBox` is an inline input with a downward suggestions/recent-search panel,
686
+ including loading and empty states. Set `presentation="header"` for a full-width
687
+ panel below the nearest semantic header (or the input): a dimmed backdrop, a
688
+ centered text list with colored type icons and recent-search dates, and no
689
+ thumbnails. Its mobile/desktop scroll limit is 70vh/60vh. The default inline
690
+ presentation is unchanged. Both presentations keep the provider's result order;
691
+ the host owns search-page navigation.
692
+ `ReaderSettings` combines language selection and content filters in one dialog:
693
+
694
+ ```tsx
695
+ <SearchBox presentation="header" onSearch={(query) => navigate(`/search?q=${encodeURIComponent(query)}`)} />
696
+ <ReaderSettings
697
+ languages={[{ value: "en", label: "English" }, { value: "es", label: "Español" }]}
698
+ language={language}
699
+ onLanguageChange={changeLanguage}
700
+ />
701
+ ```
702
+
703
+ Render these under `ContentKitProvider`. Keep its `viewer` current: guests can
704
+ change language, while signed-in readers also see the site's configured content
705
+ filters. Filter toggles save through ContentKit immediately. The language callback
706
+ belongs to the host so it can update its route and stored preference together.
@@ -0,0 +1,24 @@
1
+ import { RecentSearch } from './generated/wire.js';
2
+ /** The number of recent queries displayed, bounded like the server endpoint. */
3
+ export declare const recentSearchLimit: (limit?: number) => number;
4
+ /** Browser-owned anonymous search history. It never reads or copies account history. */
5
+ export declare class BrowserSearchHistory {
6
+ private readonly key;
7
+ private readonly browser?;
8
+ private rows;
9
+ private encoded;
10
+ private storageFailed;
11
+ private readonly listeners;
12
+ constructor(key: string, browser?: Window | undefined);
13
+ readonly snapshot: () => readonly RecentSearch[];
14
+ list(limit?: number): RecentSearch[];
15
+ readonly subscribe: (listener: () => void) => (() => void);
16
+ record(query: string, results: number): void;
17
+ clear(query?: string): void;
18
+ private readonly onStorage;
19
+ private sync;
20
+ private save;
21
+ private replace;
22
+ }
23
+ /** One same-tab store per discovery URL; server rendering never shares a store across requests. */
24
+ export declare function browserSearchHistory(mount: string): BrowserSearchHistory;
@@ -0,0 +1,92 @@
1
+ //#region src/client/browser-search-history.ts
2
+ var e = (e) => e.trim().replace(/\s+/g, " "), t = (t) => e(t).toLowerCase(), n = (e = 10) => Number.isFinite(e) ? Math.max(1, Math.min(100, Math.floor(e))) : 10;
3
+ function r(n) {
4
+ try {
5
+ let r = JSON.parse(n ?? "[]");
6
+ if (!Array.isArray(r)) return [];
7
+ let i = /* @__PURE__ */ new Set(), a = [];
8
+ for (let n of r) {
9
+ if (!n || typeof n.query != "string" || typeof n.results != "number" || !Number.isFinite(n.results) || n.results < 0 || typeof n.searched_at != "string" || !Number.isFinite(Date.parse(n.searched_at))) continue;
10
+ let r = e(n.query), o = t(r);
11
+ if (o && !i.has(o) && (i.add(o), a.push({
12
+ query: r,
13
+ results: Math.floor(n.results),
14
+ searched_at: n.searched_at
15
+ }), a.length === 100)) break;
16
+ }
17
+ return a;
18
+ } catch {
19
+ return [];
20
+ }
21
+ }
22
+ var i = class {
23
+ key;
24
+ browser;
25
+ rows = [];
26
+ encoded = "[]";
27
+ storageFailed = !1;
28
+ listeners = /* @__PURE__ */ new Set();
29
+ constructor(e, t) {
30
+ this.key = e, this.browser = t, this.sync();
31
+ }
32
+ snapshot = () => this.rows;
33
+ list(e) {
34
+ return this.sync(), this.rows.slice(0, n(e)).map((e) => ({ ...e }));
35
+ }
36
+ subscribe = (e) => (this.sync(), this.listeners.size || this.browser?.addEventListener("storage", this.onStorage), this.listeners.add(e), () => {
37
+ this.listeners.delete(e), this.listeners.size || this.browser?.removeEventListener("storage", this.onStorage);
38
+ });
39
+ record(n, r) {
40
+ n = e(n), n && (this.sync(), this.save([{
41
+ query: n,
42
+ results: Number.isFinite(r) ? Math.max(0, Math.floor(r)) : 0,
43
+ searched_at: (/* @__PURE__ */ new Date()).toISOString()
44
+ }, ...this.rows.filter((e) => t(e.query) !== t(n))].slice(0, 100)));
45
+ }
46
+ clear(e) {
47
+ this.sync(), this.save(e ? this.rows.filter((n) => t(n.query) !== t(e)) : []);
48
+ }
49
+ onStorage = (e) => {
50
+ (e.key === null || e.key === this.key) && this.sync();
51
+ };
52
+ sync() {
53
+ if (this.browser && !this.storageFailed) try {
54
+ this.replace(r(this.browser.localStorage.getItem(this.key)));
55
+ } catch {
56
+ this.storageFailed = !0;
57
+ }
58
+ }
59
+ save(e) {
60
+ if (this.browser && !this.storageFailed) try {
61
+ e.length ? this.browser.localStorage.setItem(this.key, JSON.stringify(e)) : this.browser.localStorage.removeItem(this.key);
62
+ } catch {
63
+ this.storageFailed = !0;
64
+ }
65
+ this.replace(e);
66
+ }
67
+ replace(e) {
68
+ let t = JSON.stringify(e);
69
+ if (t !== this.encoded) {
70
+ this.encoded = t, this.rows = Object.freeze(e.map((e) => Object.freeze({ ...e })));
71
+ for (let e of this.listeners) try {
72
+ e();
73
+ } catch {}
74
+ }
75
+ }
76
+ }, a = /* @__PURE__ */ new WeakMap();
77
+ function o(e) {
78
+ let t = typeof window > "u" ? void 0 : window, n = e.replace(/\/+$/, "");
79
+ if (t) try {
80
+ n = new URL(n, t.location.href).href;
81
+ } catch {}
82
+ let r = `contentkit:recent-searches:v1:${n}`;
83
+ if (!t) return new i(r);
84
+ let o = a.get(t);
85
+ o || a.set(t, o = /* @__PURE__ */ new Map());
86
+ let s = o.get(r);
87
+ return s || o.set(r, s = new i(r, t)), s;
88
+ }
89
+ //#endregion
90
+ export { i as BrowserSearchHistory, o as browserSearchHistory, n as recentSearchLimit };
91
+
92
+ //# sourceMappingURL=browser-search-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-search-history.js","names":[],"sources":["../../src/client/browser-search-history.ts"],"sourcesContent":["import type { RecentSearch } from \"./generated/wire.js\";\n\nconst MAX_SEARCHES = 100;\nconst normalized = (query: string) => query.trim().replace(/\\s+/g, \" \");\nconst identity = (query: string) => normalized(query).toLowerCase();\n\n/** The number of recent queries displayed, bounded like the server endpoint. */\nexport const recentSearchLimit = (limit = 10): number => Number.isFinite(limit) ? Math.max(1, Math.min(MAX_SEARCHES, Math.floor(limit))) : 10;\n\nfunction decode(raw: string | null): RecentSearch[] {\n try {\n const data: unknown = JSON.parse(raw ?? \"[]\");\n if (!Array.isArray(data)) return [];\n const seen = new Set<string>();\n const rows: RecentSearch[] = [];\n for (const entry of data) {\n if (!entry || typeof entry.query !== \"string\" || typeof entry.results !== \"number\" || !Number.isFinite(entry.results) || entry.results < 0 || typeof entry.searched_at !== \"string\" || !Number.isFinite(Date.parse(entry.searched_at))) continue;\n const query = normalized(entry.query), key = identity(query);\n if (!key || seen.has(key)) continue;\n seen.add(key);\n rows.push({ query, results: Math.floor(entry.results), searched_at: entry.searched_at });\n if (rows.length === MAX_SEARCHES) break;\n }\n return rows;\n } catch { return []; }\n}\n\n/** Browser-owned anonymous search history. It never reads or copies account history. */\nexport class BrowserSearchHistory {\n private rows: readonly RecentSearch[] = [];\n private encoded = \"[]\";\n private storageFailed = false;\n private readonly listeners = new Set<() => void>();\n\n constructor(private readonly key: string, private readonly browser?: Window) { this.sync(); }\n\n readonly snapshot = (): readonly RecentSearch[] => this.rows;\n\n list(limit?: number): RecentSearch[] {\n this.sync();\n return this.rows.slice(0, recentSearchLimit(limit)).map((row) => ({ ...row }));\n }\n\n readonly subscribe = (listener: () => void): (() => void) => {\n this.sync();\n if (!this.listeners.size) this.browser?.addEventListener(\"storage\", this.onStorage);\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n if (!this.listeners.size) this.browser?.removeEventListener(\"storage\", this.onStorage);\n };\n };\n\n record(query: string, results: number): void {\n query = normalized(query);\n if (!query) return;\n this.sync();\n this.save([{ query, results: Number.isFinite(results) ? Math.max(0, Math.floor(results)) : 0, searched_at: new Date().toISOString() }, ...this.rows.filter((row) => identity(row.query) !== identity(query))].slice(0, MAX_SEARCHES));\n }\n\n clear(query?: string): void {\n this.sync();\n this.save(query ? this.rows.filter((row) => identity(row.query) !== identity(query)) : []);\n }\n\n private readonly onStorage = (event: StorageEvent): void => {\n if (event.key === null || event.key === this.key) this.sync();\n };\n\n private sync(): void {\n if (!this.browser || this.storageFailed) return;\n try { this.replace(decode(this.browser.localStorage.getItem(this.key))); }\n catch { this.storageFailed = true; }\n }\n\n private save(rows: readonly RecentSearch[]): void {\n if (this.browser && !this.storageFailed) {\n try {\n if (rows.length) this.browser.localStorage.setItem(this.key, JSON.stringify(rows));\n else this.browser.localStorage.removeItem(this.key);\n } catch { this.storageFailed = true; }\n }\n this.replace(rows);\n }\n\n private replace(rows: readonly RecentSearch[]): void {\n const encoded = JSON.stringify(rows);\n if (encoded === this.encoded) return;\n this.encoded = encoded;\n this.rows = Object.freeze(rows.map((row) => Object.freeze({ ...row })));\n for (const listener of this.listeners) {\n try { listener(); } catch { /* A subscriber must not fail the search that completed. */ }\n }\n }\n}\n\nconst stores = new WeakMap<Window, Map<string, BrowserSearchHistory>>();\n\n/** One same-tab store per discovery URL; server rendering never shares a store across requests. */\nexport function browserSearchHistory(mount: string): BrowserSearchHistory {\n const browser = typeof window === \"undefined\" ? undefined : window;\n let scope = mount.replace(/\\/+$/, \"\");\n if (browser) {\n try { scope = new URL(scope, browser.location.href).href; } catch { /* A relative SSR mount remains a distinct local key. */ }\n }\n const key = `contentkit:recent-searches:v1:${scope}`;\n if (!browser) return new BrowserSearchHistory(key);\n let scoped = stores.get(browser);\n if (!scoped) stores.set(browser, (scoped = new Map()));\n let store = scoped.get(key);\n if (!store) scoped.set(key, (store = new BrowserSearchHistory(key, browser)));\n return store;\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { ContentFilters, ContentFiltersUpdate, ContentPage, ExposureInput, RecentSearches, ReleaseDates, SignalInput, Typeahead } from './generated/wire.js';
2
2
  import { Http } from './http.js';
3
+ import { BrowserSearchHistory } from './browser-search-history.js';
3
4
  /** A popularity period. */
4
5
  export type Period = "7d" | "30d" | "90d" | "365d" | "all";
5
6
  /** A list's order; position orders by the first of `groups` (an ordered group). */
@@ -55,11 +56,11 @@ export type SearchQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQue
55
56
  q: string;
56
57
  sort?: "relevance" | ListSort;
57
58
  personalize?: boolean;
58
- /** A signed-out reader's session, which keeps its recent searches; default readerSession(). */
59
+ /** Explicit legacy server-side anonymous history. Omit for public search without server history. */
59
60
  session?: string;
60
61
  };
61
62
  export interface RecentSearchesQuery {
62
- /** A signed-out reader's session; default readerSession(). */
63
+ /** Explicit legacy server-side anonymous history; no anonymous session is created by default. */
63
64
  session?: string;
64
65
  /** 10 by default, at most 100. */
65
66
  limit?: number;
@@ -91,6 +92,8 @@ export declare function query(q: object): Record<string, string | number | boole
91
92
  */
92
93
  export declare class DiscoveryClient {
93
94
  private readonly http;
95
+ /** Local anonymous history, isolated from authenticated server history. */
96
+ readonly browserSearches: BrowserSearchHistory;
94
97
  constructor(http: Http);
95
98
  /** Contents as cards, one per content item with the version that fits the reader. */
96
99
  list(q?: ContentQuery, signal?: AbortSignal): Promise<ContentPage>;
@@ -1,5 +1,5 @@
1
1
  import { call as e } from "./route.js";
2
- import { readerSession as t } from "./beacons.js";
2
+ import { browserSearchHistory as t } from "./browser-search-history.js";
3
3
  //#region src/client/discovery.ts
4
4
  var n = /* @__PURE__ */ new Set([
5
5
  "groups",
@@ -18,8 +18,9 @@ function r(e) {
18
18
  }
19
19
  var i = class {
20
20
  http;
21
+ browserSearches;
21
22
  constructor(e) {
22
- this.http = e;
23
+ this.http = e, this.browserSearches = t(e.mount("discovery"));
23
24
  }
24
25
  list(t = {}, n) {
25
26
  return e(this.http, "GET", "/contents", {
@@ -27,31 +28,22 @@ var i = class {
27
28
  signal: n
28
29
  });
29
30
  }
30
- async search(n, i) {
31
- let a = n.cursor ? null : this.http.captureChanges(), o = await e(this.http, "GET", "/search", {
32
- query: r({
33
- session: t(),
34
- ...n
35
- }),
36
- signal: i
31
+ async search(t, n) {
32
+ let i = t.cursor ? null : this.http.captureChanges(), a = await e(this.http, "GET", "/search", {
33
+ query: r(t),
34
+ signal: n
37
35
  });
38
- return a?.({ type: "searches.changed" }), o;
36
+ return i?.({ type: "searches.changed" }), a;
39
37
  }
40
- recentSearches(n = {}, r) {
38
+ recentSearches(t = {}, n) {
41
39
  return e(this.http, "GET", "/me/searches", {
42
- query: {
43
- session: t(),
44
- ...n
45
- },
46
- signal: r
40
+ query: t,
41
+ signal: n
47
42
  });
48
43
  }
49
- async clearSearches(n = {}) {
50
- let r = this.http.captureChanges();
51
- await e(this.http, "DELETE", "/me/searches", { query: {
52
- session: t(),
53
- ...n
54
- } }), r({ type: "searches.changed" });
44
+ async clearSearches(t = {}) {
45
+ let n = this.http.captureChanges();
46
+ await e(this.http, "DELETE", "/me/searches", { query: t }), n({ type: "searches.changed" });
55
47
  }
56
48
  typeahead(t, n) {
57
49
  return e(this.http, "GET", "/typeahead", {
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.js","names":[],"sources":["../../src/client/discovery.ts"],"sourcesContent":["import type {\n ContentFilters,\n ContentFiltersUpdate,\n ContentPage,\n ExposureInput,\n Exposures,\n RecentSearches,\n ReleaseDates,\n SignalInput,\n Signals,\n Typeahead,\n} from \"./generated/wire.js\";\nimport { readerSession } from \"./beacons.js\";\nimport type { Http } from \"./http.js\";\nimport { call } from \"./route.js\";\n\n/** A popularity period. */\nexport type Period = \"7d\" | \"30d\" | \"90d\" | \"365d\" | \"all\";\n/** A list's order; position orders by the first of `groups` (an ordered group). */\nexport type ListSort = \"newest\" | \"oldest\" | \"title\" | \"popular\" | `popular:${Period}` | \"top_rated\" | `top_rated:${Period}` | \"most_favorited\" | \"most_viewed\" | \"position\" | \"random\";\n/** How locked contents list: marked (lock, the default) or left out (hide). */\nexport type AccessMode = \"lock\" | \"hide\";\n\n/** Which contents a list holds. */\nexport interface ContentFilterQuery {\n kind?: string;\n /** Group ids every listed version is in. */\n groups?: readonly string[];\n /** Group ids no listed version is in. */\n exclude_groups?: readonly string[];\n /** Only contents this group owns: a channel's feed. */\n owner_group?: string;\n /** User ids: only contents one of them is credited on. */\n creators?: readonly string[];\n /** public (anyone opens it now) or the level it stays locked at. */\n access?: readonly (\"public\" | \"tier\" | \"group\" | \"ppv\" | \"group_ppv\")[];\n /** Only contents locked behind one of these tiers. */\n tiers?: readonly string[];\n /** Version traits every listed version has. */\n traits?: readonly string[];\n /** \"name:value\" of the kind's filterable attributes. */\n attribute?: readonly string[];\n /** Published on this day (YYYY-MM-DD) or month (YYYY-MM). */\n released_on?: string;\n /** IANA time zone of released_on and release dates; UTC by default. */\n tz?: string;\n /** The signed-in reader's unseen, in-progress or completed contents. */\n view_status?: \"unseen\" | \"in_progress\" | \"completed\";\n}\n\n/** How a list reads to its reader. */\nexport interface ReaderQuery {\n /** Only content with a live version in this language. */\n language?: string;\n /** Titles, names and paths in this language, and each card's preferred version's; language by default. */\n display_language?: string;\n mode?: AccessMode;\n}\n\n/** The group kinds cards name; every kind with a page by default. */\nexport interface CardQuery {\n card_groups?: readonly string[];\n}\n\nexport interface PageQuery {\n cursor?: string;\n limit?: number;\n}\n\nexport type ContentQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & { sort?: ListSort };\nexport type SearchQuery = ContentFilterQuery &\n ReaderQuery &\n CardQuery &\n PageQuery & {\n q: string;\n sort?: \"relevance\" | ListSort;\n personalize?: boolean;\n /** A signed-out reader's session, which keeps its recent searches; default readerSession(). */\n session?: string;\n };\nexport interface RecentSearchesQuery {\n /** A signed-out reader's session; default readerSession(). */\n session?: string;\n /** 10 by default, at most 100. */\n limit?: number;\n}\nexport type RailQuery = ContentFilterQuery & ReaderQuery & CardQuery & { limit?: number };\nexport type ReleaseDatesQuery = ContentFilterQuery & ReaderQuery & PageQuery & { by?: \"month\" | \"day\" };\nexport type HistoryQuery = ReaderQuery & CardQuery & PageQuery & { kind?: string; status?: \"seen\" | \"in_progress\" | \"completed\" };\nexport interface TypeaheadQuery extends ReaderQuery {\n q: string;\n /** Only contents of this kind. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n group_kinds?: readonly string[];\n /** Per section: 8 by default, at most 20. */\n limit?: number;\n}\n\n// Comma-joined list parameters; `attribute` repeats instead.\nconst LISTS = new Set([\"groups\", \"exclude_groups\", \"creators\", \"access\", \"tiers\", \"traits\", \"card_groups\", \"group_kinds\"]);\n\n/** A query with its list parameters comma-joined, as discovery routes read them. */\nexport function query(q: object): Record<string, string | number | boolean | readonly string[] | undefined> {\n const out: Record<string, string | number | boolean | readonly string[] | undefined> = {};\n for (const [k, v] of Object.entries(q)) out[k] = LISTS.has(k) && Array.isArray(v) ? (v.length ? v.join(\",\") : undefined) : v;\n return out;\n}\n\n/**\n * Discovery: lists, search, typeahead, similar and recommended contents,\n * release dates, the reader's content filters and history, signals and exposures.\n */\nexport class DiscoveryClient {\n constructor(private readonly http: Http) {}\n\n /** Contents as cards, one per content item with the version that fits the reader. */\n list(q: ContentQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents\", { query: query(q) as never, signal });\n }\n /** A first page is kept among the reader's recent searches. */\n async search(q: SearchQuery, signal?: AbortSignal): Promise<ContentPage> {\n const emit = q.cursor ? null : this.http.captureChanges();\n const out: ContentPage = await call(this.http, \"GET\", \"/search\", { query: query({ session: readerSession(), ...q }) as never, signal });\n emit?.({ type: \"searches.changed\" });\n return out;\n }\n /** The reader's recent searches, newest first, since they last cleared them. */\n recentSearches(q: RecentSearchesQuery = {}, signal?: AbortSignal): Promise<RecentSearches> {\n return call(this.http, \"GET\", \"/me/searches\", { query: { session: readerSession(), ...q }, signal });\n }\n /** Clears one recent search, or all of them. */\n async clearSearches(o: { query?: string; session?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/searches\", { query: { session: readerSession(), ...o } });\n emit({ type: \"searches.changed\" });\n }\n /** Contents and groups while the reader types. */\n typeahead(q: TypeaheadQuery, signal?: AbortSignal): Promise<Typeahead> {\n return call(this.http, \"GET\", \"/typeahead\", { query: query(q) as never, signal });\n }\n /** Contents like one, of its kind unless q.kind names another. */\n similar(id: string, q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents/{id}/similar\", { params: { id }, query: query(q) as never, signal });\n }\n /** For the signed-in reader. */\n recommendations(q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/recommendations\", { query: query(q) as never, signal });\n }\n /** Per month or day, newest first: how many contents the reader's listing shows released then, and how many are free. */\n releaseDates(q: ReleaseDatesQuery = {}, signal?: AbortSignal): Promise<ReleaseDates> {\n return call(this.http, \"GET\", \"/release-dates\", { query: query(q) as never, signal });\n }\n contentFilters(language?: string, signal?: AbortSignal): Promise<ContentFilters> {\n return call(this.http, \"GET\", \"/me/content-filters\", { query: { language }, signal });\n }\n /** Replaces the reader's filter settings; every read applies them. */\n async setContentFilters(update: ContentFiltersUpdate, language?: string): Promise<ContentFilters> {\n const emit = this.http.captureChanges();\n const out: ContentFilters = await call(this.http, \"PUT\", \"/me/content-filters\", { query: { language }, body: update });\n emit({ type: \"contentFilters.changed\" });\n return out;\n }\n /** What the reader engaged with, most recent first. */\n history(q: HistoryQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/me/history\", { query: query(q) as never, signal });\n }\n /** Erases the reader's history: of one content item, one kind or everything. */\n async forgetHistory(o: { kind?: string; content_id?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/history\", { query: o });\n emit({ type: \"history.changed\" });\n }\n /** Views and clicks; signed out, of the session named. */\n signals(signals: SignalInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Signals = { session, signals };\n return call(this.http, \"POST\", \"/signals\", { body, keepalive: o.keepalive });\n }\n /** Result lists served, rendered or shown, for click attribution. */\n exposures(exposures: ExposureInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Exposures = { session, exposures };\n return call(this.http, \"POST\", \"/exposures\", { body, keepalive: o.keepalive });\n }\n}\n"],"mappings":";;;AAoGA,IAAM,oBAAQ,IAAI,IAAI;CAAC;CAAU;CAAkB;CAAY;CAAU;CAAS;CAAU;CAAe;AAAa,CAAC;AAGzH,SAAgB,EAAM,GAAsF;CAC1G,IAAM,IAAiF,CAAC;CACxF,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,CAAC,GAAG,EAAI,KAAK,EAAM,IAAI,CAAC,KAAK,MAAM,QAAQ,CAAC,IAAK,EAAE,SAAS,EAAE,KAAK,GAAG,IAAI,KAAA,IAAa;CAC3H,OAAO;AACT;AAMA,IAAa,IAAb,MAA6B;CACE;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,KAAK,IAAkB,CAAC,GAAG,GAA4C;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,aAAa;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACjF;CAEA,MAAM,OAAO,GAAgB,GAA4C;EACvE,IAAM,IAAO,EAAE,SAAS,OAAO,KAAK,KAAK,eAAe,GAClD,IAAmB,MAAM,EAAK,KAAK,MAAM,OAAO,WAAW;GAAE,OAAO,EAAM;IAAE,SAAS,EAAc;IAAG,GAAG;GAAE,CAAC;GAAY;EAAO,CAAC;EAEtI,OADA,IAAO,EAAE,MAAM,mBAAmB,CAAC,GAC5B;CACT;CAEA,eAAe,IAAyB,CAAC,GAAG,GAA+C;EACzF,OAAO,EAAK,KAAK,MAAM,OAAO,gBAAgB;GAAE,OAAO;IAAE,SAAS,EAAc;IAAG,GAAG;GAAE;GAAG;EAAO,CAAC;CACrG;CAEA,MAAM,cAAc,IAA0C,CAAC,GAAkB;EAC/E,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,gBAAgB,EAAE,OAAO;GAAE,SAAS,EAAc;GAAG,GAAG;EAAE,EAAE,CAAC,GAC7F,EAAK,EAAE,MAAM,mBAAmB,CAAC;CACnC;CAEA,UAAU,GAAmB,GAA0C;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,cAAc;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CAClF;CAEA,QAAQ,GAAY,IAAe,CAAC,GAAG,GAA4C;EACjF,OAAO,EAAK,KAAK,MAAM,OAAO,0BAA0B;GAAE,QAAQ,EAAE,MAAG;GAAG,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CAC9G;CAEA,gBAAgB,IAAe,CAAC,GAAG,GAA4C;EAC7E,OAAO,EAAK,KAAK,MAAM,OAAO,oBAAoB;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACxF;CAEA,aAAa,IAAuB,CAAC,GAAG,GAA6C;EACnF,OAAO,EAAK,KAAK,MAAM,OAAO,kBAAkB;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACtF;CACA,eAAe,GAAmB,GAA+C;EAC/E,OAAO,EAAK,KAAK,MAAM,OAAO,uBAAuB;GAAE,OAAO,EAAE,YAAS;GAAG;EAAO,CAAC;CACtF;CAEA,MAAM,kBAAkB,GAA8B,GAA4C;EAChG,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAsB,MAAM,EAAK,KAAK,MAAM,OAAO,uBAAuB;GAAE,OAAO,EAAE,YAAS;GAAG,MAAM;EAAO,CAAC;EAErH,OADA,EAAK,EAAE,MAAM,yBAAyB,CAAC,GAChC;CACT;CAEA,QAAQ,IAAkB,CAAC,GAAG,GAA4C;EACxE,OAAO,EAAK,KAAK,MAAM,OAAO,eAAe;GAAE,OAAO,EAAM,CAAC;GAAY;EAAO,CAAC;CACnF;CAEA,MAAM,cAAc,IAA4C,CAAC,GAAkB;EACjF,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,eAAe,EAAE,OAAO,EAAE,CAAC,GAC3D,EAAK,EAAE,MAAM,kBAAkB,CAAC;CAClC;CAEA,QAAQ,GAAwB,GAAkB,IAA6B,CAAC,GAAkB;EAChG,IAAM,IAAgB;GAAE;GAAS;EAAQ;EACzC,OAAO,EAAK,KAAK,MAAM,QAAQ,YAAY;GAAE;GAAM,WAAW,EAAE;EAAU,CAAC;CAC7E;CAEA,UAAU,GAA4B,GAAkB,IAA6B,CAAC,GAAkB;EACtG,IAAM,IAAkB;GAAE;GAAS;EAAU;EAC7C,OAAO,EAAK,KAAK,MAAM,QAAQ,cAAc;GAAE;GAAM,WAAW,EAAE;EAAU,CAAC;CAC/E;AACF"}
1
+ {"version":3,"file":"discovery.js","names":[],"sources":["../../src/client/discovery.ts"],"sourcesContent":["import type {\n ContentFilters,\n ContentFiltersUpdate,\n ContentPage,\n ExposureInput,\n Exposures,\n RecentSearches,\n ReleaseDates,\n SignalInput,\n Signals,\n Typeahead,\n} from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call } from \"./route.js\";\nimport { browserSearchHistory, type BrowserSearchHistory } from \"./browser-search-history.js\";\n\n/** A popularity period. */\nexport type Period = \"7d\" | \"30d\" | \"90d\" | \"365d\" | \"all\";\n/** A list's order; position orders by the first of `groups` (an ordered group). */\nexport type ListSort = \"newest\" | \"oldest\" | \"title\" | \"popular\" | `popular:${Period}` | \"top_rated\" | `top_rated:${Period}` | \"most_favorited\" | \"most_viewed\" | \"position\" | \"random\";\n/** How locked contents list: marked (lock, the default) or left out (hide). */\nexport type AccessMode = \"lock\" | \"hide\";\n\n/** Which contents a list holds. */\nexport interface ContentFilterQuery {\n kind?: string;\n /** Group ids every listed version is in. */\n groups?: readonly string[];\n /** Group ids no listed version is in. */\n exclude_groups?: readonly string[];\n /** Only contents this group owns: a channel's feed. */\n owner_group?: string;\n /** User ids: only contents one of them is credited on. */\n creators?: readonly string[];\n /** public (anyone opens it now) or the level it stays locked at. */\n access?: readonly (\"public\" | \"tier\" | \"group\" | \"ppv\" | \"group_ppv\")[];\n /** Only contents locked behind one of these tiers. */\n tiers?: readonly string[];\n /** Version traits every listed version has. */\n traits?: readonly string[];\n /** \"name:value\" of the kind's filterable attributes. */\n attribute?: readonly string[];\n /** Published on this day (YYYY-MM-DD) or month (YYYY-MM). */\n released_on?: string;\n /** IANA time zone of released_on and release dates; UTC by default. */\n tz?: string;\n /** The signed-in reader's unseen, in-progress or completed contents. */\n view_status?: \"unseen\" | \"in_progress\" | \"completed\";\n}\n\n/** How a list reads to its reader. */\nexport interface ReaderQuery {\n /** Only content with a live version in this language. */\n language?: string;\n /** Titles, names and paths in this language, and each card's preferred version's; language by default. */\n display_language?: string;\n mode?: AccessMode;\n}\n\n/** The group kinds cards name; every kind with a page by default. */\nexport interface CardQuery {\n card_groups?: readonly string[];\n}\n\nexport interface PageQuery {\n cursor?: string;\n limit?: number;\n}\n\nexport type ContentQuery = ContentFilterQuery & ReaderQuery & CardQuery & PageQuery & { sort?: ListSort };\nexport type SearchQuery = ContentFilterQuery &\n ReaderQuery &\n CardQuery &\n PageQuery & {\n q: string;\n sort?: \"relevance\" | ListSort;\n personalize?: boolean;\n /** Explicit legacy server-side anonymous history. Omit for public search without server history. */\n session?: string;\n };\nexport interface RecentSearchesQuery {\n /** Explicit legacy server-side anonymous history; no anonymous session is created by default. */\n session?: string;\n /** 10 by default, at most 100. */\n limit?: number;\n}\nexport type RailQuery = ContentFilterQuery & ReaderQuery & CardQuery & { limit?: number };\nexport type ReleaseDatesQuery = ContentFilterQuery & ReaderQuery & PageQuery & { by?: \"month\" | \"day\" };\nexport type HistoryQuery = ReaderQuery & CardQuery & PageQuery & { kind?: string; status?: \"seen\" | \"in_progress\" | \"completed\" };\nexport interface TypeaheadQuery extends ReaderQuery {\n q: string;\n /** Only contents of this kind. */\n kind?: string;\n /** The group kinds suggested; every kind with a page by default. */\n group_kinds?: readonly string[];\n /** Per section: 8 by default, at most 20. */\n limit?: number;\n}\n\n// Comma-joined list parameters; `attribute` repeats instead.\nconst LISTS = new Set([\"groups\", \"exclude_groups\", \"creators\", \"access\", \"tiers\", \"traits\", \"card_groups\", \"group_kinds\"]);\n\n/** A query with its list parameters comma-joined, as discovery routes read them. */\nexport function query(q: object): Record<string, string | number | boolean | readonly string[] | undefined> {\n const out: Record<string, string | number | boolean | readonly string[] | undefined> = {};\n for (const [k, v] of Object.entries(q)) out[k] = LISTS.has(k) && Array.isArray(v) ? (v.length ? v.join(\",\") : undefined) : v;\n return out;\n}\n\n/**\n * Discovery: lists, search, typeahead, similar and recommended contents,\n * release dates, the reader's content filters and history, signals and exposures.\n */\nexport class DiscoveryClient {\n /** Local anonymous history, isolated from authenticated server history. */\n readonly browserSearches: BrowserSearchHistory;\n\n constructor(private readonly http: Http) {\n this.browserSearches = browserSearchHistory(http.mount(\"discovery\"));\n }\n\n /** Contents as cards, one per content item with the version that fits the reader. */\n list(q: ContentQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents\", { query: query(q) as never, signal });\n }\n /** A first page is kept among the reader's recent searches. */\n async search(q: SearchQuery, signal?: AbortSignal): Promise<ContentPage> {\n const emit = q.cursor ? null : this.http.captureChanges();\n const out: ContentPage = await call(this.http, \"GET\", \"/search\", { query: query(q) as never, signal });\n emit?.({ type: \"searches.changed\" });\n return out;\n }\n /** The reader's recent searches, newest first, since they last cleared them. */\n recentSearches(q: RecentSearchesQuery = {}, signal?: AbortSignal): Promise<RecentSearches> {\n return call(this.http, \"GET\", \"/me/searches\", { query: q, signal });\n }\n /** Clears one recent search, or all of them. */\n async clearSearches(o: { query?: string; session?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/searches\", { query: o });\n emit({ type: \"searches.changed\" });\n }\n /** Contents and groups while the reader types. */\n typeahead(q: TypeaheadQuery, signal?: AbortSignal): Promise<Typeahead> {\n return call(this.http, \"GET\", \"/typeahead\", { query: query(q) as never, signal });\n }\n /** Contents like one, of its kind unless q.kind names another. */\n similar(id: string, q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/contents/{id}/similar\", { params: { id }, query: query(q) as never, signal });\n }\n /** For the signed-in reader. */\n recommendations(q: RailQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/recommendations\", { query: query(q) as never, signal });\n }\n /** Per month or day, newest first: how many contents the reader's listing shows released then, and how many are free. */\n releaseDates(q: ReleaseDatesQuery = {}, signal?: AbortSignal): Promise<ReleaseDates> {\n return call(this.http, \"GET\", \"/release-dates\", { query: query(q) as never, signal });\n }\n contentFilters(language?: string, signal?: AbortSignal): Promise<ContentFilters> {\n return call(this.http, \"GET\", \"/me/content-filters\", { query: { language }, signal });\n }\n /** Replaces the reader's filter settings; every read applies them. */\n async setContentFilters(update: ContentFiltersUpdate, language?: string): Promise<ContentFilters> {\n const emit = this.http.captureChanges();\n const out: ContentFilters = await call(this.http, \"PUT\", \"/me/content-filters\", { query: { language }, body: update });\n emit({ type: \"contentFilters.changed\" });\n return out;\n }\n /** What the reader engaged with, most recent first. */\n history(q: HistoryQuery = {}, signal?: AbortSignal): Promise<ContentPage> {\n return call(this.http, \"GET\", \"/me/history\", { query: query(q) as never, signal });\n }\n /** Erases the reader's history: of one content item, one kind or everything. */\n async forgetHistory(o: { kind?: string; content_id?: string } = {}): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/me/history\", { query: o });\n emit({ type: \"history.changed\" });\n }\n /** Views and clicks; signed out, of the session named. */\n signals(signals: SignalInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Signals = { session, signals };\n return call(this.http, \"POST\", \"/signals\", { body, keepalive: o.keepalive });\n }\n /** Result lists served, rendered or shown, for click attribution. */\n exposures(exposures: ExposureInput[], session?: string, o: { keepalive?: boolean } = {}): Promise<void> {\n const body: Exposures = { session, exposures };\n return call(this.http, \"POST\", \"/exposures\", { body, keepalive: o.keepalive });\n }\n}\n"],"mappings":"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"}
@@ -1634,8 +1634,8 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1634
1634
  readonly method: "PUT";
1635
1635
  readonly module: "versions";
1636
1636
  readonly path: "/contents/{id}/versions/{version}/reader";
1637
- readonly auth: "user";
1638
- readonly permission: null;
1637
+ readonly auth: "editor";
1638
+ readonly permission: "root:content:write";
1639
1639
  readonly query: readonly [];
1640
1640
  readonly request: "ReaderMeta";
1641
1641
  readonly responses: {
@@ -1676,7 +1676,7 @@ export declare const CONTENTKIT_ROUTES: readonly [{
1676
1676
  readonly path: "/contents/{id}/versions/{version}/archive";
1677
1677
  readonly auth: "public";
1678
1678
  readonly permission: null;
1679
- readonly query: readonly ["for", "mark", "ticket"];
1679
+ readonly query: readonly ["for", "mark", "name", "ticket"];
1680
1680
  readonly request: null;
1681
1681
  readonly responses: {
1682
1682
  readonly 200: "application/zip";
@@ -2264,8 +2264,8 @@ var e = {
2264
2264
  method: "PUT",
2265
2265
  module: "versions",
2266
2266
  path: "/contents/{id}/versions/{version}/reader",
2267
- auth: "user",
2268
- permission: null,
2267
+ auth: "editor",
2268
+ permission: "root:content:write",
2269
2269
  query: [],
2270
2270
  request: "ReaderMeta",
2271
2271
  responses: { 204: null },
@@ -2325,6 +2325,7 @@ var e = {
2325
2325
  query: [
2326
2326
  "for",
2327
2327
  "mark",
2328
+ "name",
2328
2329
  "ticket"
2329
2330
  ],
2330
2331
  request: null,