@igstack/app-catalog-frontend-core 6.6.0 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/esm/App.d.ts +29 -3
  2. package/dist/esm/App.js +4 -2
  3. package/dist/esm/App.js.map +1 -1
  4. package/dist/esm/__tests__/modules/appCatalog/ui/catalog/AppDetailPanel.widen.test.d.ts +0 -0
  5. package/dist/esm/__tests__/modules/appCatalog/ui/components/SubResourcesSection.adminColumn.test.d.ts +0 -0
  6. package/dist/esm/__tests__/modules/appCatalog/ui/components/SubResourcesSection.rowMemo.test.d.ts +0 -0
  7. package/dist/esm/__tests__/modules/dismiss/DismissStack.test.d.ts +0 -0
  8. package/dist/esm/__tests__/modules/extensions/slots.test.d.ts +0 -0
  9. package/dist/esm/__tests__/publicEntryResolves.test.d.ts +1 -0
  10. package/dist/esm/appPropsFactory.d.ts +19 -1
  11. package/dist/esm/appPropsFactory.js +4 -3
  12. package/dist/esm/appPropsFactory.js.map +1 -1
  13. package/dist/esm/index.d.ts +15 -2
  14. package/dist/esm/index.js +5 -0
  15. package/dist/esm/index.js.map +1 -1
  16. package/dist/esm/internal.d.ts +7 -1
  17. package/dist/esm/internal.js +0 -2
  18. package/dist/esm/internal.js.map +1 -1
  19. package/dist/esm/modules/appCatalog/context/AppCatalogContext.d.ts +17 -0
  20. package/dist/esm/modules/appCatalog/context/AppCatalogContext.js +7 -16
  21. package/dist/esm/modules/appCatalog/context/AppCatalogContext.js.map +1 -1
  22. package/dist/esm/modules/appCatalog/ui/catalog/AppCatalogGrid.js +20 -11
  23. package/dist/esm/modules/appCatalog/ui/catalog/AppCatalogGrid.js.map +1 -1
  24. package/dist/esm/modules/appCatalog/ui/catalog/AppDetailPanel.js +65 -29
  25. package/dist/esm/modules/appCatalog/ui/catalog/AppDetailPanel.js.map +1 -1
  26. package/dist/esm/modules/appCatalog/ui/components/PersonBadge.js +0 -14
  27. package/dist/esm/modules/appCatalog/ui/components/PersonBadge.js.map +1 -1
  28. package/dist/esm/modules/appCatalog/ui/components/ScreenshotGallery.js +5 -18
  29. package/dist/esm/modules/appCatalog/ui/components/ScreenshotGallery.js.map +1 -1
  30. package/dist/esm/modules/appCatalog/ui/components/SubResourceDetailPanel.d.ts +12 -1
  31. package/dist/esm/modules/appCatalog/ui/components/SubResourceDetailPanel.js +3 -1
  32. package/dist/esm/modules/appCatalog/ui/components/SubResourceDetailPanel.js.map +1 -1
  33. package/dist/esm/modules/appCatalog/ui/components/SubResourcesSection.d.ts +17 -1
  34. package/dist/esm/modules/appCatalog/ui/components/SubResourcesSection.js +212 -79
  35. package/dist/esm/modules/appCatalog/ui/components/SubResourcesSection.js.map +1 -1
  36. package/dist/esm/modules/appCatalog/ui/components/TierVariantsSection.js +0 -14
  37. package/dist/esm/modules/appCatalog/ui/components/TierVariantsSection.js.map +1 -1
  38. package/dist/esm/modules/appCatalog/ui/detail/AppDetails.d.ts +1 -2
  39. package/dist/esm/modules/appCatalog/ui/detail/AppDetails.js +31 -23
  40. package/dist/esm/modules/appCatalog/ui/detail/AppDetails.js.map +1 -1
  41. package/dist/esm/modules/appCatalog/ui/detail/DetailTabs.js +5 -0
  42. package/dist/esm/modules/appCatalog/ui/detail/DetailTabs.js.map +1 -1
  43. package/dist/esm/modules/appCatalog/ui/pages/AppCatalogPage.js +5 -3
  44. package/dist/esm/modules/appCatalog/ui/pages/AppCatalogPage.js.map +1 -1
  45. package/dist/esm/modules/auth/AuthContext.d.ts +12 -0
  46. package/dist/esm/modules/auth/AuthContext.js +5 -0
  47. package/dist/esm/modules/auth/AuthContext.js.map +1 -1
  48. package/dist/esm/modules/auth/index.d.ts +1 -1
  49. package/dist/esm/modules/dismiss/DismissStack.d.ts +102 -0
  50. package/dist/esm/modules/dismiss/DismissStack.js +82 -0
  51. package/dist/esm/modules/dismiss/DismissStack.js.map +1 -0
  52. package/dist/esm/modules/extensions/ExtensionsContext.d.ts +17 -0
  53. package/dist/esm/modules/extensions/ExtensionsContext.js +15 -0
  54. package/dist/esm/modules/extensions/ExtensionsContext.js.map +1 -0
  55. package/dist/esm/modules/extensions/factories.d.ts +18 -0
  56. package/dist/esm/modules/extensions/factories.js +12 -0
  57. package/dist/esm/modules/extensions/factories.js.map +1 -0
  58. package/dist/esm/modules/extensions/features/resourceDetail.d.ts +92 -0
  59. package/dist/esm/modules/extensions/features/resourceDetail.js +14 -0
  60. package/dist/esm/modules/extensions/features/resourceDetail.js.map +1 -0
  61. package/dist/esm/modules/extensions/index.d.ts +79 -0
  62. package/dist/esm/modules/extensions/index.js +26 -0
  63. package/dist/esm/modules/extensions/index.js.map +1 -0
  64. package/dist/esm/modules/extensions/makeSlot.d.ts +46 -0
  65. package/dist/esm/modules/extensions/makeSlot.js +62 -0
  66. package/dist/esm/modules/extensions/makeSlot.js.map +1 -0
  67. package/dist/esm/modules/extensions/types.d.ts +46 -0
  68. package/dist/esm/modules/gallery/Gallery.js +6 -14
  69. package/dist/esm/modules/gallery/Gallery.js.map +1 -1
  70. package/dist/esm/routeTree.gen.d.ts +34 -34
  71. package/dist/esm/routeTree.gen.js +14 -14
  72. package/dist/esm/routeTree.gen.js.map +1 -1
  73. package/dist/esm/router.d.ts +23 -0
  74. package/dist/esm/router.js +5 -0
  75. package/dist/esm/router.js.map +1 -0
  76. package/dist/esm/routes/_layout/app.$slug.d.ts +1 -1
  77. package/dist/esm/routes/_layout/app.$slug_.sub.$subSlug.d.ts +1 -1
  78. package/dist/esm/routes/_layout/catalog.apps.index.d.ts +2 -2
  79. package/dist/esm/routes/_layout/index.d.ts +1 -1
  80. package/dist/esm/routes/_layout/login.d.ts +1 -1
  81. package/dist/esm/routes/_layout/mcp.d.ts +1 -1
  82. package/dist/esm/routes/_layout/service-desks.d.ts +1 -1
  83. package/dist/esm/routes/_layout.d.ts +1 -1
  84. package/dist/esm/routes/_layout.js +13 -2
  85. package/dist/esm/routes/_layout.js.map +1 -1
  86. package/dist/esm/routes/auth.callback.d.ts +1 -1
  87. package/dist/esm/ui/components/error/DefaultErrorComponent.js +9 -2
  88. package/dist/esm/ui/components/error/DefaultErrorComponent.js.map +1 -1
  89. package/dist/esm/ui/components/header/Header.js +0 -1
  90. package/dist/esm/ui/components/header/Header.js.map +1 -1
  91. package/dist/esm/ui/dialog.d.ts +1 -1
  92. package/dist/esm/ui/dialog.js +9 -0
  93. package/dist/esm/ui/dialog.js.map +1 -1
  94. package/dist/esm/ui/table.d.ts +25 -1
  95. package/dist/esm/ui/table.js +20 -3
  96. package/dist/esm/ui/table.js.map +1 -1
  97. package/dist/esm/util/createAcRouter.d.ts +1 -1
  98. package/package.json +19 -11
  99. package/src/App.tsx +34 -3
  100. package/src/__tests__/modules/appCatalog/ui/catalog/AppDetailPanel.appSwitch.test.tsx +4 -1
  101. package/src/__tests__/modules/appCatalog/ui/catalog/AppDetailPanel.focus.test.tsx +1 -1
  102. package/src/__tests__/modules/appCatalog/ui/catalog/AppDetailPanel.widen.test.tsx +53 -0
  103. package/src/__tests__/modules/appCatalog/ui/components/SubResourcesSection.adminColumn.test.tsx +115 -0
  104. package/src/__tests__/modules/appCatalog/ui/components/SubResourcesSection.rowMemo.test.tsx +120 -0
  105. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.background.test.tsx +3 -5
  106. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.doorAndTabs.test.tsx +9 -9
  107. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.escape.test.tsx +31 -5
  108. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.sectionOrder.test.tsx +3 -3
  109. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.subResourceTab.test.tsx +48 -6
  110. package/src/__tests__/modules/appCatalog/ui/detail/AppDetails.tags.test.tsx +1 -7
  111. package/src/__tests__/modules/dismiss/DismissStack.test.tsx +330 -0
  112. package/src/__tests__/modules/extensions/slots.test.tsx +252 -0
  113. package/src/__tests__/publicEntryResolves.test.ts +63 -0
  114. package/src/appPropsFactory.ts +23 -3
  115. package/src/index.tsx +32 -2
  116. package/src/internal.ts +7 -1
  117. package/src/modules/appCatalog/context/AppCatalogContext.tsx +21 -0
  118. package/src/modules/appCatalog/ui/catalog/AppCatalogGrid.tsx +52 -12
  119. package/src/modules/appCatalog/ui/catalog/AppDetailPanel.tsx +89 -28
  120. package/src/modules/appCatalog/ui/components/ScreenshotGallery.tsx +12 -20
  121. package/src/modules/appCatalog/ui/components/SubResourceDetailPanel.tsx +19 -0
  122. package/src/modules/appCatalog/ui/components/SubResourcesSection.tsx +396 -111
  123. package/src/modules/appCatalog/ui/detail/AppDetails.tsx +46 -26
  124. package/src/modules/appCatalog/ui/detail/DetailTabs.tsx +20 -0
  125. package/src/modules/appCatalog/ui/pages/AppCatalogPage.tsx +17 -3
  126. package/src/modules/auth/AuthContext.tsx +15 -0
  127. package/src/modules/auth/index.ts +1 -0
  128. package/src/modules/dismiss/DismissStack.tsx +224 -0
  129. package/src/modules/extensions/ExtensionsContext.tsx +36 -0
  130. package/src/modules/extensions/factories.ts +19 -0
  131. package/src/modules/extensions/features/resourceDetail.ts +74 -0
  132. package/src/modules/extensions/index.ts +121 -0
  133. package/src/modules/extensions/makeSlot.tsx +123 -0
  134. package/src/modules/extensions/types.ts +51 -0
  135. package/src/modules/gallery/Gallery.tsx +13 -17
  136. package/src/routeTree.gen.ts +31 -31
  137. package/src/router.ts +23 -0
  138. package/src/routes/_layout.tsx +51 -1
  139. package/src/ui/components/error/DefaultErrorComponent.tsx +22 -4
  140. package/src/ui/dialog.tsx +26 -0
  141. package/src/ui/table.tsx +46 -3
