@treasuryspatial/viewer-react 0.1.28 → 0.1.32

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 (39) hide show
  1. package/dist/composerRouting.d.ts +21 -0
  2. package/dist/composerRouting.d.ts.map +1 -0
  3. package/dist/composerRouting.js +44 -0
  4. package/dist/index.d.ts +20 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +11 -0
  7. package/dist/sessionAuth.d.ts +9 -0
  8. package/dist/sessionAuth.d.ts.map +1 -0
  9. package/dist/sessionAuth.js +15 -0
  10. package/dist/tsconfig.tsbuildinfo +1 -1
  11. package/dist/useClientHydrated.d.ts +2 -0
  12. package/dist/useClientHydrated.d.ts.map +1 -0
  13. package/dist/useClientHydrated.js +8 -0
  14. package/dist/useComposerAuthState.js +11 -14
  15. package/dist/useComposerSurfaceBranch.d.ts +11 -0
  16. package/dist/useComposerSurfaceBranch.d.ts.map +1 -0
  17. package/dist/useComposerSurfaceBranch.js +16 -0
  18. package/dist/useGrasshopperSession.d.ts.map +1 -1
  19. package/dist/useGrasshopperSession.js +7 -1
  20. package/dist/useModuleWorkspace.js +1 -1
  21. package/dist/usePromptPackSelectionByMode.d.ts +33 -0
  22. package/dist/usePromptPackSelectionByMode.d.ts.map +1 -0
  23. package/dist/usePromptPackSelectionByMode.js +102 -0
  24. package/dist/useSessionLogout.d.ts +9 -0
  25. package/dist/useSessionLogout.d.ts.map +1 -0
  26. package/dist/useSessionLogout.js +17 -0
  27. package/dist/useSessionRevocationPing.d.ts +8 -0
  28. package/dist/useSessionRevocationPing.d.ts.map +1 -0
  29. package/dist/useSessionRevocationPing.js +30 -0
  30. package/dist/useShellPanelToggle.d.ts +12 -0
  31. package/dist/useShellPanelToggle.d.ts.map +1 -0
  32. package/dist/useShellPanelToggle.js +40 -0
  33. package/dist/useSurfaceWarmup.d.ts +7 -0
  34. package/dist/useSurfaceWarmup.d.ts.map +1 -0
  35. package/dist/useSurfaceWarmup.js +32 -0
  36. package/dist/useThemeMode.d.ts +12 -0
  37. package/dist/useThemeMode.d.ts.map +1 -0
  38. package/dist/useThemeMode.js +26 -0
  39. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export declare function useClientHydrated(): boolean;
2
+ //# sourceMappingURL=useClientHydrated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useClientHydrated.d.ts","sourceRoot":"","sources":["../src/useClientHydrated.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C"}
@@ -0,0 +1,8 @@
1
+ import { useEffect, useState } from "react";
2
+ export function useClientHydrated() {
3
+ const [hydrated, setHydrated] = useState(false);
4
+ useEffect(() => {
5
+ setHydrated(true);
6
+ }, []);
7
+ return hydrated;
8
+ }
@@ -20,7 +20,7 @@ export function useComposerAuthState(options = {}) {
20
20
  isAuthenticated,
21
21
  };
22
22
  }, [adminRoleValue, nameCookieKey, roleCookieKey, userCookieKey]);
