@burdenoff/microfe-hookmovies 2026.706.2 → 2026.706.3

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 (48) hide show
  1. package/dist/hookmovies/HookMoviesRoutes.js +54 -6
  2. package/dist/hookmovies/HookMoviesRoutes.js.map +1 -1
  3. package/dist/hookmovies/components/index.js +1 -1
  4. package/dist/hookmovies/context/HookMoviesContext.js +64 -40
  5. package/dist/hookmovies/context/HookMoviesContext.js.map +1 -1
  6. package/dist/hookmovies/hooks/useHookMoviesApi.js +592 -134
  7. package/dist/hookmovies/hooks/useHookMoviesApi.js.map +1 -1
  8. package/dist/hookmovies/pages/ai/ConciergePage.js +330 -0
  9. package/dist/hookmovies/pages/ai/ConciergePage.js.map +1 -0
  10. package/dist/hookmovies/pages/analytics/AnalyticsPage.js +450 -0
  11. package/dist/hookmovies/pages/analytics/AnalyticsPage.js.map +1 -0
  12. package/dist/hookmovies/pages/analytics/PayoutsPage.js +499 -0
  13. package/dist/hookmovies/pages/analytics/PayoutsPage.js.map +1 -0
  14. package/dist/hookmovies/pages/auditions/AuditionsPage.js +3 -3
  15. package/dist/hookmovies/pages/catalog/PeoplePage.js +3 -3
  16. package/dist/hookmovies/pages/catalog/StudiosPage.js +3 -3
  17. package/dist/hookmovies/pages/catalog/TitleDetailPage.js +7 -7
  18. package/dist/hookmovies/pages/catalog/TitlesPage.js +4 -4
  19. package/dist/hookmovies/pages/commerce/CheckoutPage.js +430 -0
  20. package/dist/hookmovies/pages/commerce/CheckoutPage.js.map +1 -0
  21. package/dist/hookmovies/pages/consumer/DiscoveryPage.js +7 -7
  22. package/dist/hookmovies/pages/consumer/HooksPage.js +5 -5
  23. package/dist/hookmovies/pages/consumer/TitleViewPage.js +27 -27
  24. package/dist/hookmovies/pages/consumer/TitleViewPage.js.map +1 -1
  25. package/dist/hookmovies/pages/developer/DevPortalPage.js +815 -0
  26. package/dist/hookmovies/pages/developer/DevPortalPage.js.map +1 -0
  27. package/dist/hookmovies/pages/distribution/RightsWindowsPage.js +3 -3
  28. package/dist/hookmovies/pages/engagement/ReviewsPage.js +201 -143
  29. package/dist/hookmovies/pages/engagement/ReviewsPage.js.map +1 -1
  30. package/dist/hookmovies/pages/library/EntitlementsPage.js +3 -3
  31. package/dist/hookmovies/pages/library/EntitlementsPage.js.map +1 -1
  32. package/dist/hookmovies/pages/library/ListsPage.js +3 -3
  33. package/dist/hookmovies/pages/live/LivePage.js +608 -0
  34. package/dist/hookmovies/pages/live/LivePage.js.map +1 -0
  35. package/dist/hookmovies/pages/localization/LocalizationPage.js +656 -0
  36. package/dist/hookmovies/pages/localization/LocalizationPage.js.map +1 -0
  37. package/dist/hookmovies/pages/onboarding/OnboardingPage.js +366 -0
  38. package/dist/hookmovies/pages/onboarding/OnboardingPage.js.map +1 -0
  39. package/dist/hookmovies/pages/productions/ProductionsPage.js +3 -3
  40. package/dist/hookmovies/pages/social/ProfilesPage.js +701 -0
  41. package/dist/hookmovies/pages/social/ProfilesPage.js.map +1 -0
  42. package/dist/hookmovies/pages/social/WatchPartyListPage.js +281 -0
  43. package/dist/hookmovies/pages/social/WatchPartyListPage.js.map +1 -0
  44. package/dist/hookmovies/pages/social/WatchPartyRoomPage.js +567 -0
  45. package/dist/hookmovies/pages/social/WatchPartyRoomPage.js.map +1 -0
  46. package/dist/hookmovies/pages/trust/ModerationPage.js +320 -0
  47. package/dist/hookmovies/pages/trust/ModerationPage.js.map +1 -0
  48. package/package.json +1 -1