package/dist/esm/App.d.ts CHANGED
@@ -2,13 +2,39 @@ import { QueryClient } from '@tanstack/react-query';
2
2
  import { TRPCRouter } from '@igstack/app-catalog-backend-core';
3
3
  import { TRPCClient } from '@trpc/client';
4
4
  import { AcDb } from './userDb/AcDb.js';
5
- import { createAcRouter } from './util/createAcRouter.js';
5
+ import { RegisteredRouter } from '@tanstack/react-router';
6
6
  import { UiSettings } from './types/uiSettings.js';
7
+ import { AcPlugin } from './modules/extensions.js';
7
8
  export interface AppProps {
8
- router: ReturnType<typeof createAcRouter>;
9
+ /**
10
+ * `RegisteredRouter`, not `ReturnType<typeof createAcRouter>`.
11
+ *
12
+ * That was a type-only import, and a type-only import still pulls the
13
+ * referenced file's `declare module` into the program — so naming
14
+ * `createAcRouter` here put the core's `Register` augmentation into every
15
+ * consumer, and a deployment composing its own route tree could not declare
16
+ * its own without `TS2717`.
17
+ *
18
+ * This widens nothing: `RegisteredRouter` resolves per program against
19
+ * whatever `Register` that program declared, so the core's own program gets
20
+ * the core's router and a deployment gets its own, each exactly typed. A
21
+ * router built from a different route tree is still rejected here.
22
+ *
23
+ * The one blind spot, guarded by a type test on the consumer side: with NO
24
+ * `Register` anywhere in a program this silently degrades to `AnyRouter`.
25
+ */
26
+ router: RegisteredRouter;
9
27
  queryClient: QueryClient;
10
28
  trpcClient: TRPCClient<TRPCRouter>;
11
29
  db: AcDb;
12
30
  uiSettings?: UiSettings;
31
+ /**
32
+ * Deployment-specific UI, rendered at the slots the core declares. Optional:
33
+ * with none registered every slot renders nothing and the UI is identical to
34
+ * one with no slots at all.
35
+ *
36
+ * Must be a module-level constant — see `AcPlugin`.
37
+ */
38
+ extensions?: readonly AcPlugin[];
13
39
  }
