@cosmicdrift/kumiko-renderer 0.403.0 → 0.404.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.
@@ -3,7 +3,7 @@ import { isOpenToAllGranted } from "@cosmicdrift/kumiko-framework/ui-types";
3
3
  import { createContext, useContext, useEffect, useMemo, useRef, useState, } from "react";
4
4
  import { createRoot } from "react-dom/client";
5
5
  import { rendererError, rendererWarn } from "../../dev-console.js";
6
- import { AppFeaturesProvider, BasePathProvider, ClientActionsProvider, ColumnRenderersProvider, ContentEditorsProvider, CustomScreensProvider, createSessionEndedSignal, DashboardBodyProvider, DispatcherProvider, DraftStorageProvider, ErrorBoundary, ExtensionSectionsProvider, hasDetailScreen, KumikoScreen, kumikoDefaultTranslations, LeaveGuardDialog, LiveEventsProvider, LocaleProvider, mergeTranslations, NavProvider, navigateWithReturnTo, PrimitivesProvider, qualifyScreenId, ScreenMissingState, SessionEndedSignalProvider, TokensProvider, toAppSchema, translationsByLocaleFromKeys, useNav, usePrimitives, useTranslation, withSessionEndedDetection, } from "../../index.js";
6
+ import { AppFeaturesProvider, BasePathProvider, ClientActionsProvider, ColumnRenderersProvider, ContentEditorsProvider, CustomScreensProvider, createSessionEndedSignal, DashboardBodyProvider, DispatcherProvider, DraftStorageProvider, ErrorBoundary, ExtensionSectionsProvider, hasDetailScreen, KumikoScreen, kumikoDefaultTranslations, LeaveGuardDialog, LiveEventsProvider, LocaleProvider, mergeTranslations, NavProvider, navigateWithReturnTo, PrimitivesProvider, qualifyScreenId, ScreenMissingState, SessionEndedSignalProvider, TokensProvider, toAppSchema, translationsByLocaleFromKeys, useBasePath, useNav, usePrimitives, useTranslation, withSessionEndedDetection, } from "../../index.js";
7
7
  import { createLiveDispatcher } from "../../live/index.js";
8
8
  import { lastSegment } from "../layout/nav-tree.js";
9
9
  import { clearFileMetaCache } from "../primitives/file-thumbnail.js";
@@ -274,6 +274,11 @@ function AppSchemaBoundary({ app, screenQn, schemaUrl, navAdapter, onAppLoaded,
274
274
  }
275
275
  return (_jsx(BrowserNavBoot, { app: app, fallbackQn: fallbackQn, useNavApi: navAdapter, hasWorkspaces: (app.workspaces?.length ?? 0) > 0, ...(translate !== undefined && { translate }), ...(onRowClick !== undefined && { onRowClick }), ...(shell !== undefined && { shell }) }));
276
276
  }
277
+ // createKumikoApp mounts BasePathProvider above the NavProvider that calls this hook.
278
+ function useBrowserNavApiWithBasePath(navOptions) {
279
+ const basePath = useBasePath();
280
+ return useBrowserNavApi({ ...navOptions, ...(basePath !== "" && { basePath }) });
281
+ }
277
282
  export function createKumikoApp(options = {}) {
278
283
  const rootId = options.rootId ?? "root";
279
284
  const container = document.getElementById(rootId);
@@ -342,7 +347,6 @@ export function createKumikoApp(options = {}) {
342
347
  }
343
348
  const localeResolver = options.locale ?? createBrowserLocaleResolver();
344
349
  const { basePath } = options;
345
- const useBrowserNavApiWithBasePath = (navOptions) => useBrowserNavApi({ ...navOptions, ...(basePath !== undefined && { basePath }) });
346
350
  const navAdapter = options.navAdapter ?? useBrowserNavApiWithBasePath;
347
351
  const reporter = options.reportClientErrors === false ? undefined : createClientErrorReporter();
348
352
  reporter?.installGlobalHandlers();
@@ -16,7 +16,7 @@ function openSessionStorage() {
16
16
  }
17
17
  }
