jazz-tools 0.19.10 → 0.19.11

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 (84) hide show
  1. package/.turbo/turbo-build.log +53 -49
  2. package/CHANGELOG.md +11 -0
  3. package/dist/{chunk-FFEEPZEG.js → chunk-HX5S6W5E.js} +6 -2
  4. package/dist/chunk-HX5S6W5E.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/inspector/account-switcher.d.ts +4 -0
  7. package/dist/inspector/account-switcher.d.ts.map +1 -0
  8. package/dist/inspector/chunk-C6BJPHBQ.js +4096 -0
  9. package/dist/inspector/chunk-C6BJPHBQ.js.map +1 -0
  10. package/dist/inspector/contexts/node.d.ts +19 -0
  11. package/dist/inspector/contexts/node.d.ts.map +1 -0
  12. package/dist/inspector/{custom-element-P76EIWEV.js → custom-element-GJVBPZES.js} +1011 -884
  13. package/dist/inspector/custom-element-GJVBPZES.js.map +1 -0
  14. package/dist/inspector/{viewer/new-app.d.ts → in-app.d.ts} +3 -3
  15. package/dist/inspector/in-app.d.ts.map +1 -0
  16. package/dist/inspector/index.d.ts +0 -11
  17. package/dist/inspector/index.d.ts.map +1 -1
  18. package/dist/inspector/index.js +56 -3910
  19. package/dist/inspector/index.js.map +1 -1
  20. package/dist/inspector/pages/home.d.ts +2 -0
  21. package/dist/inspector/pages/home.d.ts.map +1 -0
  22. package/dist/inspector/register-custom-element.js +1 -1
  23. package/dist/inspector/router/context.d.ts +12 -0
  24. package/dist/inspector/router/context.d.ts.map +1 -0
  25. package/dist/inspector/router/hash-router.d.ts +7 -0
  26. package/dist/inspector/router/hash-router.d.ts.map +1 -0
  27. package/dist/inspector/router/in-memory-router.d.ts +7 -0
  28. package/dist/inspector/router/in-memory-router.d.ts.map +1 -0
  29. package/dist/inspector/router/index.d.ts +5 -0
  30. package/dist/inspector/router/index.d.ts.map +1 -0
  31. package/dist/inspector/standalone.d.ts +6 -0
  32. package/dist/inspector/standalone.d.ts.map +1 -0
  33. package/dist/inspector/standalone.js +420 -0
  34. package/dist/inspector/standalone.js.map +1 -0
  35. package/dist/inspector/tests/router/hash-router.test.d.ts +2 -0
  36. package/dist/inspector/tests/router/hash-router.test.d.ts.map +1 -0
  37. package/dist/inspector/tests/router/in-memory-router.test.d.ts +2 -0
  38. package/dist/inspector/tests/router/in-memory-router.test.d.ts.map +1 -0
  39. package/dist/inspector/ui/modal.d.ts +1 -0
  40. package/dist/inspector/ui/modal.d.ts.map +1 -1
  41. package/dist/inspector/viewer/breadcrumbs.d.ts +1 -7
  42. package/dist/inspector/viewer/breadcrumbs.d.ts.map +1 -1
  43. package/dist/inspector/viewer/header.d.ts +7 -0
  44. package/dist/inspector/viewer/header.d.ts.map +1 -0
  45. package/dist/inspector/viewer/page-stack.d.ts +4 -13
  46. package/dist/inspector/viewer/page-stack.d.ts.map +1 -1
  47. package/dist/inspector/viewer/page.d.ts.map +1 -1
  48. package/dist/testing.js +1 -1
  49. package/dist/tools/coValues/account.d.ts +7 -1
  50. package/dist/tools/coValues/account.d.ts.map +1 -1
  51. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +8 -1
  52. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  53. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  54. package/dist/tools/testing.d.ts.map +1 -1
  55. package/package.json +9 -4
  56. package/src/inspector/account-switcher.tsx +440 -0
  57. package/src/inspector/contexts/node.tsx +129 -0
  58. package/src/inspector/custom-element.tsx +2 -2
  59. package/src/inspector/in-app.tsx +61 -0
  60. package/src/inspector/index.tsx +2 -22
  61. package/src/inspector/pages/home.tsx +77 -0
  62. package/src/inspector/router/context.ts +21 -0
  63. package/src/inspector/router/hash-router.tsx +128 -0
  64. package/src/inspector/{viewer/use-page-path.ts → router/in-memory-router.tsx} +31 -29
  65. package/src/inspector/router/index.ts +4 -0
  66. package/src/inspector/standalone.tsx +60 -0
  67. package/src/inspector/tests/router/hash-router.test.tsx +847 -0
  68. package/src/inspector/tests/router/in-memory-router.test.tsx +724 -0
  69. package/src/inspector/ui/modal.tsx +5 -2
  70. package/src/inspector/viewer/breadcrumbs.tsx +5 -11
  71. package/src/inspector/viewer/header.tsx +67 -0
  72. package/src/inspector/viewer/page-stack.tsx +18 -26
  73. package/src/inspector/viewer/page.tsx +0 -1
  74. package/src/tools/coValues/account.ts +13 -2
  75. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +8 -1
  76. package/src/tools/tests/account.test.ts +11 -4
  77. package/src/tools/tests/schema.resolved.test.ts +3 -3
  78. package/tsup.config.ts +1 -0
  79. package/dist/chunk-FFEEPZEG.js.map +0 -1
  80. package/dist/inspector/custom-element-P76EIWEV.js.map +0 -1
  81. package/dist/inspector/viewer/new-app.d.ts.map +0 -1
  82. package/dist/inspector/viewer/use-page-path.d.ts +0 -10
  83. package/dist/inspector/viewer/use-page-path.d.ts.map +0 -1
  84. package/src/inspector/viewer/new-app.tsx +0 -156