14
- export declare function App({ router, queryClient, trpcClient, db, uiSettings, }: AppProps): import("react/jsx-runtime").JSX.Element;
40
+ export declare function App({ router, queryClient, trpcClient, db, uiSettings, extensions, }: AppProps): import("react/jsx-runtime").JSX.Element;
package/dist/esm/App.js CHANGED
@@ -4,14 +4,16 @@ import { RouterProvider } from "@tanstack/react-router";
4
4
  import { DbProvider } from "./userDb/DbContext.js";
5
5
  import { TRPCProvider } from "./api/infra/trpc.js";
6
6
  import { UiSettingsContext } from "./context/UiSettingsContext.js";
7
+ import { ExtensionsContext } from "./modules/extensions/ExtensionsContext.js";
7
8
  function App({
8
9
  router,
9
10
  queryClient,
10
11
  trpcClient,
11
12
  db,
12
- uiSettings
13
+ uiSettings,
14
+ extensions
13
15
  }) {
14
- return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(TRPCProvider, { queryClient, trpcClient, children: /* @__PURE__ */ jsx(DbProvider, { db, children: /* @__PURE__ */ jsx(UiSettingsContext, { value: uiSettings, children: /* @__PURE__ */ jsx(RouterProvider, { router }) }) }) }) });
16
+ return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(TRPCProvider, { queryClient, trpcClient, children: /* @__PURE__ */ jsx(DbProvider, { db, children: /* @__PURE__ */ jsx(UiSettingsContext, { value: uiSettings, children: /* @__PURE__ */ jsx(ExtensionsContext, { value: extensions, children: /* @__PURE__ */ jsx(RouterProvider, { router }) }) }) }) }) });
15
17
  }