23
- const initial = useMemo(() => {
23
+ const contextAuth = useMemo(() => {
24
24
  const fromContext = sessionContext?.auth ?? null;
25
25
  if (fromContext && typeof fromContext === "object") {
26
26
  return {
@@ -30,9 +30,14 @@ export function useComposerAuthState(options = {}) {
30
30
  isAuthenticated: Boolean(fromContext.isAuthenticated),
31
31
  };
32
32
  }
33
- return readAuth();
33
+ return null;
34
34
  }, [readAuth, sessionContext?.auth]);
35
- const [auth, setAuth] = useState(initial);
35
+ const [auth, setAuth] = useState(contextAuth ?? {
36
+ user: null,
37
+ name: null,
38
+ isAdmin: false,
39
+ isAuthenticated: false,
40
+ });
36
41
  const syncContext = useCallback((next) => {
37
42
  if (!setSessionContext)
38
43
  return;
@@ -50,18 +55,10 @@ export function useComposerAuthState(options = {}) {
50
55
  syncContext(next);
51
56
  }, [readAuth, syncContext]);
52
57
  useEffect(() => {
53
- if (!sessionContext)
54
- return;
55
- const fromContext = sessionContext.auth;
56
- if (!fromContext)
58
+ if (!contextAuth)
57
59
  return;
58
- setAuth({
59
- user: fromContext.user ?? null,
60
- name: fromContext.name ?? null,
61
- isAdmin: Boolean(fromContext.isAdmin),
62
- isAuthenticated: Boolean(fromContext.isAuthenticated),
63
- });
64
- }, [sessionContext?.auth]);
60
+ setAuth(contextAuth);
61
+ }, [contextAuth]);
65
62
  useEffect(() => {
66
63
  refresh();
67
64
  }, [refresh]);
@@ -0,0 +1,11 @@
1
+ import type { ComposerMode } from "./composerRouting";
2
+ export type ComposerSurfaceBranch = "guide" | "loading" | "locked" | "gate" | "map-coming" | "runner";
3
+ export type UseComposerSurfaceBranchOptions = {
4
+ hydrated: boolean;
5
+ mode: ComposerMode;
6
+ showGuide: boolean;
7
+ showLocked: boolean;
8
+ showGate: boolean;
9
+ };
10
+ export declare function useComposerSurfaceBranch({ hydrated, mode, showGuide, showLocked, showGate, }: UseComposerSurfaceBranchOptions): ComposerSurfaceBranch;
11
+ //# sourceMappingURL=useComposerSurfaceBranch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useComposerSurfaceBranch.d.ts","sourceRoot":"","sources":["../src/useComposerSurfaceBranch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,MAAM,qBAAqB,GAC7B,OAAO,GACP,SAAS,GACT,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,QAAQ,CAAC;AAEb,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,UAAU,EACV,QAAQ,GACT,EAAE,+BAA+B,GAAG,qBAAqB,CASzD"}
@@ -0,0 +1,16 @@
1
+ import { useMemo } from "react";
2
+ export function useComposerSurfaceBranch({ hydrated, mode, showGuide, showLocked, showGate, }) {
3
+ return useMemo(() => {
4
+ if (showGuide)
5
+ return "guide";
6
+ if (!hydrated)
7
+ return "loading";
8
+ if (showLocked)
9
+ return "locked";
10
+ if (showGate)
11
+ return "gate";
12
+ if (mode === "map")
13
+ return "map-coming";
14
+ return "runner";
15
+ }, [hydrated, mode, showGate, showGuide, showLocked]);
16
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"useGrasshopperSession.d.ts","sourceRoot":"","sources":["../src/useGrasshopperSession.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EACV,gBAAgB,EAChB,sBAAsB,EACtB,YAAY,EACb,MAAM,6BAA6B,CAAC;AAQrC,KAAK,iBAAiB,GAAG,gBAAgB,GAAG;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiGF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;QAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;IACnD,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAClD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACrD,KAAK,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;IAC5F,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EACpC,QAA0B,EAC1B,OAAO,EACP,kBAAkB,EAClB,UAAU,EACV,OAAO,GACR,GAAE,yBAA8B,GAAG,uBAAuB,CA0H1D"}
1
+ {"version":3,"file":"useGrasshopperSession.d.ts","sourceRoot":"","sources":["../src/useGrasshopperSession.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EACV,gBAAgB,EAChB,sBAAsB,EACtB,YAAY,EACb,MAAM,6BAA6B,CAAC;AAQrC,KAAK,iBAAiB,GAAG,gBAAgB,GAAG;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiGF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;QACnB,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;QAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;IACnD,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAClD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACrD,KAAK,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;IAC5F,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EACpC,QAA0B,EAC1B,OAAO,EACP,kBAAkB,EAClB,UAAU,EACV,OAAO,GACR,GAAE,yBAA8B,GAAG,uBAAuB,CAgI1D"}
@@ -113,7 +113,13 @@ export function useGrasshopperSession({ apiRoute = "/api/gh-solve", preview, all
113
113
  id: "gh-inputs",
114
114
  label: "grasshopper inputs",
115
115
  description: "Inputs detected from the uploaded Grasshopper definition.",
116
- fields: inputs.map((input, index) => buildGhPanelField(input, index)),
116
+ groups: [
117
+ {
118
+ id: "gh-inputs-main",
119
+ label: "inputs",
120
+ fields: inputs.map((input, index) => buildGhPanelField(input, index)),
121
+ },
122
+ ],
117
123
  },
118
124
  ],
119
125
  };
@@ -7,7 +7,7 @@ export function useModuleWorkspace({ enabled = true, registry, grasshopper, init
7
7
  const [sourceMode, setSourceMode] = useState(initialSourceMode);
8
8
  const handleUploadSolved = useCallback((result) => {
9
9
  ghSession.applyUploadResult(result);
10
- if (result.kind === "grasshopper") {
10
+ if (result.kind === "grasshopper" || result.kind === "geometry3dm" || result.kind === "mesh") {
11
11
  setSourceMode("upload");
12
12
  }
13
13
  }, [ghSession]);
@@ -0,0 +1,33 @@
1
+ export type PromptPackMode = "imagine" | "design" | "map";
2
+ export type PromptPackRef = {
3
+ id?: string;
4
+ version?: string;
5
+ };
6
+ export type PromptPackSelectionByMode = Partial<Record<PromptPackMode, PromptPackRef>>;
7
+ export type PromptPackCatalogEntry = {
8
+ id: string;
9
+ label: string;
10
+ version?: string;
11
+ description?: string;
12
+ };
13
+ export type UsePromptPackSelectionByModeOptions = {
14
+ mode: PromptPackMode;
15
+ catalog: PromptPackCatalogEntry[];
16
+ envSelection: PromptPackSelectionByMode;
17
+ overrideSelection?: PromptPackSelectionByMode | null;
18
+ sessionContext?: Record<string, unknown> | null;
19
+ setSessionContext?: (partial: Record<string, unknown>) => void;
20
+ contextKey?: string;
21
+ legacyContextKey?: string;
22
+ };
23
+ export declare const PROMPT_PACK_MODES: PromptPackMode[];
24
+ export declare function usePromptPackSelectionByMode({ mode, catalog, envSelection, overrideSelection, sessionContext, setSessionContext, contextKey, legacyContextKey, }: UsePromptPackSelectionByModeOptions): {
25
+ activeRef: PromptPackRef | undefined;
26
+ activeLabel: string;
27
+ labelsByMode: Record<PromptPackMode, string>;
28
+ catalogById: Record<string, PromptPackCatalogEntry>;
29
+ setSelection: (mode: PromptPackMode, packId: string) => void;
30
+ resetSelection: () => void;
31
+ mergedSelection: PromptPackSelectionByMode;
32
+ };
33
+ //# sourceMappingURL=usePromptPackSelectionByMode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePromptPackSelectionByMode.d.ts","sourceRoot":"","sources":["../src/usePromptPackSelectionByMode.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,CAAC;AAEvF,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG;IAChD,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClC,YAAY,EAAE,yBAAyB,CAAC;IACxC,iBAAiB,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChD,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAAc,EAAiC,CAAC;AAiChF,wBAAgB,4BAA4B,CAAC,EAC3C,IAAI,EACJ,OAAO,EACP,YAAY,EACZ,iBAAwB,EACxB,cAAqB,EACrB,iBAAiB,EACjB,UAAkC,EAClC,gBAAgD,GACjD,EAAE,mCAAmC,GAAG;IACvC,SAAS,EAAE,aAAa,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACpD,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,yBAAyB,CAAC;CAC5C,CAiFA"}
@@ -0,0 +1,102 @@
1
+ import { useCallback, useMemo } from "react";
2
+ export const PROMPT_PACK_MODES = ["imagine", "design", "map"];
3
+ const normalizeSelection = (raw) => {
4
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
5
+ return null;
6
+ const next = {};
7
+ PROMPT_PACK_MODES.forEach((mode) => {
8
+ const entry = raw[mode];
9
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
10
+ return;
11
+ const id = typeof entry.id === "string" ? entry.id?.trim() : undefined;
12
+ const version = typeof entry.version === "string" ? entry.version?.trim() : undefined;
13
+ if (id || version) {
14
+ next[mode] = { id: id || undefined, version: version || undefined };
15
+ }
16
+ });
17
+ return Object.keys(next).length ? next : null;
18
+ };
19
+ const mergeRef = (base, override) => {
20
+ const id = override?.id ?? base?.id;
21
+ const version = override?.version ?? base?.version;
22
+ if (!id && !version)
23
+ return undefined;
24
+ return { id, version };
25
+ };
26
+ const formatLabel = (ref, catalogById) => {
27
+ if (!ref?.id)
28
+ return "default";
29
+ const entry = catalogById[ref.id];
30
+ const label = entry?.label || ref.id;
31
+ const version = ref.version ?? entry?.version;
32
+ return version ? `${label}@${version}` : label;
33
+ };
34
+ export function usePromptPackSelectionByMode({ mode, catalog, envSelection, overrideSelection = null, sessionContext = null, setSessionContext, contextKey = "promptPackRefByMode", legacyContextKey = "promptPackRefByModeOverride", }) {
35
+ const catalogById = useMemo(() => Object.fromEntries(catalog.map((entry) => [entry.id, entry])), [catalog]);
36
+ const sessionSelection = useMemo(() => {
37
+ const raw = (sessionContext ?? {})[contextKey] ?? (sessionContext ?? {})[legacyContextKey];
38
+ return normalizeSelection(raw);
39
+ }, [contextKey, legacyContextKey, sessionContext]);
40
+ const mergedOverrides = useMemo(() => {
41
+ if (!overrideSelection && !sessionSelection)
42
+ return null;
43
+ return {
44
+ ...(overrideSelection ?? {}),
45
+ ...(sessionSelection ?? {}),
46
+ };
47
+ }, [overrideSelection, sessionSelection]);
48
+ const mergedSelection = useMemo(() => {
49
+ if (!mergedOverrides)
50
+ return envSelection;
51
+ return {
52
+ imagine: mergeRef(envSelection.imagine, mergedOverrides.imagine),
53
+ design: mergeRef(envSelection.design, mergedOverrides.design),
54
+ map: mergeRef(envSelection.map, mergedOverrides.map),
55
+ };
56
+ }, [envSelection, mergedOverrides]);
57
+ const activeRef = useMemo(() => mergedSelection[mode], [mergedSelection, mode]);
58
+ const activeLabel = useMemo(() => formatLabel(activeRef, catalogById), [activeRef, catalogById]);
59
+ const labelsByMode = useMemo(() => ({
60
+ imagine: formatLabel(mergedSelection.imagine, catalogById),
61
+ design: formatLabel(mergedSelection.design, catalogById),
62
+ map: formatLabel(mergedSelection.map, catalogById),
63
+ }), [catalogById, mergedSelection]);
64
+ const setSelection = useCallback((modeKey, packId) => {
65
+ if (!setSessionContext)
66
+ return;
67
+ const trimmedPackId = packId.trim();
68
+ const existing = (sessionSelection ?? {});
69
+ const next = { ...existing };
70
+ if (!trimmedPackId) {
71
+ delete next[modeKey];
72
+ }
73
+ else {
74
+ const catalogEntry = catalogById[trimmedPackId];
75
+ next[modeKey] = {
76
+ id: trimmedPackId,
77
+ version: catalogEntry?.version || undefined,
78
+ };
79
+ }
80
+ setSessionContext({
81
+ [contextKey]: Object.keys(next).length ? next : null,
82
+ [legacyContextKey]: null,
83
+ });
84
+ }, [catalogById, contextKey, legacyContextKey, sessionSelection, setSessionContext]);
85
+ const resetSelection = useCallback(() => {
86
+ if (!setSessionContext)
87
+ return;
88
+ setSessionContext({
89
+ [contextKey]: null,
90
+ [legacyContextKey]: null,
91
+ });
92
+ }, [contextKey, legacyContextKey, setSessionContext]);
93
+ return {
94
+ activeRef,
95
+ activeLabel,
96
+ labelsByMode,
97
+ catalogById,
98
+ setSelection,
99
+ resetSelection,
100
+ mergedSelection,
101
+ };
102
+ }
@@ -0,0 +1,9 @@
1
+ import { type SessionCookieKeys } from "./sessionAuth";
2
+ export type UseSessionLogoutOptions = {
3
+ logoutRoute?: string;
4
+ redirectPath?: string;
5
+ cookieKeys?: SessionCookieKeys;
6
+ onRedirect?: (path: string) => void;
7
+ };
8
+ export declare function useSessionLogout({ logoutRoute, redirectPath, cookieKeys, onRedirect, }?: UseSessionLogoutOptions): () => void;
9
+ //# sourceMappingURL=useSessionLogout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSessionLogout.d.ts","sourceRoot":"","sources":["../src/useSessionLogout.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5E,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,WAAwC,EACxC,YAAuB,EACvB,UAAU,EACV,UAAU,GACX,GAAE,uBAA4B,GAAG,MAAM,IAAI,CAc3C"}
@@ -0,0 +1,17 @@
1
+ import { useCallback } from "react";
2
+ import { clearSessionCookies } from "./sessionAuth";
3
+ export function useSessionLogout({ logoutRoute = "/api/auth/session/logout", redirectPath = "/login", cookieKeys, onRedirect, } = {}) {
4
+ return useCallback(() => {
5
+ fetch(logoutRoute, { method: "POST" }).catch(() => {
6
+ // ignore logout sync failures
7
+ });
8
+ clearSessionCookies(cookieKeys);
9
+ if (onRedirect) {
10
+ onRedirect(redirectPath);
11
+ return;
12
+ }
13
+ if (typeof window !== "undefined") {
14
+ window.location.href = redirectPath;
15
+ }
16
+ }, [cookieKeys, logoutRoute, onRedirect, redirectPath]);
17
+ }
@@ -0,0 +1,8 @@
1
+ export type UseSessionRevocationPingOptions = {
2
+ enabled: boolean;
3
+ pingRoute?: string;
4
+ intervalMs?: number;
5
+ onRevoked?: () => void;
6
+ };
7
+ export declare function useSessionRevocationPing({ enabled, pingRoute, intervalMs, onRevoked, }: UseSessionRevocationPingOptions): void;
8
+ //# sourceMappingURL=useSessionRevocationPing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSessionRevocationPing.d.ts","sourceRoot":"","sources":["../src/useSessionRevocationPing.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,+BAA+B,GAAG;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,SAAoC,EACpC,UAAmB,EACnB,SAAS,GACV,EAAE,+BAA+B,QA2BjC"}
@@ -0,0 +1,30 @@
1
+ import { useEffect } from "react";
2
+ export function useSessionRevocationPing({ enabled, pingRoute = "/api/auth/session/ping", intervalMs = 60_000, onRevoked, }) {
3
+ useEffect(() => {
4
+ if (!enabled)
5
+ return;
6
+ let active = true;
7
+ const ping = async () => {
8
+ try {
9
+ const response = await fetch(pingRoute, { method: "POST" });
10
+ const data = await response.json().catch(() => null);
11
+ if (!active)
12
+ return;
13
+ if (data?.revoked) {
14
+ onRevoked?.();
15
+ }
16
+ }
17
+ catch {
18
+ // ignore ping failures
19
+ }
20
+ };
21
+ void ping();
22
+ const interval = window.setInterval(() => {
23
+ void ping();
24
+ }, intervalMs);
25
+ return () => {
26
+ active = false;
27
+ window.clearInterval(interval);
28
+ };
29
+ }, [enabled, intervalMs, onRevoked, pingRoute]);
30
+ }
@@ -0,0 +1,12 @@
1
+ export type UseShellPanelToggleOptions = {
2
+ enabled?: boolean;
3
+ bodyClassName?: string;
4
+ eventName?: string;
5
+ };
6
+ export type ShellPanelToggleState = {
7
+ collapsed: boolean;
8
+ toggle: () => void;
9
+ setCollapsed: (next: boolean) => void;
10
+ };
11
+ export declare function useShellPanelToggle({ enabled, bodyClassName, eventName, }?: UseShellPanelToggleOptions): ShellPanelToggleState;
12
+ //# sourceMappingURL=useShellPanelToggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useShellPanelToggle.d.ts","sourceRoot":"","sources":["../src/useShellPanelToggle.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,OAAc,EACd,aAA+B,EAC/B,SAAiC,GAClC,GAAE,0BAA+B,GAAG,qBAAqB,CA2CzD"}
@@ -0,0 +1,40 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ export function useShellPanelToggle({ enabled = true, bodyClassName = "nav-collapsed", eventName = "treasury:nav-toggle", } = {}) {
3
+ const [collapsed, setCollapsedState] = useState(false);
4
+ const setCollapsed = useCallback((next) => {
5
+ setCollapsedState(next);
6
+ if (typeof document !== "undefined") {
7
+ document.body.classList.toggle(bodyClassName, next);
8
+ }
9
+ if (typeof window !== "undefined") {
10
+ window.dispatchEvent(new CustomEvent(eventName, { detail: { hidden: next } }));
11
+ }
12
+ }, [bodyClassName, eventName]);
13
+ useEffect(() => {
14
+ if (!enabled) {
15
+ setCollapsed(false);
16
+ return;
17
+ }
18
+ if (typeof document === "undefined" || typeof window === "undefined")
19
+ return;
20
+ setCollapsedState(document.body.classList.contains(bodyClassName));
21
+ const handleToggle = (event) => {
22
+ const detail = event.detail;
23
+ setCollapsedState(Boolean(detail?.hidden));
24
+ };
25
+ window.addEventListener(eventName, handleToggle);
26
+ return () => {
27
+ window.removeEventListener(eventName, handleToggle);
28
+ };
29
+ }, [bodyClassName, enabled, eventName, setCollapsed]);
30
+ const toggle = useCallback(() => {
31
+ if (!enabled)
32
+ return;
33
+ setCollapsed(!collapsed);
34
+ }, [collapsed, enabled, setCollapsed]);
35
+ return {
36
+ collapsed,
37
+ toggle,
38
+ setCollapsed,
39
+ };
40
+ }
@@ -0,0 +1,7 @@
1
+ export type UseSurfaceWarmupOptions = {
2
+ enabled: boolean;
3
+ intervalMs?: number;
4
+ run: () => Promise<void>;
5
+ };
6
+ export declare function useSurfaceWarmup({ enabled, intervalMs, run }: UseSurfaceWarmupOptions): void;
7
+ //# sourceMappingURL=useSurfaceWarmup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSurfaceWarmup.d.ts","sourceRoot":"","sources":["../src/useSurfaceWarmup.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,uBAAuB,QA8BrF"}
@@ -0,0 +1,32 @@
1
+ import { useEffect, useRef } from "react";
2
+ export function useSurfaceWarmup({ enabled, intervalMs, run }) {
3
+ const warmupStartedRef = useRef(false);
4
+ const inFlightRef = useRef(false);
5
+ useEffect(() => {
6
+ if (!enabled || warmupStartedRef.current)
7
+ return;
8
+ warmupStartedRef.current = true;
9
+ const invoke = async () => {
10
+ if (inFlightRef.current)
11
+ return;
12
+ inFlightRef.current = true;
13
+ try {
14
+ await run();
15
+ }
16
+ finally {
17
+ inFlightRef.current = false;
18
+ }
19
+ };
20
+ void invoke();
21
+ if (!intervalMs || !Number.isFinite(intervalMs) || intervalMs <= 0)
22
+ return;
23
+ const interval = window.setInterval(() => {
24
+ if (document.visibilityState === "hidden")
25
+ return;
26
+ void invoke();
27
+ }, intervalMs);
28
+ return () => {
29
+ window.clearInterval(interval);
30
+ };
31
+ }, [enabled, intervalMs, run]);
32
+ }
@@ -0,0 +1,12 @@
1
+ export type ThemeMode = "dark" | "light";
2
+ export type UseThemeModeOptions = {
3
+ storageKey?: string;
4
+ attributeName?: string;
5
+ defaultTheme?: ThemeMode;
6
+ };
7
+ export declare function useThemeMode({ storageKey, attributeName, defaultTheme, }?: UseThemeModeOptions): {
8
+ theme: ThemeMode;
9
+ setTheme: (next: ThemeMode) => void;
10
+ toggleTheme: () => void;
11
+ };
12
+ //# sourceMappingURL=useThemeMode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useThemeMode.d.ts","sourceRoot":"","sources":["../src/useThemeMode.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,SAAS,CAAC;CAC1B,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,UAA0B,EAC1B,aAA4B,EAC5B,YAAqB,GACtB,GAAE,mBAAwB,GAAG;IAC5B,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB,CA2BA"}
@@ -0,0 +1,26 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ export function useThemeMode({ storageKey = "rv_theme_v2", attributeName = "data-theme", defaultTheme = "dark", } = {}) {
3
+ const [theme, setTheme] = useState(defaultTheme);
4
+ useEffect(() => {
5
+ if (typeof window === "undefined")
6
+ return;
7
+ const stored = window.localStorage.getItem(storageKey);
8
+ const prefersDark = window.matchMedia?.("(prefers-color-scheme: dark)").matches;
9
+ const resolved = stored === "light" || stored === "dark" ? stored : prefersDark ? "dark" : "light";
10
+ setTheme(resolved);
11
+ }, [storageKey]);
12
+ useEffect(() => {
13
+ if (typeof document === "undefined" || typeof window === "undefined")
14
+ return;
15
+ document.documentElement.setAttribute(attributeName, theme);
16
+ window.localStorage.setItem(storageKey, theme);
17
+ }, [attributeName, storageKey, theme]);
18
+ const toggleTheme = useCallback(() => {
19
+ setTheme((prev) => (prev === "dark" ? "light" : "dark"));
20
+ }, []);
21
+ return {
22
+ theme,
23
+ setTheme,
24
+ toggleTheme,
25
+ };
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treasuryspatial/viewer-react",
3
- "version": "0.1.28",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/index.js",