@@ -0,0 +1,129 @@
1
+ import {
2
+ createContext,
3
+ type PropsWithChildren,
4
+ useContext,
5
+ useEffect,
6
+ useState,
7
+ } from "react";
8
+ import { CoID, LocalNode, RawAccount } from "cojson";
9
+ import { WasmCrypto } from "cojson/crypto/WasmCrypto";
10
+ import type { PureJSCrypto } from "cojson/dist/crypto/PureJSCrypto";
11
+ import { createWebSocketPeer } from "cojson-transport-ws";
12
+
13
+ type NodeContextType = {
14
+ accountID: CoID<RawAccount> | null;
15
+ localNode: LocalNode | null;
16
+ server: string;
17
+ createLocalNode: (
18
+ accountID: CoID<RawAccount>,
19
+ clientSecret: string,
20
+ server: string,
21
+ ) => Promise<void>;
22
+ reset: () => void;
23
+ };
24
+
25
+ export const NodeContext = createContext<NodeContextType>({
26
+ accountID: null,
27
+ localNode: null,
28
+ server: "wss://cloud.jazz.tools/",
29
+ createLocalNode: async () => {
30
+ throw new Error("createLocalNode not implemented");
31
+ },
32
+ reset: () => {
33
+ throw new Error("reset not implemented");
34
+ },
35
+ });
36
+
37
+ type NodeProviderProps = PropsWithChildren<{
38
+ localNode?: LocalNode | null;
39
+ accountID?: CoID<RawAccount> | null;
40
+ server?: string;
41
+ }>;
42
+
43
+ let crypto: WasmCrypto | PureJSCrypto | null = null;
44
+
45
+ async function getCrypto() {
46
+ if (crypto) return crypto;
47
+ crypto = await WasmCrypto.create();
48
+ return crypto;
49
+ }
50
+
51
+ export function NodeProvider(props: NodeProviderProps) {
52
+ const [accountID, setAccountID] = useState<CoID<RawAccount> | null>(
53
+ props?.accountID ?? null,
54
+ );
55
+ const [localNode, setLocalNode] = useState<LocalNode | null>(
56
+ props?.localNode ?? null,
57
+ );
58
+ const [server, setServer] = useState<string>(
59
+ props?.server ?? "wss://cloud.jazz.tools/",
60
+ );
61
+
62
+ useEffect(() => {
63
+ if (props.localNode !== undefined) setLocalNode(props.localNode);
64
+
65
+ if (props.accountID !== undefined) setAccountID(props.accountID);
66
+
67
+ if (props.server !== undefined) setServer(props.server);
68
+ }, [props.localNode, props.accountID, props.server]);
69
+
70
+ async function createLocalNode(
71
+ accountID: CoID<RawAccount>,
72
+ clientSecret: string,
73
+ server: string,
74
+ ) {
75
+ if (localNode) {
76
+ localNode.gracefulShutdown();
77
+ }
78
+
79
+ setLocalNode(null);
80
+
81
+ const wsPeer = createWebSocketPeer({
82
+ id: "cloud",
83
+ websocket: new WebSocket(server),
84
+ role: "server",
85
+ });
86
+
87
+ const crypto = await getCrypto();
88
+
89
+ const node = await LocalNode.withLoadedAccount({
90
+ accountID: accountID,
91
+ accountSecret: clientSecret as any,
92
+ sessionID: crypto.newRandomSessionID(accountID),
93
+ peers: [wsPeer],
94
+ crypto,
95
+ migration: async () => {
96
+ console.log("Not running any migration in inspector");
97
+ },
98
+ });
99
+
100
+ setLocalNode(node);
101
+ setAccountID(accountID);
102
+ setServer(server);
103
+ }
104
+
105
+ function reset() {
106
+ if (localNode) {
107
+ localNode.gracefulShutdown();
108
+ }
109
+ setLocalNode(null);
110
+ setAccountID(null);
111
+ setServer("wss://cloud.jazz.tools/");
112
+ }
113
+
114
+ return (
115
+ <NodeContext.Provider
116
+ value={{ accountID, localNode, server, createLocalNode, reset }}
117
+ >
118
+ {props.children}
119
+ </NodeContext.Provider>
120
+ );
121
+ }
122
+
123
+ export function useNode() {
124
+ const context = useContext(NodeContext);
125
+ if (!context) {
126
+ throw new Error("useNode must be used within a NodeProvider");
127
+ }
128
+ return context;
129
+ }
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import { setup } from "goober";
3
3
  import { Account } from "jazz-tools";