16
18
  export {
17
19
  App
@@ -1 +1 @@
1
- {"version":3,"file":"App.js","sources":["../../src/App.tsx"],"sourcesContent":["import { QueryClientProvider } from '@tanstack/react-query'\nimport { RouterProvider } from '@tanstack/react-router'\nimport { DbProvider } from './userDb/DbContext'\nimport type { QueryClient } from '@tanstack/react-query'\nimport type { TRPCRouter } from '@igstack/app-catalog-backend-core'\nimport type { TRPCClient } from '@trpc/client'\nimport type { AcDb } from './userDb/AcDb'\nimport type { createAcRouter } from './util/createAcRouter'\nimport { TRPCProvider } from './api/infra/trpc'\nimport type { UiSettings } from './types/uiSettings'\nimport { UiSettingsContext } from './context/UiSettingsContext'\n\nexport interface AppProps {\n router: ReturnType<typeof createAcRouter>\n queryClient: QueryClient\n trpcClient: TRPCClient<TRPCRouter>\n db: AcDb\n uiSettings?: UiSettings\n}\n\nexport function App({\n router,\n queryClient,\n trpcClient,\n db,\n uiSettings,\n}: AppProps) {\n return (\n <QueryClientProvider client={queryClient}>\n <TRPCProvider queryClient={queryClient} trpcClient={trpcClient}>\n <DbProvider db={db}>\n <UiSettingsContext value={uiSettings}>\n <RouterProvider router={router} />\n </UiSettingsContext>\n </DbProvider>\n </TRPCProvider>\n </QueryClientProvider>\n )\n}\n"],"names":[],"mappings":";;;;;;AAoBO,SAAS,IAAI;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAa;AACX,SACE,oBAAC,uBAAoB,QAAQ,aAC3B,8BAAC,cAAA,EAAa,aAA0B,YACtC,UAAA,oBAAC,YAAA,EAAW,IACV,UAAA,oBAAC,mBAAA,EAAkB,OAAO,YACxB,UAAA,oBAAC,kBAAe,OAAA,CAAgB,EAAA,CAClC,EAAA,CACF,EAAA,CACF,EAAA,CACF;AAEJ;"}
1
+ {"version":3,"file":"App.js","sources":["../../src/App.tsx"],"sourcesContent":["import { QueryClientProvider } from '@tanstack/react-query'\nimport { RouterProvider } from '@tanstack/react-router'\nimport { DbProvider } from './userDb/DbContext'\nimport type { QueryClient } from '@tanstack/react-query'\nimport type { TRPCRouter } from '@igstack/app-catalog-backend-core'\nimport type { TRPCClient } from '@trpc/client'\nimport type { AcDb } from './userDb/AcDb'\nimport type { RegisteredRouter } from '@tanstack/react-router'\nimport { TRPCProvider } from './api/infra/trpc'\nimport type { UiSettings } from './types/uiSettings'\nimport { UiSettingsContext } from './context/UiSettingsContext'\nimport { ExtensionsContext } from './modules/extensions'\nimport type { AcPlugin } from './modules/extensions'\n\nexport interface AppProps {\n /**\n * `RegisteredRouter`, not `ReturnType<typeof createAcRouter>`.\n *\n * That was a type-only import, and a type-only import still pulls the\n * referenced file's `declare module` into the program — so naming\n * `createAcRouter` here put the core's `Register` augmentation into every\n * consumer, and a deployment composing its own route tree could not declare\n * its own without `TS2717`.\n *\n * This widens nothing: `RegisteredRouter` resolves per program against\n * whatever `Register` that program declared, so the core's own program gets\n * the core's router and a deployment gets its own, each exactly typed. A\n * router built from a different route tree is still rejected here.\n *\n * The one blind spot, guarded by a type test on the consumer side: with NO\n * `Register` anywhere in a program this silently degrades to `AnyRouter`.\n */\n router: RegisteredRouter\n queryClient: QueryClient\n trpcClient: TRPCClient<TRPCRouter>\n db: AcDb\n uiSettings?: UiSettings\n /**\n * Deployment-specific UI, rendered at the slots the core declares. Optional:\n * with none registered every slot renders nothing and the UI is identical to\n * one with no slots at all.\n *\n * Must be a module-level constant — see `AcPlugin`.\n */\n extensions?: readonly AcPlugin[]\n}\n\nexport function App({\n router,\n queryClient,\n trpcClient,\n db,\n uiSettings,\n extensions,\n}: AppProps) {\n return (\n <QueryClientProvider client={queryClient}>\n <TRPCProvider queryClient={queryClient} trpcClient={trpcClient}>\n <DbProvider db={db}>\n <UiSettingsContext value={uiSettings}>\n {/* Above the router, so a slot anywhere in the tree can read it. */}\n <ExtensionsContext value={extensions}>\n <RouterProvider router={router} />\n </ExtensionsContext>\n </UiSettingsContext>\n </DbProvider>\n </TRPCProvider>\n </QueryClientProvider>\n )\n}\n"],"names":[],"mappings":";;;;;;;AA+CO,SAAS,IAAI;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAa;AACX,SACE,oBAAC,qBAAA,EAAoB,QAAQ,aAC3B,UAAA,oBAAC,cAAA,EAAa,aAA0B,YACtC,UAAA,oBAAC,YAAA,EAAW,IACV,UAAA,oBAAC,mBAAA,EAAkB,OAAO,YAExB,UAAA,oBAAC,mBAAA,EAAkB,OAAO,YACxB,UAAA,oBAAC,gBAAA,EAAe,QAAgB,EAAA,CAClC,EAAA,CACF,EAAA,CACF,EAAA,CACF,GACF;AAEJ;"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,2 +1,20 @@
1
+ import { RegisteredRouter } from '@tanstack/react-router';
1
2
  import { AppProps } from './App.js';
2
- export declare function appPropsFactory(): AppProps;
3
+ import { AcRouterInitParams } from './types/types.js';
4
+ export interface AppPropsFactoryOptions {
5
+ /**
6
+ * How to build the router.
7
+ *
8
+ * Injected rather than imported, because the ROUTE TREE belongs to the
9
+ * deployment. A deployment that composes its own tree declares its own
10
+ * `declare module … Register`, and importing the core's `createAcRouter`
11
+ * here would put the core's competing augmentation into that program — a
12
+ * hard `TS2717`, and `import type` would not help: augmentations are
13
+ * collected when the program is built.
14
+ *
15
+ * Using the core's tree unchanged? Pass `createAcRouter` from
16
+ * `@igstack/app-catalog-frontend-core/router`.
17
+ */
18
+ createRouter: (init: AcRouterInitParams) => RegisteredRouter;
19
+ }
20
+ export declare function appPropsFactory({ createRouter, }: AppPropsFactoryOptions): AppProps;
@@ -2,8 +2,9 @@ import { createBrowserHistory } from "@tanstack/react-router";
2
2
  import { createTRPCClient, httpBatchLink } from "@trpc/client";
3
3
  import { AcDb } from "./userDb/AcDb.js";
4
4
  import { createQueryClient } from "./api/infra/createQueryClient.js";
5
- import { createAcRouter } from "./util/createAcRouter.js";
6
- function appPropsFactory() {
5
+ function appPropsFactory({
6
+ createRouter
7
+ }) {
7
8
  const trpcClient = createTRPCClient({
8
9
  links: [
9
10
  httpBatchLink({
@@ -16,7 +17,7 @@ function appPropsFactory() {
16
17
  const plugins = [
17
18
  // Future plugins can be added here
18
19
  ];
19
- const router = createAcRouter({
20
+ const router = createRouter({
20
21
  history: createBrowserHistory(),
21
22
  context: {
22
23
  queryClient,
@@ -1 +1 @@
1
- {"version":3,"file":"appPropsFactory.js","sources":["../../src/appPropsFactory.ts"],"sourcesContent":["import { createBrowserHistory } from '@tanstack/react-router'\nimport { createTRPCClient, httpBatchLink } from '@trpc/client'\nimport { AcDb } from './userDb/AcDb'\nimport type { TRPCRouter } from '@igstack/app-catalog-backend-core'\nimport type { AppProps } from './App'\nimport type { AcPlugin } from './modules/pluginCore/types'\nimport { createQueryClient } from './api/infra/createQueryClient'\nimport { createAcRouter } from './util/createAcRouter'\n\n// registerSW();\nexport function appPropsFactory(): AppProps {\n const trpcClient = createTRPCClient<TRPCRouter>({\n links: [\n httpBatchLink({\n url: `${window.location.origin}/api/trpc`,\n }),\n ],\n })\n\n const db = new AcDb()\n const queryClient = createQueryClient({ trpcClient, db })\n const plugins: AcPlugin[] = [\n // Future plugins can be added here\n ]\n const router = createAcRouter({\n history: createBrowserHistory(),\n context: {\n queryClient,\n trpcClient,\n db,\n plugins,\n boostrapHealth: {},\n },\n })\n\n return { router, queryClient, trpcClient, db }\n}\n"],"names":[],"mappings":";;;;;AAUO,SAAS,kBAA4B;AAC1C,QAAM,aAAa,iBAA6B;AAAA,IAC9C,OAAO;AAAA,MACL,cAAc;AAAA,QACZ,KAAK,GAAG,OAAO,SAAS,MAAM;AAAA,MAAA,CAC/B;AAAA,IAAA;AAAA,EACH,CACD;AAED,QAAM,KAAK,IAAI,KAAA;AACf,QAAM,cAAc,kBAAkB,EAAE,YAAY,IAAI;AACxD,QAAM,UAAsB;AAAA;AAAA,EAAA;AAG5B,QAAM,SAAS,eAAe;AAAA,IAC5B,SAAS,qBAAA;AAAA,IACT,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,CAAA;AAAA,IAAC;AAAA,EACnB,CACD;AAED,SAAO,EAAE,QAAQ,aAAa,YAAY,GAAA;AAC5C;"}
1
+ {"version":3,"file":"appPropsFactory.js","sources":["../../src/appPropsFactory.ts"],"sourcesContent":["import { createBrowserHistory } from '@tanstack/react-router'\nimport { createTRPCClient, httpBatchLink } from '@trpc/client'\nimport { AcDb } from './userDb/AcDb'\nimport type { TRPCRouter } from '@igstack/app-catalog-backend-core'\nimport type { RegisteredRouter } from '@tanstack/react-router'\nimport type { AppProps } from './App'\nimport type { AcPlugin } from './modules/pluginCore/types'\nimport type { AcRouterInitParams } from './types/types'\nimport { createQueryClient } from './api/infra/createQueryClient'\n\nexport interface AppPropsFactoryOptions {\n /**\n * How to build the router.\n *\n * Injected rather than imported, because the ROUTE TREE belongs to the\n * deployment. A deployment that composes its own tree declares its own\n * `declare module … Register`, and importing the core's `createAcRouter`\n * here would put the core's competing augmentation into that program — a\n * hard `TS2717`, and `import type` would not help: augmentations are\n * collected when the program is built.\n *\n * Using the core's tree unchanged? Pass `createAcRouter` from\n * `@igstack/app-catalog-frontend-core/router`.\n */\n createRouter: (init: AcRouterInitParams) => RegisteredRouter\n}\n\n// registerSW();\nexport function appPropsFactory({\n createRouter,\n}: AppPropsFactoryOptions): AppProps {\n const trpcClient = createTRPCClient<TRPCRouter>({\n links: [\n httpBatchLink({\n url: `${window.location.origin}/api/trpc`,\n }),\n ],\n })\n\n const db = new AcDb()\n const queryClient = createQueryClient({ trpcClient, db })\n const plugins: AcPlugin[] = [\n // Future plugins can be added here\n ]\n const router = createRouter({\n history: createBrowserHistory(),\n context: {\n queryClient,\n trpcClient,\n db,\n plugins,\n boostrapHealth: {},\n },\n })\n\n return { router, queryClient, trpcClient, db }\n}\n"],"names":[],"mappings":";;;;AA4BO,SAAS,gBAAgB;AAAA,EAC9B;AACF,GAAqC;AACnC,QAAM,aAAa,iBAA6B;AAAA,IAC9C,OAAO;AAAA,MACL,cAAc;AAAA,QACZ,KAAK,GAAG,OAAO,SAAS,MAAM;AAAA,MAAA,CAC/B;AAAA,IAAA;AAAA,EACH,CACD;AAED,QAAM,KAAK,IAAI,KAAA;AACf,QAAM,cAAc,kBAAkB,EAAE,YAAY,IAAI;AACxD,QAAM,UAAsB;AAAA;AAAA,EAAA;AAG5B,QAAM,SAAS,aAAa;AAAA,IAC1B,SAAS,qBAAA;AAAA,IACT,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,CAAA;AAAA,IAAC;AAAA,EACnB,CACD;AAED,SAAO,EAAE,QAAQ,aAAa,YAAY,GAAA;AAC5C;"}
@@ -1,7 +1,20 @@
1
1
  export { appPropsFactory } from './appPropsFactory.js';
2
+ export type { AppPropsFactoryOptions } from './appPropsFactory.js';
3
+ export type { AcRouterContext, AcRouterInitParams } from './types/types.js';
2
4
  export { App } from './App.js';
3
5
  export type { AppProps } from './App.js';
4
6
  export type { AreaIcon, AreasSettings, UiSettings } from './types/uiSettings.js';
5
7
  export { DAY_TO_DAY_AREA_KEY, DAY_TO_DAY_TAG, } from './modules/appCatalog/utils/areaGrouping.js';
6
- export { PwaAutoUpdateProvider, usePwaAutoUpdate } from './modules/pwa.js';
7
- export type { PwaAutoUpdateOptions, PwaUpdateHandle } from './modules/pwa.js';
8
+ export { PwaAutoUpdateProvider, usePwaAutoUpdate } from './modules/pwa/index.js';
9
+ export type { PwaAutoUpdateOptions, PwaUpdateHandle } from './modules/pwa/index.js';
10
+ export type { AcPlugin, PluginSlots, PluginWrappers, PluginUser, SlotSpec, WrapperSpec, } from './modules/extensions/index.js';
11
+ export type { Resource } from '@igstack/app-catalog-backend-core';
12
+ export { useCatalogResources } from './modules/appCatalog/context/AppCatalogContext.js';
13
+ /**
14
+ * Escape handling, for a deployment's own overlays.
15
+ *
16
+ * Call `useDismissable(close)` in any overlay and Escape dismisses whatever
17
+ * opened last, across core and deployment layers alike. Nothing needs to know
18
+ * what else is on screen.
19
+ */
20
+ export { useDismissable, useDismissStack } from './modules/dismiss/DismissStack.js';
package/dist/esm/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { appPropsFactory } from "./appPropsFactory.js";
2
2
  import { App } from "./App.js";
3
3
  import { DAY_TO_DAY_AREA_KEY, DAY_TO_DAY_TAG } from "./modules/appCatalog/utils/areaGrouping.js";
4
+ import { useCatalogResources } from "./modules/appCatalog/context/AppCatalogContext.js";
5
+ import { useDismissStack, useDismissable } from "./modules/dismiss/DismissStack.js";
4
6
  import { PwaAutoUpdateProvider, usePwaAutoUpdate } from "./modules/pwa/PwaAutoUpdateContext.js";
5
7
  export {
6
8
  App,
@@ -8,6 +10,9 @@ export {
8
10
  DAY_TO_DAY_TAG,
9
11
  PwaAutoUpdateProvider,
10
12
  appPropsFactory,
13
+ useCatalogResources,
14
+ useDismissStack,
15
+ useDismissable,
11
16
  usePwaAutoUpdate
12
17
  };
13
18
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
@@ -5,9 +5,15 @@
5
5
  *
6
6
  * No stability guarantees: this moves in lockstep with
7
7
  * `@igstack/app-catalog-test-kit`, which is its only intended consumer.
8
+ *
9
+ * Must stay free of `createAcRouter`. The test kit runs inside a DEPLOYMENT's
10
+ * test program, which declares its own `Register` augmentation for its own
11
+ * route tree — and re-exporting `createAcRouter` here would drag the core's
12
+ * competing augmentation in with it, which is a hard `TS2717`. The router
13
+ * factory is injected instead; see `setRouterFactory` in the test kit. The
14
+ * core's own router is published separately, at `/router`.
8
15
  */
9
16
  export { App } from './App.js';
10
17
  export type { AppProps } from './App.js';
11
18
  export { AcDb, dbCacheDbKeys } from './userDb/AcDb.js';
12
- export { createAcRouter } from './util/createAcRouter.js';
13
19
  export { SEARCH_STORAGE_KEY, clearSessionState, seedSessionState, } from './modules/appCatalog/hooks/useSessionSyncedState.js';
@@ -1,13 +1,11 @@
1
1
  import { App } from "./App.js";
2
2
  import { AcDb, dbCacheDbKeys } from "./userDb/AcDb.js";
3
- import { createAcRouter } from "./util/createAcRouter.js";
4
3
  import { SEARCH_STORAGE_KEY, clearSessionState, seedSessionState } from "./modules/appCatalog/hooks/useSessionSyncedState.js";
5
4
  export {
6
5
  AcDb,
7
6
  App,
8
7
  SEARCH_STORAGE_KEY,
9
8
  clearSessionState,
10
- createAcRouter,
11
9
  dbCacheDbKeys,
12
10
  seedSessionState
13
11
  };
@@ -1 +1 @@
1
- {"version":3,"file":"internal.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
1
+ {"version":3,"file":"internal.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -15,4 +15,21 @@ interface AppCatalogProviderProps {
15
15
  }
16
16
  export declare function AppCatalogProvider({ children }: AppCatalogProviderProps): import("react/jsx-runtime").JSX.Element;
17
17
  export declare function useAppCatalogContext(): AppCatalogContextIface;
18
+ /**
19
+ * The catalog, for a deployment that owns its own routes.
20
+ *
21
+ * A deployment composing its own route tree has to resolve its own path params
22
+ * against the catalog — `/app/$slug/…/$account` means nothing until `$account`
23
+ * is matched to a resource. It needs the resources to do that, and the loading
24
+ * flag to tell "still fetching" from "no such resource", which is the
25
+ * difference between a spinner and a redirect.
26
+ *
27
+ * Deliberately narrower than `useAppCatalogContext`: persons, groups, tags and
28
+ * approval methods are the core's own rendering concerns, and publishing them
29
+ * would make every one of them part of the supported surface.
30
+ */
31
+ export declare function useCatalogResources(): {
32
+ resources: Resource[];
33
+ isLoading: boolean;
34
+ };
18
35
  export {};
@@ -1,21 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useQuery } from "@tanstack/react-query";
3
- import { createContext, useMemo, useEffect, use } from "react";
3
+ import { use, createContext, useMemo, useEffect } from "react";
4
4
  import { ApiQueryMagazineAppCatalog } from "../api/ApiQueryMagazineAppCatalog.js";
5
- import "../ui/context/AppCatalogFiltersContext.js";
6
- import "@tanstack/react-router";
7
- import "clsx";
8
- import "tailwind-merge";
9
- import "next-themes";
10
- import "../../../ui/button.js";
11
- import "../../auth/AuthModalContext.js";
12
- import "@radix-ui/react-dialog";
13
- import "lucide-react";
14
- import "../../config/GlobalConfigContext.js";
15
- import "../../pluginCore/PluginManagerContext.js";
16
- import "@radix-ui/react-tooltip";
17
- import "../../../ui/empty.js";
18
- import "../ui/pages/AppCatalogPage.js";
19
5
  import { useUiSettings } from "../../../context/UiSettingsContext.js";
20
6
  import { mergeFrontendBuildId } from "./mergeFrontendBuildId.js";
21
7
  const AppCatalogContext = createContext(void 0);
@@ -68,9 +54,14 @@ function useAppCatalogContext() {
68
54
  }
69
55
  return context;
70
56
  }
57
+ function useCatalogResources() {
58
+ const { resources, isLoadingApps } = useAppCatalogContext();
59
+ return { resources, isLoading: isLoadingApps };
60
+ }
71
61
  export {
72
62
  AppCatalogContext,
73
63
  AppCatalogProvider,
74
- useAppCatalogContext
64
+ useAppCatalogContext,
65
+ useCatalogResources
75
66
  };
76
67
  //# sourceMappingURL=AppCatalogContext.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppCatalogContext.js","sources":["../../../../../src/modules/appCatalog/context/AppCatalogContext.tsx"],"sourcesContent":["import type {\n AppApprovalMethod,\n AppVersionInfo,\n Group,\n GroupingTagDefinition,\n Person,\n Resource,\n} from '@igstack/app-catalog-backend-core'\nimport { useQuery } from '@tanstack/react-query'\nimport type { ReactNode } from 'react'\nimport { createContext, use, useEffect, useMemo } from 'react'\nimport { ApiQueryMagazineAppCatalog } from '~/modules/appCatalog'\nimport { useUiSettings } from '~/context/UiSettingsContext'\nimport { mergeFrontendBuildId } from '~/modules/appCatalog/context/mergeFrontendBuildId'\n\nexport interface AppCatalogContextIface {\n resources: Resource[]\n isLoadingApps: boolean\n tagsDefinitions: GroupingTagDefinition[]\n approvalMethods: AppApprovalMethod[]\n persons: Person[]\n groups: Group[]\n versions?: AppVersionInfo\n}\n\nexport const AppCatalogContext = createContext<\n AppCatalogContextIface | undefined\n>(undefined)\n\ninterface AppCatalogProviderProps {\n children: ReactNode\n}\n\nexport function AppCatalogProvider({ children }: AppCatalogProviderProps) {\n const { data, isLoading: isLoadingApps } = useQuery(\n ApiQueryMagazineAppCatalog.getAppCatalog(),\n )\n const uiSettings = useUiSettings()\n\n const contextValue = useMemo<AppCatalogContextIface>(\n () => ({\n resources: data?.resources ?? [],\n isLoadingApps,\n tagsDefinitions: data?.tagsDefinitions ?? [],\n approvalMethods: data?.approvalMethods ?? [],\n persons: data?.persons ?? [],\n groups: data?.groups ?? [],\n versions: mergeFrontendBuildId(\n data?.versions ?? {},\n uiSettings.frontendBuildId,\n ),\n }),\n [\n data?.approvalMethods,\n data?.resources,\n data?.tagsDefinitions,\n data?.persons,\n data?.groups,\n data?.versions,\n uiSettings.frontendBuildId,\n isLoadingApps,\n ],\n )\n\n // Update document title based on backend version\n useEffect(() => {\n if (data?.versions?.backend?.displayName === 'local') {\n document.title = 'Local'\n } else {\n document.title = 'App Catalog'\n }\n }, [data?.versions?.backend?.displayName])\n\n return <AppCatalogContext value={contextValue}>{children}</AppCatalogContext>\n}\n\nexport function useAppCatalogContext(): AppCatalogContextIface {\n const context = use(AppCatalogContext)\n if (!context) {\n throw new Error(\n 'useAppCatalogContext must be used within AppCatalogProvider',\n )\n }\n return context\n}\n"],"names":["_b","_a"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBO,MAAM,oBAAoB,cAE/B,MAAS;AAMJ,SAAS,mBAAmB,EAAE,YAAqC;;AACxE,QAAM,EAAE,MAAM,WAAW,cAAA,IAAkB;AAAA,IACzC,2BAA2B,cAAA;AAAA,EAAc;AAE3C,QAAM,aAAa,cAAA;AAEnB,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA,MACL,YAAW,6BAAM,cAAa,CAAA;AAAA,MAC9B;AAAA,MACA,kBAAiB,6BAAM,oBAAmB,CAAA;AAAA,MAC1C,kBAAiB,6BAAM,oBAAmB,CAAA;AAAA,MAC1C,UAAS,6BAAM,YAAW,CAAA;AAAA,MAC1B,SAAQ,6BAAM,WAAU,CAAA;AAAA,MACxB,UAAU;AAAA,SACR,6BAAM,aAAY,CAAA;AAAA,QAClB,WAAW;AAAA,MAAA;AAAA,IACb;AAAA,IAEF;AAAA,MACE,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,WAAW;AAAA,MACX;AAAA,IAAA;AAAA,EACF;AAIF,YAAU,MAAM;;AACd,UAAIA,OAAAC,MAAA,6BAAM,aAAN,gBAAAA,IAAgB,YAAhB,gBAAAD,IAAyB,iBAAgB,SAAS;AACpD,eAAS,QAAQ;AAAA,IACnB,OAAO;AACL,eAAS,QAAQ;AAAA,IACnB;AAAA,EACF,GAAG,EAAC,wCAAM,aAAN,mBAAgB,YAAhB,mBAAyB,WAAW,CAAC;AAEzC,SAAO,oBAAC,mBAAA,EAAkB,OAAO,cAAe,SAAA,CAAS;AAC3D;AAEO,SAAS,uBAA+C;AAC7D,QAAM,UAAU,IAAI,iBAAiB;AACrC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,SAAO;AACT;"}
1
+ {"version":3,"file":"AppCatalogContext.js","sources":["../../../../../src/modules/appCatalog/context/AppCatalogContext.tsx"],"sourcesContent":["import type {\n AppApprovalMethod,\n AppVersionInfo,\n Group,\n GroupingTagDefinition,\n Person,\n Resource,\n} from '@igstack/app-catalog-backend-core'\nimport { useQuery } from '@tanstack/react-query'\nimport type { ReactNode } from 'react'\nimport { createContext, use, useEffect, useMemo } from 'react'\nimport { ApiQueryMagazineAppCatalog } from '~/modules/appCatalog'\nimport { useUiSettings } from '~/context/UiSettingsContext'\nimport { mergeFrontendBuildId } from '~/modules/appCatalog/context/mergeFrontendBuildId'\n\nexport interface AppCatalogContextIface {\n resources: Resource[]\n isLoadingApps: boolean\n tagsDefinitions: GroupingTagDefinition[]\n approvalMethods: AppApprovalMethod[]\n persons: Person[]\n groups: Group[]\n versions?: AppVersionInfo\n}\n\nexport const AppCatalogContext = createContext<\n AppCatalogContextIface | undefined\n>(undefined)\n\ninterface AppCatalogProviderProps {\n children: ReactNode\n}\n\nexport function AppCatalogProvider({ children }: AppCatalogProviderProps) {\n const { data, isLoading: isLoadingApps } = useQuery(\n ApiQueryMagazineAppCatalog.getAppCatalog(),\n )\n const uiSettings = useUiSettings()\n\n const contextValue = useMemo<AppCatalogContextIface>(\n () => ({\n resources: data?.resources ?? [],\n isLoadingApps,\n tagsDefinitions: data?.tagsDefinitions ?? [],\n approvalMethods: data?.approvalMethods ?? [],\n persons: data?.persons ?? [],\n groups: data?.groups ?? [],\n versions: mergeFrontendBuildId(\n data?.versions ?? {},\n uiSettings.frontendBuildId,\n ),\n }),\n [\n data?.approvalMethods,\n data?.resources,\n data?.tagsDefinitions,\n data?.persons,\n data?.groups,\n data?.versions,\n uiSettings.frontendBuildId,\n isLoadingApps,\n ],\n )\n\n // Update document title based on backend version\n useEffect(() => {\n if (data?.versions?.backend?.displayName === 'local') {\n document.title = 'Local'\n } else {\n document.title = 'App Catalog'\n }\n }, [data?.versions?.backend?.displayName])\n\n return <AppCatalogContext value={contextValue}>{children}</AppCatalogContext>\n}\n\nexport function useAppCatalogContext(): AppCatalogContextIface {\n const context = use(AppCatalogContext)\n if (!context) {\n throw new Error(\n 'useAppCatalogContext must be used within AppCatalogProvider',\n )\n }\n return context\n}\n\n/**\n * The catalog, for a deployment that owns its own routes.\n *\n * A deployment composing its own route tree has to resolve its own path params\n * against the catalog — `/app/$slug/…/$account` means nothing until `$account`\n * is matched to a resource. It needs the resources to do that, and the loading\n * flag to tell \"still fetching\" from \"no such resource\", which is the\n * difference between a spinner and a redirect.\n *\n * Deliberately narrower than `useAppCatalogContext`: persons, groups, tags and\n * approval methods are the core's own rendering concerns, and publishing them\n * would make every one of them part of the supported surface.\n */\nexport function useCatalogResources(): {\n resources: Resource[]\n isLoading: boolean\n} {\n const { resources, isLoadingApps } = useAppCatalogContext()\n return { resources, isLoading: isLoadingApps }\n}\n"],"names":["_b","_a"],"mappings":";;;;;;AAyBO,MAAM,oBAAoB,cAE/B,MAAS;AAMJ,SAAS,mBAAmB,EAAE,YAAqC;;AACxE,QAAM,EAAE,MAAM,WAAW,cAAA,IAAkB;AAAA,IACzC,2BAA2B,cAAA;AAAA,EAAc;AAE3C,QAAM,aAAa,cAAA;AAEnB,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA,MACL,YAAW,6BAAM,cAAa,CAAA;AAAA,MAC9B;AAAA,MACA,kBAAiB,6BAAM,oBAAmB,CAAA;AAAA,MAC1C,kBAAiB,6BAAM,oBAAmB,CAAA;AAAA,MAC1C,UAAS,6BAAM,YAAW,CAAA;AAAA,MAC1B,SAAQ,6BAAM,WAAU,CAAA;AAAA,MACxB,UAAU;AAAA,SACR,6BAAM,aAAY,CAAA;AAAA,QAClB,WAAW;AAAA,MAAA;AAAA,IACb;AAAA,IAEF;AAAA,MACE,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,6BAAM;AAAA,MACN,WAAW;AAAA,MACX;AAAA,IAAA;AAAA,EACF;AAIF,YAAU,MAAM;;AACd,UAAIA,OAAAC,MAAA,6BAAM,aAAN,gBAAAA,IAAgB,YAAhB,gBAAAD,IAAyB,iBAAgB,SAAS;AACpD,eAAS,QAAQ;AAAA,IACnB,OAAO;AACL,eAAS,QAAQ;AAAA,IACnB;AAAA,EACF,GAAG,EAAC,wCAAM,aAAN,mBAAgB,YAAhB,mBAAyB,WAAW,CAAC;AAEzC,SAAO,oBAAC,mBAAA,EAAkB,OAAO,cAAe,SAAA,CAAS;AAC3D;AAEO,SAAS,uBAA+C;AAC7D,QAAM,UAAU,IAAI,iBAAiB;AACrC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,SAAO;AACT;AAeO,SAAS,sBAGd;AACA,QAAM,EAAE,WAAW,cAAA,IAAkB,qBAAA;AACrC,SAAO,EAAE,WAAW,WAAW,cAAA;AACjC;"}
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { Search, X, ArrowUpRight } from "lucide-react";
3
- import { useState, use, useEffect, useMemo, useRef, useCallback } from "react";
3
+ import { useState, use, useEffect, useMemo, useDeferredValue, useRef, useCallback } from "react";
4
4
  import { cn } from "../../../../lib/utils.js";
5
5
  import { useAppClickHistory } from "../../hooks/useAppClickHistory.js";
6
6
  import { markdownToPlainText } from "../../utils/markdownToPlainText.js";
@@ -446,7 +446,9 @@ function AppCatalogGrid({
446
446
  return [...apps].filter((a) => !promoted.has(a.slug)).sort((a, b) => a.displayName.localeCompare(b.displayName));
447
447
  }, [apps, frequent]);
448
448
  const browseGroups = useMemo(() => groupByArea(browse), [browse]);
449
- const isSearching = searchValue.trim() !== "";
449
+ const deferredSearch = useDeferredValue(searchValue);
450
+ const isSearching = deferredSearch.trim() !== "";
451
+ const isSearchPending = searchValue !== deferredSearch;
450
452
  const searchRef = useRef(null);
451
453
  useEffect(() => {
452
454
  const onKey = (e) => {
@@ -496,16 +498,23 @@ function AppCatalogGrid({
496
498
  ] }) })
497
499
  ] }),
498
500
  isSearching && /* @__PURE__ */ jsx(
499
- SearchResultsList,
501
+ "div",
500
502
  {
501
- apps: allResources ?? apps,
502
- searchValue,
503
- onAppClick,
504
- onSubClick,
505
- onLaunch,
506
- onClear: () => onSearchChange(""),
507
- keyboardEnabled: !detailOpen,
508
- selectedSubSlug
503
+ className: `transition-opacity ${isSearchPending ? "opacity-60" : ""}`,
504
+ "aria-busy": isSearchPending || void 0,
505
+ children: /* @__PURE__ */ jsx(
506
+ SearchResultsList,
507
+ {
508
+ apps: allResources ?? apps,
509
+ searchValue: deferredSearch,
510
+ onAppClick,
511
+ onSubClick,
512
+ onLaunch,
513
+ onClear: () => onSearchChange(""),
514
+ keyboardEnabled: !detailOpen,
515
+ selectedSubSlug
516
+ }
517
+ )
509
518
  }
510
519
  ),
511
520
  !isSearching && /* @__PURE__ */ jsxs(Fragment, { children: [