@burdenoff/microfe-hookmovies 2026.716.1 → 2026.720.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"HookMoviesRoutes.d.ts","sourceRoot":"","sources":["../../src/hookmovies/HookMoviesRoutes.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,EAAkB,MAAM,OAAO,CAAC;AA4IhD,eAAO,MAAM,gBAAgB,EAAE,EA6F9B,CAAC"}
1
+ {"version":3,"file":"HookMoviesRoutes.d.ts","sourceRoot":"","sources":["../../src/hookmovies/HookMoviesRoutes.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,EAAqD,MAAM,OAAO,CAAC;AAmKnF,eAAO,MAAM,gBAAgB,EAAE,EA6F9B,CAAC"}
@@ -3,16 +3,35 @@ import { HomePage as t } from "./pages/HomePage.js";
3
3
  import { Suspense as n, lazy as r } from "react";
4
4
  import { Navigate as i, Route as a, Routes as o, useLocation as s } from "react-router-dom";
5
5
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
+ import { FeatureGate as u } from "@burdenoff/fe-libs/shared/feature-flags";
6
7
  //#region src/hookmovies/HookMoviesRoutes.tsx
7
- var u = r(() => import("./pages/catalog/TitlesPage.js").then((e) => ({ default: e.TitlesPage }))), d = r(() => import("./pages/catalog/TitleCreatePage.js").then((e) => ({ default: e.TitleCreatePage }))), f = r(() => import("./pages/catalog/TitleDetailPage.js").then((e) => ({ default: e.TitleDetailPage }))), p = r(() => import("./pages/catalog/StudiosPage.js").then((e) => ({ default: e.StudiosPage }))), m = r(() => import("./pages/catalog/PeoplePage.js").then((e) => ({ default: e.PeoplePage }))), h = r(() => import("./pages/productions/ProductionsPage.js").then((e) => ({ default: e.ProductionsPage }))), g = r(() => import("./pages/productions/ProductionCreatePage.js").then((e) => ({ default: e.ProductionCreatePage }))), _ = r(() => import("./pages/productions/ProductionDetailPage.js").then((e) => ({ default: e.ProductionDetailPage }))), v = r(() => import("./pages/auditions/AuditionsPage.js").then((e) => ({ default: e.AuditionsPage }))), y = r(() => import("./pages/auditions/AuditionCreatePage.js").then((e) => ({ default: e.AuditionCreatePage }))), b = r(() => import("./pages/auditions/AuditionDetailPage.js").then((e) => ({ default: e.AuditionDetailPage }))), x = r(() => import("./pages/engagement/ReviewsPage.js").then((e) => ({ default: e.ReviewsPage }))), S = r(() => import("./pages/distribution/RightsWindowsPage.js").then((e) => ({ default: e.RightsWindowsPage }))), C = r(() => import("./pages/library/ListsPage.js").then((e) => ({ default: e.ListsPage }))), w = r(() => import("./pages/library/EntitlementsPage.js").then((e) => ({ default: e.EntitlementsPage }))), T = r(() => import("./pages/consumer/DiscoveryPage.js").then((e) => ({ default: e.DiscoveryPage }))), E = r(() => import("./pages/consumer/TitleViewPage.js").then((e) => ({ default: e.TitleViewPage }))), D = r(() => import("./pages/consumer/PlayerPage.js").then((e) => ({ default: e.PlayerPage }))), O = r(() => import("./pages/consumer/HooksPage.js").then((e) => ({ default: e.HooksPage }))), k = r(() => import("./pages/social/ProfilesPage.js").then((e) => ({ default: e.ProfilesPage }))), A = r(() => import("./pages/social/WatchPartyListPage.js").then((e) => ({ default: e.WatchPartyListPage }))), j = r(() => import("./pages/social/WatchPartyRoomPage.js").then((e) => ({ default: e.WatchPartyRoomPage }))), M = r(() => import("./pages/commerce/CheckoutPage.js").then((e) => ({ default: e.CheckoutPage }))), N = r(() => import("./pages/analytics/PayoutsPage.js").then((e) => ({ default: e.PayoutsPage }))), P = r(() => import("./pages/analytics/AnalyticsPage.js").then((e) => ({ default: e.AnalyticsPage }))), F = r(() => import("./pages/trust/ModerationPage.js").then((e) => ({ default: e.ModerationPage }))), I = r(() => import("./pages/onboarding/OnboardingPage.js").then((e) => ({ default: e.OnboardingPage }))), L = r(() => import("./pages/developer/DevPortalPage.js").then((e) => ({ default: e.DevPortalPage }))), R = r(() => import("./pages/live/LivePage.js").then((e) => ({ default: e.LivePage }))), z = r(() => import("./pages/localization/LocalizationPage.js").then((e) => ({ default: e.LocalizationPage }))), B = r(() => import("./pages/ai/ConciergePage.js").then((e) => ({ default: e.ConciergePage }))), V = () => /* @__PURE__ */ c("div", {
8
+ var d = r(() => import("./pages/catalog/TitlesPage.js").then((e) => ({ default: e.TitlesPage }))), f = r(() => import("./pages/catalog/TitleCreatePage.js").then((e) => ({ default: e.TitleCreatePage }))), p = r(() => import("./pages/catalog/TitleDetailPage.js").then((e) => ({ default: e.TitleDetailPage }))), m = r(() => import("./pages/catalog/StudiosPage.js").then((e) => ({ default: e.StudiosPage }))), h = r(() => import("./pages/catalog/PeoplePage.js").then((e) => ({ default: e.PeoplePage }))), g = r(() => import("./pages/productions/ProductionsPage.js").then((e) => ({ default: e.ProductionsPage }))), _ = r(() => import("./pages/productions/ProductionCreatePage.js").then((e) => ({ default: e.ProductionCreatePage }))), v = r(() => import("./pages/productions/ProductionDetailPage.js").then((e) => ({ default: e.ProductionDetailPage }))), y = r(() => import("./pages/auditions/AuditionsPage.js").then((e) => ({ default: e.AuditionsPage }))), b = r(() => import("./pages/auditions/AuditionCreatePage.js").then((e) => ({ default: e.AuditionCreatePage }))), x = r(() => import("./pages/auditions/AuditionDetailPage.js").then((e) => ({ default: e.AuditionDetailPage }))), S = r(() => import("./pages/engagement/ReviewsPage.js").then((e) => ({ default: e.ReviewsPage }))), C = r(() => import("./pages/distribution/RightsWindowsPage.js").then((e) => ({ default: e.RightsWindowsPage }))), w = r(() => import("./pages/library/ListsPage.js").then((e) => ({ default: e.ListsPage }))), T = r(() => import("./pages/library/EntitlementsPage.js").then((e) => ({ default: e.EntitlementsPage }))), E = r(() => import("./pages/consumer/DiscoveryPage.js").then((e) => ({ default: e.DiscoveryPage }))), D = r(() => import("./pages/consumer/TitleViewPage.js").then((e) => ({ default: e.TitleViewPage }))), O = r(() => import("./pages/consumer/PlayerPage.js").then((e) => ({ default: e.PlayerPage }))), k = r(() => import("./pages/consumer/HooksPage.js").then((e) => ({ default: e.HooksPage }))), A = r(() => import("./pages/social/ProfilesPage.js").then((e) => ({ default: e.ProfilesPage }))), j = r(() => import("./pages/social/WatchPartyListPage.js").then((e) => ({ default: e.WatchPartyListPage }))), M = r(() => import("./pages/social/WatchPartyRoomPage.js").then((e) => ({ default: e.WatchPartyRoomPage }))), N = r(() => import("./pages/commerce/CheckoutPage.js").then((e) => ({ default: e.CheckoutPage }))), P = r(() => import("./pages/analytics/PayoutsPage.js").then((e) => ({ default: e.PayoutsPage }))), F = r(() => import("./pages/analytics/AnalyticsPage.js").then((e) => ({ default: e.AnalyticsPage }))), I = r(() => import("./pages/trust/ModerationPage.js").then((e) => ({ default: e.ModerationPage }))), L = r(() => import("./pages/onboarding/OnboardingPage.js").then((e) => ({ default: e.OnboardingPage }))), R = r(() => import("./pages/developer/DevPortalPage.js").then((e) => ({ default: e.DevPortalPage }))), z = r(() => import("./pages/live/LivePage.js").then((e) => ({ default: e.LivePage }))), B = r(() => import("./pages/localization/LocalizationPage.js").then((e) => ({ default: e.LocalizationPage }))), V = r(() => import("./pages/ai/ConciergePage.js").then((e) => ({ default: e.ConciergePage }))), H = () => /* @__PURE__ */ c("div", {
8
9
  className: "flex min-h-[400px] items-center justify-center",
9
10
  children: /* @__PURE__ */ c("div", { className: "h-8 w-8 animate-spin rounded-full border-b-2 border-action-primary-bg" })
10
- }), H = () => {
11
- let { basePath: r, isStandalone: H } = e(), U = s(), W = r === "/" ? "" : (r ?? "").replace(/\/$/, "");
12
- return W && !H && !(U.pathname === W || U.pathname.startsWith(W + "/")) ? null : /* @__PURE__ */ c("div", {
11
+ }), U = () => /* @__PURE__ */ l("div", {
12
+ className: "flex min-h-[400px] flex-col items-center justify-center gap-2 px-6 text-center",
13
+ children: [/* @__PURE__ */ c("h2", {
14
+ className: "text-lg font-semibold text-text-primary",
15
+ children: "Coming soon"
16
+ }), /* @__PURE__ */ c("p", {
17
+ className: "max-w-sm text-sm text-text-secondary",
18
+ children: "This feature is currently disabled for this workspace."
19
+ })]
20
+ });
21
+ function W(e, t) {
22
+ return /* @__PURE__ */ c(u, {
23
+ flag: e,
24
+ defaultValue: !0,
25
+ fallback: /* @__PURE__ */ c(U, {}),
26
+ children: t
27
+ });
28
+ }
29
+ var G = () => {
30
+ let { basePath: r, isStandalone: u } = e(), U = s(), G = r === "/" ? "" : (r ?? "").replace(/\/$/, "");
31
+ return G && !u && !(U.pathname === G || U.pathname.startsWith(G + "/")) ? null : /* @__PURE__ */ c("div", {
13
32
  className: "flex min-h-full flex-col",
14
33
  children: /* @__PURE__ */ c(n, {
15
- fallback: /* @__PURE__ */ c(V, {}),
34
+ fallback: /* @__PURE__ */ c(H, {}),
16
35
  children: /* @__PURE__ */ l(o, { children: [
17
36
  /* @__PURE__ */ c(a, {
18
37
  index: !0,
@@ -20,131 +39,131 @@ var u = r(() => import("./pages/catalog/TitlesPage.js").then((e) => ({ default:
20
39
  }),
21
40
  /* @__PURE__ */ c(a, {
22
41
  path: "catalog/titles",
23
- element: /* @__PURE__ */ c(u, {})
42
+ element: /* @__PURE__ */ c(d, {})
24
43
  }),
25
44
  /* @__PURE__ */ c(a, {
26
45
  path: "catalog/titles/new",
27
- element: /* @__PURE__ */ c(d, {})
46
+ element: /* @__PURE__ */ c(f, {})
28
47
  }),
29
48
  /* @__PURE__ */ c(a, {
30
49
  path: "catalog/titles/:id",
31
- element: /* @__PURE__ */ c(f, {})
50
+ element: /* @__PURE__ */ c(p, {})
32
51
  }),
33
52
  /* @__PURE__ */ c(a, {
34
53
  path: "catalog/studios",
35
- element: /* @__PURE__ */ c(p, {})
54
+ element: /* @__PURE__ */ c(m, {})
36
55
  }),
37
56
  /* @__PURE__ */ c(a, {
38
57
  path: "catalog/people",
39
- element: /* @__PURE__ */ c(m, {})
58
+ element: /* @__PURE__ */ c(h, {})
40
59
  }),
41
60
  /* @__PURE__ */ c(a, {
42
61
  path: "productions",
43
- element: /* @__PURE__ */ c(h, {})
62
+ element: /* @__PURE__ */ c(g, {})
44
63
  }),
45
64
  /* @__PURE__ */ c(a, {
46
65
  path: "productions/new",
47
- element: /* @__PURE__ */ c(g, {})
66
+ element: /* @__PURE__ */ c(_, {})
48
67
  }),
49
68
  /* @__PURE__ */ c(a, {
50
69
  path: "productions/:id",
51
- element: /* @__PURE__ */ c(_, {})
70
+ element: /* @__PURE__ */ c(v, {})
52
71
  }),
53
72
  /* @__PURE__ */ c(a, {
54
73
  path: "auditions",
55
- element: /* @__PURE__ */ c(v, {})
74
+ element: /* @__PURE__ */ c(y, {})
56
75
  }),
57
76
  /* @__PURE__ */ c(a, {
58
77
  path: "auditions/new",
59
- element: /* @__PURE__ */ c(y, {})
78
+ element: /* @__PURE__ */ c(b, {})
60
79
  }),
61
80
  /* @__PURE__ */ c(a, {
62
81
  path: "auditions/:id",
63
- element: /* @__PURE__ */ c(b, {})
82
+ element: /* @__PURE__ */ c(x, {})
64
83
  }),
65
84
  /* @__PURE__ */ c(a, {
66
85
  path: "engagement/reviews",
67
- element: /* @__PURE__ */ c(x, {})
86
+ element: /* @__PURE__ */ c(S, {})
68
87
  }),
69
88
  /* @__PURE__ */ c(a, {
70
89
  path: "distribution/rights",
71
- element: /* @__PURE__ */ c(S, {})
90
+ element: /* @__PURE__ */ c(C, {})
72
91
  }),
73
92
  /* @__PURE__ */ c(a, {
74
93
  path: "library/lists",
75
- element: /* @__PURE__ */ c(C, {})
94
+ element: /* @__PURE__ */ c(w, {})
76
95
  }),
77
96
  /* @__PURE__ */ c(a, {
78
97
  path: "library/entitlements",
79
- element: /* @__PURE__ */ c(w, {})
98
+ element: /* @__PURE__ */ c(T, {})
80
99
  }),
81
100
  /* @__PURE__ */ c(a, {
82
101
  path: "browse",
83
- element: /* @__PURE__ */ c(T, {})
102
+ element: /* @__PURE__ */ c(E, {})
84
103
  }),
85
104
  /* @__PURE__ */ c(a, {
86
105
  path: "title/:id",
87
- element: /* @__PURE__ */ c(E, {})
106
+ element: /* @__PURE__ */ c(D, {})
88
107
  }),
89
108
  /* @__PURE__ */ c(a, {
90
109
  path: "watch/:titleId",
91
- element: /* @__PURE__ */ c(D, {})
110
+ element: /* @__PURE__ */ c(O, {})
92
111
  }),
93
112
  /* @__PURE__ */ c(a, {
94
113
  path: "watch/:titleId/episode/:episodeId",
95
- element: /* @__PURE__ */ c(D, {})
114
+ element: /* @__PURE__ */ c(O, {})
96
115
  }),
97
116
  /* @__PURE__ */ c(a, {
98
117
  path: "hooks",
99
- element: /* @__PURE__ */ c(O, {})
118
+ element: W("hookmovies.hooks.enabled", /* @__PURE__ */ c(k, {}))
100
119
  }),
101
120
  /* @__PURE__ */ c(a, {
102
121
  path: "profiles",
103
- element: /* @__PURE__ */ c(k, {})
122
+ element: /* @__PURE__ */ c(A, {})
104
123
  }),
105
124
  /* @__PURE__ */ c(a, {
106
125
  path: "watch-party",
107
- element: /* @__PURE__ */ c(A, {})
126
+ element: /* @__PURE__ */ c(j, {})
108
127
  }),
109
128
  /* @__PURE__ */ c(a, {
110
129
  path: "watch-party/:id",
111
- element: /* @__PURE__ */ c(j, {})
130
+ element: /* @__PURE__ */ c(M, {})
112
131
  }),
113
132
  /* @__PURE__ */ c(a, {
114
133
  path: "checkout/:titleId",
115
- element: /* @__PURE__ */ c(M, {})
134
+ element: /* @__PURE__ */ c(N, {})
116
135
  }),
117
136
  /* @__PURE__ */ c(a, {
118
137
  path: "payouts",
119
- element: /* @__PURE__ */ c(N, {})
138
+ element: /* @__PURE__ */ c(P, {})
120
139
  }),
121
140
  /* @__PURE__ */ c(a, {
122
141
  path: "analytics",
123
- element: /* @__PURE__ */ c(P, {})
142
+ element: /* @__PURE__ */ c(F, {})
124
143
  }),
125
144
  /* @__PURE__ */ c(a, {
126
145
  path: "trust/moderation",
127
- element: /* @__PURE__ */ c(F, {})
146
+ element: /* @__PURE__ */ c(I, {})
128
147
  }),
129
148
  /* @__PURE__ */ c(a, {
130
149
  path: "onboarding/languages",
131
- element: /* @__PURE__ */ c(I, {})
150
+ element: /* @__PURE__ */ c(L, {})
132
151
  }),
133
152
  /* @__PURE__ */ c(a, {
134
153
  path: "developer",
135
- element: /* @__PURE__ */ c(L, {})
154
+ element: /* @__PURE__ */ c(R, {})
136
155
  }),
137
156
  /* @__PURE__ */ c(a, {
138
157
  path: "live",
139
- element: /* @__PURE__ */ c(R, {})
158
+ element: W("hookmovies.fast.enabled", /* @__PURE__ */ c(z, {}))
140
159
  }),
141
160
  /* @__PURE__ */ c(a, {
142
161
  path: "title/:id/localization",
143
- element: /* @__PURE__ */ c(z, {})
162
+ element: /* @__PURE__ */ c(B, {})
144
163
  }),
145
164
  /* @__PURE__ */ c(a, {
146
165
  path: "concierge",
147
- element: /* @__PURE__ */ c(B, {})
166
+ element: W("hookmovies.ai.concierge", /* @__PURE__ */ c(V, {}))
148
167
  }),
149
168
  /* @__PURE__ */ c(a, {
150
169
  path: "*",
@@ -158,6 +177,6 @@ var u = r(() => import("./pages/catalog/TitlesPage.js").then((e) => ({ default:
158
177
  });
159
178
  };
160
179
  //#endregion
161
- export { H as HookMoviesRoutes };
180
+ export { G as HookMoviesRoutes };
162
181
 
163
182
  //# sourceMappingURL=HookMoviesRoutes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HookMoviesRoutes.js","names":[],"sources":["../../src/hookmovies/HookMoviesRoutes.tsx"],"sourcesContent":["/**\n * HookMovies Routes\n * @module hookmovies\n *\n * Real domain routes for the HookMovies workspace product: catalog (titles,\n * studios, people), production lifecycle, casting auditions, audience\n * engagement (reviews), rights & distribution, and the user library (lists +\n * entitlements). All data comes from the live `hookmovies` subgraph.\n */\n\nimport { type FC, lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\nimport { useHookMovies } from './context/HookMoviesContext';\n\n// Eager — primary landing\nimport { HomePage } from './pages/HomePage';\n\n// Catalog\nconst TitlesPage = lazy(() =>\n import('./pages/catalog/TitlesPage').then((m) => ({ default: m.TitlesPage }))\n);\nconst TitleCreatePage = lazy(() =>\n import('./pages/catalog/TitleCreatePage').then((m) => ({ default: m.TitleCreatePage }))\n);\nconst TitleDetailPage = lazy(() =>\n import('./pages/catalog/TitleDetailPage').then((m) => ({ default: m.TitleDetailPage }))\n);\nconst StudiosPage = lazy(() =>\n import('./pages/catalog/StudiosPage').then((m) => ({ default: m.StudiosPage }))\n);\nconst PeoplePage = lazy(() =>\n import('./pages/catalog/PeoplePage').then((m) => ({ default: m.PeoplePage }))\n);\n\n// Productions\nconst ProductionsPage = lazy(() =>\n import('./pages/productions/ProductionsPage').then((m) => ({ default: m.ProductionsPage }))\n);\nconst ProductionCreatePage = lazy(() =>\n import('./pages/productions/ProductionCreatePage').then((m) => ({\n default: m.ProductionCreatePage,\n }))\n);\nconst ProductionDetailPage = lazy(() =>\n import('./pages/productions/ProductionDetailPage').then((m) => ({\n default: m.ProductionDetailPage,\n }))\n);\n\n// Auditions\nconst AuditionsPage = lazy(() =>\n import('./pages/auditions/AuditionsPage').then((m) => ({ default: m.AuditionsPage }))\n);\nconst AuditionCreatePage = lazy(() =>\n import('./pages/auditions/AuditionCreatePage').then((m) => ({ default: m.AuditionCreatePage }))\n);\nconst AuditionDetailPage = lazy(() =>\n import('./pages/auditions/AuditionDetailPage').then((m) => ({ default: m.AuditionDetailPage }))\n);\n\n// Engagement\nconst ReviewsPage = lazy(() =>\n import('./pages/engagement/ReviewsPage').then((m) => ({ default: m.ReviewsPage }))\n);\n\n// Distribution\nconst RightsWindowsPage = lazy(() =>\n import('./pages/distribution/RightsWindowsPage').then((m) => ({ default: m.RightsWindowsPage }))\n);\n\n// Library\nconst ListsPage = lazy(() =>\n import('./pages/library/ListsPage').then((m) => ({ default: m.ListsPage }))\n);\nconst EntitlementsPage = lazy(() =>\n import('./pages/library/EntitlementsPage').then((m) => ({ default: m.EntitlementsPage }))\n);\n\n// Consumer (audience-facing) — discovery, title view, playback, hooks feed\nconst DiscoveryPage = lazy(() =>\n import('./pages/consumer/DiscoveryPage').then((m) => ({ default: m.DiscoveryPage }))\n);\nconst TitleViewPage = lazy(() =>\n import('./pages/consumer/TitleViewPage').then((m) => ({ default: m.TitleViewPage }))\n);\nconst PlayerPage = lazy(() =>\n import('./pages/consumer/PlayerPage').then((m) => ({ default: m.PlayerPage }))\n);\nconst HooksPage = lazy(() =>\n import('./pages/consumer/HooksPage').then((m) => ({ default: m.HooksPage }))\n);\n\n// Social — viewing profiles & synchronized watch parties\nconst ProfilesPage = lazy(() =>\n import('./pages/social/ProfilesPage').then((m) => ({ default: m.ProfilesPage }))\n);\nconst WatchPartyListPage = lazy(() =>\n import('./pages/social/WatchPartyListPage').then((m) => ({ default: m.WatchPartyListPage }))\n);\nconst WatchPartyRoomPage = lazy(() =>\n import('./pages/social/WatchPartyRoomPage').then((m) => ({ default: m.WatchPartyRoomPage }))\n);\n\n// Commerce — checkout (TVOD purchase / rental) & creator payouts\nconst CheckoutPage = lazy(() =>\n import('./pages/commerce/CheckoutPage').then((m) => ({ default: m.CheckoutPage }))\n);\nconst PayoutsPage = lazy(() =>\n import('./pages/analytics/PayoutsPage').then((m) => ({ default: m.PayoutsPage }))\n);\n\n// Analytics — creator / operator performance dashboards\nconst AnalyticsPage = lazy(() =>\n import('./pages/analytics/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\n\n// Trust & safety — moderation queue\nconst ModerationPage = lazy(() =>\n import('./pages/trust/ModerationPage').then((m) => ({ default: m.ModerationPage }))\n);\n\n// Onboarding — language / content preferences\nconst OnboardingPage = lazy(() =>\n import('./pages/onboarding/OnboardingPage').then((m) => ({ default: m.OnboardingPage }))\n);\n\n// Developer portal — API keys\nconst DevPortalPage = lazy(() =>\n import('./pages/developer/DevPortalPage').then((m) => ({ default: m.DevPortalPage }))\n);\n\n// Live — FAST linear channels & schedules\nconst LivePage = lazy(() => import('./pages/live/LivePage').then((m) => ({ default: m.LivePage })));\n\n// Localization — per-title subtitle / caption / dub track management\nconst LocalizationPage = lazy(() =>\n import('./pages/localization/LocalizationPage').then((m) => ({ default: m.LocalizationPage }))\n);\n\n// AI — concierge / recommendations assistant\nconst ConciergePage = lazy(() =>\n import('./pages/ai/ConciergePage').then((m) => ({ default: m.ConciergePage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-b-2 border-action-primary-bg\" />\n </div>\n);\n\nexport const HookMoviesRoutes: FC = () => {\n const { basePath, isStandalone } = useHookMovies();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Overview / dashboard */}\n <Route index element={<HomePage />} />\n\n {/* Catalog */}\n <Route path=\"catalog/titles\" element={<TitlesPage />} />\n <Route path=\"catalog/titles/new\" element={<TitleCreatePage />} />\n <Route path=\"catalog/titles/:id\" element={<TitleDetailPage />} />\n <Route path=\"catalog/studios\" element={<StudiosPage />} />\n <Route path=\"catalog/people\" element={<PeoplePage />} />\n\n {/* Productions */}\n <Route path=\"productions\" element={<ProductionsPage />} />\n <Route path=\"productions/new\" element={<ProductionCreatePage />} />\n <Route path=\"productions/:id\" element={<ProductionDetailPage />} />\n\n {/* Auditions */}\n <Route path=\"auditions\" element={<AuditionsPage />} />\n <Route path=\"auditions/new\" element={<AuditionCreatePage />} />\n <Route path=\"auditions/:id\" element={<AuditionDetailPage />} />\n\n {/* Engagement */}\n <Route path=\"engagement/reviews\" element={<ReviewsPage />} />\n\n {/* Distribution */}\n <Route path=\"distribution/rights\" element={<RightsWindowsPage />} />\n\n {/* Library */}\n <Route path=\"library/lists\" element={<ListsPage />} />\n <Route path=\"library/entitlements\" element={<EntitlementsPage />} />\n\n {/* Consumer (audience-facing) */}\n <Route path=\"browse\" element={<DiscoveryPage />} />\n <Route path=\"title/:id\" element={<TitleViewPage />} />\n <Route path=\"watch/:titleId\" element={<PlayerPage />} />\n <Route path=\"watch/:titleId/episode/:episodeId\" element={<PlayerPage />} />\n <Route path=\"hooks\" element={<HooksPage />} />\n\n {/* Social — profiles & watch parties */}\n <Route path=\"profiles\" element={<ProfilesPage />} />\n <Route path=\"watch-party\" element={<WatchPartyListPage />} />\n <Route path=\"watch-party/:id\" element={<WatchPartyRoomPage />} />\n\n {/* Commerce — checkout & payouts */}\n <Route path=\"checkout/:titleId\" element={<CheckoutPage />} />\n <Route path=\"payouts\" element={<PayoutsPage />} />\n\n {/* Analytics */}\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n\n {/* Trust & safety */}\n <Route path=\"trust/moderation\" element={<ModerationPage />} />\n\n {/* Onboarding */}\n <Route path=\"onboarding/languages\" element={<OnboardingPage />} />\n\n {/* Developer portal */}\n <Route path=\"developer\" element={<DevPortalPage />} />\n\n {/* Live — FAST channels */}\n <Route path=\"live\" element={<LivePage />} />\n\n {/* Localization */}\n <Route path=\"title/:id/localization\" element={<LocalizationPage />} />\n\n {/* AI concierge */}\n <Route path=\"concierge\" element={<ConciergePage />} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":";;;;;;AAkBA,IAAM,IAAa,QACjB,OAAO,iCAA8B,MAAM,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAC9E,EACK,IAAkB,QACtB,OAAO,sCAAmC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CACxF,EACK,IAAkB,QACtB,OAAO,sCAAmC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CACxF,EACK,IAAc,QAClB,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAChF,EACK,IAAa,QACjB,OAAO,iCAA8B,MAAM,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAC9E,EAGK,IAAkB,QACtB,OAAO,0CAAuC,MAAM,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAC5F,EACK,IAAuB,QAC3B,OAAO,+CAA4C,MAAM,OAAO,EAC9D,SAAS,EAAE,sBACZ,EAAE,CACJ,EACK,IAAuB,QAC3B,OAAO,+CAA4C,MAAM,OAAO,EAC9D,SAAS,EAAE,sBACZ,EAAE,CACJ,EAGK,IAAgB,QACpB,OAAO,sCAAmC,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CACtF,EACK,IAAqB,QACzB,OAAO,2CAAwC,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAChG,EACK,IAAqB,QACzB,OAAO,2CAAwC,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAChG,EAGK,IAAc,QAClB,OAAO,qCAAkC,MAAM,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CACnF,EAGK,IAAoB,QACxB,OAAO,6CAA0C,MAAM,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,CACjG,EAGK,IAAY,QAChB,OAAO,gCAA6B,MAAM,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAC5E,EACK,IAAmB,QACvB,OAAO,uCAAoC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC1F,EAGK,IAAgB,QACpB,OAAO,qCAAkC,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CACrF,EACK,IAAgB,QACpB,OAAO,qCAAkC,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CACrF,EACK,IAAa,QACjB,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAC/E,EACK,IAAY,QAChB,OAAO,iCAA8B,MAAM,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAC7E,EAGK,IAAe,QACnB,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CACjF,EACK,IAAqB,QACzB,OAAO,wCAAqC,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAC7F,EACK,IAAqB,QACzB,OAAO,wCAAqC,MAAM,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAC7F,EAGK,IAAe,QACnB,OAAO,oCAAiC,MAAM,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CACnF,EACK,IAAc,QAClB,OAAO,oCAAiC,MAAM,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAClF,EAGK,IAAgB,QACpB,OAAO,sCAAmC,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CACtF,EAGK,IAAiB,QACrB,OAAO,mCAAgC,MAAM,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,CACpF,EAGK,IAAiB,QACrB,OAAO,wCAAqC,MAAM,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,CACzF,EAGK,IAAgB,QACpB,OAAO,sCAAmC,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CACtF,EAGK,IAAW,QAAW,OAAO,4BAAyB,MAAM,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,EAG7F,IAAmB,QACvB,OAAO,4CAAyC,MAAM,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAC/F,EAGK,IAAgB,QACpB,OAAO,+BAA4B,MAAM,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CAC/E,EAEK,UACJ,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,OAAD,EAAK,WAAU,yEAA0E,CAAA;CACrF,CAAA,EAGK,UAA6B;CACxC,IAAM,EAAE,aAAU,oBAAiB,GAAe,EAC5C,IAAW,GAAa,EAExB,IAAqB,MAAa,MAAM,MAAM,KAAY,IAAI,QAAQ,OAAO,GAAG;AAYtF,QAVI,KAAsB,CAAC,KAKrB,EAHF,EAAS,aAAa,KACtB,EAAS,SAAS,WAAW,IAAqB,IAAI,IAG/C,OAKT,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GAAU,UAAU,kBAAC,GAAD,EAAc,CAAA;aAChC,kBAAC,GAAD,EAAA,UAAA;IAEE,kBAAC,GAAD;KAAO,OAAA;KAAM,SAAS,kBAAC,GAAD,EAAY,CAAA;KAAI,CAAA;IAGtC,kBAAC,GAAD;KAAO,MAAK;KAAiB,SAAS,kBAAC,GAAD,EAAc,CAAA;KAAI,CAAA;IACxD,kBAAC,GAAD;KAAO,MAAK;KAAqB,SAAS,kBAAC,GAAD,EAAmB,CAAA;KAAI,CAAA;IACjE,kBAAC,GAAD;KAAO,MAAK;KAAqB,SAAS,kBAAC,GAAD,EAAmB,CAAA;KAAI,CAAA;IACjE,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,EAAe,CAAA;KAAI,CAAA;IAC1D,kBAAC,GAAD;KAAO,MAAK;KAAiB,SAAS,kBAAC,GAAD,EAAc,CAAA;KAAI,CAAA;IAGxD,kBAAC,GAAD;KAAO,MAAK;KAAc,SAAS,kBAAC,GAAD,EAAmB,CAAA;KAAI,CAAA;IAC1D,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,EAAwB,CAAA;KAAI,CAAA;IACnE,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,EAAwB,CAAA;KAAI,CAAA;IAGnE,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IACtD,kBAAC,GAAD;KAAO,MAAK;KAAgB,SAAS,kBAAC,GAAD,EAAsB,CAAA;KAAI,CAAA;IAC/D,kBAAC,GAAD;KAAO,MAAK;KAAgB,SAAS,kBAAC,GAAD,EAAsB,CAAA;KAAI,CAAA;IAG/D,kBAAC,GAAD;KAAO,MAAK;KAAqB,SAAS,kBAAC,GAAD,EAAe,CAAA;KAAI,CAAA;IAG7D,kBAAC,GAAD;KAAO,MAAK;KAAsB,SAAS,kBAAC,GAAD,EAAqB,CAAA;KAAI,CAAA;IAGpE,kBAAC,GAAD;KAAO,MAAK;KAAgB,SAAS,kBAAC,GAAD,EAAa,CAAA;KAAI,CAAA;IACtD,kBAAC,GAAD;KAAO,MAAK;KAAuB,SAAS,kBAAC,GAAD,EAAoB,CAAA;KAAI,CAAA;IAGpE,kBAAC,GAAD;KAAO,MAAK;KAAS,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IACnD,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IACtD,kBAAC,GAAD;KAAO,MAAK;KAAiB,SAAS,kBAAC,GAAD,EAAc,CAAA;KAAI,CAAA;IACxD,kBAAC,GAAD;KAAO,MAAK;KAAoC,SAAS,kBAAC,GAAD,EAAc,CAAA;KAAI,CAAA;IAC3E,kBAAC,GAAD;KAAO,MAAK;KAAQ,SAAS,kBAAC,GAAD,EAAa,CAAA;KAAI,CAAA;IAG9C,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,EAAgB,CAAA;KAAI,CAAA;IACpD,kBAAC,GAAD;KAAO,MAAK;KAAc,SAAS,kBAAC,GAAD,EAAsB,CAAA;KAAI,CAAA;IAC7D,kBAAC,GAAD;KAAO,MAAK;KAAkB,SAAS,kBAAC,GAAD,EAAsB,CAAA;KAAI,CAAA;IAGjE,kBAAC,GAAD;KAAO,MAAK;KAAoB,SAAS,kBAAC,GAAD,EAAgB,CAAA;KAAI,CAAA;IAC7D,kBAAC,GAAD;KAAO,MAAK;KAAU,SAAS,kBAAC,GAAD,EAAe,CAAA;KAAI,CAAA;IAGlD,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IAGtD,kBAAC,GAAD;KAAO,MAAK;KAAmB,SAAS,kBAAC,GAAD,EAAkB,CAAA;KAAI,CAAA;IAG9D,kBAAC,GAAD;KAAO,MAAK;KAAuB,SAAS,kBAAC,GAAD,EAAkB,CAAA;KAAI,CAAA;IAGlE,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IAGtD,kBAAC,GAAD;KAAO,MAAK;KAAO,SAAS,kBAAC,GAAD,EAAY,CAAA;KAAI,CAAA;IAG5C,kBAAC,GAAD;KAAO,MAAK;KAAyB,SAAS,kBAAC,GAAD,EAAoB,CAAA;KAAI,CAAA;IAGtE,kBAAC,GAAD;KAAO,MAAK;KAAY,SAAS,kBAAC,GAAD,EAAiB,CAAA;KAAI,CAAA;IAGtD,kBAAC,GAAD;KAAO,MAAK;KAAI,SAAS,kBAAC,GAAD;MAAU,IAAG;MAAG,SAAA;MAAU,CAAA;KAAI,CAAA;IAChD,EAAA,CAAA;GACA,CAAA;EACP,CAAA"}
1
+ {"version":3,"file":"HookMoviesRoutes.js","names":[],"sources":["../../src/hookmovies/HookMoviesRoutes.tsx"],"sourcesContent":["/**\n * HookMovies Routes\n * @module hookmovies\n *\n * Real domain routes for the HookMovies workspace product: catalog (titles,\n * studios, people), production lifecycle, casting auditions, audience\n * engagement (reviews), rights & distribution, and the user library (lists +\n * entitlements). All data comes from the live `hookmovies` subgraph.\n */\n\nimport { type FC, lazy, Suspense, type ReactNode, type ReactElement } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useHookMovies } from './context/HookMoviesContext';\n\n// Eager — primary landing\nimport { HomePage } from './pages/HomePage';\n\n// Catalog\nconst TitlesPage = lazy(() =>\n import('./pages/catalog/TitlesPage').then((m) => ({ default: m.TitlesPage }))\n);\nconst TitleCreatePage = lazy(() =>\n import('./pages/catalog/TitleCreatePage').then((m) => ({ default: m.TitleCreatePage }))\n);\nconst TitleDetailPage = lazy(() =>\n import('./pages/catalog/TitleDetailPage').then((m) => ({ default: m.TitleDetailPage }))\n);\nconst StudiosPage = lazy(() =>\n import('./pages/catalog/StudiosPage').then((m) => ({ default: m.StudiosPage }))\n);\nconst PeoplePage = lazy(() =>\n import('./pages/catalog/PeoplePage').then((m) => ({ default: m.PeoplePage }))\n);\n\n// Productions\nconst ProductionsPage = lazy(() =>\n import('./pages/productions/ProductionsPage').then((m) => ({ default: m.ProductionsPage }))\n);\nconst ProductionCreatePage = lazy(() =>\n import('./pages/productions/ProductionCreatePage').then((m) => ({\n default: m.ProductionCreatePage,\n }))\n);\nconst ProductionDetailPage = lazy(() =>\n import('./pages/productions/ProductionDetailPage').then((m) => ({\n default: m.ProductionDetailPage,\n }))\n);\n\n// Auditions\nconst AuditionsPage = lazy(() =>\n import('./pages/auditions/AuditionsPage').then((m) => ({ default: m.AuditionsPage }))\n);\nconst AuditionCreatePage = lazy(() =>\n import('./pages/auditions/AuditionCreatePage').then((m) => ({ default: m.AuditionCreatePage }))\n);\nconst AuditionDetailPage = lazy(() =>\n import('./pages/auditions/AuditionDetailPage').then((m) => ({ default: m.AuditionDetailPage }))\n);\n\n// Engagement\nconst ReviewsPage = lazy(() =>\n import('./pages/engagement/ReviewsPage').then((m) => ({ default: m.ReviewsPage }))\n);\n\n// Distribution\nconst RightsWindowsPage = lazy(() =>\n import('./pages/distribution/RightsWindowsPage').then((m) => ({ default: m.RightsWindowsPage }))\n);\n\n// Library\nconst ListsPage = lazy(() =>\n import('./pages/library/ListsPage').then((m) => ({ default: m.ListsPage }))\n);\nconst EntitlementsPage = lazy(() =>\n import('./pages/library/EntitlementsPage').then((m) => ({ default: m.EntitlementsPage }))\n);\n\n// Consumer (audience-facing) — discovery, title view, playback, hooks feed\nconst DiscoveryPage = lazy(() =>\n import('./pages/consumer/DiscoveryPage').then((m) => ({ default: m.DiscoveryPage }))\n);\nconst TitleViewPage = lazy(() =>\n import('./pages/consumer/TitleViewPage').then((m) => ({ default: m.TitleViewPage }))\n);\nconst PlayerPage = lazy(() =>\n import('./pages/consumer/PlayerPage').then((m) => ({ default: m.PlayerPage }))\n);\nconst HooksPage = lazy(() =>\n import('./pages/consumer/HooksPage').then((m) => ({ default: m.HooksPage }))\n);\n\n// Social — viewing profiles & synchronized watch parties\nconst ProfilesPage = lazy(() =>\n import('./pages/social/ProfilesPage').then((m) => ({ default: m.ProfilesPage }))\n);\nconst WatchPartyListPage = lazy(() =>\n import('./pages/social/WatchPartyListPage').then((m) => ({ default: m.WatchPartyListPage }))\n);\nconst WatchPartyRoomPage = lazy(() =>\n import('./pages/social/WatchPartyRoomPage').then((m) => ({ default: m.WatchPartyRoomPage }))\n);\n\n// Commerce — checkout (TVOD purchase / rental) & creator payouts\nconst CheckoutPage = lazy(() =>\n import('./pages/commerce/CheckoutPage').then((m) => ({ default: m.CheckoutPage }))\n);\nconst PayoutsPage = lazy(() =>\n import('./pages/analytics/PayoutsPage').then((m) => ({ default: m.PayoutsPage }))\n);\n\n// Analytics — creator / operator performance dashboards\nconst AnalyticsPage = lazy(() =>\n import('./pages/analytics/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\n\n// Trust & safety — moderation queue\nconst ModerationPage = lazy(() =>\n import('./pages/trust/ModerationPage').then((m) => ({ default: m.ModerationPage }))\n);\n\n// Onboarding — language / content preferences\nconst OnboardingPage = lazy(() =>\n import('./pages/onboarding/OnboardingPage').then((m) => ({ default: m.OnboardingPage }))\n);\n\n// Developer portal — API keys\nconst DevPortalPage = lazy(() =>\n import('./pages/developer/DevPortalPage').then((m) => ({ default: m.DevPortalPage }))\n);\n\n// Live — FAST linear channels & schedules\nconst LivePage = lazy(() => import('./pages/live/LivePage').then((m) => ({ default: m.LivePage })));\n\n// Localization — per-title subtitle / caption / dub track management\nconst LocalizationPage = lazy(() =>\n import('./pages/localization/LocalizationPage').then((m) => ({ default: m.LocalizationPage }))\n);\n\n// AI — concierge / recommendations assistant\nconst ConciergePage = lazy(() =>\n import('./pages/ai/ConciergePage').then((m) => ({ default: m.ConciergePage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-b-2 border-action-primary-bg\" />\n </div>\n);\n\nconst FeatureDisabledFallback: FC = () => (\n <div className=\"flex min-h-[400px] flex-col items-center justify-center gap-2 px-6 text-center\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Coming soon</h2>\n <p className=\"max-w-sm text-sm text-text-secondary\">\n This feature is currently disabled for this workspace.\n </p>\n </div>\n);\n\n/**\n * Wrap a route element in a `FeatureGate` with the shared \"coming soon\"\n * fallback. `defaultValue={true}` keeps already-shipped screens visible\n * before their flag is seeded — seeding must never hide a working feature.\n */\nfunction gated(flag: string, element: ReactNode): ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={true} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nexport const HookMoviesRoutes: FC = () => {\n const { basePath, isStandalone } = useHookMovies();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Overview / dashboard */}\n <Route index element={<HomePage />} />\n\n {/* Catalog */}\n <Route path=\"catalog/titles\" element={<TitlesPage />} />\n <Route path=\"catalog/titles/new\" element={<TitleCreatePage />} />\n <Route path=\"catalog/titles/:id\" element={<TitleDetailPage />} />\n <Route path=\"catalog/studios\" element={<StudiosPage />} />\n <Route path=\"catalog/people\" element={<PeoplePage />} />\n\n {/* Productions */}\n <Route path=\"productions\" element={<ProductionsPage />} />\n <Route path=\"productions/new\" element={<ProductionCreatePage />} />\n <Route path=\"productions/:id\" element={<ProductionDetailPage />} />\n\n {/* Auditions */}\n <Route path=\"auditions\" element={<AuditionsPage />} />\n <Route path=\"auditions/new\" element={<AuditionCreatePage />} />\n <Route path=\"auditions/:id\" element={<AuditionDetailPage />} />\n\n {/* Engagement */}\n <Route path=\"engagement/reviews\" element={<ReviewsPage />} />\n\n {/* Distribution */}\n <Route path=\"distribution/rights\" element={<RightsWindowsPage />} />\n\n {/* Library */}\n <Route path=\"library/lists\" element={<ListsPage />} />\n <Route path=\"library/entitlements\" element={<EntitlementsPage />} />\n\n {/* Consumer (audience-facing) */}\n <Route path=\"browse\" element={<DiscoveryPage />} />\n <Route path=\"title/:id\" element={<TitleViewPage />} />\n <Route path=\"watch/:titleId\" element={<PlayerPage />} />\n <Route path=\"watch/:titleId/episode/:episodeId\" element={<PlayerPage />} />\n <Route path=\"hooks\" element={gated('hookmovies.hooks.enabled', <HooksPage />)} />\n\n {/* Social — profiles & watch parties */}\n <Route path=\"profiles\" element={<ProfilesPage />} />\n <Route path=\"watch-party\" element={<WatchPartyListPage />} />\n <Route path=\"watch-party/:id\" element={<WatchPartyRoomPage />} />\n\n {/* Commerce — checkout & payouts */}\n <Route path=\"checkout/:titleId\" element={<CheckoutPage />} />\n <Route path=\"payouts\" element={<PayoutsPage />} />\n\n {/* Analytics */}\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n\n {/* Trust & safety */}\n <Route path=\"trust/moderation\" element={<ModerationPage />} />\n\n {/* Onboarding */}\n <Route path=\"onboarding/languages\" element={<OnboardingPage />} />\n\n {/* Developer portal */}\n <Route path=\"developer\" element={<DevPortalPage />} />\n\n {/* Live — FAST channels */}\n <Route path=\"live\" element={gated('hookmovies.fast.enabled', <LivePage />)} />\n\n {/* Localization */}\n <Route path=\"title/:id/localization\" element={<LocalizationPage />} />\n\n {/* AI concierge */}\n <Route path=\"concierge\" element={gated('hookmovies.ai.concierge', <ConciergePage />)} />\n\n {/* Fallback */}\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -140,7 +140,8 @@ var M = [
140
140
  {
141
141
  title: "Point at the GraphQL endpoint",
142
142
  body: /* @__PURE__ */ A(O, { children: [
143
- "All HookMovies data is served from a single Apollo Federation gateway. The endpoint is shown in the ",
143
+ "All HookMovies data is served from a single GraphQL API. The endpoint is shown in the",
144
+ " ",
144
145
  /* @__PURE__ */ k("strong", { children: "GraphQL API" }),
145
146
  " tab."
146
147
  ] })
@@ -236,7 +237,7 @@ var M = [
236
237
  /* @__PURE__ */ k(z, {
237
238
  icon: v,
238
239
  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
+ children: "One GraphQL endpoint serves the entire HookMovies domain. Send POST requests with your API key as a bearer token."
240
241
  }),
241
242
  /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
242
243
  className: "mb-2 text-sm font-semibold text-text-primary",
@@ -778,7 +779,7 @@ var M = [
778
779
  return /* @__PURE__ */ A(n, {
779
780
  title: "Developer Portal",
780
781
  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
+ purpose: "Everything you need to integrate with HookMovies — authenticate with an API key, query the 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
783
  children: [/* @__PURE__ */ k("div", {
783
784
  className: "mb-6 flex gap-1 overflow-x-auto rounded-xl border border-border-seam bg-bg-surface p-1",
784
785
  role: "tablist",
@@ -1 +1 @@
1
- {"version":3,"file":"DevPortalPage.js","names":[],"sources":["../../../../src/hookmovies/pages/developer/DevPortalPage.tsx"],"sourcesContent":["/**\n * Developer Portal\n * @module hookmovies/pages/developer\n *\n * Thin, product-branded developer surface (~95% reuse). It is documentation +\n * a live API-key manager, NOT a new key store: the keys panel reuses the\n * platform's global-auth service verbatim through the shared data-layer hooks\n * (`useMyApiKeys` / `useGenerateApiKey` / `useRevokeApiKey`, all on the GLOBAL\n * gateway). The rest of the page is static, tokenized reference content:\n *\n * - Getting Started — how to authenticate and issue a first request.\n * - GraphQL API — the real workspace gateway endpoint plus a runnable\n * `hokListTitles` query / response sample.\n * - API Keys — list, mint (secret shown exactly once) and revoke.\n * - SDKs & MCP — links to the real HookMovies packages.\n * - Embeddable Player — a copy-paste <iframe> embed for `/hookmovies/watch/:id`.\n */\n\nimport { type FC, type ReactNode, type FormEvent, useEffect, useMemo, useState } from 'react';\nimport {\n Rocket,\n Code2,\n KeyRound,\n Package,\n Clapperboard,\n Copy,\n Check,\n Plus,\n Trash2,\n ShieldAlert,\n ExternalLink,\n Terminal,\n BookOpen,\n Boxes,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useMyApiKeys,\n useGenerateApiKey,\n useRevokeApiKey,\n type ApiKey,\n type ApiKeyResult,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Section model\n// ============================================================================\n\ntype SectionId = 'overview' | 'graphql' | 'keys' | 'sdks' | 'embed';\n\ninterface SectionMeta {\n id: SectionId;\n label: string;\n icon: typeof Rocket;\n}\n\nconst SECTIONS: SectionMeta[] = [\n { id: 'overview', label: 'Getting Started', icon: Rocket },\n { id: 'graphql', label: 'GraphQL API', icon: Code2 },\n { id: 'keys', label: 'API Keys', icon: KeyRound },\n { id: 'sdks', label: 'SDKs & MCP', icon: Package },\n { id: 'embed', label: 'Embeddable Player', icon: Clapperboard },\n];\n\n/** API-key scopes offered by the mint form (global-auth scopes are free-form strings). */\nconst SCOPE_OPTIONS: { value: string; label: string; description: string }[] = [\n { value: 'read', label: 'Read', description: 'Query the catalog, titles, reviews and metadata.' },\n {\n value: 'write',\n label: 'Write',\n description: 'Create and update titles, reviews and other resources.',\n },\n];\n\n// ============================================================================\n// Copy-to-clipboard button\n// ============================================================================\n\nconst useCopy = (): [boolean, (text: string) => void] => {\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (!copied) return;\n const timer = window.setTimeout(() => setCopied(false), 1800);\n return () => window.clearTimeout(timer);\n }, [copied]);\n\n const copy = (text: string) => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) return;\n void navigator.clipboard.writeText(text).then(\n () => setCopied(true),\n () => setCopied(false)\n );\n };\n\n return [copied, copy];\n};\n\nconst CopyButton: FC<{ value: string; label?: string; className?: string }> = ({\n value,\n label,\n className,\n}) => {\n const [copied, copy] = useCopy();\n return (\n <button\n type=\"button\"\n onClick={() => copy(value)}\n className={cn(\n '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',\n className\n )}\n aria-label={copied ? 'Copied' : `Copy ${label ?? 'to clipboard'}`}\n >\n {copied ? (\n <Check className=\"h-3.5 w-3.5 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n )}\n {copied ? 'Copied' : (label ?? 'Copy')}\n </button>\n );\n};\n\n// ============================================================================\n// Tokenized code block (semantic tokens only — no raw colours)\n// ============================================================================\n\n/**\n * Lightweight, allocation-safe syntax highlighter. A single ordered alternation\n * classifies comments, strings, numbers and a small keyword set (GraphQL / shell\n * / JS) and maps each class onto a fe-libs semantic colour token. Everything else\n * renders as `text-text-primary`, so the block degrades gracefully for any input.\n */\nconst HIGHLIGHT_RE =\n /(#[^\\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;\n\nconst highlightCode = (code: string): ReactNode[] => {\n const nodes: ReactNode[] = [];\n let lastIndex = 0;\n let key = 0;\n for (const match of code.matchAll(HIGHLIGHT_RE)) {\n const index = match.index ?? 0;\n if (index > lastIndex) {\n nodes.push(<span key={key++}>{code.slice(lastIndex, index)}</span>);\n }\n const [full, comment, str, num, keyword] = match;\n if (comment) {\n nodes.push(\n <span key={key++} className=\"text-text-muted italic\">\n {full}\n </span>\n );\n } else if (str) {\n nodes.push(\n <span key={key++} className=\"text-status-success-text\">\n {full}\n </span>\n );\n } else if (num) {\n nodes.push(\n <span key={key++} className=\"text-status-warning-text\">\n {full}\n </span>\n );\n } else if (keyword) {\n nodes.push(\n <span key={key++} className=\"text-status-info-text font-medium\">\n {full}\n </span>\n );\n } else {\n nodes.push(<span key={key++}>{full}</span>);\n }\n lastIndex = index + full.length;\n }\n if (lastIndex < code.length) {\n nodes.push(<span key={key++}>{code.slice(lastIndex)}</span>);\n }\n return nodes;\n};\n\nconst CodeBlock: FC<{ code: string; language: string; className?: string }> = ({\n code,\n language,\n className,\n}) => (\n <div\n className={cn('overflow-hidden rounded-xl border border-border-seam bg-bg-sunken', className)}\n >\n <div className=\"flex items-center justify-between border-b border-border-seam bg-bg-surface px-3 py-1.5\">\n <span className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-text-muted\">\n <Terminal className=\"h-3 w-3\" aria-hidden=\"true\" />\n {language}\n </span>\n <CopyButton value={code} label=\"Copy\" />\n </div>\n <div className=\"overflow-x-auto\">\n <pre className=\"min-w-0 px-4 py-3 text-[13px] leading-relaxed\">\n <code className=\"font-mono text-text-primary\">{highlightCode(code)}</code>\n </pre>\n </div>\n </div>\n);\n\n// ============================================================================\n// Small presentational helpers\n// ============================================================================\n\nconst SectionHeading: FC<{ icon: typeof Rocket; title: string; children?: ReactNode }> = ({\n icon: Icon,\n title,\n children,\n}) => (\n <div className=\"mb-4\">\n <h2 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n {title}\n </h2>\n {children && <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">{children}</p>}\n </div>\n);\n\nconst InlineCode: FC<{ children: ReactNode }> = ({ children }) => (\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-[0.85em] text-text-primary\">\n {children}\n </code>\n);\n\n// ============================================================================\n// Getting Started\n// ============================================================================\n\nconst GETTING_STARTED_STEPS: { title: string; body: ReactNode }[] = [\n {\n title: 'Create an API key',\n body: (\n <>\n Head to the <strong>API Keys</strong> tab and mint a key for your registered app. The secret\n is shown once at creation — copy it somewhere safe.\n </>\n ),\n },\n {\n title: 'Point at the GraphQL endpoint',\n body: (\n <>\n All HookMovies data is served from a single Apollo Federation gateway. The endpoint is shown\n in the <strong>GraphQL API</strong> tab.\n </>\n ),\n },\n {\n title: 'Authenticate every request',\n body: (\n <>\n Send your key as a bearer token in the <InlineCode>Authorization</InlineCode> header:{' '}\n <InlineCode>Authorization: Bearer &lt;apiSecret&gt;</InlineCode>.\n </>\n ),\n },\n {\n title: 'Make your first query',\n body: (\n <>\n Call <InlineCode>hokListTitles</InlineCode> to page through the catalog, then drill into a\n title with <InlineCode>hokTitle(id)</InlineCode>.\n </>\n ),\n },\n];\n\nconst GettingStartedSection: FC<{ endpoint: string }> = ({ endpoint }) => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Rocket} title=\"Getting Started\">\n Build with the HookMovies platform API — a single GraphQL gateway that powers the catalog,\n productions, auditions, entitlements and playback surfaces you see in this app.\n </SectionHeading>\n\n <ol className=\"grid gap-4 sm:grid-cols-2\">\n {GETTING_STARTED_STEPS.map((step, i) => (\n <li key={step.title}>\n <GlassCard treatment=\"glass\" className=\"h-full border-border-seam p-4\">\n <div className=\"flex items-start gap-3\">\n <span 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\">\n {i + 1}\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary\">{step.title}</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">{step.body}</p>\n </div>\n </div>\n </GlassCard>\n </li>\n ))}\n </ol>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <BookOpen className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Base endpoint\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Every request targets the workspace GraphQL gateway:\n </p>\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <code 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\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </GlassCard>\n </div>\n);\n\n// ============================================================================\n// GraphQL API\n// ============================================================================\n\nconst SAMPLE_QUERY = `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}`;\n\nconst SAMPLE_RESPONSE = `{\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}`;\n\nconst GraphqlSection: FC<{ endpoint: string }> = ({ endpoint }) => {\n const curl = `curl -X POST '${endpoint}' \\\\\n -H 'Content-Type: application/json' \\\\\n -H 'Authorization: Bearer <apiSecret>' \\\\\n -d '{\"query\": \"query { hokListTitles(pagination: { limit: 5 }) { items { id title } totalCount } }\"}'`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Code2} title=\"GraphQL API\">\n One federated GraphQL endpoint serves the entire HookMovies domain. Send POST requests with\n your API key as a bearer token.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Endpoint</h3>\n <div className=\"flex flex-wrap items-center gap-2\">\n <code 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\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample query</h3>\n <CodeBlock language=\"graphql\" code={SAMPLE_QUERY} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample response</h3>\n <CodeBlock language=\"json\" code={SAMPLE_RESPONSE} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Run it with cURL</h3>\n <CodeBlock language=\"bash\" code={curl} />\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// API Keys — reuses global-auth via the shared data-layer hooks\n// ============================================================================\n\nconst SecretReveal: FC<{ result: ApiKeyResult; onDismiss: () => void }> = ({\n result,\n onDismiss,\n}) => (\n <GlassCard\n treatment=\"glass\"\n className=\"mb-6 border-status-warning-border bg-status-warning-bg-subtle p-5\"\n >\n <div className=\"flex items-start gap-3\">\n <ShieldAlert\n className=\"mt-0.5 h-5 w-5 shrink-0 text-status-warning-text\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Save your API secret now</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n This secret is shown only once and cannot be retrieved again. Store it somewhere safe\n before dismissing this panel.\n </p>\n\n <dl className=\"mt-4 space-y-3\">\n {(\n [\n { label: 'API key', value: result.apiKey },\n { label: 'API secret', value: result.apiSecret },\n { label: 'Key ID', value: result.keyId },\n ] as const\n ).map((row) => (\n <div key={row.label}>\n <dt className=\"mb-1 text-xs font-medium text-text-secondary\">{row.label}</dt>\n <dd className=\"flex flex-wrap items-center gap-2\">\n <code 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\">\n {row.value}\n </code>\n <CopyButton value={row.value} label=\"Copy\" />\n </dd>\n </div>\n ))}\n </dl>\n\n <button\n type=\"button\"\n onClick={onDismiss}\n 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\"\n >\n I've saved it — dismiss\n </button>\n </div>\n </div>\n </GlassCard>\n);\n\nconst fieldClass =\n '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)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nconst GenerateKeyForm: FC<{\n onGenerated: (result: ApiKeyResult) => void;\n onClose: () => void;\n}> = ({ onGenerated, onClose }) => {\n const generate = useGenerateApiKey();\n const [name, setName] = useState('');\n const [appId, setAppId] = useState('');\n const [scopes, setScopes] = useState<string[]>(['read']);\n const [expiresAt, setExpiresAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const toggleScope = (value: string) => {\n setScopes((prev) =>\n prev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value]\n );\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Give the key a recognisable name.');\n return;\n }\n if (!appId.trim()) {\n setError('Enter the App ID this key will act on behalf of.');\n return;\n }\n if (scopes.length === 0) {\n setError('Select at least one scope.');\n return;\n }\n try {\n const result = await generate.mutateAsync({\n appId: appId.trim(),\n name: name.trim(),\n scope: scopes,\n expiresAt: expiresAt ? new Date(expiresAt).toISOString() : undefined,\n });\n onGenerated(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to generate the API key.');\n }\n };\n\n return (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Generate a new API key\n </h3>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"key-name\" className={labelClass}>\n Key name *\n </label>\n <input\n id=\"key-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Production ingest\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"key-app-id\" className={labelClass}>\n App ID *\n </label>\n <input\n id=\"key-app-id\"\n value={appId}\n onChange={(e) => setAppId(e.target.value)}\n placeholder=\"Your registered app's ID\"\n className={fieldClass}\n required\n />\n <p className=\"mt-1 text-[11px] text-text-muted\">\n The client app this key acts on behalf of — find it in your workspace app settings.\n </p>\n </div>\n </div>\n\n <fieldset>\n <legend className={labelClass}>Scopes *</legend>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n {SCOPE_OPTIONS.map((scope) => {\n const checked = scopes.includes(scope.value);\n return (\n <label\n key={scope.value}\n className={cn(\n 'flex cursor-pointer items-start gap-2 rounded-lg border p-3 text-sm transition-colors',\n checked\n ? 'border-action-primary-bg bg-action-primary-bg/10'\n : 'border-border-subtle hover:border-border-strong'\n )}\n >\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={() => toggleScope(scope.value)}\n className=\"mt-0.5 accent-[var(--color-action-primary-bg)]\"\n />\n <span className=\"min-w-0\">\n <span className=\"block font-medium text-text-primary\">{scope.label}</span>\n <span className=\"block text-xs text-text-secondary\">{scope.description}</span>\n </span>\n </label>\n );\n })}\n </div>\n </fieldset>\n\n <div>\n <label htmlFor=\"key-expires\" className={labelClass}>\n Expires (optional)\n </label>\n <input\n id=\"key-expires\"\n type=\"date\"\n value={expiresAt}\n onChange={(e) => setExpiresAt(e.target.value)}\n className={cn(fieldClass, 'sm:max-w-xs')}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"submit\"\n disabled={generate.isPending}\n 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\"\n >\n {generate.isPending ? 'Generating…' : 'Generate key'}\n </button>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n </form>\n </GlassCard>\n );\n};\n\nconst ApiKeyRow: FC<{ apiKey: ApiKey }> = ({ apiKey }) => {\n const revoke = useRevokeApiKey();\n const [confirming, setConfirming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleRevoke = async () => {\n setError(null);\n try {\n await revoke.mutateAsync(apiKey.id);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to revoke this key.');\n setConfirming(false);\n }\n };\n\n return (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-text-primary\">{apiKey.name}</span>\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-xs text-text-secondary\">\n {apiKey.prefix}…\n </code>\n </div>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-muted\">\n {apiKey.appName && <span>{apiKey.appName}</span>}\n <span aria-hidden=\"true\">·</span>\n <span>\n Created {formatDate(apiKey.createdAt, { hour: undefined, minute: undefined })}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>\n {apiKey.lastUsedAt\n ? `Last used ${formatRelativeTime(apiKey.lastUsedAt)}`\n : 'Never used'}\n </span>\n {apiKey.expiresAt && (\n <>\n <span aria-hidden=\"true\">·</span>\n <span>\n Expires {formatDate(apiKey.expiresAt, { hour: undefined, minute: undefined })}\n </span>\n </>\n )}\n </div>\n {apiKey.scopes.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {apiKey.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-text-secondary\"\n >\n {scope}\n </span>\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0\">\n {confirming ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={revoke.isPending}\n onClick={() => void handleRevoke()}\n 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\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {revoke.isPending ? 'Revoking…' : 'Confirm revoke'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n 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\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n 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\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Revoke\n </button>\n )}\n </div>\n </div>\n {error && <p className=\"mt-2 text-xs text-status-error-text\">{error}</p>}\n </GlassCard>\n );\n};\n\nconst ApiKeysSection: FC = () => {\n const { data, isLoading, isError, refetch } = useMyApiKeys();\n const [showForm, setShowForm] = useState(false);\n const [revealed, setRevealed] = useState<ApiKeyResult | null>(null);\n\n const keys = data?.apiKeys ?? [];\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <SectionHeading icon={KeyRound} title=\"API Keys\">\n Keys authenticate server-to-server calls to the HookMovies GraphQL gateway. They are\n issued and revoked through the platform identity service — HookMovies never stores your\n secret.\n </SectionHeading>\n <button\n type=\"button\"\n onClick={() => setShowForm((v) => !v)}\n 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\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n New key\n </button>\n </div>\n\n {revealed && <SecretReveal result={revealed} onDismiss={() => setRevealed(null)} />}\n\n {showForm && (\n <GenerateKeyForm\n onGenerated={(result) => {\n setRevealed(result);\n setShowForm(false);\n }}\n onClose={() => setShowForm(false)}\n />\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading your API keys…\" />\n ) : isError ? (\n <ErrorState message=\"We couldn't load your API keys.\" onRetry={() => void refetch()} />\n ) : keys.length === 0 ? (\n <EmptyState\n icon={<KeyRound className=\"h-12 w-12\" />}\n title=\"No API keys yet\"\n description=\"Generate your first key to start calling the HookMovies API.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowForm(true)}\n 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\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Generate key\n </button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {keys.map((apiKey) => (\n <ApiKeyRow key={apiKey.id} apiKey={apiKey} />\n ))}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// SDKs & MCP\n// ============================================================================\n\ninterface PackageCard {\n name: string;\n repo: string;\n description: string;\n icon: typeof Package;\n install: { language: string; code: string };\n}\n\nconst PACKAGES: PackageCard[] = [\n {\n name: 'Node SDK',\n repo: 'hookmovies-sdk-node',\n description:\n 'Typed TypeScript client for the HookMovies GraphQL API — catalog, titles, reviews and playback.',\n icon: Boxes,\n install: {\n language: 'bash',\n code: 'bun add hookmovies-sdk-node\\n# or: npm install hookmovies-sdk-node',\n },\n },\n {\n name: 'Python SDK',\n repo: 'hookmovies-sdk-python',\n description: 'Pythonic client with async support for backend integrations and data pipelines.',\n icon: Package,\n install: { language: 'bash', code: 'pip install hookmovies-sdk-python' },\n },\n {\n name: 'MCP Server',\n repo: 'hookmovies-mcp',\n description:\n 'Model Context Protocol server that exposes HookMovies tools to AI agents and assistants.',\n icon: Boxes,\n install: { language: 'bash', code: 'bunx hookmovies-mcp --help' },\n },\n {\n name: 'CLI',\n repo: 'hookmovies-cli',\n description:\n 'Command-line tool to manage titles, productions and rights from your terminal or CI.',\n icon: Terminal,\n install: { language: 'bash', code: 'bunx hookmovies-cli titles list' },\n },\n];\n\nconst SdksSection: FC = () => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Package} title=\"SDKs & MCP\">\n Official HookMovies packages wrap the GraphQL API so you can build in your language of choice\n — or plug the platform straight into an AI agent via MCP.\n </SectionHeading>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {PACKAGES.map((pkg) => {\n const Icon = pkg.icon;\n const repoUrl = `https://github.com/algoshred/${pkg.repo}`;\n return (\n <GlassCard\n key={pkg.repo}\n treatment=\"glass\"\n className=\"flex h-full flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n </span>\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">{pkg.name}</h3>\n <code className=\"text-[11px] text-text-muted\">{pkg.repo}</code>\n </div>\n </div>\n <a\n href={repoUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n 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\"\n >\n Repo\n <ExternalLink className=\"h-3 w-3\" aria-hidden=\"true\" />\n </a>\n </div>\n <p className=\"mt-3 flex-1 text-sm text-text-secondary\">{pkg.description}</p>\n <div className=\"mt-3\">\n <CodeBlock language={pkg.install.language} code={pkg.install.code} />\n </div>\n </GlassCard>\n );\n })}\n </div>\n </div>\n);\n\n// ============================================================================\n// Embeddable Player\n// ============================================================================\n\nconst EmbedSection: FC<{ watchUrl: string }> = ({ watchUrl }) => {\n const iframeSnippet = `<iframe\n src=\"${watchUrl}\"\n width=\"100%\"\n height=\"480\"\n frameborder=\"0\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n></iframe>`;\n\n const responsiveSnippet = `<div style=\"position: relative; padding-top: 56.25%;\">\n <iframe\n src=\"${watchUrl}\"\n style=\"position: absolute; inset: 0; width: 100%; height: 100%; border: 0;\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n ></iframe>\n</div>`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Clapperboard} title=\"Embeddable Player\">\n Drop a HookMovies title into any web page with a single{' '}\n <InlineCode>&lt;iframe&gt;</InlineCode>. Replace <InlineCode>YOUR_TITLE_ID</InlineCode> with\n the id of any published title.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Basic embed</h3>\n <CodeBlock language=\"html\" code={iframeSnippet} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Responsive (16:9) embed</h3>\n <p className=\"mb-2 text-sm text-text-secondary\">\n Wrap the iframe in an aspect-ratio box so the player scales fluidly on any device.\n </p>\n <CodeBlock language=\"html\" code={responsiveSnippet} />\n </div>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <p className=\"text-sm text-text-secondary\">\n The embedded player respects the viewer's entitlements and profile preferences. Viewers\n who aren't entitled to a title are prompted to sign in or purchase access inside the\n frame.\n </p>\n </GlassCard>\n </div>\n );\n};\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport const DevPortalPage: FC = () => {\n const { apiGatewayUrl, getPath } = useHookMovies();\n const [active, setActive] = useState<SectionId>('overview');\n\n const endpoint = apiGatewayUrl || 'https://graphqlworkspaces.burdenoff.com/workspaces/graphql';\n\n const watchUrl = useMemo(() => {\n const origin =\n typeof window !== 'undefined' ? window.location.origin : 'https://app.hookmovies.com';\n return `${origin}${getPath('watch/YOUR_TITLE_ID')}`;\n }, [getPath]);\n\n return (\n <PageLayout\n title=\"Developer Portal\"\n description=\"Build on the HookMovies platform API\"\n 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.\"\n >\n {/* Section navigation — horizontal, scrollable on small screens */}\n <div\n className=\"mb-6 flex gap-1 overflow-x-auto rounded-xl border border-border-seam bg-bg-surface p-1\"\n role=\"tablist\"\n aria-label=\"Developer portal sections\"\n >\n {SECTIONS.map((section) => {\n const Icon = section.icon;\n const selected = active === section.id;\n return (\n <button\n key={section.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={selected}\n onClick={() => setActive(section.id)}\n className={cn(\n 'inline-flex shrink-0 items-center gap-2 rounded-lg px-3.5 py-2 text-sm font-medium transition-colors',\n selected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <Icon className=\"h-4 w-4\" aria-hidden=\"true\" />\n {section.label}\n </button>\n );\n })}\n </div>\n\n <div className=\"max-w-4xl\">\n {active === 'overview' && <GettingStartedSection endpoint={endpoint} />}\n {active === 'graphql' && <GraphqlSection endpoint={endpoint} />}\n {active === 'keys' && <ApiKeysSection />}\n {active === 'sdks' && <SdksSection />}\n {active === 'embed' && <EmbedSection watchUrl={watchUrl} />}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"DevPortalPage.js","names":[],"sources":["../../../../src/hookmovies/pages/developer/DevPortalPage.tsx"],"sourcesContent":["/**\n * Developer Portal\n * @module hookmovies/pages/developer\n *\n * Thin, product-branded developer surface (~95% reuse). It is documentation +\n * a live API-key manager, NOT a new key store: the keys panel reuses the\n * platform's global-auth service verbatim through the shared data-layer hooks\n * (`useMyApiKeys` / `useGenerateApiKey` / `useRevokeApiKey`, all on the GLOBAL\n * gateway). The rest of the page is static, tokenized reference content:\n *\n * - Getting Started — how to authenticate and issue a first request.\n * - GraphQL API — the real workspace gateway endpoint plus a runnable\n * `hokListTitles` query / response sample.\n * - API Keys — list, mint (secret shown exactly once) and revoke.\n * - SDKs & MCP — links to the real HookMovies packages.\n * - Embeddable Player — a copy-paste <iframe> embed for `/hookmovies/watch/:id`.\n */\n\nimport { type FC, type ReactNode, type FormEvent, useEffect, useMemo, useState } from 'react';\nimport {\n Rocket,\n Code2,\n KeyRound,\n Package,\n Clapperboard,\n Copy,\n Check,\n Plus,\n Trash2,\n ShieldAlert,\n ExternalLink,\n Terminal,\n BookOpen,\n Boxes,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useMyApiKeys,\n useGenerateApiKey,\n useRevokeApiKey,\n type ApiKey,\n type ApiKeyResult,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Section model\n// ============================================================================\n\ntype SectionId = 'overview' | 'graphql' | 'keys' | 'sdks' | 'embed';\n\ninterface SectionMeta {\n id: SectionId;\n label: string;\n icon: typeof Rocket;\n}\n\nconst SECTIONS: SectionMeta[] = [\n { id: 'overview', label: 'Getting Started', icon: Rocket },\n { id: 'graphql', label: 'GraphQL API', icon: Code2 },\n { id: 'keys', label: 'API Keys', icon: KeyRound },\n { id: 'sdks', label: 'SDKs & MCP', icon: Package },\n { id: 'embed', label: 'Embeddable Player', icon: Clapperboard },\n];\n\n/** API-key scopes offered by the mint form (global-auth scopes are free-form strings). */\nconst SCOPE_OPTIONS: { value: string; label: string; description: string }[] = [\n { value: 'read', label: 'Read', description: 'Query the catalog, titles, reviews and metadata.' },\n {\n value: 'write',\n label: 'Write',\n description: 'Create and update titles, reviews and other resources.',\n },\n];\n\n// ============================================================================\n// Copy-to-clipboard button\n// ============================================================================\n\nconst useCopy = (): [boolean, (text: string) => void] => {\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (!copied) return;\n const timer = window.setTimeout(() => setCopied(false), 1800);\n return () => window.clearTimeout(timer);\n }, [copied]);\n\n const copy = (text: string) => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) return;\n void navigator.clipboard.writeText(text).then(\n () => setCopied(true),\n () => setCopied(false)\n );\n };\n\n return [copied, copy];\n};\n\nconst CopyButton: FC<{ value: string; label?: string; className?: string }> = ({\n value,\n label,\n className,\n}) => {\n const [copied, copy] = useCopy();\n return (\n <button\n type=\"button\"\n onClick={() => copy(value)}\n className={cn(\n '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',\n className\n )}\n aria-label={copied ? 'Copied' : `Copy ${label ?? 'to clipboard'}`}\n >\n {copied ? (\n <Check className=\"h-3.5 w-3.5 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n )}\n {copied ? 'Copied' : (label ?? 'Copy')}\n </button>\n );\n};\n\n// ============================================================================\n// Tokenized code block (semantic tokens only — no raw colours)\n// ============================================================================\n\n/**\n * Lightweight, allocation-safe syntax highlighter. A single ordered alternation\n * classifies comments, strings, numbers and a small keyword set (GraphQL / shell\n * / JS) and maps each class onto a fe-libs semantic colour token. Everything else\n * renders as `text-text-primary`, so the block degrades gracefully for any input.\n */\nconst HIGHLIGHT_RE =\n /(#[^\\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;\n\nconst highlightCode = (code: string): ReactNode[] => {\n const nodes: ReactNode[] = [];\n let lastIndex = 0;\n let key = 0;\n for (const match of code.matchAll(HIGHLIGHT_RE)) {\n const index = match.index ?? 0;\n if (index > lastIndex) {\n nodes.push(<span key={key++}>{code.slice(lastIndex, index)}</span>);\n }\n const [full, comment, str, num, keyword] = match;\n if (comment) {\n nodes.push(\n <span key={key++} className=\"text-text-muted italic\">\n {full}\n </span>\n );\n } else if (str) {\n nodes.push(\n <span key={key++} className=\"text-status-success-text\">\n {full}\n </span>\n );\n } else if (num) {\n nodes.push(\n <span key={key++} className=\"text-status-warning-text\">\n {full}\n </span>\n );\n } else if (keyword) {\n nodes.push(\n <span key={key++} className=\"text-status-info-text font-medium\">\n {full}\n </span>\n );\n } else {\n nodes.push(<span key={key++}>{full}</span>);\n }\n lastIndex = index + full.length;\n }\n if (lastIndex < code.length) {\n nodes.push(<span key={key++}>{code.slice(lastIndex)}</span>);\n }\n return nodes;\n};\n\nconst CodeBlock: FC<{ code: string; language: string; className?: string }> = ({\n code,\n language,\n className,\n}) => (\n <div\n className={cn('overflow-hidden rounded-xl border border-border-seam bg-bg-sunken', className)}\n >\n <div className=\"flex items-center justify-between border-b border-border-seam bg-bg-surface px-3 py-1.5\">\n <span className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-text-muted\">\n <Terminal className=\"h-3 w-3\" aria-hidden=\"true\" />\n {language}\n </span>\n <CopyButton value={code} label=\"Copy\" />\n </div>\n <div className=\"overflow-x-auto\">\n <pre className=\"min-w-0 px-4 py-3 text-[13px] leading-relaxed\">\n <code className=\"font-mono text-text-primary\">{highlightCode(code)}</code>\n </pre>\n </div>\n </div>\n);\n\n// ============================================================================\n// Small presentational helpers\n// ============================================================================\n\nconst SectionHeading: FC<{ icon: typeof Rocket; title: string; children?: ReactNode }> = ({\n icon: Icon,\n title,\n children,\n}) => (\n <div className=\"mb-4\">\n <h2 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n {title}\n </h2>\n {children && <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">{children}</p>}\n </div>\n);\n\nconst InlineCode: FC<{ children: ReactNode }> = ({ children }) => (\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-[0.85em] text-text-primary\">\n {children}\n </code>\n);\n\n// ============================================================================\n// Getting Started\n// ============================================================================\n\nconst GETTING_STARTED_STEPS: { title: string; body: ReactNode }[] = [\n {\n title: 'Create an API key',\n body: (\n <>\n Head to the <strong>API Keys</strong> tab and mint a key for your registered app. The secret\n is shown once at creation — copy it somewhere safe.\n </>\n ),\n },\n {\n title: 'Point at the GraphQL endpoint',\n body: (\n <>\n All HookMovies data is served from a single GraphQL API. The endpoint is shown in the{' '}\n <strong>GraphQL API</strong> tab.\n </>\n ),\n },\n {\n title: 'Authenticate every request',\n body: (\n <>\n Send your key as a bearer token in the <InlineCode>Authorization</InlineCode> header:{' '}\n <InlineCode>Authorization: Bearer &lt;apiSecret&gt;</InlineCode>.\n </>\n ),\n },\n {\n title: 'Make your first query',\n body: (\n <>\n Call <InlineCode>hokListTitles</InlineCode> to page through the catalog, then drill into a\n title with <InlineCode>hokTitle(id)</InlineCode>.\n </>\n ),\n },\n];\n\nconst GettingStartedSection: FC<{ endpoint: string }> = ({ endpoint }) => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Rocket} title=\"Getting Started\">\n Build with the HookMovies platform API — a single GraphQL gateway that powers the catalog,\n productions, auditions, entitlements and playback surfaces you see in this app.\n </SectionHeading>\n\n <ol className=\"grid gap-4 sm:grid-cols-2\">\n {GETTING_STARTED_STEPS.map((step, i) => (\n <li key={step.title}>\n <GlassCard treatment=\"glass\" className=\"h-full border-border-seam p-4\">\n <div className=\"flex items-start gap-3\">\n <span 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\">\n {i + 1}\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary\">{step.title}</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">{step.body}</p>\n </div>\n </div>\n </GlassCard>\n </li>\n ))}\n </ol>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <BookOpen className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Base endpoint\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Every request targets the workspace GraphQL gateway:\n </p>\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <code 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\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </GlassCard>\n </div>\n);\n\n// ============================================================================\n// GraphQL API\n// ============================================================================\n\nconst SAMPLE_QUERY = `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}`;\n\nconst SAMPLE_RESPONSE = `{\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}`;\n\nconst GraphqlSection: FC<{ endpoint: string }> = ({ endpoint }) => {\n const curl = `curl -X POST '${endpoint}' \\\\\n -H 'Content-Type: application/json' \\\\\n -H 'Authorization: Bearer <apiSecret>' \\\\\n -d '{\"query\": \"query { hokListTitles(pagination: { limit: 5 }) { items { id title } totalCount } }\"}'`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Code2} title=\"GraphQL API\">\n One GraphQL endpoint serves the entire HookMovies domain. Send POST requests with your API\n key as a bearer token.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Endpoint</h3>\n <div className=\"flex flex-wrap items-center gap-2\">\n <code 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\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample query</h3>\n <CodeBlock language=\"graphql\" code={SAMPLE_QUERY} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample response</h3>\n <CodeBlock language=\"json\" code={SAMPLE_RESPONSE} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Run it with cURL</h3>\n <CodeBlock language=\"bash\" code={curl} />\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// API Keys — reuses global-auth via the shared data-layer hooks\n// ============================================================================\n\nconst SecretReveal: FC<{ result: ApiKeyResult; onDismiss: () => void }> = ({\n result,\n onDismiss,\n}) => (\n <GlassCard\n treatment=\"glass\"\n className=\"mb-6 border-status-warning-border bg-status-warning-bg-subtle p-5\"\n >\n <div className=\"flex items-start gap-3\">\n <ShieldAlert\n className=\"mt-0.5 h-5 w-5 shrink-0 text-status-warning-text\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Save your API secret now</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n This secret is shown only once and cannot be retrieved again. Store it somewhere safe\n before dismissing this panel.\n </p>\n\n <dl className=\"mt-4 space-y-3\">\n {(\n [\n { label: 'API key', value: result.apiKey },\n { label: 'API secret', value: result.apiSecret },\n { label: 'Key ID', value: result.keyId },\n ] as const\n ).map((row) => (\n <div key={row.label}>\n <dt className=\"mb-1 text-xs font-medium text-text-secondary\">{row.label}</dt>\n <dd className=\"flex flex-wrap items-center gap-2\">\n <code 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\">\n {row.value}\n </code>\n <CopyButton value={row.value} label=\"Copy\" />\n </dd>\n </div>\n ))}\n </dl>\n\n <button\n type=\"button\"\n onClick={onDismiss}\n 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\"\n >\n I've saved it — dismiss\n </button>\n </div>\n </div>\n </GlassCard>\n);\n\nconst fieldClass =\n '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)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nconst GenerateKeyForm: FC<{\n onGenerated: (result: ApiKeyResult) => void;\n onClose: () => void;\n}> = ({ onGenerated, onClose }) => {\n const generate = useGenerateApiKey();\n const [name, setName] = useState('');\n const [appId, setAppId] = useState('');\n const [scopes, setScopes] = useState<string[]>(['read']);\n const [expiresAt, setExpiresAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const toggleScope = (value: string) => {\n setScopes((prev) =>\n prev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value]\n );\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Give the key a recognisable name.');\n return;\n }\n if (!appId.trim()) {\n setError('Enter the App ID this key will act on behalf of.');\n return;\n }\n if (scopes.length === 0) {\n setError('Select at least one scope.');\n return;\n }\n try {\n const result = await generate.mutateAsync({\n appId: appId.trim(),\n name: name.trim(),\n scope: scopes,\n expiresAt: expiresAt ? new Date(expiresAt).toISOString() : undefined,\n });\n onGenerated(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to generate the API key.');\n }\n };\n\n return (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Generate a new API key\n </h3>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"key-name\" className={labelClass}>\n Key name *\n </label>\n <input\n id=\"key-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Production ingest\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"key-app-id\" className={labelClass}>\n App ID *\n </label>\n <input\n id=\"key-app-id\"\n value={appId}\n onChange={(e) => setAppId(e.target.value)}\n placeholder=\"Your registered app's ID\"\n className={fieldClass}\n required\n />\n <p className=\"mt-1 text-[11px] text-text-muted\">\n The client app this key acts on behalf of — find it in your workspace app settings.\n </p>\n </div>\n </div>\n\n <fieldset>\n <legend className={labelClass}>Scopes *</legend>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n {SCOPE_OPTIONS.map((scope) => {\n const checked = scopes.includes(scope.value);\n return (\n <label\n key={scope.value}\n className={cn(\n 'flex cursor-pointer items-start gap-2 rounded-lg border p-3 text-sm transition-colors',\n checked\n ? 'border-action-primary-bg bg-action-primary-bg/10'\n : 'border-border-subtle hover:border-border-strong'\n )}\n >\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={() => toggleScope(scope.value)}\n className=\"mt-0.5 accent-[var(--color-action-primary-bg)]\"\n />\n <span className=\"min-w-0\">\n <span className=\"block font-medium text-text-primary\">{scope.label}</span>\n <span className=\"block text-xs text-text-secondary\">{scope.description}</span>\n </span>\n </label>\n );\n })}\n </div>\n </fieldset>\n\n <div>\n <label htmlFor=\"key-expires\" className={labelClass}>\n Expires (optional)\n </label>\n <input\n id=\"key-expires\"\n type=\"date\"\n value={expiresAt}\n onChange={(e) => setExpiresAt(e.target.value)}\n className={cn(fieldClass, 'sm:max-w-xs')}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"submit\"\n disabled={generate.isPending}\n 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\"\n >\n {generate.isPending ? 'Generating…' : 'Generate key'}\n </button>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n </form>\n </GlassCard>\n );\n};\n\nconst ApiKeyRow: FC<{ apiKey: ApiKey }> = ({ apiKey }) => {\n const revoke = useRevokeApiKey();\n const [confirming, setConfirming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleRevoke = async () => {\n setError(null);\n try {\n await revoke.mutateAsync(apiKey.id);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to revoke this key.');\n setConfirming(false);\n }\n };\n\n return (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-text-primary\">{apiKey.name}</span>\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-xs text-text-secondary\">\n {apiKey.prefix}…\n </code>\n </div>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-muted\">\n {apiKey.appName && <span>{apiKey.appName}</span>}\n <span aria-hidden=\"true\">·</span>\n <span>\n Created {formatDate(apiKey.createdAt, { hour: undefined, minute: undefined })}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>\n {apiKey.lastUsedAt\n ? `Last used ${formatRelativeTime(apiKey.lastUsedAt)}`\n : 'Never used'}\n </span>\n {apiKey.expiresAt && (\n <>\n <span aria-hidden=\"true\">·</span>\n <span>\n Expires {formatDate(apiKey.expiresAt, { hour: undefined, minute: undefined })}\n </span>\n </>\n )}\n </div>\n {apiKey.scopes.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {apiKey.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-text-secondary\"\n >\n {scope}\n </span>\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0\">\n {confirming ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={revoke.isPending}\n onClick={() => void handleRevoke()}\n 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\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {revoke.isPending ? 'Revoking…' : 'Confirm revoke'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n 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\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n 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\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Revoke\n </button>\n )}\n </div>\n </div>\n {error && <p className=\"mt-2 text-xs text-status-error-text\">{error}</p>}\n </GlassCard>\n );\n};\n\nconst ApiKeysSection: FC = () => {\n const { data, isLoading, isError, refetch } = useMyApiKeys();\n const [showForm, setShowForm] = useState(false);\n const [revealed, setRevealed] = useState<ApiKeyResult | null>(null);\n\n const keys = data?.apiKeys ?? [];\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <SectionHeading icon={KeyRound} title=\"API Keys\">\n Keys authenticate server-to-server calls to the HookMovies GraphQL gateway. They are\n issued and revoked through the platform identity service — HookMovies never stores your\n secret.\n </SectionHeading>\n <button\n type=\"button\"\n onClick={() => setShowForm((v) => !v)}\n 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\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n New key\n </button>\n </div>\n\n {revealed && <SecretReveal result={revealed} onDismiss={() => setRevealed(null)} />}\n\n {showForm && (\n <GenerateKeyForm\n onGenerated={(result) => {\n setRevealed(result);\n setShowForm(false);\n }}\n onClose={() => setShowForm(false)}\n />\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading your API keys…\" />\n ) : isError ? (\n <ErrorState message=\"We couldn't load your API keys.\" onRetry={() => void refetch()} />\n ) : keys.length === 0 ? (\n <EmptyState\n icon={<KeyRound className=\"h-12 w-12\" />}\n title=\"No API keys yet\"\n description=\"Generate your first key to start calling the HookMovies API.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowForm(true)}\n 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\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Generate key\n </button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {keys.map((apiKey) => (\n <ApiKeyRow key={apiKey.id} apiKey={apiKey} />\n ))}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// SDKs & MCP\n// ============================================================================\n\ninterface PackageCard {\n name: string;\n repo: string;\n description: string;\n icon: typeof Package;\n install: { language: string; code: string };\n}\n\nconst PACKAGES: PackageCard[] = [\n {\n name: 'Node SDK',\n repo: 'hookmovies-sdk-node',\n description:\n 'Typed TypeScript client for the HookMovies GraphQL API — catalog, titles, reviews and playback.',\n icon: Boxes,\n install: {\n language: 'bash',\n code: 'bun add hookmovies-sdk-node\\n# or: npm install hookmovies-sdk-node',\n },\n },\n {\n name: 'Python SDK',\n repo: 'hookmovies-sdk-python',\n description: 'Pythonic client with async support for backend integrations and data pipelines.',\n icon: Package,\n install: { language: 'bash', code: 'pip install hookmovies-sdk-python' },\n },\n {\n name: 'MCP Server',\n repo: 'hookmovies-mcp',\n description:\n 'Model Context Protocol server that exposes HookMovies tools to AI agents and assistants.',\n icon: Boxes,\n install: { language: 'bash', code: 'bunx hookmovies-mcp --help' },\n },\n {\n name: 'CLI',\n repo: 'hookmovies-cli',\n description:\n 'Command-line tool to manage titles, productions and rights from your terminal or CI.',\n icon: Terminal,\n install: { language: 'bash', code: 'bunx hookmovies-cli titles list' },\n },\n];\n\nconst SdksSection: FC = () => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Package} title=\"SDKs & MCP\">\n Official HookMovies packages wrap the GraphQL API so you can build in your language of choice\n — or plug the platform straight into an AI agent via MCP.\n </SectionHeading>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {PACKAGES.map((pkg) => {\n const Icon = pkg.icon;\n const repoUrl = `https://github.com/algoshred/${pkg.repo}`;\n return (\n <GlassCard\n key={pkg.repo}\n treatment=\"glass\"\n className=\"flex h-full flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n </span>\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">{pkg.name}</h3>\n <code className=\"text-[11px] text-text-muted\">{pkg.repo}</code>\n </div>\n </div>\n <a\n href={repoUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n 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\"\n >\n Repo\n <ExternalLink className=\"h-3 w-3\" aria-hidden=\"true\" />\n </a>\n </div>\n <p className=\"mt-3 flex-1 text-sm text-text-secondary\">{pkg.description}</p>\n <div className=\"mt-3\">\n <CodeBlock language={pkg.install.language} code={pkg.install.code} />\n </div>\n </GlassCard>\n );\n })}\n </div>\n </div>\n);\n\n// ============================================================================\n// Embeddable Player\n// ============================================================================\n\nconst EmbedSection: FC<{ watchUrl: string }> = ({ watchUrl }) => {\n const iframeSnippet = `<iframe\n src=\"${watchUrl}\"\n width=\"100%\"\n height=\"480\"\n frameborder=\"0\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n></iframe>`;\n\n const responsiveSnippet = `<div style=\"position: relative; padding-top: 56.25%;\">\n <iframe\n src=\"${watchUrl}\"\n style=\"position: absolute; inset: 0; width: 100%; height: 100%; border: 0;\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n ></iframe>\n</div>`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Clapperboard} title=\"Embeddable Player\">\n Drop a HookMovies title into any web page with a single{' '}\n <InlineCode>&lt;iframe&gt;</InlineCode>. Replace <InlineCode>YOUR_TITLE_ID</InlineCode> with\n the id of any published title.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Basic embed</h3>\n <CodeBlock language=\"html\" code={iframeSnippet} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Responsive (16:9) embed</h3>\n <p className=\"mb-2 text-sm text-text-secondary\">\n Wrap the iframe in an aspect-ratio box so the player scales fluidly on any device.\n </p>\n <CodeBlock language=\"html\" code={responsiveSnippet} />\n </div>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <p className=\"text-sm text-text-secondary\">\n The embedded player respects the viewer's entitlements and profile preferences. Viewers\n who aren't entitled to a title are prompted to sign in or purchase access inside the\n frame.\n </p>\n </GlassCard>\n </div>\n );\n};\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport const DevPortalPage: FC = () => {\n const { apiGatewayUrl, getPath } = useHookMovies();\n const [active, setActive] = useState<SectionId>('overview');\n\n const endpoint = apiGatewayUrl || 'https://graphqlworkspaces.burdenoff.com/workspaces/graphql';\n\n const watchUrl = useMemo(() => {\n const origin =\n typeof window !== 'undefined' ? window.location.origin : 'https://app.hookmovies.com';\n return `${origin}${getPath('watch/YOUR_TITLE_ID')}`;\n }, [getPath]);\n\n return (\n <PageLayout\n title=\"Developer Portal\"\n description=\"Build on the HookMovies platform API\"\n purpose=\"Everything you need to integrate with HookMovies — authenticate with an API key, query the 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.\"\n >\n {/* Section navigation — horizontal, scrollable on small screens */}\n <div\n className=\"mb-6 flex gap-1 overflow-x-auto rounded-xl border border-border-seam bg-bg-surface p-1\"\n role=\"tablist\"\n aria-label=\"Developer portal sections\"\n >\n {SECTIONS.map((section) => {\n const Icon = section.icon;\n const selected = active === section.id;\n return (\n <button\n key={section.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={selected}\n onClick={() => setActive(section.id)}\n className={cn(\n 'inline-flex shrink-0 items-center gap-2 rounded-lg px-3.5 py-2 text-sm font-medium transition-colors',\n selected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <Icon className=\"h-4 w-4\" aria-hidden=\"true\" />\n {section.label}\n </button>\n );\n })}\n </div>\n\n <div className=\"max-w-4xl\">\n {active === 'overview' && <GettingStartedSection endpoint={endpoint} />}\n {active === 'graphql' && <GraphqlSection endpoint={endpoint} />}\n {active === 'keys' && <ApiKeysSection />}\n {active === 'sdks' && <SdksSection />}\n {active === 'embed' && <EmbedSection watchUrl={watchUrl} />}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { ErrorState } from '../hookmovies/components/StateViews';
3
+
4
+ declare const meta: Meta<typeof ErrorState>;
5
+ export default meta;
6
+ type Story = StoryObj<typeof meta>;
7
+ export declare const Default: Story;
8
+ export declare const WithMessage: Story;
9
+ export declare const WithRetry: Story;
10
+ //# sourceMappingURL=ErrorState.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorState.stories.d.ts","sourceRoot":"","sources":["../../src/stories/ErrorState.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAEjE,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,UAAU,CASjC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAKzB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAMvB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { LoadingState } from '../hookmovies/components/StateViews';
3
+
4
+ declare const meta: Meta<typeof LoadingState>;
5
+ export default meta;
6
+ type Story = StoryObj<typeof meta>;
7
+ export declare const Default: Story;
8
+ export declare const CustomLabel: Story;
9
+ //# sourceMappingURL=LoadingState.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoadingState.stories.d.ts","sourceRoot":"","sources":["../../src/stories/LoadingState.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAEnE,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAMnC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAIzB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { Pagination } from '../hookmovies/components/Pagination';
3
+
4
+ declare const meta: Meta<typeof Pagination>;
5
+ export default meta;
6
+ type Story = StoryObj<typeof meta>;
7
+ export declare const FirstPage: Story;
8
+ export declare const MiddlePage: Story;
9
+ export declare const LastPage: Story;
10
+ //# sourceMappingURL=Pagination.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.stories.d.ts","sourceRoot":"","sources":["../../src/stories/Pagination.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAEjE,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,UAAU,CASjC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,SAAS,EAAE,KAOvB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAOxB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC"}
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Page context gathering — captures the user's current browser context
3
3
  * to be sent alongside every assistant prompt for better AI awareness.
4
+ *
5
+ * The route table below is kept in lockstep with the real routes declared
6
+ * in `../hookmovies/HookMoviesRoutes.tsx` (mounted under the `/hookmovies`
7
+ * base path by `hookmovies-app/src/routes/AppRoutes.tsx`). When routes
8
+ * change there, update the corresponding entry here in the same PR.
4
9
  */
5
10
  interface ConversationContextShape {
6
11
  entityType?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"pageContext.d.ts","sourceRoot":"","sources":["../../src/utils/pageContext.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,UAAU,wBAAwB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAoB/C;AAMD,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,wBAAwB,CAyC1B"}
1
+ {"version":3,"file":"pageContext.d.ts","sourceRoot":"","sources":["../../src/utils/pageContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,UAAU,wBAAwB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAoB/C;AAsGD,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,wBAAwB,CAoC1B"}
@@ -13,27 +13,154 @@ function e() {
13
13
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
14
14
  };
15
15
  }
16
- function t(e, t) {
17
- return t.match(e);
18
- }
16
+ var t = [
17
+ {
18
+ pattern: /^\/hookmovies\/catalog\/titles\/new\/?$/,
19
+ entityType: "HOOKMOVIES_TITLE_CREATE"
20
+ },
21
+ {
22
+ pattern: /^\/hookmovies\/productions\/new\/?$/,
23
+ entityType: "HOOKMOVIES_PRODUCTION_CREATE"
24
+ },
25
+ {
26
+ pattern: /^\/hookmovies\/auditions\/new\/?$/,
27
+ entityType: "HOOKMOVIES_AUDITION_CREATE"
28
+ },
29
+ {
30
+ pattern: /^\/hookmovies\/catalog\/titles\/([^/]+)\/?$/,
31
+ entityType: "HOOKMOVIES_TITLE"
32
+ },
33
+ {
34
+ pattern: /^\/hookmovies\/catalog\/titles\/?$/,
35
+ entityType: "HOOKMOVIES_TITLE_LIST"
36
+ },
37
+ {
38
+ pattern: /^\/hookmovies\/catalog\/studios\/?$/,
39
+ entityType: "HOOKMOVIES_STUDIO_LIST"
40
+ },
41
+ {
42
+ pattern: /^\/hookmovies\/catalog\/people\/?$/,
43
+ entityType: "HOOKMOVIES_PERSON_LIST"
44
+ },
45
+ {
46
+ pattern: /^\/hookmovies\/productions\/([^/]+)\/?$/,
47
+ entityType: "HOOKMOVIES_PRODUCTION"
48
+ },
49
+ {
50
+ pattern: /^\/hookmovies\/productions\/?$/,
51
+ entityType: "HOOKMOVIES_PRODUCTION_LIST"
52
+ },
53
+ {
54
+ pattern: /^\/hookmovies\/auditions\/([^/]+)\/?$/,
55
+ entityType: "HOOKMOVIES_AUDITION"
56
+ },
57
+ {
58
+ pattern: /^\/hookmovies\/auditions\/?$/,
59
+ entityType: "HOOKMOVIES_AUDITION_LIST"
60
+ },
61
+ {
62
+ pattern: /^\/hookmovies\/engagement\/reviews\/?$/,
63
+ entityType: "HOOKMOVIES_REVIEWS"
64
+ },
65
+ {
66
+ pattern: /^\/hookmovies\/distribution\/rights\/?$/,
67
+ entityType: "HOOKMOVIES_RIGHTS_WINDOW_LIST"
68
+ },
69
+ {
70
+ pattern: /^\/hookmovies\/library\/lists\/?$/,
71
+ entityType: "HOOKMOVIES_LIBRARY_LISTS"
72
+ },
73
+ {
74
+ pattern: /^\/hookmovies\/library\/entitlements\/?$/,
75
+ entityType: "HOOKMOVIES_LIBRARY_ENTITLEMENTS"
76
+ },
77
+ {
78
+ pattern: /^\/hookmovies\/browse\/?$/,
79
+ entityType: "HOOKMOVIES_DISCOVERY"
80
+ },
81
+ {
82
+ pattern: /^\/hookmovies\/title\/([^/]+)\/localization\/?$/,
83
+ entityType: "HOOKMOVIES_TITLE_LOCALIZATION"
84
+ },
85
+ {
86
+ pattern: /^\/hookmovies\/title\/([^/]+)\/?$/,
87
+ entityType: "HOOKMOVIES_TITLE"
88
+ },
89
+ {
90
+ pattern: /^\/hookmovies\/watch\/([^/]+)\/episode\/([^/]+)\/?$/,
91
+ entityType: "HOOKMOVIES_PLAYBACK",
92
+ metadataFromMatch: (e) => ({ episodeId: e[2] })
93
+ },
94
+ {
95
+ pattern: /^\/hookmovies\/watch\/([^/]+)\/?$/,
96
+ entityType: "HOOKMOVIES_PLAYBACK"
97
+ },
98
+ {
99
+ pattern: /^\/hookmovies\/hooks\/?$/,
100
+ entityType: "HOOKMOVIES_HOOKS_FEED"
101
+ },
102
+ {
103
+ pattern: /^\/hookmovies\/profiles\/?$/,
104
+ entityType: "HOOKMOVIES_PROFILES"
105
+ },
106
+ {
107
+ pattern: /^\/hookmovies\/watch-party\/([^/]+)\/?$/,
108
+ entityType: "HOOKMOVIES_WATCH_PARTY"
109
+ },
110
+ {
111
+ pattern: /^\/hookmovies\/watch-party\/?$/,
112
+ entityType: "HOOKMOVIES_WATCH_PARTY_LIST"
113
+ },
114
+ {
115
+ pattern: /^\/hookmovies\/checkout\/([^/]+)\/?$/,
116
+ entityType: "HOOKMOVIES_CHECKOUT"
117
+ },
118
+ {
119
+ pattern: /^\/hookmovies\/payouts\/?$/,
120
+ entityType: "HOOKMOVIES_PAYOUTS"
121
+ },
122
+ {
123
+ pattern: /^\/hookmovies\/analytics\/?$/,
124
+ entityType: "HOOKMOVIES_ANALYTICS"
125
+ },
126
+ {
127
+ pattern: /^\/hookmovies\/trust\/moderation\/?$/,
128
+ entityType: "HOOKMOVIES_MODERATION"
129
+ },
130
+ {
131
+ pattern: /^\/hookmovies\/onboarding\/languages\/?$/,
132
+ entityType: "HOOKMOVIES_ONBOARDING"
133
+ },
134
+ {
135
+ pattern: /^\/hookmovies\/developer\/?$/,
136
+ entityType: "HOOKMOVIES_DEVELOPER_PORTAL"
137
+ },
138
+ {
139
+ pattern: /^\/hookmovies\/live\/?$/,
140
+ entityType: "HOOKMOVIES_LIVE"
141
+ },
142
+ {
143
+ pattern: /^\/hookmovies\/concierge\/?$/,
144
+ entityType: "HOOKMOVIES_CONCIERGE"
145
+ },
146
+ {
147
+ pattern: /^\/hookmovies\/?$/,
148
+ entityType: "HOOKMOVIES_HOME"
149
+ }
150
+ ];
19
151
  function n(e, n) {
20
152
  let r = new URLSearchParams(n);
21
- for (let [n, r] of [
22
- [/^\/hookmovies\/catalog\/titles\/([^/]+)$/, "HOOKMOVIES_TITLE"],
23
- [/^\/hookmovies\/catalog\/studios\/([^/]+)$/, "HOOKMOVIES_STUDIO"],
24
- [/^\/hookmovies\/catalog\/people\/([^/]+)$/, "HOOKMOVIES_PERSON"],
25
- [/^\/hookmovies\/productions\/([^/]+)$/, "HOOKMOVIES_PRODUCTION"],
26
- [/^\/hookmovies\/auditions\/([^/]+)$/, "HOOKMOVIES_AUDITION"],
27
- [/^\/hookmovies\/distribution\/rights\/([^/]+)$/, "HOOKMOVIES_RIGHTS_WINDOW"],
28
- [/^\/hookmovies\/live\/([^/]+)$/, "HOOKMOVIES_LIVE_EVENT"],
29
- [/^\/hookmovies\/social\/profiles\/([^/]+)$/, "HOOKMOVIES_PROFILE"],
30
- [/^\/hookmovies\/social\/watch-parties\/([^/]+)$/, "HOOKMOVIES_WATCH_PARTY"]
31
- ]) {
32
- let i = t(n, e);
33
- if (i) return {
34
- entityType: r,
35
- entityId: i[1],
36
- metadata: { routePattern: n.source }
153
+ for (let n of t) {
154
+ let t = e.match(n.pattern);
155
+ if (!t) continue;
156
+ let r = t[1], i = n.metadataFromMatch?.(t) ?? {};
157
+ return {
158
+ entityType: n.entityType,
159
+ entityId: r,
160
+ metadata: {
161
+ routePattern: n.pattern.source,
162
+ ...i
163
+ }
37
164
  };
38
165
  }
39
166
  let i = r.get("tab") ?? (() => {
@@ -1 +1 @@
1
- {"version":3,"file":"pageContext.js","names":[],"sources":["../../src/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering — captures the user's current browser context\n * to be sent alongside every assistant prompt for better AI awareness.\n */\n\ninterface ConversationContextShape {\n entityType?: string;\n entityId?: string;\n activeTab?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface PageContext {\n currentUrl: string;\n currentPage: string;\n entityType?: string;\n entityId?: string;\n platform: string;\n version: string;\n viewportSize: string;\n userAgent: string;\n timestamp: string;\n}\n\nexport function gatherPageContext(): PageContext {\n const url = window.location.href;\n const pathSegments = window.location.pathname.split('/').filter(Boolean);\n const currentPage = pathSegments.slice(0, 3).join('/') || 'home';\n const entity = resolveHookMoviesConversationContext(\n window.location.pathname,\n window.location.search\n );\n\n return {\n currentUrl: url,\n currentPage,\n entityType: entity.entityType,\n entityId: entity.entityId,\n platform: 'hookmovies',\n version: import.meta.env.VITE_APP_VERSION ?? '2026.615.1',\n viewportSize: `${window.innerWidth}x${window.innerHeight}`,\n userAgent: navigator.userAgent,\n timestamp: new Date().toISOString(),\n };\n}\n\nfunction matchPath(pattern: RegExp, pathname: string): RegExpMatchArray | null {\n return pathname.match(pattern);\n}\n\nexport function resolveHookMoviesConversationContext(\n pathname: string,\n search: string\n): ConversationContextShape {\n const params = new URLSearchParams(search);\n const directMatches: Array<[RegExp, string]> = [\n [/^\\/hookmovies\\/catalog\\/titles\\/([^/]+)$/, 'HOOKMOVIES_TITLE'],\n [/^\\/hookmovies\\/catalog\\/studios\\/([^/]+)$/, 'HOOKMOVIES_STUDIO'],\n [/^\\/hookmovies\\/catalog\\/people\\/([^/]+)$/, 'HOOKMOVIES_PERSON'],\n [/^\\/hookmovies\\/productions\\/([^/]+)$/, 'HOOKMOVIES_PRODUCTION'],\n [/^\\/hookmovies\\/auditions\\/([^/]+)$/, 'HOOKMOVIES_AUDITION'],\n [/^\\/hookmovies\\/distribution\\/rights\\/([^/]+)$/, 'HOOKMOVIES_RIGHTS_WINDOW'],\n [/^\\/hookmovies\\/live\\/([^/]+)$/, 'HOOKMOVIES_LIVE_EVENT'],\n [/^\\/hookmovies\\/social\\/profiles\\/([^/]+)$/, 'HOOKMOVIES_PROFILE'],\n [/^\\/hookmovies\\/social\\/watch-parties\\/([^/]+)$/, 'HOOKMOVIES_WATCH_PARTY'],\n ];\n\n for (const [pattern, entityType] of directMatches) {\n const match = matchPath(pattern, pathname);\n if (match) {\n return {\n entityType,\n entityId: match[1],\n metadata: {\n routePattern: pattern.source,\n },\n };\n }\n }\n\n const activeTab =\n params.get('tab') ??\n (() => {\n const segments = pathname.split('/').filter(Boolean);\n return segments.length > 2 ? segments[segments.length - 1] : undefined;\n })();\n\n return {\n activeTab: activeTab ?? undefined,\n metadata: {\n ...(activeTab ? { activeTab } : {}),\n ...(params.get('selectedId') ? { selectedId: params.get('selectedId') } : {}),\n },\n };\n}\n"],"mappings":";AAwBA,SAAgB,IAAiC;CAC/C,IAAM,IAAM,OAAO,SAAS,MAEtB,IADe,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ,CACvC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,IAAI,QACpD,IAAS,EACb,OAAO,SAAS,UAChB,OAAO,SAAS,OACjB;AAED,QAAO;EACL,YAAY;EACZ;EACA,YAAY,EAAO;EACnB,UAAU,EAAO;EACjB,UAAU;EACV,SAA6C;EAC7C,cAAc,GAAG,OAAO,WAAW,GAAG,OAAO;EAC7C,WAAW,UAAU;EACrB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACpC;;AAGH,SAAS,EAAU,GAAiB,GAA2C;AAC7E,QAAO,EAAS,MAAM,EAAQ;;AAGhC,SAAgB,EACd,GACA,GAC0B;CAC1B,IAAM,IAAS,IAAI,gBAAgB,EAAO;AAa1C,MAAK,IAAM,CAAC,GAAS,MAZ0B;EAC7C,CAAC,4CAA4C,mBAAmB;EAChE,CAAC,6CAA6C,oBAAoB;EAClE,CAAC,4CAA4C,oBAAoB;EACjE,CAAC,wCAAwC,wBAAwB;EACjE,CAAC,sCAAsC,sBAAsB;EAC7D,CAAC,iDAAiD,2BAA2B;EAC7E,CAAC,iCAAiC,wBAAwB;EAC1D,CAAC,6CAA6C,qBAAqB;EACnE,CAAC,kDAAkD,yBAAyB;EAC7E,EAEkD;EACjD,IAAM,IAAQ,EAAU,GAAS,EAAS;AAC1C,MAAI,EACF,QAAO;GACL;GACA,UAAU,EAAM;GAChB,UAAU,EACR,cAAc,EAAQ,QACvB;GACF;;CAIL,IAAM,IACJ,EAAO,IAAI,MAAM,WACV;EACL,IAAM,IAAW,EAAS,MAAM,IAAI,CAAC,OAAO,QAAQ;AACpD,SAAO,EAAS,SAAS,IAAI,EAAS,EAAS,SAAS,KAAK,KAAA;KAC3D;AAEN,QAAO;EACL,WAAW,KAAa,KAAA;EACxB,UAAU;GACR,GAAI,IAAY,EAAE,cAAW,GAAG,EAAE;GAClC,GAAI,EAAO,IAAI,aAAa,GAAG,EAAE,YAAY,EAAO,IAAI,aAAa,EAAE,GAAG,EAAE;GAC7E;EACF"}
1
+ {"version":3,"file":"pageContext.js","names":[],"sources":["../../src/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering — captures the user's current browser context\n * to be sent alongside every assistant prompt for better AI awareness.\n *\n * The route table below is kept in lockstep with the real routes declared\n * in `../hookmovies/HookMoviesRoutes.tsx` (mounted under the `/hookmovies`\n * base path by `hookmovies-app/src/routes/AppRoutes.tsx`). When routes\n * change there, update the corresponding entry here in the same PR.\n */\n\ninterface ConversationContextShape {\n entityType?: string;\n entityId?: string;\n activeTab?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface PageContext {\n currentUrl: string;\n currentPage: string;\n entityType?: string;\n entityId?: string;\n platform: string;\n version: string;\n viewportSize: string;\n userAgent: string;\n timestamp: string;\n}\n\nexport function gatherPageContext(): PageContext {\n const url = window.location.href;\n const pathSegments = window.location.pathname.split('/').filter(Boolean);\n const currentPage = pathSegments.slice(0, 3).join('/') || 'home';\n const entity = resolveHookMoviesConversationContext(\n window.location.pathname,\n window.location.search\n );\n\n return {\n currentUrl: url,\n currentPage,\n entityType: entity.entityType,\n entityId: entity.entityId,\n platform: 'hookmovies',\n version: import.meta.env.VITE_APP_VERSION ?? '2026.615.1',\n viewportSize: `${window.innerWidth}x${window.innerHeight}`,\n userAgent: navigator.userAgent,\n timestamp: new Date().toISOString(),\n };\n}\n\n/**\n * One entry per real route in HookMoviesRoutes.tsx. Order matters: more\n * specific patterns (e.g. a `/new` create form, or a route with extra\n * trailing segments) MUST be listed before the more general pattern they'd\n * otherwise be swallowed by (e.g. `title/:id/localization` before\n * `title/:id`, or `catalog/titles/new` before `catalog/titles/:id`).\n */\ninterface RouteContextRule {\n pattern: RegExp;\n entityType: string;\n /** Extra metadata derived from the regex match (e.g. secondary IDs). */\n metadataFromMatch?: (match: RegExpMatchArray) => Record<string, unknown>;\n}\n\nconst ROUTE_CONTEXT_RULES: RouteContextRule[] = [\n // Catalog — create forms (must precede the :id rules below)\n { pattern: /^\\/hookmovies\\/catalog\\/titles\\/new\\/?$/, entityType: 'HOOKMOVIES_TITLE_CREATE' },\n {\n pattern: /^\\/hookmovies\\/productions\\/new\\/?$/,\n entityType: 'HOOKMOVIES_PRODUCTION_CREATE',\n },\n { pattern: /^\\/hookmovies\\/auditions\\/new\\/?$/, entityType: 'HOOKMOVIES_AUDITION_CREATE' },\n\n // Catalog — detail routes with a real :id param\n { pattern: /^\\/hookmovies\\/catalog\\/titles\\/([^/]+)\\/?$/, entityType: 'HOOKMOVIES_TITLE' },\n { pattern: /^\\/hookmovies\\/catalog\\/titles\\/?$/, entityType: 'HOOKMOVIES_TITLE_LIST' },\n { pattern: /^\\/hookmovies\\/catalog\\/studios\\/?$/, entityType: 'HOOKMOVIES_STUDIO_LIST' },\n { pattern: /^\\/hookmovies\\/catalog\\/people\\/?$/, entityType: 'HOOKMOVIES_PERSON_LIST' },\n\n // Productions\n { pattern: /^\\/hookmovies\\/productions\\/([^/]+)\\/?$/, entityType: 'HOOKMOVIES_PRODUCTION' },\n { pattern: /^\\/hookmovies\\/productions\\/?$/, entityType: 'HOOKMOVIES_PRODUCTION_LIST' },\n\n // Auditions\n { pattern: /^\\/hookmovies\\/auditions\\/([^/]+)\\/?$/, entityType: 'HOOKMOVIES_AUDITION' },\n { pattern: /^\\/hookmovies\\/auditions\\/?$/, entityType: 'HOOKMOVIES_AUDITION_LIST' },\n\n // Engagement\n { pattern: /^\\/hookmovies\\/engagement\\/reviews\\/?$/, entityType: 'HOOKMOVIES_REVIEWS' },\n\n // Distribution — list only, no detail route exists\n {\n pattern: /^\\/hookmovies\\/distribution\\/rights\\/?$/,\n entityType: 'HOOKMOVIES_RIGHTS_WINDOW_LIST',\n },\n\n // Library\n { pattern: /^\\/hookmovies\\/library\\/lists\\/?$/, entityType: 'HOOKMOVIES_LIBRARY_LISTS' },\n {\n pattern: /^\\/hookmovies\\/library\\/entitlements\\/?$/,\n entityType: 'HOOKMOVIES_LIBRARY_ENTITLEMENTS',\n },\n\n // Consumer — discovery, title view, playback, hooks feed\n { pattern: /^\\/hookmovies\\/browse\\/?$/, entityType: 'HOOKMOVIES_DISCOVERY' },\n {\n // Must precede title/:id — same entity, but the localization tab.\n pattern: /^\\/hookmovies\\/title\\/([^/]+)\\/localization\\/?$/,\n entityType: 'HOOKMOVIES_TITLE_LOCALIZATION',\n },\n { pattern: /^\\/hookmovies\\/title\\/([^/]+)\\/?$/, entityType: 'HOOKMOVIES_TITLE' },\n {\n pattern: /^\\/hookmovies\\/watch\\/([^/]+)\\/episode\\/([^/]+)\\/?$/,\n entityType: 'HOOKMOVIES_PLAYBACK',\n metadataFromMatch: (match) => ({ episodeId: match[2] }),\n },\n { pattern: /^\\/hookmovies\\/watch\\/([^/]+)\\/?$/, entityType: 'HOOKMOVIES_PLAYBACK' },\n { pattern: /^\\/hookmovies\\/hooks\\/?$/, entityType: 'HOOKMOVIES_HOOKS_FEED' },\n\n // Social — profiles & watch parties (real paths have no `/social` prefix)\n { pattern: /^\\/hookmovies\\/profiles\\/?$/, entityType: 'HOOKMOVIES_PROFILES' },\n { pattern: /^\\/hookmovies\\/watch-party\\/([^/]+)\\/?$/, entityType: 'HOOKMOVIES_WATCH_PARTY' },\n { pattern: /^\\/hookmovies\\/watch-party\\/?$/, entityType: 'HOOKMOVIES_WATCH_PARTY_LIST' },\n\n // Commerce\n { pattern: /^\\/hookmovies\\/checkout\\/([^/]+)\\/?$/, entityType: 'HOOKMOVIES_CHECKOUT' },\n { pattern: /^\\/hookmovies\\/payouts\\/?$/, entityType: 'HOOKMOVIES_PAYOUTS' },\n\n // Analytics\n { pattern: /^\\/hookmovies\\/analytics\\/?$/, entityType: 'HOOKMOVIES_ANALYTICS' },\n\n // Trust & safety\n { pattern: /^\\/hookmovies\\/trust\\/moderation\\/?$/, entityType: 'HOOKMOVIES_MODERATION' },\n\n // Onboarding\n { pattern: /^\\/hookmovies\\/onboarding\\/languages\\/?$/, entityType: 'HOOKMOVIES_ONBOARDING' },\n\n // Developer portal\n { pattern: /^\\/hookmovies\\/developer\\/?$/, entityType: 'HOOKMOVIES_DEVELOPER_PORTAL' },\n\n // Live — FAST channels (route is just `live`, no :id)\n { pattern: /^\\/hookmovies\\/live\\/?$/, entityType: 'HOOKMOVIES_LIVE' },\n\n // AI concierge\n { pattern: /^\\/hookmovies\\/concierge\\/?$/, entityType: 'HOOKMOVIES_CONCIERGE' },\n\n // Home / dashboard\n { pattern: /^\\/hookmovies\\/?$/, entityType: 'HOOKMOVIES_HOME' },\n];\n\nexport function resolveHookMoviesConversationContext(\n pathname: string,\n search: string\n): ConversationContextShape {\n const params = new URLSearchParams(search);\n\n for (const rule of ROUTE_CONTEXT_RULES) {\n const match = pathname.match(rule.pattern);\n if (!match) continue;\n\n const entityId = match[1];\n const extraMetadata = rule.metadataFromMatch?.(match) ?? {};\n\n return {\n entityType: rule.entityType,\n entityId,\n metadata: {\n routePattern: rule.pattern.source,\n ...extraMetadata,\n },\n };\n }\n\n // Fallback for any path that doesn't match a known route (e.g. a future\n // route added to HookMoviesRoutes.tsx but not yet mapped here).\n const activeTab =\n params.get('tab') ??\n (() => {\n const segments = pathname.split('/').filter(Boolean);\n return segments.length > 2 ? segments[segments.length - 1] : undefined;\n })();\n\n return {\n activeTab: activeTab ?? undefined,\n metadata: {\n ...(activeTab ? { activeTab } : {}),\n ...(params.get('selectedId') ? { selectedId: params.get('selectedId') } : {}),\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-hookmovies",
3
- "version": "2026.716.1",
3
+ "version": "2026.720.1",
4
4
  "description": "Burdenoff HookMovies Microfrontend — discovery, catalog, engagement, auditions, productions, distribution, and library surfaces for the global creative-media network.",
5
5
  "type": "module",
6
6
  "files": [