4
4
  import { createRoot } from "react-dom/client";
5
- import { JazzInspectorInternal } from "./viewer/new-app.js";
5
+ import { InspectorInApp } from "./in-app.js";
6
6
 
7
7
  setup(React.createElement);
8
8
 
@@ -56,7 +56,7 @@ export class JazzInspectorElement extends HTMLElement {
56
56
  }
57
57
 
58
58
  this.root?.render(
59
- <JazzInspectorInternal
59
+ <InspectorInApp
60
60
  localNode={this.account.$jazz.localNode}
61
61
  accountId={this.account.$jazz.raw.id}
62
62
  />,
@@ -0,0 +1,61 @@
1
+ import { CoID, LocalNode, RawAccount } from "cojson";
2
+ import { styled } from "goober";
3
+ import { PageStack } from "./viewer/page-stack.js";
4
+ import { GlobalStyles } from "./ui/global-styles.js";
5
+ import { InspectorButton, type Position } from "./viewer/inspector-button.js";
6
+ import { useOpenInspector } from "./viewer/use-open-inspector.js";
7
+ import { NodeProvider } from "./contexts/node.js";
8
+ import { InMemoryRouterProvider } from "./router/in-memory-router.js";
9
+ import { Header } from "./viewer/header.js";
10
+
11
+ export function InspectorInApp({
12
+ position = "right",
13
+ localNode,
14
+ accountId,
15
+ }: {
16
+ position?: Position;
17
+ localNode?: LocalNode;
18
+ accountId?: CoID<RawAccount>;
19
+ }) {
20
+ const [open, setOpen] = useOpenInspector();
21
+
22
+ if (!open) {
23
+ return (
24
+ <InspectorButton position={position} onClick={() => setOpen(true)} />
25
+ );
26
+ }
27
+
28
+ return (
29
+ <NodeProvider localNode={localNode ?? null} accountID={accountId ?? null}>
30
+ <InMemoryRouterProvider>
31
+ <InspectorContainer as={GlobalStyles} style={{ zIndex: 999 }}>
32
+ <Header
33
+ showDeleteLocalData={true}
34
+ showClose={true}
35
+ onClose={() => setOpen(false)}
36
+ />
37
+
38
+ <PageStack />
39
+ </InspectorContainer>
40
+ </InMemoryRouterProvider>
41
+ </NodeProvider>
42
+ );
43
+ }
44
+
45
+ const InspectorContainer = styled("div")`
46
+ position: fixed;
47
+ height: 50vh;
48
+ max-height: 800px;
49
+ display: flex;
50
+ flex-direction: column;
51
+ bottom: 0;
52
+ left: 0;
53
+ width: 100%;
54
+ background-color: white;
55
+ border-top: 1px solid var(--j-border-color);
56
+ color: var(--j-text-color);
57
+
58
+ @media (prefers-color-scheme: dark) {
59
+ background-color: var(--j-background);
60
+ }
61
+ `;
@@ -1,28 +1,8 @@
1
1
  import React, { useEffect, useState } from "react";
2
-
3
- export { JazzInspectorInternal } from "./viewer/new-app.js";
4
- export { PageStack } from "./viewer/page-stack.js";
5
- export { Breadcrumbs } from "./viewer/breadcrumbs.js";
6
- export { AccountOrGroupText } from "./viewer/account-or-group-text.js";
7
-
8
- export { Button } from "./ui/button.js";
9
- export { Input } from "./ui/input.js";
10
- export { Select } from "./ui/select.js";
11
- export { Icon } from "./ui/icon.js";
12
- export { GlobalStyles } from "./ui/global-styles.js";
13
-
14
- export {
15
- resolveCoValue,
16
- useResolvedCoValue,
17
- } from "./viewer/use-resolve-covalue.js";
18
-
19
- export type { PageInfo } from "./viewer/types.js";
20
-
21
2
  import { setup } from "goober";
22
3
  import { useJazzContext } from "jazz-tools/react-core";
23
4
  import { Account } from "jazz-tools";
24
-
25
- import { JazzInspectorInternal } from "./viewer/new-app.js";
5
+ import { InspectorInApp } from "./in-app.js";
26
6
  import { Position } from "./viewer/inspector-button.js";
27
7
 
28
8
  export function JazzInspector({ position = "right" }: { position?: Position }) {
@@ -40,7 +20,7 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
40
20
  }
41
21
 
42
22
  return (
43
- <JazzInspectorInternal
23
+ <InspectorInApp
44
24
  position={position}
45
25
  localNode={localNode}
46
26
  accountId={me?.$jazz.raw.id}
@@ -0,0 +1,77 @@
1
+ import { styled } from "goober";
2
+ import { useNode } from "../contexts/node";
3
+ import { Heading } from "../ui/heading";
4
+ import { Button, Input } from "../ui";
5
+ import { useState } from "react";
6
+ import { CoID, RawCoValue } from "cojson";
7
+ import { useRouter } from "../router";
8
+
9
+ export function HomePage() {
10
+ const { localNode, accountID } = useNode();
11
+ const { path, setPage } = useRouter();
12
+ const [coValueId, setCoValueId] = useState<CoID<RawCoValue> | "">("");
13
+
14
+ if (!localNode || !accountID) {
15
+ return <div>Loading...</div>;
16
+ }
17
+
18
+ const handleCoValueIdSubmit = (e: React.FormEvent) => {
19
+ e.preventDefault();
20
+ if (coValueId) {
21
+ setPage(coValueId);
22
+ }
23
+ setCoValueId("");
24
+ };
25
+
26
+ return (
27
+ <>
28
+ <CenteredForm
29
+ onSubmit={handleCoValueIdSubmit}
30
+ aria-hidden={path.length !== 0}
31
+ >
32
+ <Heading>Jazz CoValue Inspector</Heading>
33
+
34
+ <Input
35
+ label="CoValue ID"
36
+ className="font-mono"
37
+ hideLabel
38
+ placeholder="co_z1234567890abcdef123456789"
39
+ value={coValueId}
40
+ onChange={(e) => setCoValueId(e.target.value as CoID<RawCoValue>)}
41
+ />
42
+
43
+ <Button type="submit" variant="primary">
44
+ Inspect CoValue
45
+ </Button>
46
+
47
+ <OrText>or</OrText>
48
+
49
+ <Button
50
+ variant="secondary"
51
+ onClick={() => {
52
+ setPage(accountID);
53
+ }}
54
+ >
55
+ Inspect my account
56
+ </Button>
57
+ </CenteredForm>
58
+ </>
59
+ );
60
+ }
61
+
62
+ const CenteredForm = styled("form")`
63
+ display: flex;
64
+ flex-direction: column;
65
+ position: relative;
66
+ top: -1.5rem;
67
+ justify-content: center;
68
+ gap: 0.5rem;
69
+ height: 100%;
70
+ width: 100%;
71
+ max-width: 24rem;
72
+ margin: 0 auto;
73
+ `;
74
+
75
+ const OrText = styled("p")`
76
+ text-align: center;
77
+ `;
@@ -0,0 +1,21 @@
1
+ import { CoID, RawCoValue } from "cojson";
2
+ import { PageInfo } from "../viewer/types.js";
3
+ import { createContext, useContext } from "react";
4
+
5
+ export interface Router {
6
+ path: PageInfo[];
7
+ setPage: (coId: CoID<RawCoValue>) => void;
8
+ addPages: (newPages: PageInfo[]) => void;
9
+ goToIndex: (index: number) => void;
10
+ goBack: () => void;
11
+ }
12
+
13
+ export const RouterContext = createContext<Router | null>(null);
14
+
15
+ export function useRouter(): Router {
16
+ const context = useContext(RouterContext);
17
+ if (!context) {
18
+ throw new Error("useRouter must be used within a RouterProvider");
19
+ }
20
+ return context;
21
+ }
@@ -0,0 +1,128 @@
1
+ import React, {
2
+ ReactNode,
3
+ useState,
4
+ useCallback,
5
+ useEffect,
6
+ useMemo,
7
+ } from "react";
8
+ import { Router, RouterContext } from "./context.js";
9
+ import { PageInfo } from "../viewer/types.js";
10
+ import { CoID, RawCoValue } from "cojson";
11
+
12
+ export function HashRouterProvider({
13
+ children,
14
+ defaultPath,
15
+ }: {
16
+ children: ReactNode;
17
+ defaultPath?: PageInfo[];
18
+ }) {
19
+ const [path, setPath] = useState<PageInfo[]>(() => {
20
+ if (typeof window === "undefined") return defaultPath || [];
21
+ const hash = window.location.hash.slice(2); // Remove '#/'
22
+
23
+ const defaultEncoded = encodePathToHash(defaultPath || []);
24
+
25
+ if (defaultPath) {
26
+ window.history.pushState({}, "", `#/${defaultEncoded}`);
27
+ return defaultPath;
28
+ }
29
+
30
+ if (hash) {
31
+ const path = decodePathFromHash(hash);
32
+ return path;
33
+ }
34
+
35
+ window.history.pushState({}, "", `#/${encodePathToHash([])}`);
36
+ return [];
37
+ });
38
+
39
+ const updatePath = useCallback((newPath: PageInfo[]) => {
40
+ setPath(newPath);
41
+ if (typeof window !== "undefined") {
42
+ const hash = encodePathToHash(newPath);
43
+ window.history.pushState({}, "", `#/${hash}`);
44
+ }
45
+ }, []);
46
+
47
+ useEffect(() => {
48
+ if (typeof window === "undefined") return;
49
+
50
+ const handleHashChange = () => {
51
+ const hash = window.location.hash.slice(2);
52
+ const currentPath = encodePathToHash(path);
53
+
54
+ if (hash === currentPath) return;
55
+
56
+ if (hash) {
57
+ try {
58
+ const newPath = decodePathFromHash(hash);
59
+ setPath(newPath);
60
+ } catch (e) {
61
+ console.error("Failed to parse hash:", e);
62
+ }
63
+ } else if (defaultPath) {
64
+ setPath(defaultPath);
65
+ }
66
+ };
67
+
68
+ window.addEventListener("hashchange", handleHashChange);
69
+ return () => window.removeEventListener("hashchange", handleHashChange);
70
+ }, [path, defaultPath]);
71
+
72
+ useEffect(() => {
73
+ if (defaultPath) {
74
+ updatePath(defaultPath);
75
+ }
76
+ }, [defaultPath]);
77
+
78
+ const router: Router = useMemo(() => {
79
+ const addPages = (newPages: PageInfo[]) => {
80
+ updatePath([...path, ...newPages]);
81
+ };
82
+
83
+ const goToIndex = (index: number) => {
84
+ updatePath(path.slice(0, index + 1));
85
+ };
86
+
87
+ const setPage = (coId: CoID<RawCoValue>) => {
88
+ updatePath([{ coId, name: "Root" }]);
89
+ };
90
+
91
+ const goBack = () => {
92
+ updatePath(path.slice(0, path.length - 1));
93
+ };
94
+
95
+ return {
96
+ path,
97
+ addPages,
98
+ goToIndex,
99
+ setPage,
100
+ goBack,
101
+ };
102
+ }, [path, updatePath]);
103
+
104
+ return (
105
+ <RouterContext.Provider value={router}>{children}</RouterContext.Provider>
106
+ );
107
+ }
108
+
109
+ function encodePathToHash(path: PageInfo[]): string {
110
+ return path
111
+ .map((page) => {
112
+ if (page.name && page.name !== "Root") {
113
+ return `${page.coId}:${encodeURIComponent(page.name)}`;
114
+ }
115
+ return page.coId;
116
+ })
117
+ .join("/");
118
+ }
119
+
120
+ function decodePathFromHash(hash: string): PageInfo[] {
121
+ return hash.split("/").map((segment) => {
122
+ const [coId, encodedName] = segment.split(":");
123
+ return {
124
+ coId,
125
+ name: encodedName ? decodeURIComponent(encodedName) : undefined,
126
+ } as PageInfo;
127
+ });
128
+ }
@@ -1,10 +1,17 @@
1
- import { CoID, RawCoValue } from "cojson";
2
- import { useCallback, useEffect, useState } from "react";
3
- import { PageInfo } from "./types.js";
1
+ import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
2
+ import type { CoID, RawCoValue } from "cojson";
3
+ import { Router, RouterContext } from "./context.js";
4
+ import { PageInfo } from "../viewer/types.js";
4
5
 
5
6
  const STORAGE_KEY = "jazz-inspector-paths";
6
7
 
7
- export function usePagePath(defaultPath?: PageInfo[]) {
8
+ export function InMemoryRouterProvider({
9
+ children,
10
+ defaultPath,
11
+ }: {
12
+ children: ReactNode;
13
+ defaultPath?: PageInfo[];
14
+ }) {
8
15
  const [path, setPath] = useState<PageInfo[]>(() => {
9
16
  if (typeof window === "undefined") return [];
10
17
  const stored = localStorage.getItem(STORAGE_KEY);
@@ -29,38 +36,33 @@ export function usePagePath(defaultPath?: PageInfo[]) {
29
36
  }
30
37
  }, [defaultPath, path, updatePath]);
31
38
 
32
- const addPages = useCallback(
33
- (newPages: PageInfo[]) => {
39
+ const router: Router = useMemo(() => {
40
+ const addPages = (newPages: PageInfo[]) => {
34
41
  updatePath([...path, ...newPages]);
35
- },
36
- [path, updatePath],
37
- );
42
+ };
38
43
 
39
- const goToIndex = useCallback(
40
- (index: number) => {
44
+ const goToIndex = (index: number) => {
41
45
  updatePath(path.slice(0, index + 1));
42
- },
43
- [path, updatePath],
44
- );
46
+ };
45
47
 
46
- const setPage = useCallback(
47
- (coId: CoID<RawCoValue>) => {
48
+ const setPage = (coId: CoID<RawCoValue>) => {
48
49
  updatePath([{ coId, name: "Root" }]);
49
- },
50
- [updatePath],
51
- );
50
+ };
52
51
 
53
- const goBack = useCallback(() => {
54
- if (path.length > 1) {
52
+ const goBack = () => {
55
53
  updatePath(path.slice(0, path.length - 1));
56
- }
54
+ };
55
+
56
+ return {
57
+ path,
58
+ addPages,
59
+ goToIndex,
60
+ setPage,
61
+ goBack,
62
+ };
57
63
  }, [path, updatePath]);
58
64
 
59
- return {
60
- path,
61
- setPage,
62
- addPages,
63
- goToIndex,
64
- goBack,
65
- };
65
+ return (
66
+ <RouterContext.Provider value={router}>{children}</RouterContext.Provider>
67
+ );
66
68
  }
@@ -0,0 +1,4 @@
1
+ export type { Router } from "./context.js";
2
+ export { useRouter } from "./context.js";
3
+ export { InMemoryRouterProvider } from "./in-memory-router.js";
4
+ export { HashRouterProvider } from "./hash-router.js";
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { HashRouterProvider } from "./router";
3
+ import { setup, styled } from "goober";
4
+ import { NodeContext, NodeProvider } from "./contexts/node";
5
+ import { Header } from "./viewer/header";
6
+ import { GlobalStyles } from "./ui/global-styles";
7
+ import { PageStack } from "./viewer/page-stack";
8
+ import { AccountSwitcher } from "./account-switcher";
9
+
10
+ type InspectorAppProps = {
11
+ defaultSyncServer?: string;
12
+ };
13
+
14
+ setup(React.createElement);
15
+
16
+ export default function InspectorStandalone(props: InspectorAppProps) {
17
+ return (
18
+ <HashRouterProvider>
19
+ <NodeProvider>
20
+ <InspectorContainer as={GlobalStyles}>
21
+ <Header>
22
+ <AccountSwitcher defaultSyncServer={props.defaultSyncServer} />
23
+ </Header>
24
+ <NodeContext.Consumer>
25
+ {({ accountID }) =>
26
+ accountID ? (
27
+ <PageStack />
28
+ ) : (
29
+ <CenteredMessage>
30
+ Select an account to connect to the inspector.
31
+ </CenteredMessage>
32
+ )
33
+ }
34
+ </NodeContext.Consumer>
35
+ </InspectorContainer>
36
+ </NodeProvider>
37
+ </HashRouterProvider>
38
+ );
39
+ }
40
+
41
+ const InspectorContainer = styled("div")`
42
+ height: 100vh;
43
+ overflow: hidden;
44
+ display: flex;
45
+ flex-direction: column;
46
+ color: #44403c; /* text-stone-700 */
47
+ background-color: #fff; /* bg-white */
48
+
49
+ @media (prefers-color-scheme: dark) {
50
+ color: #d6d3d1; /* text-stone-300 */
51
+ background-color: #0c0a09; /* bg-stone-950 */
52
+ }
53
+ `;
54
+
55
+ const CenteredMessage = styled("p")`
56
+ text-align: center;
57
+ margin: 0;
58
+ padding: 1rem;
59
+ color: var(--j-text-color);
60
+ `;