@openrails/contentkit-ui 0.89.1 → 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 (37) hide show
  1. package/README.md +25 -0
  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.js +14 -14
  9. package/dist/components/content/index.d.ts +1 -0
  10. package/dist/components/content/reader-settings.d.ts +19 -0
  11. package/dist/components/content/reader-settings.js +95 -0
  12. package/dist/components/content/reader-settings.js.map +1 -0
  13. package/dist/components/content/search.d.ts +2 -0
  14. package/dist/components/content/search.js +334 -137
  15. package/dist/components/content/search.js.map +1 -1
  16. package/dist/index.js +17 -16
  17. package/dist/locales/discovery/de.js +9 -0
  18. package/dist/locales/discovery/de.js.map +1 -1
  19. package/dist/locales/discovery/en.d.ts +9 -0
  20. package/dist/locales/discovery/en.js +9 -0
  21. package/dist/locales/discovery/en.js.map +1 -1
  22. package/dist/locales/discovery/es.js +9 -0
  23. package/dist/locales/discovery/es.js.map +1 -1
  24. package/dist/locales/discovery/ja.js +9 -0
  25. package/dist/locales/discovery/ja.js.map +1 -1
  26. package/dist/locales/discovery/ko.js +9 -0
  27. package/dist/locales/discovery/ko.js.map +1 -1
  28. package/dist/locales/discovery/zh.js +9 -0
  29. package/dist/locales/discovery/zh.js.map +1 -1
  30. package/dist/locales/en.d.ts +9 -0
  31. package/dist/react/discovery.d.ts +1 -1
  32. package/dist/react/discovery.js +145 -92
  33. package/dist/react/discovery.js.map +1 -1
  34. package/dist/styles.css +1 -1
  35. package/dist/ui/dialog.js +16 -10
  36. package/dist/ui/dialog.js.map +1 -1
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -679,3 +679,28 @@ poll by default. Call `client.notifications.refresh()` when your application's
679
679
  event channel announces new data. `useNotifications` and `useNotificationUnread`
680
680
  are available from `/react` for a custom presentation. Keep provider viewer and
681
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":"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"}
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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"}
package/dist/client.js CHANGED
@@ -25,18 +25,18 @@ import { CodesClient as He } from "./client/codes.js";
25
25
  import { ContentsClient as Ue, IMAGE_SCHEME as We, imageRef as Ge, imageRefs as Ke } from "./client/contents.js";
26
26
  import { BansClient as qe } from "./client/content/bans.js";
27
27
  import { CommentsClient as Je } from "./client/content/comments.js";
28
- import { Beacons as Ye, eventId as Xe, readerSession as Ze } from "./client/beacons.js";
29
- import { DiscoveryClient as Qe } from "./client/discovery.js";
30
- import { FavoritesClient as $e } from "./client/content/favorites.js";
31
- import { ModerationClient as et } from "./client/content/moderation.js";
32
- import { PollsClient as tt } from "./client/content/polls.js";
33
- import { ReactionsClient as nt } from "./client/content/reactions.js";
34
- import { GroupsClient as rt } from "./client/groups.js";
35
- import { OffersClient as it } from "./client/offers.js";
36
- import { StatsClient as at } from "./client/stats.js";
37
- import { VersionsClient as ot, isPending as st } from "./client/versions.js";
38
- import { NotificationsClient as ct } from "./client/notifications.js";
39
- import { createContentKitClient as lt } from "./client/client.js";
40
- import { LETTERS as ut, attributeInput as dt, fold as ft, fromLocalInput as pt, humanize as mt, letterOf as ht, parseAttribute as gt, parseAttributes as _t, slugify as vt, toLocalInput as yt } from "./client/group-fields.js";
28
+ import { DiscoveryClient as Ye } from "./client/discovery.js";
29
+ import { FavoritesClient as Xe } from "./client/content/favorites.js";
30
+ import { ModerationClient as Ze } from "./client/content/moderation.js";
31
+ import { PollsClient as Qe } from "./client/content/polls.js";
32
+ import { ReactionsClient as $e } from "./client/content/reactions.js";
33
+ import { GroupsClient as et } from "./client/groups.js";
34
+ import { OffersClient as tt } from "./client/offers.js";
35
+ import { StatsClient as nt } from "./client/stats.js";
36
+ import { VersionsClient as rt, isPending as it } from "./client/versions.js";
37
+ import { NotificationsClient as at } from "./client/notifications.js";
38
+ import { createContentKitClient as ot } from "./client/client.js";
39
+ import { LETTERS as st, attributeInput as ct, fold as lt, fromLocalInput as ut, humanize as dt, letterOf as ft, parseAttribute as pt, parseAttributes as mt, slugify as ht, toLocalInput as gt } from "./client/group-fields.js";
40
+ import { Beacons as _t, eventId as vt, readerSession as yt } from "./client/beacons.js";
41
41
  import { emptyReaderMeta as bt, matchAudio as xt, readerMetaProblems as St, readerNames as Ct } from "./client/reader-meta.js";