18
18
  function prefixedKeys(storage) {
19
- return Array.from({ length: storage.length }, (_, index) => storage.key(index)).filter((key) => key !== null && key.startsWith(KEY_PREFIX));
19
+ return Array.from({ length: storage.length }, (_, index) => storage.key(index)).filter((key) => key?.startsWith(KEY_PREFIX) === true);
20
20
  }
21
21
  function removeAllRemembered(storage) {
22
22
  for (const key of prefixedKeys(storage))
@@ -2,91 +2,91 @@ import type { NavIconKey } from "@cosmicdrift/kumiko-framework/ui-types";
2
2
  import type { ReactNode } from "react";
3
3
  export type { NavIconKey } from "@cosmicdrift/kumiko-framework/ui-types";
4
4
  export declare const NAV_ICONS: {
5
- readonly dashboard: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
6
- readonly "layout-grid": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
7
- readonly "book-open": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
8
- readonly "clipboard-list": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
9
- readonly package: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
10
- readonly gauge: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
11
- readonly list: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
12
- readonly table: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
13
- readonly layers: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
14
- readonly building: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
15
- readonly calculator: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
16
- readonly wallet: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
17
- readonly coins: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
18
- readonly "credit-card": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
19
- readonly "piggy-bank": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
20
- readonly receipt: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
21
- readonly chart: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
22
- readonly "bar-chart": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
23
- readonly trending: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
24
- readonly sparkles: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
25
- readonly wand: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
26
- readonly calendar: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
27
- readonly file: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
28
- readonly folder: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
29
- readonly "folder-open": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
30
- readonly home: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
31
- readonly bell: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
32
- readonly shield: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
33
- readonly "shield-check": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
34
- readonly send: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
35
- readonly settings: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
36
- readonly users: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
37
- readonly user: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
38
- readonly search: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
39
- readonly tag: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
40
- readonly key: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
41
- readonly link: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
42
- readonly palette: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
43
- readonly share: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
44
- readonly server: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
45
- readonly mail: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
46
- readonly lock: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
47
- readonly hash: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
48
- readonly download: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
49
- readonly upload: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
50
- readonly rocket: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
51
- readonly plus: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
52
- readonly languages: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
53
- readonly trash: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
54
- readonly x: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
55
- readonly check: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
56
- readonly "arrow-left": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
57
- readonly "arrow-right": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
58
- readonly copy: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
59
- readonly pencil: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
60
- readonly eye: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
61
- readonly "eye-off": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
62
- readonly filter: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
63
- readonly refresh: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
64
- readonly "more-horizontal": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
65
- readonly "more-vertical": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
66
- readonly "external-link": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
67
- readonly "chevron-down": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
68
- readonly "chevron-left": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
69
- readonly "chevron-right": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
70
- readonly "chevron-up": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
71
- readonly save: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
72
- readonly undo: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
73
- readonly archive: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
74
- readonly star: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
75
- readonly flag: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
76
- readonly clock: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
77
- readonly "map-pin": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
78
- readonly phone: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
79
- readonly printer: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
80
- readonly "alert-triangle": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
81
- readonly info: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
82
- readonly "check-circle": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
83
- readonly "x-circle": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
84
- readonly loader: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
85
- readonly mic: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
86
- readonly "circle-stop": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
87
- readonly image: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
88
- readonly camera: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
89
- readonly headphones: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
5
+ readonly dashboard: import("lucide-react").LucideIcon;
6
+ readonly "layout-grid": import("lucide-react").LucideIcon;
7
+ readonly "book-open": import("lucide-react").LucideIcon;
8
+ readonly "clipboard-list": import("lucide-react").LucideIcon;
9
+ readonly package: import("lucide-react").LucideIcon;
10
+ readonly gauge: import("lucide-react").LucideIcon;
11
+ readonly list: import("lucide-react").LucideIcon;
12
+ readonly table: import("lucide-react").LucideIcon;
13
+ readonly layers: import("lucide-react").LucideIcon;
14
+ readonly building: import("lucide-react").LucideIcon;
15
+ readonly calculator: import("lucide-react").LucideIcon;
16
+ readonly wallet: import("lucide-react").LucideIcon;
17
+ readonly coins: import("lucide-react").LucideIcon;
18
+ readonly "credit-card": import("lucide-react").LucideIcon;
19
+ readonly "piggy-bank": import("lucide-react").LucideIcon;
20
+ readonly receipt: import("lucide-react").LucideIcon;
21
+ readonly chart: import("lucide-react").LucideIcon;
22
+ readonly "bar-chart": import("lucide-react").LucideIcon;
23
+ readonly trending: import("lucide-react").LucideIcon;
24
+ readonly sparkles: import("lucide-react").LucideIcon;
25
+ readonly wand: import("lucide-react").LucideIcon;
26
+ readonly calendar: import("lucide-react").LucideIcon;
27
+ readonly file: import("lucide-react").LucideIcon;
28
+ readonly folder: import("lucide-react").LucideIcon;
29
+ readonly "folder-open": import("lucide-react").LucideIcon;
30
+ readonly home: import("lucide-react").LucideIcon;
31
+ readonly bell: import("lucide-react").LucideIcon;
32
+ readonly shield: import("lucide-react").LucideIcon;
33
+ readonly "shield-check": import("lucide-react").LucideIcon;
34
+ readonly send: import("lucide-react").LucideIcon;
35
+ readonly settings: import("lucide-react").LucideIcon;
36
+ readonly users: import("lucide-react").LucideIcon;
37
+ readonly user: import("lucide-react").LucideIcon;
38
+ readonly search: import("lucide-react").LucideIcon;
39
+ readonly tag: import("lucide-react").LucideIcon;
40
+ readonly key: import("lucide-react").LucideIcon;
41
+ readonly link: import("lucide-react").LucideIcon;
42
+ readonly palette: import("lucide-react").LucideIcon;
43
+ readonly share: import("lucide-react").LucideIcon;
44
+ readonly server: import("lucide-react").LucideIcon;
45
+ readonly mail: import("lucide-react").LucideIcon;
46
+ readonly lock: import("lucide-react").LucideIcon;
47
+ readonly hash: import("lucide-react").LucideIcon;
48
+ readonly download: import("lucide-react").LucideIcon;
49
+ readonly upload: import("lucide-react").LucideIcon;
50
+ readonly rocket: import("lucide-react").LucideIcon;
51
+ readonly plus: import("lucide-react").LucideIcon;
52
+ readonly languages: import("lucide-react").LucideIcon;
53
+ readonly trash: import("lucide-react").LucideIcon;
54
+ readonly x: import("lucide-react").LucideIcon;
55
+ readonly check: import("lucide-react").LucideIcon;
56
+ readonly "arrow-left": import("lucide-react").LucideIcon;
57
+ readonly "arrow-right": import("lucide-react").LucideIcon;
58
+ readonly copy: import("lucide-react").LucideIcon;
59
+ readonly pencil: import("lucide-react").LucideIcon;
60
+ readonly eye: import("lucide-react").LucideIcon;
61
+ readonly "eye-off": import("lucide-react").LucideIcon;
62
+ readonly filter: import("lucide-react").LucideIcon;
63
+ readonly refresh: import("lucide-react").LucideIcon;
64
+ readonly "more-horizontal": import("lucide-react").LucideIcon;
65
+ readonly "more-vertical": import("lucide-react").LucideIcon;
66
+ readonly "external-link": import("lucide-react").LucideIcon;
67
+ readonly "chevron-down": import("lucide-react").LucideIcon;
68
+ readonly "chevron-left": import("lucide-react").LucideIcon;
69
+ readonly "chevron-right": import("lucide-react").LucideIcon;
70
+ readonly "chevron-up": import("lucide-react").LucideIcon;
71
+ readonly save: import("lucide-react").LucideIcon;
72
+ readonly undo: import("lucide-react").LucideIcon;
73
+ readonly archive: import("lucide-react").LucideIcon;
74
+ readonly star: import("lucide-react").LucideIcon;
75
+ readonly flag: import("lucide-react").LucideIcon;
76
+ readonly clock: import("lucide-react").LucideIcon;
77
+ readonly "map-pin": import("lucide-react").LucideIcon;
78
+ readonly phone: import("lucide-react").LucideIcon;
79
+ readonly printer: import("lucide-react").LucideIcon;
80
+ readonly "alert-triangle": import("lucide-react").LucideIcon;
81
+ readonly info: import("lucide-react").LucideIcon;
82
+ readonly "check-circle": import("lucide-react").LucideIcon;
83
+ readonly "x-circle": import("lucide-react").LucideIcon;
84
+ readonly loader: import("lucide-react").LucideIcon;
85
+ readonly mic: import("lucide-react").LucideIcon;
86
+ readonly "circle-stop": import("lucide-react").LucideIcon;
87
+ readonly image: import("lucide-react").LucideIcon;
88
+ readonly camera: import("lucide-react").LucideIcon;
89
+ readonly headphones: import("lucide-react").LucideIcon;
90
90
  };
91
91
  export declare function Icon({ name, className, }: {
92
92
  readonly name: NavIconKey;
@@ -7,19 +7,19 @@ import "../react-css-custom-properties.ts";
7
7
  export declare function fieldLabelId(id: string): string;
8
8
  export declare function DefaultField(props: FieldProps): ReactNode;
9
9
  declare const FIELD_ICONS: {
10
- readonly mail: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
11
- readonly lock: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
12
- readonly hash: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
13
- readonly search: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
14
- readonly user: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
15
- readonly phone: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
16
- readonly calendar: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
17
- readonly link: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
18
- readonly tag: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
19
- readonly building: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
20
- readonly globe: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
21
- readonly key: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
22
- readonly "map-pin": import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
10
+ readonly mail: import("lucide-react").LucideIcon;
11
+ readonly lock: import("lucide-react").LucideIcon;
12
+ readonly hash: import("lucide-react").LucideIcon;
13
+ readonly search: import("lucide-react").LucideIcon;
14
+ readonly user: import("lucide-react").LucideIcon;
15
+ readonly phone: import("lucide-react").LucideIcon;
16
+ readonly calendar: import("lucide-react").LucideIcon;
17
+ readonly link: import("lucide-react").LucideIcon;
18
+ readonly tag: import("lucide-react").LucideIcon;
19
+ readonly building: import("lucide-react").LucideIcon;
20
+ readonly globe: import("lucide-react").LucideIcon;
21
+ readonly key: import("lucide-react").LucideIcon;
22
+ readonly "map-pin": import("lucide-react").LucideIcon;
23
23
  };
24
24
  export declare function fieldIconFor(icon: string | undefined): (typeof FIELD_ICONS)[FieldIconKey] | undefined;
25
25
  export declare const FIELD_ICON_CLASS = "pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground";
@@ -10,6 +10,8 @@ export type CreateEventSourceLiveEventsOptions = {
10
10
  readonly onSessionEnded?: () => void;
11
11
  /** Reconnect-timer seam; defaults to the global setTimeout/clearTimeout. */
12
12
  readonly timers?: LiveEventTimers;
13
+ /** Stall-watchdog timer seam; defaults to the global setTimeout/clearTimeout. */
14
+ readonly stallTimers?: LiveEventTimers;
13
15
  /** Jitter source in [0, 1); defaults to Math.random. */
14
16
  readonly random?: () => number;
15
17
  /** Clock for synthetic resync events; defaults to Date.now. */
@@ -21,6 +23,7 @@ export type LiveEventTimers = {
21
23
  };
22
24
  export declare const RECONNECT_BASE_DELAY_MS = 1000;
23
25
  export declare const RECONNECT_MAX_DELAY_MS = 30000;
26
+ export declare const SSE_STALL_TIMEOUT_MS = 30000;
24
27
  export declare function reconnectDelayMs(attempt: number, random: number): number;
25
28
  /** Returns an EventSource-backed `LiveEventSubscriber`.
26
29
  * Typically called once during app bootstrap and passed as a value to
@@ -3,6 +3,9 @@ import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
3
3
  import { isSessionEndedError, LIVE_RESYNC_EVENT_TYPE, } from "../../index.js";
4
4
  export const RECONNECT_BASE_DELAY_MS = 1_000;
5
5
  export const RECONNECT_MAX_DELAY_MS = 30_000;
6
+ // Two missed server heartbeats (15 s each, SSE_HEARTBEAT_INTERVAL_MS in framework sse-route.ts).
7
+ // A KEDA interceptor can leave the stream hanging without an `error` event (kedacore/http-add-on#1803).
8
+ export const SSE_STALL_TIMEOUT_MS = 30_000;
6
9
  const defaultTimers = {
7
10
  set: (callback, delayMs) => {
8
11
  const handle = setTimeout(callback, delayMs);
@@ -67,6 +70,7 @@ async function isSessionEndedRejection(url) {
67
70
  export function createEventSourceLiveEvents(options = {}) {
68
71
  const url = options.url ?? "/api/sse";
69
72
  const timers = options.timers ?? defaultTimers;
73
+ const stallTimers = options.stallTimers ?? defaultTimers;
70
74
  const random = options.random ?? Math.random;
71
75
  const now = options.now ?? Date.now;
72
76
  const subscribers = new Set();
@@ -74,6 +78,25 @@ export function createEventSourceLiveEvents(options = {}) {
74
78
  const wiredEntities = new Set();
75
79
  let cancelReconnect;
76
80
  let reconnectAttempt = 0;
81
+ let cancelStallWatchdog;
82
+ const disarmStallWatchdog = () => {
83
+ cancelStallWatchdog?.();
84
+ cancelStallWatchdog = undefined;
85
+ };
86
+ const armStallWatchdog = (connection) => {
87
+ // skip: a frame from a connection that is no longer the current one
88
+ if (source !== connection)
89
+ return;
90
+ disarmStallWatchdog();
91
+ cancelStallWatchdog = stallTimers.set(() => {
92
+ cancelStallWatchdog = undefined;
93
+ // skip: a late timer of a connection that was already replaced
94
+ if (source !== connection)
95
+ return;
96
+ connection.close();
97
+ scheduleReconnect();
98
+ }, SSE_STALL_TIMEOUT_MS);
99
+ };
77
100
  // `subscribers` is a flat Set across all entities — the browser-side
78
101
  // addEventListener(entityName, ...) gate below only filters which frames
79
102
  // arrive at all, not which subscriber a given frame is for. Without this
@@ -119,6 +142,7 @@ export function createEventSourceLiveEvents(options = {}) {
119
142
  return;
120
143
  connection.addEventListener(entityName, (e) => {
121
144
  const data = propertyOf(e, "data");
145
+ armStallWatchdog(connection);
122
146
  if (typeof data === "string")
123
147
  handleEvent(data, entityName);
124
148
  });
@@ -142,14 +166,22 @@ export function createEventSourceLiveEvents(options = {}) {
142
166
  sessionEndedSignalled = true;
143
167
  onSessionEnded?.();
144
168
  };
169
+ armStallWatchdog(connection);
170
+ connection.addEventListener("ping", () => armStallWatchdog(connection));
145
171
  connection.addEventListener("open", () => {
172
+ armStallWatchdog(connection);
146
173
  reconnectAttempt = 0;
147
174
  emitResync();
148
175
  });
149
176
  connection.addEventListener(SESSION_ENDED_EVENT, (e) => {
150
177
  const code = sessionEndedFrameCode(e);
151
- if (code !== undefined && isSessionEndedError({ code, httpStatus: 401 }))
178
+ if (code !== undefined && isSessionEndedError({ code, httpStatus: 401 })) {
179
+ disarmStallWatchdog();
152
180
  signalSessionEnded();
181
+ // skip: a reconnect after a session end would only be refused again
182
+ return;
183
+ }
184
+ armStallWatchdog(connection);
153
185
  });
154
186
  connection.addEventListener("error", () => {
155
187
  // skip: CONNECTING means a dropped stream the browser retries itself;
@@ -161,10 +193,12 @@ export function createEventSourceLiveEvents(options = {}) {
161
193
  if (source !== connection)
162
194
  return;
163
195
  if (sessionEnded) {
196
+ disarmStallWatchdog();
164
197
  signalSessionEnded();
165
198
  // skip: the session ended, reconnecting would only be refused again
166
199
  return;
167
200
  }
201
+ disarmStallWatchdog();
168
202
  scheduleReconnect();
169
203
  });
170
204
  });
@@ -196,6 +230,7 @@ export function createEventSourceLiveEvents(options = {}) {
196
230
  cancelReconnect = undefined;
197
231
  }
198
232
  reconnectAttempt = 0;
233
+ disarmStallWatchdog();
199
234
  // skip: already closed, nothing to close
200
235
  if (source === undefined)
201
236
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-renderer",
3
- "version": "0.403.0",
3
+ "version": "0.404.1",
4
4
  "description": "React renderer for Kumiko screens. The root export holds the platform-agnostic logic (primitives contract, hooks, KumikoScreen, navigation and SSE abstractions); ./headless the DOM-free UI logic, ./live the HTTP dispatcher, ./web the browser bindings (HTML primitives, history navigation, EventSource live events, createKumikoApp).",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -48,21 +48,21 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@cosmicdrift/kumiko-framework": "0.403.0",
52
- "@cosmicdrift/kumiko-types": "0.403.0",
53
- "@types/qrcode": "^1.5.5",
51
+ "@cosmicdrift/kumiko-framework": "0.404.1",
52
+ "@cosmicdrift/kumiko-types": "0.404.1",
53
+ "@types/qrcode": "^1.5.6",
54
54
  "class-variance-authority": "^0.7.1",
55
55
  "clsx": "^2.1.1",
56
56
  "cmdk": "^1.1.1",
57
- "lucide-react": "^1.14.0",
58
- "pdfjs-dist": "6.3.289",
57
+ "lucide-react": "^1.65.0",
58
+ "pdfjs-dist": "6.4.299",
59
59
  "qrcode": "^1.5.4",
60
60
  "radix-ui": "^1.8.1",
61
- "react": "^19.2.6",
62
- "react-day-picker": "^10.0.0",
63
- "react-dom": "^19.2.6",
64
- "react-resizable-panels": "^4.12.2",
65
- "tailwind-merge": "^3.6.0",
61
+ "react": "^19.3.1",
62
+ "react-day-picker": "^10.0.2",
63
+ "react-dom": "^19.3.1",
64
+ "react-resizable-panels": "^4.15.2",
65
+ "tailwind-merge": "^3.7.0",
66
66
  "temporal-polyfill": "^0.3.2",
67
67
  "zod": "^4.6.7"
68
68
  },
@@ -79,13 +79,13 @@
79
79
  }
80
80
  },
81
81
  "devDependencies": {
82
- "@cosmicdrift/kumiko-locale-de": "0.403.0",
83
- "@cosmicdrift/kumiko-testing": "0.403.0",
82
+ "@cosmicdrift/kumiko-locale-de": "0.404.1",
83
+ "@cosmicdrift/kumiko-testing": "0.404.1",
84
84
  "@tailwindcss/cli": "^4.3.3",
85
85
  "@testing-library/react": "^16.3.3",
86
86
  "@testing-library/user-event": "^14.6.8",
87
- "@types/react": "^19.2.14",
88
- "@types/react-dom": "^19.2.3",
87
+ "@types/react": "^19.3.0",
88
+ "@types/react-dom": "^19.3.0",
89
89
  "jsdom": "^29.1.1",
90
90
  "tailwindcss": "^4.3.3"
91
91
  },
package/src/changes.json CHANGED
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.404.0",
4
+ "type": "fix",
5
+ "title": "SSE client reconnects after a stalled stream",
6
+ "detail": "createEventSourceLiveEvents arms a 30 s watchdog (two missed server heartbeats) that is reset by open, ping and every entity or session-ended frame. On expiry it closes the connection and reconnects through the existing backoff path, which resyncs all listeners. The timer is cleared on close and error. The seam is the new stallTimers option."
7
+ },
2
8
  {
3
9
  "version": "0.403.0",
4
10
  "type": "improvement",