@@ -0,0 +1,815 @@
1
+ import { useHookMovies as e } from "../../context/HookMoviesContext.js";
2
+ import { cn as t } from "../../utils/cn.js";
3
+ import { PageLayout as n } from "../../components/PageLayout.js";
4
+ import { useGenerateApiKey as r, useMyApiKeys as i, useRevokeApiKey as a } from "../../hooks/useHookMoviesApi.js";
5
+ import { formatDate as o, formatRelativeTime as s } from "../../utils/formatters.js";
6
+ import { EmptyState as c } from "../../components/EmptyState.js";
7
+ import { ErrorState as l, LoadingState as u } from "../../components/StateViews.js";
8
+ import { useEffect as d, useMemo as f, useState as p } from "react";
9
+ import { BookOpen as m, Boxes as h, Check as g, Clapperboard as _, Code2 as v, Copy as y, ExternalLink as b, KeyRound as x, Package as S, Plus as C, Rocket as w, ShieldAlert as T, Terminal as E, Trash2 as D } from "lucide-react";
10
+ import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
11
+ import { GlassCard as j } from "@burdenoff/fe-libs/ui";
12
+ //#region src/hookmovies/pages/developer/DevPortalPage.tsx
13
+ var M = [
14
+ {
15
+ id: "overview",
16
+ label: "Getting Started",
17
+ icon: w
18
+ },
19
+ {
20
+ id: "graphql",
21
+ label: "GraphQL API",
22
+ icon: v
23
+ },
24
+ {
25
+ id: "keys",
26
+ label: "API Keys",
27
+ icon: x
28
+ },
29
+ {
30
+ id: "sdks",
31
+ label: "SDKs & MCP",
32
+ icon: S
33
+ },
34
+ {
35
+ id: "embed",
36
+ label: "Embeddable Player",
37
+ icon: _
38
+ }
39
+ ], N = [{
40
+ value: "read",
41
+ label: "Read",
42
+ description: "Query the catalog, titles, reviews and metadata."
43
+ }, {
44
+ value: "write",
45
+ label: "Write",
46
+ description: "Create and update titles, reviews and other resources."
47
+ }], P = () => {
48
+ let [e, t] = p(!1);
49
+ return d(() => {
50
+ if (!e) return;
51
+ let n = window.setTimeout(() => t(!1), 1800);
52
+ return () => window.clearTimeout(n);
53
+ }, [e]), [e, (e) => {
54
+ typeof navigator > "u" || !navigator.clipboard || navigator.clipboard.writeText(e).then(() => t(!0), () => t(!1));
55
+ }];
56
+ }, F = ({ value: e, label: n, className: r }) => {
57
+ let [i, a] = P();
58
+ return /* @__PURE__ */ A("button", {
59
+ type: "button",
60
+ onClick: () => a(e),
61
+ className: t("inline-flex items-center gap-1.5 rounded-md border border-border-seam bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:border-border-strong hover:text-text-primary", r),
62
+ "aria-label": i ? "Copied" : `Copy ${n ?? "to clipboard"}`,
63
+ children: [i ? /* @__PURE__ */ k(g, {
64
+ className: "h-3.5 w-3.5 text-status-success-text",
65
+ "aria-hidden": "true"
66
+ }) : /* @__PURE__ */ k(y, {
67
+ className: "h-3.5 w-3.5",
68
+ "aria-hidden": "true"
69
+ }), i ? "Copied" : n ?? "Copy"]
70
+ });
71
+ }, I = /(#[^\n]*|\/\/[^\n]*)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)|(\b\d+(?:\.\d+)?\b)|(\b(?:query|mutation|subscription|fragment|type|input|enum|schema|on|true|false|null|const|let|var|import|from|export|default|function|return|await|async|new|curl|POST|GET|Authorization|Bearer|Content-Type)\b)/g, L = (e) => {
72
+ let t = [], n = 0, r = 0;
73
+ for (let i of e.matchAll(I)) {
74
+ let a = i.index ?? 0;
75
+ a > n && t.push(/* @__PURE__ */ k("span", { children: e.slice(n, a) }, r++));
76
+ let [o, s, c, l, u] = i;
77
+ s ? t.push(/* @__PURE__ */ k("span", {
78
+ className: "text-text-muted italic",
79
+ children: o
80
+ }, r++)) : c ? t.push(/* @__PURE__ */ k("span", {
81
+ className: "text-status-success-text",
82
+ children: o
83
+ }, r++)) : l ? t.push(/* @__PURE__ */ k("span", {
84
+ className: "text-status-warning-text",
85
+ children: o
86
+ }, r++)) : u ? t.push(/* @__PURE__ */ k("span", {
87
+ className: "text-status-info-text font-medium",
88
+ children: o
89
+ }, r++)) : t.push(/* @__PURE__ */ k("span", { children: o }, r++)), n = a + o.length;
90
+ }
91
+ return n < e.length && t.push(/* @__PURE__ */ k("span", { children: e.slice(n) }, r++)), t;
92
+ }, R = ({ code: e, language: n, className: r }) => /* @__PURE__ */ A("div", {
93
+ className: t("overflow-hidden rounded-xl border border-border-seam bg-bg-sunken", r),
94
+ children: [/* @__PURE__ */ A("div", {
95
+ className: "flex items-center justify-between border-b border-border-seam bg-bg-surface px-3 py-1.5",
96
+ children: [/* @__PURE__ */ A("span", {
97
+ className: "inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-text-muted",
98
+ children: [/* @__PURE__ */ k(E, {
99
+ className: "h-3 w-3",
100
+ "aria-hidden": "true"
101
+ }), n]
102
+ }), /* @__PURE__ */ k(F, {
103
+ value: e,
104
+ label: "Copy"
105
+ })]
106
+ }), /* @__PURE__ */ k("div", {
107
+ className: "overflow-x-auto",
108
+ children: /* @__PURE__ */ k("pre", {
109
+ className: "min-w-0 px-4 py-3 text-[13px] leading-relaxed",
110
+ children: /* @__PURE__ */ k("code", {
111
+ className: "font-mono text-text-primary",
112
+ children: L(e)
113
+ })
114
+ })
115
+ })]
116
+ }), z = ({ icon: e, title: t, children: n }) => /* @__PURE__ */ A("div", {
117
+ className: "mb-4",
118
+ children: [/* @__PURE__ */ A("h2", {
119
+ className: "flex items-center gap-2 text-lg font-semibold text-text-primary",
120
+ children: [/* @__PURE__ */ k(e, {
121
+ className: "h-5 w-5 text-action-primary-bg",
122
+ "aria-hidden": "true"
123
+ }), t]
124
+ }), n && /* @__PURE__ */ k("p", {
125
+ className: "mt-1 max-w-2xl text-sm text-text-secondary",
126
+ children: n
127
+ })]
128
+ }), B = ({ children: e }) => /* @__PURE__ */ k("code", {
129
+ className: "rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-[0.85em] text-text-primary",
130
+ children: e
131
+ }), V = [
132
+ {
133
+ title: "Create an API key",
134
+ body: /* @__PURE__ */ A(O, { children: [
135
+ "Head to the ",
136
+ /* @__PURE__ */ k("strong", { children: "API Keys" }),
137
+ " tab and mint a key for your registered app. The secret is shown once at creation — copy it somewhere safe."
138
+ ] })
139
+ },
140
+ {
141
+ title: "Point at the GraphQL endpoint",
142
+ body: /* @__PURE__ */ A(O, { children: [
143
+ "All HookMovies data is served from a single Apollo Federation gateway. The endpoint is shown in the ",
144
+ /* @__PURE__ */ k("strong", { children: "GraphQL API" }),
145
+ " tab."
146
+ ] })
147
+ },
148
+ {
149
+ title: "Authenticate every request",
150
+ body: /* @__PURE__ */ A(O, { children: [
151
+ "Send your key as a bearer token in the ",
152
+ /* @__PURE__ */ k(B, { children: "Authorization" }),
153
+ " header:",
154
+ " ",
155
+ /* @__PURE__ */ k(B, { children: "Authorization: Bearer <apiSecret>" }),
156
+ "."
157
+ ] })
158
+ },
159
+ {
160
+ title: "Make your first query",
161
+ body: /* @__PURE__ */ A(O, { children: [
162
+ "Call ",
163
+ /* @__PURE__ */ k(B, { children: "hokListTitles" }),
164
+ " to page through the catalog, then drill into a title with ",
165
+ /* @__PURE__ */ k(B, { children: "hokTitle(id)" }),
166
+ "."
167
+ ] })
168
+ }
169
+ ], H = ({ endpoint: e }) => /* @__PURE__ */ A("div", {
170
+ className: "space-y-6",
171
+ children: [
172
+ /* @__PURE__ */ k(z, {
173
+ icon: w,
174
+ title: "Getting Started",
175
+ children: "Build with the HookMovies platform API — a single GraphQL gateway that powers the catalog, productions, auditions, entitlements and playback surfaces you see in this app."
176
+ }),
177
+ /* @__PURE__ */ k("ol", {
178
+ className: "grid gap-4 sm:grid-cols-2",
179
+ children: V.map((e, t) => /* @__PURE__ */ k("li", { children: /* @__PURE__ */ k(j, {
180
+ treatment: "glass",
181
+ className: "h-full border-border-seam p-4",
182
+ children: /* @__PURE__ */ A("div", {
183
+ className: "flex items-start gap-3",
184
+ children: [/* @__PURE__ */ k("span", {
185
+ className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-sm font-semibold text-action-primary-text",
186
+ children: t + 1
187
+ }), /* @__PURE__ */ A("div", {
188
+ className: "min-w-0",
189
+ children: [/* @__PURE__ */ k("h3", {
190
+ className: "text-sm font-semibold text-text-primary",
191
+ children: e.title
192
+ }), /* @__PURE__ */ k("p", {
193
+ className: "mt-1 text-sm text-text-secondary",
194
+ children: e.body
195
+ })]
196
+ })]
197
+ })
198
+ }) }, e.title))
199
+ }),
200
+ /* @__PURE__ */ A(j, {
201
+ treatment: "metric",
202
+ className: "border-border-seam p-4",
203
+ children: [
204
+ /* @__PURE__ */ A("div", {
205
+ className: "flex items-center gap-2 text-sm font-medium text-text-primary",
206
+ children: [/* @__PURE__ */ k(m, {
207
+ className: "h-4 w-4 text-action-primary-bg",
208
+ "aria-hidden": "true"
209
+ }), "Base endpoint"]
210
+ }),
211
+ /* @__PURE__ */ k("p", {
212
+ className: "mt-1 text-sm text-text-secondary",
213
+ children: "Every request targets the workspace GraphQL gateway:"
214
+ }),
215
+ /* @__PURE__ */ A("div", {
216
+ className: "mt-3 flex flex-wrap items-center gap-2",
217
+ children: [/* @__PURE__ */ k("code", {
218
+ className: "min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 font-mono text-sm text-text-primary",
219
+ children: e
220
+ }), /* @__PURE__ */ k(F, {
221
+ value: e,
222
+ label: "Copy URL"
223
+ })]
224
+ })
225
+ ]
226
+ })
227
+ ]
228
+ }), U = "query FirstTitles {\n hokListTitles(pagination: { limit: 10, offset: 0 }, status: PUBLISHED) {\n items {\n id\n title\n type\n status\n releaseYear\n genres\n }\n pageInfo { cursor hasMore }\n totalCount\n }\n}", W = "{\n \"data\": {\n \"hokListTitles\": {\n \"items\": [\n {\n \"id\": \"9f2c…\",\n \"title\": \"Midnight in Marseille\",\n \"type\": \"MOVIE\",\n \"status\": \"PUBLISHED\",\n \"releaseYear\": 2025,\n \"genres\": [\"Drama\", \"Thriller\"]\n }\n ],\n \"pageInfo\": { \"cursor\": null, \"hasMore\": true },\n \"totalCount\": 128\n }\n }\n}", G = ({ endpoint: e }) => {
229
+ let t = `curl -X POST '${e}' \\
230
+ -H 'Content-Type: application/json' \\
231
+ -H 'Authorization: Bearer <apiSecret>' \\
232
+ -d '{"query": "query { hokListTitles(pagination: { limit: 5 }) { items { id title } totalCount } }"}'`;
233
+ return /* @__PURE__ */ A("div", {
234
+ className: "space-y-6",
235
+ children: [
236
+ /* @__PURE__ */ k(z, {
237
+ icon: v,
238
+ title: "GraphQL API",
239
+ children: "One federated GraphQL endpoint serves the entire HookMovies domain. Send POST requests with your API key as a bearer token."
240
+ }),
241
+ /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
242
+ className: "mb-2 text-sm font-semibold text-text-primary",
243
+ children: "Endpoint"
244
+ }), /* @__PURE__ */ A("div", {
245
+ className: "flex flex-wrap items-center gap-2",
246
+ children: [/* @__PURE__ */ k("code", {
247
+ className: "min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 font-mono text-sm text-text-primary",
248
+ children: e
249
+ }), /* @__PURE__ */ k(F, {
250
+ value: e,
251
+ label: "Copy URL"
252
+ })]
253
+ })] }),
254
+ /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
255
+ className: "mb-2 text-sm font-semibold text-text-primary",
256
+ children: "Sample query"
257
+ }), /* @__PURE__ */ k(R, {
258
+ language: "graphql",
259
+ code: U
260
+ })] }),
261
+ /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
262
+ className: "mb-2 text-sm font-semibold text-text-primary",
263
+ children: "Sample response"
264
+ }), /* @__PURE__ */ k(R, {
265
+ language: "json",
266
+ code: W
267
+ })] }),
268
+ /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
269
+ className: "mb-2 text-sm font-semibold text-text-primary",
270
+ children: "Run it with cURL"
271
+ }), /* @__PURE__ */ k(R, {
272
+ language: "bash",
273
+ code: t
274
+ })] })
275
+ ]
276
+ });
277
+ }, K = ({ result: e, onDismiss: t }) => /* @__PURE__ */ k(j, {
278
+ treatment: "glass",
279
+ className: "mb-6 border-status-warning-border bg-status-warning-bg-subtle p-5",
280
+ children: /* @__PURE__ */ A("div", {
281
+ className: "flex items-start gap-3",
282
+ children: [/* @__PURE__ */ k(T, {
283
+ className: "mt-0.5 h-5 w-5 shrink-0 text-status-warning-text",
284
+ "aria-hidden": "true"
285
+ }), /* @__PURE__ */ A("div", {
286
+ className: "min-w-0 flex-1",
287
+ children: [
288
+ /* @__PURE__ */ k("h3", {
289
+ className: "text-sm font-semibold text-text-primary",
290
+ children: "Save your API secret now"
291
+ }),
292
+ /* @__PURE__ */ k("p", {
293
+ className: "mt-1 text-sm text-text-secondary",
294
+ children: "This secret is shown only once and cannot be retrieved again. Store it somewhere safe before dismissing this panel."
295
+ }),
296
+ /* @__PURE__ */ k("dl", {
297
+ className: "mt-4 space-y-3",
298
+ children: [
299
+ {
300
+ label: "API key",
301
+ value: e.apiKey
302
+ },
303
+ {
304
+ label: "API secret",
305
+ value: e.apiSecret
306
+ },
307
+ {
308
+ label: "Key ID",
309
+ value: e.keyId
310
+ }
311
+ ].map((e) => /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("dt", {
312
+ className: "mb-1 text-xs font-medium text-text-secondary",
313
+ children: e.label
314
+ }), /* @__PURE__ */ A("dd", {
315
+ className: "flex flex-wrap items-center gap-2",
316
+ children: [/* @__PURE__ */ k("code", {
317
+ className: "min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-surface px-3 py-2 font-mono text-sm text-text-primary",
318
+ children: e.value
319
+ }), /* @__PURE__ */ k(F, {
320
+ value: e.value,
321
+ label: "Copy"
322
+ })]
323
+ })] }, e.label))
324
+ }),
325
+ /* @__PURE__ */ k("button", {
326
+ type: "button",
327
+ onClick: t,
328
+ className: "mt-4 rounded-lg border border-border-seam bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
329
+ children: "I've saved it — dismiss"
330
+ })
331
+ ]
332
+ })]
333
+ })
334
+ }), q = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", J = "mb-1.5 block text-xs font-medium text-text-secondary", Y = ({ onGenerated: e, onClose: n }) => {
335
+ let i = r(), [a, o] = p(""), [s, c] = p(""), [l, u] = p(["read"]), [d, f] = p(""), [m, h] = p(null), g = (e) => {
336
+ u((t) => t.includes(e) ? t.filter((t) => t !== e) : [...t, e]);
337
+ };
338
+ return /* @__PURE__ */ A(j, {
339
+ treatment: "glass",
340
+ className: "mb-6 border-border-seam p-5",
341
+ children: [/* @__PURE__ */ A("h3", {
342
+ className: "mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary",
343
+ children: [/* @__PURE__ */ k(x, {
344
+ className: "h-4 w-4 text-action-primary-bg",
345
+ "aria-hidden": "true"
346
+ }), "Generate a new API key"]
347
+ }), /* @__PURE__ */ A("form", {
348
+ onSubmit: async (t) => {
349
+ if (t.preventDefault(), h(null), !a.trim()) {
350
+ h("Give the key a recognisable name.");
351
+ return;
352
+ }
353
+ if (!s.trim()) {
354
+ h("Enter the App ID this key will act on behalf of.");
355
+ return;
356
+ }
357
+ if (l.length === 0) {
358
+ h("Select at least one scope.");
359
+ return;
360
+ }
361
+ try {
362
+ e(await i.mutateAsync({
363
+ appId: s.trim(),
364
+ name: a.trim(),
365
+ scope: l,
366
+ expiresAt: d ? new Date(d).toISOString() : void 0
367
+ }));
368
+ } catch (e) {
369
+ h(e instanceof Error ? e.message : "Failed to generate the API key.");
370
+ }
371
+ },
372
+ className: "space-y-4",
373
+ children: [
374
+ m && /* @__PURE__ */ k("div", {
375
+ className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
376
+ children: m
377
+ }),
378
+ /* @__PURE__ */ A("div", {
379
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
380
+ children: [/* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("label", {
381
+ htmlFor: "key-name",
382
+ className: J,
383
+ children: "Key name *"
384
+ }), /* @__PURE__ */ k("input", {
385
+ id: "key-name",
386
+ value: a,
387
+ onChange: (e) => o(e.target.value),
388
+ placeholder: "e.g. Production ingest",
389
+ className: q,
390
+ required: !0
391
+ })] }), /* @__PURE__ */ A("div", { children: [
392
+ /* @__PURE__ */ k("label", {
393
+ htmlFor: "key-app-id",
394
+ className: J,
395
+ children: "App ID *"
396
+ }),
397
+ /* @__PURE__ */ k("input", {
398
+ id: "key-app-id",
399
+ value: s,
400
+ onChange: (e) => c(e.target.value),
401
+ placeholder: "Your registered app's ID",
402
+ className: q,
403
+ required: !0
404
+ }),
405
+ /* @__PURE__ */ k("p", {
406
+ className: "mt-1 text-[11px] text-text-muted",
407
+ children: "The client app this key acts on behalf of — find it in your workspace app settings."
408
+ })
409
+ ] })]
410
+ }),
411
+ /* @__PURE__ */ A("fieldset", { children: [/* @__PURE__ */ k("legend", {
412
+ className: J,
413
+ children: "Scopes *"
414
+ }), /* @__PURE__ */ k("div", {
415
+ className: "grid grid-cols-1 gap-2 sm:grid-cols-2",
416
+ children: N.map((e) => {
417
+ let n = l.includes(e.value);
418
+ return /* @__PURE__ */ A("label", {
419
+ className: t("flex cursor-pointer items-start gap-2 rounded-lg border p-3 text-sm transition-colors", n ? "border-action-primary-bg bg-action-primary-bg/10" : "border-border-subtle hover:border-border-strong"),
420
+ children: [/* @__PURE__ */ k("input", {
421
+ type: "checkbox",
422
+ checked: n,
423
+ onChange: () => g(e.value),
424
+ className: "mt-0.5 accent-[var(--color-action-primary-bg)]"
425
+ }), /* @__PURE__ */ A("span", {
426
+ className: "min-w-0",
427
+ children: [/* @__PURE__ */ k("span", {
428
+ className: "block font-medium text-text-primary",
429
+ children: e.label
430
+ }), /* @__PURE__ */ k("span", {
431
+ className: "block text-xs text-text-secondary",
432
+ children: e.description
433
+ })]
434
+ })]
435
+ }, e.value);
436
+ })
437
+ })] }),
438
+ /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("label", {
439
+ htmlFor: "key-expires",
440
+ className: J,
441
+ children: "Expires (optional)"
442
+ }), /* @__PURE__ */ k("input", {
443
+ id: "key-expires",
444
+ type: "date",
445
+ value: d,
446
+ onChange: (e) => f(e.target.value),
447
+ className: t(q, "sm:max-w-xs")
448
+ })] }),
449
+ /* @__PURE__ */ A("div", {
450
+ className: "flex items-center gap-2",
451
+ children: [/* @__PURE__ */ k("button", {
452
+ type: "submit",
453
+ disabled: i.isPending,
454
+ className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50",
455
+ children: i.isPending ? "Generating…" : "Generate key"
456
+ }), /* @__PURE__ */ k("button", {
457
+ type: "button",
458
+ onClick: n,
459
+ className: "rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary",
460
+ children: "Cancel"
461
+ })]
462
+ })
463
+ ]
464
+ })]
465
+ });
466
+ }, X = ({ apiKey: e }) => {
467
+ let t = a(), [n, r] = p(!1), [i, c] = p(null), l = async () => {
468
+ c(null);
469
+ try {
470
+ await t.mutateAsync(e.id);
471
+ } catch (e) {
472
+ c(e instanceof Error ? e.message : "Failed to revoke this key."), r(!1);
473
+ }
474
+ };
475
+ return /* @__PURE__ */ A(j, {
476
+ treatment: "metric",
477
+ className: "border-border-seam p-4",
478
+ children: [/* @__PURE__ */ A("div", {
479
+ className: "flex flex-wrap items-start justify-between gap-3",
480
+ children: [/* @__PURE__ */ A("div", {
481
+ className: "min-w-0",
482
+ children: [
483
+ /* @__PURE__ */ A("div", {
484
+ className: "flex flex-wrap items-center gap-2",
485
+ children: [/* @__PURE__ */ k("span", {
486
+ className: "text-sm font-semibold text-text-primary",
487
+ children: e.name
488
+ }), /* @__PURE__ */ A("code", {
489
+ className: "rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-xs text-text-secondary",
490
+ children: [e.prefix, "…"]
491
+ })]
492
+ }),
493
+ /* @__PURE__ */ A("div", {
494
+ className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-muted",
495
+ children: [
496
+ e.appName && /* @__PURE__ */ k("span", { children: e.appName }),
497
+ /* @__PURE__ */ k("span", {
498
+ "aria-hidden": "true",
499
+ children: "·"
500
+ }),
501
+ /* @__PURE__ */ A("span", { children: ["Created ", o(e.createdAt, {
502
+ hour: void 0,
503
+ minute: void 0
504
+ })] }),
505
+ /* @__PURE__ */ k("span", {
506
+ "aria-hidden": "true",
507
+ children: "·"
508
+ }),
509
+ /* @__PURE__ */ k("span", { children: e.lastUsedAt ? `Last used ${s(e.lastUsedAt)}` : "Never used" }),
510
+ e.expiresAt && /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k("span", {
511
+ "aria-hidden": "true",
512
+ children: "·"
513
+ }), /* @__PURE__ */ A("span", { children: ["Expires ", o(e.expiresAt, {
514
+ hour: void 0,
515
+ minute: void 0
516
+ })] })] })
517
+ ]
518
+ }),
519
+ e.scopes.length > 0 && /* @__PURE__ */ k("div", {
520
+ className: "mt-2 flex flex-wrap gap-1.5",
521
+ children: e.scopes.map((e) => /* @__PURE__ */ k("span", {
522
+ className: "rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-text-secondary",
523
+ children: e
524
+ }, e))
525
+ })
526
+ ]
527
+ }), /* @__PURE__ */ k("div", {
528
+ className: "shrink-0",
529
+ children: n ? /* @__PURE__ */ A("div", {
530
+ className: "flex items-center gap-2",
531
+ children: [/* @__PURE__ */ A("button", {
532
+ type: "button",
533
+ disabled: t.isPending,
534
+ onClick: () => void l(),
535
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-opacity hover:opacity-90 disabled:opacity-50",
536
+ children: [/* @__PURE__ */ k(D, {
537
+ className: "h-3.5 w-3.5",
538
+ "aria-hidden": "true"
539
+ }), t.isPending ? "Revoking…" : "Confirm revoke"]
540
+ }), /* @__PURE__ */ k("button", {
541
+ type: "button",
542
+ onClick: () => r(!1),
543
+ className: "rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary",
544
+ children: "Cancel"
545
+ })]
546
+ }) : /* @__PURE__ */ A("button", {
547
+ type: "button",
548
+ onClick: () => r(!0),
549
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:border-status-error-border hover:text-status-error-text",
550
+ children: [/* @__PURE__ */ k(D, {
551
+ className: "h-3.5 w-3.5",
552
+ "aria-hidden": "true"
553
+ }), "Revoke"]
554
+ })
555
+ })]
556
+ }), i && /* @__PURE__ */ k("p", {
557
+ className: "mt-2 text-xs text-status-error-text",
558
+ children: i
559
+ })]
560
+ });
561
+ }, Z = () => {
562
+ let { data: e, isLoading: t, isError: n, refetch: r } = i(), [a, o] = p(!1), [s, d] = p(null), f = e?.apiKeys ?? [];
563
+ return /* @__PURE__ */ A("div", {
564
+ className: "space-y-4",
565
+ children: [
566
+ /* @__PURE__ */ A("div", {
567
+ className: "flex flex-wrap items-start justify-between gap-3",
568
+ children: [/* @__PURE__ */ k(z, {
569
+ icon: x,
570
+ title: "API Keys",
571
+ children: "Keys authenticate server-to-server calls to the HookMovies GraphQL gateway. They are issued and revoked through the platform identity service — HookMovies never stores your secret."
572
+ }), /* @__PURE__ */ A("button", {
573
+ type: "button",
574
+ onClick: () => o((e) => !e),
575
+ className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90",
576
+ children: [/* @__PURE__ */ k(C, {
577
+ className: "h-4 w-4",
578
+ "aria-hidden": "true"
579
+ }), "New key"]
580
+ })]
581
+ }),
582
+ s && /* @__PURE__ */ k(K, {
583
+ result: s,
584
+ onDismiss: () => d(null)
585
+ }),
586
+ a && /* @__PURE__ */ k(Y, {
587
+ onGenerated: (e) => {
588
+ d(e), o(!1);
589
+ },
590
+ onClose: () => o(!1)
591
+ }),
592
+ t ? /* @__PURE__ */ k(u, { label: "Loading your API keys…" }) : n ? /* @__PURE__ */ k(l, {
593
+ message: "We couldn't load your API keys.",
594
+ onRetry: () => void r()
595
+ }) : f.length === 0 ? /* @__PURE__ */ k(c, {
596
+ icon: /* @__PURE__ */ k(x, { className: "h-12 w-12" }),
597
+ title: "No API keys yet",
598
+ description: "Generate your first key to start calling the HookMovies API.",
599
+ action: /* @__PURE__ */ A("button", {
600
+ type: "button",
601
+ onClick: () => o(!0),
602
+ className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90",
603
+ children: [/* @__PURE__ */ k(C, {
604
+ className: "h-4 w-4",
605
+ "aria-hidden": "true"
606
+ }), "Generate key"]
607
+ })
608
+ }) : /* @__PURE__ */ k("div", {
609
+ className: "space-y-3",
610
+ children: f.map((e) => /* @__PURE__ */ k(X, { apiKey: e }, e.id))
611
+ })
612
+ ]
613
+ });
614
+ }, Q = [
615
+ {
616
+ name: "Node SDK",
617
+ repo: "hookmovies-sdk-node",
618
+ description: "Typed TypeScript client for the HookMovies GraphQL API — catalog, titles, reviews and playback.",
619
+ icon: h,
620
+ install: {
621
+ language: "bash",
622
+ code: "bun add hookmovies-sdk-node\n# or: npm install hookmovies-sdk-node"
623
+ }
624
+ },
625
+ {
626
+ name: "Python SDK",
627
+ repo: "hookmovies-sdk-python",
628
+ description: "Pythonic client with async support for backend integrations and data pipelines.",
629
+ icon: S,
630
+ install: {
631
+ language: "bash",
632
+ code: "pip install hookmovies-sdk-python"
633
+ }
634
+ },
635
+ {
636
+ name: "MCP Server",
637
+ repo: "hookmovies-mcp",
638
+ description: "Model Context Protocol server that exposes HookMovies tools to AI agents and assistants.",
639
+ icon: h,
640
+ install: {
641
+ language: "bash",
642
+ code: "bunx hookmovies-mcp --help"
643
+ }
644
+ },
645
+ {
646
+ name: "CLI",
647
+ repo: "hookmovies-cli",
648
+ description: "Command-line tool to manage titles, productions and rights from your terminal or CI.",
649
+ icon: E,
650
+ install: {
651
+ language: "bash",
652
+ code: "bunx hookmovies-cli titles list"
653
+ }
654
+ }
655
+ ], $ = () => /* @__PURE__ */ A("div", {
656
+ className: "space-y-6",
657
+ children: [/* @__PURE__ */ k(z, {
658
+ icon: S,
659
+ title: "SDKs & MCP",
660
+ children: "Official HookMovies packages wrap the GraphQL API so you can build in your language of choice — or plug the platform straight into an AI agent via MCP."
661
+ }), /* @__PURE__ */ k("div", {
662
+ className: "grid gap-4 sm:grid-cols-2",
663
+ children: Q.map((e) => {
664
+ let t = e.icon, n = `https://github.com/algoshred/${e.repo}`;
665
+ return /* @__PURE__ */ A(j, {
666
+ treatment: "glass",
667
+ className: "flex h-full flex-col border-border-seam p-4",
668
+ children: [
669
+ /* @__PURE__ */ A("div", {
670
+ className: "flex items-start justify-between gap-3",
671
+ children: [/* @__PURE__ */ A("div", {
672
+ className: "flex items-center gap-2",
673
+ children: [/* @__PURE__ */ k("span", {
674
+ className: "flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10",
675
+ children: /* @__PURE__ */ k(t, {
676
+ className: "h-5 w-5 text-action-primary-bg",
677
+ "aria-hidden": "true"
678
+ })
679
+ }), /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
680
+ className: "text-sm font-semibold text-text-primary",
681
+ children: e.name
682
+ }), /* @__PURE__ */ k("code", {
683
+ className: "text-[11px] text-text-muted",
684
+ children: e.repo
685
+ })] })]
686
+ }), /* @__PURE__ */ A("a", {
687
+ href: n,
688
+ target: "_blank",
689
+ rel: "noreferrer noopener",
690
+ className: "inline-flex items-center gap-1 rounded-md border border-border-seam px-2 py-1 text-xs font-medium text-text-secondary transition-colors hover:border-border-strong hover:text-text-primary",
691
+ children: ["Repo", /* @__PURE__ */ k(b, {
692
+ className: "h-3 w-3",
693
+ "aria-hidden": "true"
694
+ })]
695
+ })]
696
+ }),
697
+ /* @__PURE__ */ k("p", {
698
+ className: "mt-3 flex-1 text-sm text-text-secondary",
699
+ children: e.description
700
+ }),
701
+ /* @__PURE__ */ k("div", {
702
+ className: "mt-3",
703
+ children: /* @__PURE__ */ k(R, {
704
+ language: e.install.language,
705
+ code: e.install.code
706
+ })
707
+ })
708
+ ]
709
+ }, e.repo);
710
+ })
711
+ })]
712
+ }), ee = ({ watchUrl: e }) => {
713
+ let t = `<iframe
714
+ src="${e}"
715
+ width="100%"
716
+ height="480"
717
+ frameborder="0"
718
+ allow="autoplay; fullscreen; encrypted-media"
719
+ allowfullscreen
720
+ title="HookMovies player"
721
+ ></iframe>`, n = `<div style="position: relative; padding-top: 56.25%;">
722
+ <iframe
723
+ src="${e}"
724
+ style="position: absolute; inset: 0; width: 100%; height: 100%; border: 0;"
725
+ allow="autoplay; fullscreen; encrypted-media"
726
+ allowfullscreen
727
+ title="HookMovies player"
728
+ ></iframe>
729
+ </div>`;
730
+ return /* @__PURE__ */ A("div", {
731
+ className: "space-y-6",
732
+ children: [
733
+ /* @__PURE__ */ A(z, {
734
+ icon: _,
735
+ title: "Embeddable Player",
736
+ children: [
737
+ "Drop a HookMovies title into any web page with a single",
738
+ " ",
739
+ /* @__PURE__ */ k(B, { children: "<iframe>" }),
740
+ ". Replace ",
741
+ /* @__PURE__ */ k(B, { children: "YOUR_TITLE_ID" }),
742
+ " with the id of any published title."
743
+ ]
744
+ }),
745
+ /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
746
+ className: "mb-2 text-sm font-semibold text-text-primary",
747
+ children: "Basic embed"
748
+ }), /* @__PURE__ */ k(R, {
749
+ language: "html",
750
+ code: t
751
+ })] }),
752
+ /* @__PURE__ */ A("div", { children: [
753
+ /* @__PURE__ */ k("h3", {
754
+ className: "mb-2 text-sm font-semibold text-text-primary",
755
+ children: "Responsive (16:9) embed"
756
+ }),
757
+ /* @__PURE__ */ k("p", {
758
+ className: "mb-2 text-sm text-text-secondary",
759
+ children: "Wrap the iframe in an aspect-ratio box so the player scales fluidly on any device."
760
+ }),
761
+ /* @__PURE__ */ k(R, {
762
+ language: "html",
763
+ code: n
764
+ })
765
+ ] }),
766
+ /* @__PURE__ */ k(j, {
767
+ treatment: "metric",
768
+ className: "border-border-seam p-4",
769
+ children: /* @__PURE__ */ k("p", {
770
+ className: "text-sm text-text-secondary",
771
+ children: "The embedded player respects the viewer's entitlements and profile preferences. Viewers who aren't entitled to a title are prompted to sign in or purchase access inside the frame."
772
+ })
773
+ })
774
+ ]
775
+ });
776
+ }, te = () => {
777
+ let { apiGatewayUrl: r, getPath: i } = e(), [a, o] = p("overview"), s = r || "https://graphqlworkspaces.burdenoff.com/workspaces/graphql", c = f(() => `${typeof window < "u" ? window.location.origin : "https://app.hookmovies.com"}${i("watch/YOUR_TITLE_ID")}`, [i]);
778
+ return /* @__PURE__ */ A(n, {
779
+ title: "Developer Portal",
780
+ description: "Build on the HookMovies platform API",
781
+ purpose: "Everything you need to integrate with HookMovies — authenticate with an API key, query the federated GraphQL gateway, pull in the official SDKs or MCP server, and embed the player anywhere. API keys are issued through the platform identity service; HookMovies never stores your secret.",
782
+ children: [/* @__PURE__ */ k("div", {
783
+ className: "mb-6 flex gap-1 overflow-x-auto rounded-xl border border-border-seam bg-bg-surface p-1",
784
+ role: "tablist",
785
+ "aria-label": "Developer portal sections",
786
+ children: M.map((e) => {
787
+ let n = e.icon, r = a === e.id;
788
+ return /* @__PURE__ */ A("button", {
789
+ type: "button",
790
+ role: "tab",
791
+ "aria-selected": r,
792
+ onClick: () => o(e.id),
793
+ className: t("inline-flex shrink-0 items-center gap-2 rounded-lg px-3.5 py-2 text-sm font-medium transition-colors", r ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:text-text-primary"),
794
+ children: [/* @__PURE__ */ k(n, {
795
+ className: "h-4 w-4",
796
+ "aria-hidden": "true"
797
+ }), e.label]
798
+ }, e.id);
799
+ })
800
+ }), /* @__PURE__ */ A("div", {
801
+ className: "max-w-4xl",
802
+ children: [
803
+ a === "overview" && /* @__PURE__ */ k(H, { endpoint: s }),
804
+ a === "graphql" && /* @__PURE__ */ k(G, { endpoint: s }),
805
+ a === "keys" && /* @__PURE__ */ k(Z, {}),
806
+ a === "sdks" && /* @__PURE__ */ k($, {}),
807
+ a === "embed" && /* @__PURE__ */ k(ee, { watchUrl: c })
808
+ ]
809
+ })]
810
+ });
811
+ };
812
+ //#endregion
813
+ export { te as DevPortalPage };
814
+
815
+ //# sourceMappingURL=DevPortalPage.js.map