42
- export { he as ABR_DEFAULTS, qe as BansClient, Ye as Beacons, i as CLIENT_ERROR_CODES, Ve as CatalogClient, He as CodesClient, Je as CommentsClient, a as ContentKitError, Ue as ContentsClient, e as DEFAULT_DENSITY, Qe as DiscoveryClient, o as ERROR_CODES, $e as FavoritesClient, rt as GroupsClient, We as IMAGE_SCHEME, ae as ItemUploadQueue, ut as LETTERS, T as MAX_PART_SIZE, E as MAX_SINGLE_PUT, D as MIN_PART_SIZE, w as MediaApi, N as MediaClient, et as ModerationClient, ge as NETWORK_FAILURES_BEFORE_ERROR, ct as NotificationsClient, it as OffersClient, Fe as PLAYBACK_SPEEDS, Ie as PLAYER_TRACKS_KEY, Le as PLAYER_VOLUME_KEY, k as Pacer, tt as PollsClient, ue as READ_CHUNK, _e as REFRESH_BEFORE_EXPIRY, nt as ReactionsClient, at as StatsClient, ot as VersionsClient, ve as abrHlsConfig, L as acceptOf, g as aspectOf, dt as attributeInput, P as backoff, v as call, ye as capRung, Y as centeredCrop, b as checkRef, de as chunkOffsets, be as classifyHlsError, xe as classifyMediaError, X as constrainCrop, lt as createContentKitClient, oe as decodeImage, R as defaultImage, A as defaultTransport, t as densityFor, Z as editOf, Q as editedSize, bt as emptyReaderMeta, ie as encodeRemaining, Xe as eventId, Se as expiryDelay, s as failureError, j as fetchTransport, z as filesFor, f as fill, ft as fold, Oe as formatDuration, pt as fromLocalInput, $ as fromRotated, ke as galleryItems, Ce as hlsConfig, mt as humanize, Ge as imageRef, Ke as imageRefs, we as initialEstimate, se as isAnimatedImage, Ae as isAudioType, x as isContentId, c as isContentKitError, je as isImageType, B as isPattern, st as isPending, fe as isProcessing, Me as isSubtitleType, Ne as isVideoType, S as itemKey, ht as letterOf, xt as matchAudio, pe as mergeReads, gt as parseAttribute, _t as parseAttributes, Re as parseSpriteVtt, n as pickRendition, ze as playerKeyAction, me as processing, p as publicRenditions, m as publicURL, _ as ratio, V as ratioLabel, ce as reactionVerb, l as readContentKitError, St as readerMetaProblems, Ct as readerNames, Ze as readerSession, Te as refreshable, Be as resumeAt, ee as rotation, y as routeURL, H as ruleDir, U as ruleFor, te as sameEdit, C as sameItem, F as samePath, le as sameTarget, W as screenFiles, O as sha256Hex, vt as slugify, r as sortRenditions, h as srcSet, G as stableImage, Pe as stageAspect, Ee as startRung, De as statusKind, I as stem, u as toContentKitError, yt as toLocalInput, ne as toOriginal, re as toRotated, d as transportFailure, K as uniqueName, q as uploadRules, J as withMediaType, M as xhrTransport };
42
+ export { he as ABR_DEFAULTS, qe as BansClient, _t as Beacons, i as CLIENT_ERROR_CODES, Ve as CatalogClient, He as CodesClient, Je as CommentsClient, a as ContentKitError, Ue as ContentsClient, e as DEFAULT_DENSITY, Ye as DiscoveryClient, o as ERROR_CODES, Xe as FavoritesClient, et as GroupsClient, We as IMAGE_SCHEME, ae as ItemUploadQueue, st as LETTERS, T as MAX_PART_SIZE, E as MAX_SINGLE_PUT, D as MIN_PART_SIZE, w as MediaApi, N as MediaClient, Ze as ModerationClient, ge as NETWORK_FAILURES_BEFORE_ERROR, at as NotificationsClient, tt as OffersClient, Fe as PLAYBACK_SPEEDS, Ie as PLAYER_TRACKS_KEY, Le as PLAYER_VOLUME_KEY, k as Pacer, Qe as PollsClient, ue as READ_CHUNK, _e as REFRESH_BEFORE_EXPIRY, $e as ReactionsClient, nt as StatsClient, rt as VersionsClient, ve as abrHlsConfig, L as acceptOf, g as aspectOf, ct as attributeInput, P as backoff, v as call, ye as capRung, Y as centeredCrop, b as checkRef, de as chunkOffsets, be as classifyHlsError, xe as classifyMediaError, X as constrainCrop, ot as createContentKitClient, oe as decodeImage, R as defaultImage, A as defaultTransport, t as densityFor, Z as editOf, Q as editedSize, bt as emptyReaderMeta, ie as encodeRemaining, vt as eventId, Se as expiryDelay, s as failureError, j as fetchTransport, z as filesFor, f as fill, lt as fold, Oe as formatDuration, ut as fromLocalInput, $ as fromRotated, ke as galleryItems, Ce as hlsConfig, dt as humanize, Ge as imageRef, Ke as imageRefs, we as initialEstimate, se as isAnimatedImage, Ae as isAudioType, x as isContentId, c as isContentKitError, je as isImageType, B as isPattern, it as isPending, fe as isProcessing, Me as isSubtitleType, Ne as isVideoType, S as itemKey, ft as letterOf, xt as matchAudio, pe as mergeReads, pt as parseAttribute, mt as parseAttributes, Re as parseSpriteVtt, n as pickRendition, ze as playerKeyAction, me as processing, p as publicRenditions, m as publicURL, _ as ratio, V as ratioLabel, ce as reactionVerb, l as readContentKitError, St as readerMetaProblems, Ct as readerNames, yt as readerSession, Te as refreshable, Be as resumeAt, ee as rotation, y as routeURL, H as ruleDir, U as ruleFor, te as sameEdit, C as sameItem, F as samePath, le as sameTarget, W as screenFiles, O as sha256Hex, ht as slugify, r as sortRenditions, h as srcSet, G as stableImage, Pe as stageAspect, Ee as startRung, De as statusKind, I as stem, u as toContentKitError, gt as toLocalInput, ne as toOriginal, re as toRotated, d as transportFailure, K as uniqueName, q as uploadRules, J as withMediaType, M as xhrTransport };
@@ -1,6 +1,7 @@
1
1
  export { ContentCard, OwnerChip, PostCard, lockLabel, type ContentCardProps, type PostCardProps } from './card.js';
2
2
  export { CardGrid, ContentGrid, ContentHistory, ContentRail, SearchResults, type CardListProps, type ContentRailProps, type LockedCards } from './grid.js';
3
3
  export { SearchBox, type SearchBoxProps } from './search.js';
4
+ export { ReaderSettings, type ReaderSettingsProps } from './reader-settings.js';
4
5
  export { ContentFilterSettings, ReleaseDateIndex, type ContentFilterSettingsProps, type ReleaseDateIndexProps } from './filters.js';
5
6
  export { ContentListing, sortLabel, type ContentListingProps } from './listing.js';
6
7
  export { Paywall, VersionPicker, priceLabel, useVersionMenuItems, type PaywallProps, type VersionMenuOptions, type VersionPickerProps } from './paywall.js';
@@ -0,0 +1,19 @@
1
+ import { ContentKitUiAppearance } from '../../appearance.js';
2
+ import { ContentKitClient } from '../../client/client.js';
3
+ import { ContentKitErrorHandler } from '../../react/context.js';
4
+ export interface ReaderSettingsProps {
5
+ languages: readonly {
6
+ value: string;
7
+ label: string;
8
+ }[];
9
+ /** The current language, controlled by the host. */
10
+ language: string;
11
+ /** The host applies its own route and preference changes. */
12
+ onLanguageChange: (language: string) => void;
13
+ client?: ContentKitClient;
14
+ onError?: ContentKitErrorHandler;
15
+ className?: string;
16
+ appearance?: ContentKitUiAppearance;
17
+ }
18
+ /** One reader-settings dialog for language and the signed-in reader's filters. */
19
+ export declare function ReaderSettings(p: ReaderSettingsProps): import('react').JSX.Element;
@@ -0,0 +1,95 @@
1
+ import { useMessages as e } from "../../i18n/context.js";
2
+ import { AppearanceContext as t, ContentKitUiRoot as n } from "../../scope.js";
3
+ import { ContentKitContext as r } from "../../react/context.js";
4
+ import { Button as i } from "../../ui/button.js";
5
+ import { Dialog as a, DialogContent as o, DialogDescription as s, DialogHeader as c, DialogTitle as l, DialogTrigger as u } from "../../ui/dialog.js";
6
+ import { Label as d } from "../../ui/label.js";
7
+ import { Select as f, SelectContent as p, SelectItem as m, SelectTrigger as h, SelectValue as g } from "../../ui/select.js";
8
+ import { useContentFilters as _ } from "../../react/discovery.js";
9
+ import { ContentFilterSettings as v } from "./filters.js";
10
+ import { useContext as y, useId as b } from "react";
11
+ import { jsx as x, jsxs as S } from "react/jsx-runtime";
12
+ import { Settings01Icon as C } from "@hugeicons/core-free-icons";
13
+ import { HugeiconsIcon as w } from "@hugeicons/react";
14
+ //#region src/components/content/reader-settings.tsx
15
+ function T(_) {
16
+ let { t: v } = e(), T = b(), D = y(r)?.viewer, O = y(t), k = _.appearance ?? O;
17
+ return /* @__PURE__ */ x(t.Provider, {
18
+ value: k,
19
+ children: /* @__PURE__ */ x(n, {
20
+ className: _.className,
21
+ appearance: k,
22
+ "data-ckui": "reader-settings",
23
+ children: /* @__PURE__ */ S(a, { children: [/* @__PURE__ */ S(u, {
24
+ render: /* @__PURE__ */ x(i, {
25
+ type: "button",
26
+ variant: "ghost",
27
+ size: "sm",
28
+ "aria-label": v("readerSettings.title"),
29
+ "data-ckui": "reader-settings-trigger"
30
+ }),
31
+ children: [/* @__PURE__ */ x(w, {
32
+ icon: C,
33
+ strokeWidth: 2,
34
+ className: "size-4",
35
+ "aria-hidden": !0
36
+ }), /* @__PURE__ */ x("span", { children: _.language.toUpperCase() })]
37
+ }), /* @__PURE__ */ S(o, {
38
+ className: "max-h-[calc(100dvh-2rem)] overflow-y-auto",
39
+ children: [
40
+ /* @__PURE__ */ S(c, { children: [/* @__PURE__ */ x(l, { children: v("readerSettings.title") }), /* @__PURE__ */ x(s, {
41
+ className: "sr-only",
42
+ children: v("readerSettings.description")
43
+ })] }),
44
+ /* @__PURE__ */ S("div", {
45
+ className: "grid gap-3",
46
+ children: [/* @__PURE__ */ x(d, {
47
+ htmlFor: `${T}-language`,
48
+ children: v("listing.language")
49
+ }), /* @__PURE__ */ S(f, {
50
+ value: _.language,
51
+ onValueChange: (e) => {
52
+ e && e !== _.language && _.onLanguageChange(e);
53
+ },
54
+ children: [/* @__PURE__ */ x(h, {
55
+ id: `${T}-language`,
56
+ className: "w-full",
57
+ children: /* @__PURE__ */ x(g, { children: _.languages.find((e) => e.value === _.language)?.label ?? _.language })
58
+ }), /* @__PURE__ */ x(p, { children: _.languages.map((e) => /* @__PURE__ */ x(m, {
59
+ value: e.value,
60
+ children: e.label
61
+ }, e.value)) })]
62
+ })]
63
+ }),
64
+ D != null && /* @__PURE__ */ x(E, {
65
+ language: _.language,
66
+ client: _.client,
67
+ onError: _.onError
68
+ })
69
+ ]
70
+ })] })
71
+ })
72
+ });
73
+ }
74
+ function E(t) {
75
+ let { t: n } = e(), r = b(), { filters: i } = _({
76
+ language: t.language,
77
+ client: t.client
78
+ });
79
+ return i?.filters.length === 0 ? null : /* @__PURE__ */ S("section", {
80
+ "aria-labelledby": r,
81
+ className: "grid gap-3",
82
+ children: [/* @__PURE__ */ x("h3", {
83
+ id: r,
84
+ className: "font-medium",
85
+ children: n("readerSettings.filters")
86
+ }), /* @__PURE__ */ x(v, {
87
+ ...t,
88
+ includeDeleted: !1
89
+ })]
90
+ });
91
+ }
92
+ //#endregion
93
+ export { T as ReaderSettings };
94
+
95
+ //# sourceMappingURL=reader-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader-settings.js","names":[],"sources":["../../../src/components/content/reader-settings.tsx"],"sourcesContent":["import { Settings01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { useContext, useId } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { ContentKitContext, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useContentFilters } from \"../../react/discovery.js\";\nimport { AppearanceContext, ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from \"#ckui/ui/dialog\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { ContentFilterSettings } from \"./filters.js\";\n\nexport interface ReaderSettingsProps {\n languages: readonly { value: string; label: string }[];\n /** The current language, controlled by the host. */\n language: string;\n /** The host applies its own route and preference changes. */\n onLanguageChange: (language: string) => void;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** One reader-settings dialog for language and the signed-in reader's filters. */\nexport function ReaderSettings(p: ReaderSettingsProps) {\n const { t } = useMessages();\n const id = useId();\n const viewer = useContext(ContentKitContext)?.viewer;\n const inheritedAppearance = useContext(AppearanceContext);\n const appearance = p.appearance ?? inheritedAppearance;\n return (\n <AppearanceContext.Provider value={appearance}>\n <ContentKitUiRoot className={p.className} appearance={appearance} data-ckui=\"reader-settings\">\n <Dialog>\n <DialogTrigger render={<Button type=\"button\" variant=\"ghost\" size=\"sm\" aria-label={t(\"readerSettings.title\")} data-ckui=\"reader-settings-trigger\" />}>\n <HugeiconsIcon icon={Settings01Icon} strokeWidth={2} className=\"size-4\" aria-hidden />\n <span>{p.language.toUpperCase()}</span>\n </DialogTrigger>\n <DialogContent className=\"max-h-[calc(100dvh-2rem)] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>{t(\"readerSettings.title\")}</DialogTitle>\n <DialogDescription className=\"sr-only\">{t(\"readerSettings.description\")}</DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-3\">\n <Label htmlFor={`${id}-language`}>{t(\"listing.language\")}</Label>\n <Select value={p.language} onValueChange={(value) => { if (value && value !== p.language) p.onLanguageChange(value); }}>\n <SelectTrigger id={`${id}-language`} className=\"w-full\">\n <SelectValue>{p.languages.find((item) => item.value === p.language)?.label ?? p.language}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {p.languages.map((item) => <SelectItem key={item.value} value={item.value}>{item.label}</SelectItem>)}\n </SelectContent>\n </Select>\n </div>\n {viewer != null && <ReaderFilters language={p.language} client={p.client} onError={p.onError} />}\n </DialogContent>\n </Dialog>\n </ContentKitUiRoot>\n </AppearanceContext.Provider>\n );\n}\n\nfunction ReaderFilters(p: Pick<ReaderSettingsProps, \"language\" | \"client\" | \"onError\">) {\n const { t } = useMessages();\n const id = useId();\n const { filters } = useContentFilters({ language: p.language, client: p.client });\n if (filters?.filters.length === 0) return null;\n return (\n <section aria-labelledby={id} className=\"grid gap-3\">\n <h3 id={id} className=\"font-medium\">{t(\"readerSettings.filters\")}</h3>\n <ContentFilterSettings {...p} includeDeleted={false} />\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AA4BA,SAAgB,EAAe,GAAwB;CACrD,IAAM,EAAE,SAAM,EAAY,GACpB,IAAK,EAAM,GACX,IAAS,EAAW,CAAiB,CAAC,EAAE,QACxC,IAAsB,EAAW,CAAiB,GAClD,IAAa,EAAE,cAAc;CACnC,OACE,kBAAC,EAAkB,UAAnB;EAA4B,OAAO;EACjC,UAAA,kBAAC,GAAD;GAAkB,WAAW,EAAE;GAAuB;GAAY,aAAU;GAC1E,UAAA,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAe,QAAQ,kBAAC,GAAD;KAAQ,MAAK;KAAS,SAAQ;KAAQ,MAAK;KAAK,cAAY,EAAE,sBAAsB;KAAG,aAAU;IAA2B,CAAA;IAAnJ,UAAA,CACE,kBAAC,GAAD;KAAe,MAAM;KAAgB,aAAa;KAAG,WAAU;KAAS,eAAA;IAAa,CAAA,GACrF,kBAAC,QAAD,EAAA,UAAO,EAAE,SAAS,YAAY,EAAQ,CAAA,CACzB;GACf,CAAA,GAAA,kBAAC,GAAD;IAAe,WAAU;IAAzB,UAAA;KACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAc,EAAE,sBAAsB,EAAe,CAAA,GACrD,kBAAC,GAAD;MAAmB,WAAU;MAAW,UAAA,EAAE,4BAA4B;KAAqB,CAAA,CAC/E,EAAA,CAAA;KACd,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,kBAAC,GAAD;OAAO,SAAS,GAAG,EAAG;OAAa,UAAA,EAAE,kBAAkB;MAAS,CAAA,GAChE,kBAAC,GAAD;OAAQ,OAAO,EAAE;OAAU,gBAAgB,MAAU;QAAE,AAAI,KAAS,MAAU,EAAE,YAAU,EAAE,iBAAiB,CAAK;OAAG;OAArH,UAAA,CACE,kBAAC,GAAD;QAAe,IAAI,GAAG,EAAG;QAAY,WAAU;QAC7C,UAAA,kBAAC,GAAD,EAAA,UAAc,EAAE,UAAU,MAAM,MAAS,EAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAsB,CAAA;OACzF,CAAA,GACf,kBAAC,GAAD,EAAA,UACG,EAAE,UAAU,KAAK,MAAS,kBAAC,GAAD;QAA6B,OAAO,EAAK;QAAQ,UAAA,EAAK;OAAkB,GAAvD,EAAK,KAAkD,CAAC,EACvF,CAAA,CACT;MACL,CAAA,CAAA;;KACJ,KAAU,QAAQ,kBAAC,GAAD;MAAe,UAAU,EAAE;MAAU,QAAQ,EAAE;MAAQ,SAAS,EAAE;KAAU,CAAA;IAClF;GACT,CAAA,CAAA,EAAA,CAAA;EACQ,CAAA;CACQ,CAAA;AAEhC;AAEA,SAAS,EAAc,GAAiE;CACtF,IAAM,EAAE,SAAM,EAAY,GACpB,IAAK,EAAM,GACX,EAAE,eAAY,EAAkB;EAAE,UAAU,EAAE;EAAU,QAAQ,EAAE;CAAO,CAAC;CAEhF,OADI,GAAS,QAAQ,WAAW,IAAU,OAExC,kBAAC,WAAD;EAAS,mBAAiB;EAAI,WAAU;EAAxC,UAAA,CACE,kBAAC,MAAD;GAAQ;GAAI,WAAU;GAAe,UAAA,EAAE,wBAAwB;EAAM,CAAA,GACrE,kBAAC,GAAD;GAAuB,GAAI;GAAG,gBAAgB;EAAQ,CAAA,CAC/C;;AAEb"}
@@ -16,6 +16,8 @@ export interface SearchBoxProps {
16
16
  labels?: Labels;
17
17
  /** The reader's recent searches while the box is empty. Default true. */
18
18
  recent?: boolean;
19
+ /** Header-wide dropdown under the nearest header/banner; default stays inline. */
20
+ presentation?: "inline" | "header";
19
21
  client?: ContentKitClient;
20
22
  className?: string;
21
23
  appearance?: ContentKitUiAppearance;