jazz-tools 0.19.10 → 0.19.12

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 (112) hide show
  1. package/.turbo/turbo-build.log +58 -54
  2. package/CHANGELOG.md +23 -0
  3. package/dist/{chunk-FFEEPZEG.js → chunk-AGF4HEDH.js} +61 -28
  4. package/dist/chunk-AGF4HEDH.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-YQNK5Y7B.js +4108 -0
  9. package/dist/inspector/chunk-YQNK5Y7B.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-KYV64IOC.js} +1057 -918
  13. package/dist/inspector/custom-element-KYV64IOC.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/tests/utils/transactions-changes.test.d.ts +2 -0
  40. package/dist/inspector/tests/utils/transactions-changes.test.d.ts.map +1 -0
  41. package/dist/inspector/ui/modal.d.ts +1 -0
  42. package/dist/inspector/ui/modal.d.ts.map +1 -1
  43. package/dist/inspector/utils/transactions-changes.d.ts +13 -13
  44. package/dist/inspector/utils/transactions-changes.d.ts.map +1 -1
  45. package/dist/inspector/viewer/breadcrumbs.d.ts +1 -7
  46. package/dist/inspector/viewer/breadcrumbs.d.ts.map +1 -1
  47. package/dist/inspector/viewer/header.d.ts +7 -0
  48. package/dist/inspector/viewer/header.d.ts.map +1 -0
  49. package/dist/inspector/viewer/page-stack.d.ts +4 -13
  50. package/dist/inspector/viewer/page-stack.d.ts.map +1 -1
  51. package/dist/inspector/viewer/page.d.ts.map +1 -1
  52. package/dist/react/index.js +4 -1
  53. package/dist/react/index.js.map +1 -1
  54. package/dist/react/provider.d.ts.map +1 -1
  55. package/dist/react-core/index.js +2 -2
  56. package/dist/react-core/index.js.map +1 -1
  57. package/dist/react-native/index.js +4 -1
  58. package/dist/react-native/index.js.map +1 -1
  59. package/dist/react-native-core/index.js +4 -1
  60. package/dist/react-native-core/index.js.map +1 -1
  61. package/dist/react-native-core/provider.d.ts.map +1 -1
  62. package/dist/testing.js +1 -1
  63. package/dist/tools/coValues/account.d.ts +7 -1
  64. package/dist/tools/coValues/account.d.ts.map +1 -1
  65. package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
  66. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +8 -1
  67. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  68. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  69. package/dist/tools/subscribe/SubscriptionScope.d.ts +3 -6
  70. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  71. package/dist/tools/testing.d.ts.map +1 -1
  72. package/package.json +9 -4
  73. package/src/inspector/account-switcher.tsx +440 -0
  74. package/src/inspector/contexts/node.tsx +129 -0
  75. package/src/inspector/custom-element.tsx +2 -2
  76. package/src/inspector/in-app.tsx +61 -0
  77. package/src/inspector/index.tsx +2 -22
  78. package/src/inspector/pages/home.tsx +77 -0
  79. package/src/inspector/router/context.ts +21 -0
  80. package/src/inspector/router/hash-router.tsx +128 -0
  81. package/src/inspector/{viewer/use-page-path.ts → router/in-memory-router.tsx} +31 -29
  82. package/src/inspector/router/index.ts +4 -0
  83. package/src/inspector/standalone.tsx +60 -0
  84. package/src/inspector/tests/router/hash-router.test.tsx +847 -0
  85. package/src/inspector/tests/router/in-memory-router.test.tsx +724 -0
  86. package/src/inspector/tests/utils/transactions-changes.test.ts +102 -0
  87. package/src/inspector/ui/icons/add-icon.tsx +3 -3
  88. package/src/inspector/ui/modal.tsx +5 -2
  89. package/src/inspector/utils/history.ts +6 -6
  90. package/src/inspector/utils/transactions-changes.ts +37 -3
  91. package/src/inspector/viewer/breadcrumbs.tsx +5 -11
  92. package/src/inspector/viewer/header.tsx +67 -0
  93. package/src/inspector/viewer/history-view.tsx +13 -13
  94. package/src/inspector/viewer/page-stack.tsx +18 -26
  95. package/src/inspector/viewer/page.tsx +0 -1
  96. package/src/react/provider.tsx +6 -1
  97. package/src/react-core/hooks.ts +2 -2
  98. package/src/react-core/tests/useSuspenseCoState.test.tsx +47 -0
  99. package/src/react-native-core/provider.tsx +6 -1
  100. package/src/tools/coValues/account.ts +13 -2
  101. package/src/tools/implementation/ContextManager.ts +10 -0
  102. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +8 -1
  103. package/src/tools/subscribe/SubscriptionScope.ts +61 -39
  104. package/src/tools/tests/account.test.ts +11 -4
  105. package/src/tools/tests/schema.resolved.test.ts +3 -3
  106. package/tsup.config.ts +1 -0
  107. package/dist/chunk-FFEEPZEG.js.map +0 -1
  108. package/dist/inspector/custom-element-P76EIWEV.js.map +0 -1
  109. package/dist/inspector/viewer/new-app.d.ts.map +0 -1
  110. package/dist/inspector/viewer/use-page-path.d.ts +0 -10
  111. package/dist/inspector/viewer/use-page-path.d.ts.map +0 -1
  112. package/src/inspector/viewer/new-app.tsx +0 -156
@@ -0,0 +1,4108 @@
1
+ "use client";
2
+
3
+ // src/inspector/contexts/node.tsx
4
+ import {
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ useState
9
+ } from "react";
10
+ import { LocalNode } from "cojson";
11
+ import { WasmCrypto } from "cojson/crypto/WasmCrypto";
12
+ import { createWebSocketPeer } from "cojson-transport-ws";
13
+ import { jsx } from "react/jsx-runtime";
14
+ var NodeContext = createContext({
15
+ accountID: null,
16
+ localNode: null,
17
+ server: "wss://cloud.jazz.tools/",
18
+ createLocalNode: async () => {
19
+ throw new Error("createLocalNode not implemented");
20
+ },
21
+ reset: () => {
22
+ throw new Error("reset not implemented");
23
+ }
24
+ });
25
+ var crypto = null;
26
+ async function getCrypto() {
27
+ if (crypto) return crypto;
28
+ crypto = await WasmCrypto.create();
29
+ return crypto;
30
+ }
31
+ function NodeProvider(props) {
32
+ const [accountID, setAccountID] = useState(
33
+ props?.accountID ?? null
34
+ );
35
+ const [localNode, setLocalNode] = useState(
36
+ props?.localNode ?? null
37
+ );
38
+ const [server, setServer] = useState(
39
+ props?.server ?? "wss://cloud.jazz.tools/"
40
+ );
41
+ useEffect(() => {
42
+ if (props.localNode !== void 0) setLocalNode(props.localNode);
43
+ if (props.accountID !== void 0) setAccountID(props.accountID);
44
+ if (props.server !== void 0) setServer(props.server);
45
+ }, [props.localNode, props.accountID, props.server]);
46
+ async function createLocalNode(accountID2, clientSecret, server2) {
47
+ if (localNode) {
48
+ localNode.gracefulShutdown();
49
+ }
50
+ setLocalNode(null);
51
+ const wsPeer = createWebSocketPeer({
52
+ id: "cloud",
53
+ websocket: new WebSocket(server2),
54
+ role: "server"
55
+ });
56
+ const crypto2 = await getCrypto();
57
+ const node = await LocalNode.withLoadedAccount({
58
+ accountID: accountID2,
59
+ accountSecret: clientSecret,
60
+ sessionID: crypto2.newRandomSessionID(accountID2),
61
+ peers: [wsPeer],
62
+ crypto: crypto2,
63
+ migration: async () => {
64
+ console.log("Not running any migration in inspector");
65
+ }
66
+ });
67
+ setLocalNode(node);
68
+ setAccountID(accountID2);
69
+ setServer(server2);
70
+ }
71
+ function reset() {
72
+ if (localNode) {
73
+ localNode.gracefulShutdown();
74
+ }
75
+ setLocalNode(null);
76
+ setAccountID(null);
77
+ setServer("wss://cloud.jazz.tools/");
78
+ }
79
+ return /* @__PURE__ */ jsx(
80
+ NodeContext.Provider,
81
+ {
82
+ value: { accountID, localNode, server, createLocalNode, reset },
83
+ children: props.children
84
+ }
85
+ );
86
+ }
87
+ function useNode() {
88
+ const context = useContext(NodeContext);
89
+ if (!context) {
90
+ throw new Error("useNode must be used within a NodeProvider");
91
+ }
92
+ return context;
93
+ }
94
+
95
+ // src/inspector/router/hash-router.tsx
96
+ import {
97
+ useState as useState2,
98
+ useCallback,
99
+ useEffect as useEffect2,
100
+ useMemo
101
+ } from "react";
102
+
103
+ // src/inspector/router/context.ts
104
+ import { createContext as createContext2, useContext as useContext2 } from "react";
105
+ var RouterContext = createContext2(null);
106
+ function useRouter() {
107
+ const context = useContext2(RouterContext);
108
+ if (!context) {
109
+ throw new Error("useRouter must be used within a RouterProvider");
110
+ }
111
+ return context;
112
+ }
113
+
114
+ // src/inspector/router/hash-router.tsx
115
+ import { jsx as jsx2 } from "react/jsx-runtime";
116
+ function HashRouterProvider({
117
+ children,
118
+ defaultPath
119
+ }) {
120
+ const [path, setPath] = useState2(() => {
121
+ if (typeof window === "undefined") return defaultPath || [];
122
+ const hash = window.location.hash.slice(2);
123
+ const defaultEncoded = encodePathToHash(defaultPath || []);
124
+ if (defaultPath) {
125
+ window.history.pushState({}, "", `#/${defaultEncoded}`);
126
+ return defaultPath;
127
+ }
128
+ if (hash) {
129
+ const path2 = decodePathFromHash(hash);
130
+ return path2;
131
+ }
132
+ window.history.pushState({}, "", `#/${encodePathToHash([])}`);
133
+ return [];
134
+ });
135
+ const updatePath = useCallback((newPath) => {
136
+ setPath(newPath);
137
+ if (typeof window !== "undefined") {
138
+ const hash = encodePathToHash(newPath);
139
+ window.history.pushState({}, "", `#/${hash}`);
140
+ }
141
+ }, []);
142
+ useEffect2(() => {
143
+ if (typeof window === "undefined") return;
144
+ const handleHashChange = () => {
145
+ const hash = window.location.hash.slice(2);
146
+ const currentPath = encodePathToHash(path);
147
+ if (hash === currentPath) return;
148
+ if (hash) {
149
+ try {
150
+ const newPath = decodePathFromHash(hash);
151
+ setPath(newPath);
152
+ } catch (e) {
153
+ console.error("Failed to parse hash:", e);
154
+ }
155
+ } else if (defaultPath) {
156
+ setPath(defaultPath);
157
+ }
158
+ };
159
+ window.addEventListener("hashchange", handleHashChange);
160
+ return () => window.removeEventListener("hashchange", handleHashChange);
161
+ }, [path, defaultPath]);
162
+ useEffect2(() => {
163
+ if (defaultPath) {
164
+ updatePath(defaultPath);
165
+ }
166
+ }, [defaultPath]);
167
+ const router = useMemo(() => {
168
+ const addPages = (newPages) => {
169
+ updatePath([...path, ...newPages]);
170
+ };
171
+ const goToIndex = (index) => {
172
+ updatePath(path.slice(0, index + 1));
173
+ };
174
+ const setPage = (coId) => {
175
+ updatePath([{ coId, name: "Root" }]);
176
+ };
177
+ const goBack = () => {
178
+ updatePath(path.slice(0, path.length - 1));
179
+ };
180
+ return {
181
+ path,
182
+ addPages,
183
+ goToIndex,
184
+ setPage,
185
+ goBack
186
+ };
187
+ }, [path, updatePath]);
188
+ return /* @__PURE__ */ jsx2(RouterContext.Provider, { value: router, children });
189
+ }
190
+ function encodePathToHash(path) {
191
+ return path.map((page) => {
192
+ if (page.name && page.name !== "Root") {
193
+ return `${page.coId}:${encodeURIComponent(page.name)}`;
194
+ }
195
+ return page.coId;
196
+ }).join("/");
197
+ }
198
+ function decodePathFromHash(hash) {
199
+ return hash.split("/").map((segment) => {
200
+ const [coId, encodedName] = segment.split(":");
201
+ return {
202
+ coId,
203
+ name: encodedName ? decodeURIComponent(encodedName) : void 0
204
+ };
205
+ });
206
+ }
207
+
208
+ // src/inspector/router/in-memory-router.tsx
209
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useState as useState3 } from "react";
210
+ import { jsx as jsx3 } from "react/jsx-runtime";
211
+ var STORAGE_KEY = "jazz-inspector-paths";
212
+ function InMemoryRouterProvider({
213
+ children,
214
+ defaultPath
215
+ }) {
216
+ const [path, setPath] = useState3(() => {
217
+ if (typeof window === "undefined") return [];
218
+ const stored = localStorage.getItem(STORAGE_KEY);
219
+ if (stored) {
220
+ try {
221
+ return JSON.parse(stored);
222
+ } catch (e) {
223
+ console.warn("Failed to parse stored path:", e);
224
+ }
225
+ }
226
+ return defaultPath || [];
227
+ });
228
+ const updatePath = useCallback2((newPath) => {
229
+ setPath(newPath);
230
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(newPath));
231
+ }, []);
232
+ useEffect3(() => {
233
+ if (defaultPath && JSON.stringify(path) !== JSON.stringify(defaultPath)) {
234
+ updatePath(defaultPath);
235
+ }
236
+ }, [defaultPath, path, updatePath]);
237
+ const router = useMemo2(() => {
238
+ const addPages = (newPages) => {
239
+ updatePath([...path, ...newPages]);
240
+ };
241
+ const goToIndex = (index) => {
242
+ updatePath(path.slice(0, index + 1));
243
+ };
244
+ const setPage = (coId) => {
245
+ updatePath([{ coId, name: "Root" }]);
246
+ };
247
+ const goBack = () => {
248
+ updatePath(path.slice(0, path.length - 1));
249
+ };
250
+ return {
251
+ path,
252
+ addPages,
253
+ goToIndex,
254
+ setPage,
255
+ goBack
256
+ };
257
+ }, [path, updatePath]);
258
+ return /* @__PURE__ */ jsx3(RouterContext.Provider, { value: router, children });
259
+ }
260
+
261
+ // src/inspector/viewer/page-stack.tsx
262
+ import { styled as styled24 } from "goober";
263
+
264
+ // src/inspector/viewer/page.tsx
265
+ import { styled as styled21 } from "goober";
266
+ import React5 from "react";
267
+
268
+ // src/inspector/ui/badge.tsx
269
+ import { styled } from "goober";
270
+ import { jsx as jsx4 } from "react/jsx-runtime";
271
+ var StyledBadge = styled("span")`
272
+ font-size: 0.875rem;
273
+ font-weight: 500;
274
+ padding: 0.125rem 0.25rem;
275
+ margin-left: -0.125rem;
276
+ border-radius: var(--j-radius-sm);
277
+ background-color: var(--j-foreground);
278
+ display: inline-block;
279
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
280
+ color: var(--j-text-color-strong);
281
+ `;
282
+ function Badge({
283
+ children,
284
+ className
285
+ }) {
286
+ return /* @__PURE__ */ jsx4(StyledBadge, { className, children });
287
+ }
288
+
289
+ // src/inspector/ui/heading.tsx
290
+ import { styled as styled2 } from "goober";
291
+ import { jsx as jsx5 } from "react/jsx-runtime";
292
+ var StyledHeading = styled2("h1")`
293
+ font-size: 1.125rem;
294
+ text-align: center;
295
+ font-weight: 500;
296
+ color: var(--j-text-color-strong);
297
+ `;
298
+ function Heading({
299
+ children,
300
+ className,
301
+ id
302
+ }) {
303
+ return /* @__PURE__ */ jsx5(StyledHeading, { className, id, children });
304
+ }
305
+
306
+ // src/inspector/ui/text.tsx
307
+ import { styled as styled3 } from "goober";
308
+ import React2 from "react";
309
+ import { jsx as jsx6 } from "react/jsx-runtime";
310
+ var BaseText = React2.forwardRef(
311
+ ({ muted, strong, small, inline, mono, ...rest }, ref) => /* @__PURE__ */ jsx6("div", { ref, ...rest })
312
+ );
313
+ var StyledText = styled3(BaseText)`
314
+ ${(props) => props.muted && `
315
+ color: var(--j-neutral-500);
316
+ `}
317
+
318
+ ${(props) => props.strong && `
319
+ font-weight: 500;
320
+ color: var(--j-text-color-strong);
321
+ `}
322
+
323
+ ${(props) => props.small && `
324
+ font-size: 0.875rem;
325
+ `}
326
+
327
+ ${(props) => props.inline && `
328
+ display: inline;
329
+ `}
330
+
331
+ ${(props) => props.mono && `
332
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
333
+ `}
334
+ `;
335
+ function Text(props) {
336
+ return /* @__PURE__ */ jsx6(StyledText, { ...props });
337
+ }
338
+
339
+ // src/inspector/viewer/account-or-group-text.tsx
340
+ import { useEffect as useEffect6, useState as useState6 } from "react";
341
+
342
+ // src/inspector/ui/button.tsx
343
+ import { styled as styled4 } from "goober";
344
+ import { forwardRef } from "react";
345
+ import { jsx as jsx7 } from "react/jsx-runtime";
346
+ var StyledButton = styled4("button")`
347
+ display: inline-flex;
348
+ align-items: center;
349
+ justify-content: center;
350
+ gap: 0.5rem;
351
+ text-align: center;
352
+ transition: colors 0.2s;
353
+ border-radius: var(--j-radius-lg);
354
+ pointer-events: ${(props) => props.disabled ? "none" : "auto"};
355
+ opacity: ${(props) => props.disabled ? 0.5 : 1};
356
+ cursor: ${(props) => props.disabled ? "not-allowed" : "pointer"};
357
+
358
+ ${(props) => {
359
+ switch (props.variant) {
360
+ case "primary":
361
+ return `
362
+ padding: 0.375rem 0.75rem;
363
+ background-color: var(--j-primary-color);
364
+ border-color: var(--j-primary-color);
365
+ color: white;
366
+ font-weight: 500;
367
+ &:hover {
368
+ opacity: 0.8;
369
+ }
370
+ `;
371
+ case "secondary":
372
+ return `
373
+ padding: 0.375rem 0.75rem;
374
+ color: var(--j-text-color-strong);
375
+ border: 1px solid var(--j-border-color);
376
+ font-weight: 500;
377
+ &:hover {
378
+ border-color: var(--j-border-color-hover);
379
+ }
380
+ `;
381
+ case "link":
382
+ return `
383
+ color: var(--j-link-color);
384
+ &:hover {
385
+ text-decoration: underline;
386
+ }
387
+ `;
388
+ case "destructive":
389
+ return `
390
+ padding: 0.375rem 0.75rem;
391
+ background-color: var(--j-destructive-color);
392
+ border-color: var(--j-destructive-color);
393
+ color: white;
394
+ font-weight: 500;
395
+ &:hover {
396
+ opacity: 0.8;
397
+ }
398
+ `;
399
+ default:
400
+ return "";
401
+ }
402
+ }}
403
+ `;
404
+ var Button = forwardRef(
405
+ ({
406
+ className,
407
+ children,
408
+ variant = "primary",
409
+ disabled,
410
+ type = "button",
411
+ ...buttonProps
412
+ }, ref) => {
413
+ return /* @__PURE__ */ jsx7(
414
+ StyledButton,
415
+ {
416
+ ref,
417
+ ...buttonProps,
418
+ disabled,
419
+ className,
420
+ type,
421
+ variant,
422
+ children
423
+ }
424
+ );
425
+ }
426
+ );
427
+
428
+ // src/inspector/viewer/use-resolve-covalue.ts
429
+ import { useEffect as useEffect5, useState as useState5 } from "react";
430
+
431
+ // src/inspector/viewer/co-stream-view.tsx
432
+ import { base64URLtoBytes } from "cojson";
433
+ import { styled as styled5 } from "goober";
434
+ import { useEffect as useEffect4, useState as useState4 } from "react";
435
+ import { Fragment, jsx as jsx8, jsxs } from "react/jsx-runtime";
436
+ function isBinaryStreamStart(item) {
437
+ return typeof item === "object" && item !== null && "type" in item && item.type === "start";
438
+ }
439
+ function detectCoStreamType(value) {
440
+ const firstKey = Object.keys(value.items)[0];
441
+ if (!firstKey)
442
+ return {
443
+ type: "unknown"
444
+ };
445
+ const items = value.items[firstKey]?.map((v) => v.value);
446
+ if (!items)
447
+ return {
448
+ type: "unknown"
449
+ };
450
+ const firstItem = items[0];
451
+ if (!firstItem)
452
+ return {
453
+ type: "unknown"
454
+ };
455
+ if (isBinaryStreamStart(firstItem)) {
456
+ return {
457
+ type: "binary",
458
+ items
459
+ };
460
+ } else {
461
+ return {
462
+ type: "coStream"
463
+ };
464
+ }
465
+ }
466
+ async function getBlobFromCoStream({
467
+ items,
468
+ onlyFirstChunk = false
469
+ }) {
470
+ if (onlyFirstChunk && items.length > 1) {
471
+ items = items.slice(0, 2);
472
+ }
473
+ const chunks = [];
474
+ const binary_U_prefixLength = 8;
475
+ let lastProgressUpdate = Date.now();
476
+ for (const item of items.slice(1)) {
477
+ if (item.type === "end") {
478
+ break;
479
+ }
480
+ if (item.type !== "chunk") {
481
+ console.error("Invalid binary stream chunk", item);
482
+ return void 0;
483
+ }
484
+ const chunk = base64URLtoBytes(item.chunk.slice(binary_U_prefixLength));
485
+ chunks.push(chunk);
486
+ if (Date.now() - lastProgressUpdate > 100) {
487
+ lastProgressUpdate = Date.now();
488
+ }
489
+ }
490
+ const defaultMime = items[0] && "mimeType" in items[0] ? items[0].mimeType : null;
491
+ const blob = new Blob(chunks, defaultMime ? { type: defaultMime } : {});
492
+ const mimeType = defaultMime === "" ? await detectPDFMimeType(blob) : defaultMime;
493
+ return {
494
+ blob,
495
+ mimeType,
496
+ unfinishedChunks: items.length > 1,
497
+ totalSize: items[0] && "totalSizeBytes" in items[0] ? items[0].totalSizeBytes : void 0
498
+ };
499
+ }
500
+ var detectPDFMimeType = async (blob) => {
501
+ const arrayBuffer = await blob.slice(0, 4).arrayBuffer();
502
+ const uint8Array = new Uint8Array(arrayBuffer);
503
+ const header = uint8Array.reduce(
504
+ (acc, byte) => acc + String.fromCharCode(byte),
505
+ ""
506
+ );
507
+ if (header === "%PDF") {
508
+ return "application/pdf";
509
+ }
510
+ return "unknown";
511
+ };
512
+ var BinaryDownloadButton = ({
513
+ pdfBlob,
514
+ fileName = "document",
515
+ label,
516
+ mimeType
517
+ }) => {
518
+ const downloadFile = () => {
519
+ const url = URL.createObjectURL(
520
+ new Blob([pdfBlob], mimeType ? { type: mimeType } : {})
521
+ );
522
+ const link = document.createElement("a");
523
+ link.href = url;
524
+ link.download = mimeType === "application/pdf" ? `${fileName}.pdf` : fileName;
525
+ document.body.appendChild(link);
526
+ link.click();
527
+ document.body.removeChild(link);
528
+ URL.revokeObjectURL(url);
529
+ };
530
+ return /* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: downloadFile, children: [
531
+ "\u2B07\uFE0F ",
532
+ label
533
+ ] });
534
+ };
535
+ var LabelContentPairContainer = styled5("div")`
536
+ display: flex;
537
+ flex-direction: column;
538
+ gap: 0.375rem;
539
+ `;
540
+ var BinaryStreamGrid = styled5("div")`
541
+ display: grid;
542
+ grid-template-columns: repeat(3, 1fr);
543
+ gap: 0.5rem;
544
+ max-width: 48rem;
545
+ `;
546
+ var ImagePreviewContainer = styled5("div")`
547
+ background-color: rgb(249 250 251);
548
+ padding: 0.75rem;
549
+ border-radius: var(--j-radius-md);
550
+ @media (prefers-color-scheme: dark) {
551
+ background-color: rgb(28 25 23);
552
+ }
553
+ `;
554
+ var CoStreamGrid = styled5("div")`
555
+ display: grid;
556
+ grid-template-columns: repeat(3, 1fr);
557
+ gap: 0.5rem;
558
+ `;
559
+ var CoStreamItemContainer = styled5("div")`
560
+ padding: 0.75rem;
561
+ border-radius: var(--j-radius-lg);
562
+ overflow: hidden;
563
+ border: 1px solid rgb(229 231 235);
564
+ cursor: pointer;
565
+ box-shadow: var(--j-shadow-sm);
566
+ &:hover {
567
+ background-color: rgb(243 244 246 / 0.05);
568
+ }
569
+ `;
570
+ var LabelContentPair = ({
571
+ label,
572
+ content
573
+ }) => {
574
+ return /* @__PURE__ */ jsxs(LabelContentPairContainer, { children: [
575
+ /* @__PURE__ */ jsx8("span", { children: label }),
576
+ /* @__PURE__ */ jsx8("span", { children: content })
577
+ ] });
578
+ };
579
+ function RenderCoBinaryStream({
580
+ value,
581
+ items
582
+ }) {
583
+ const [file, setFile] = useState4(null);
584
+ const [isLoading, setIsLoading] = useState4(true);
585
+ useEffect4(() => {
586
+ getBlobFromCoStream({
587
+ items,
588
+ onlyFirstChunk: true
589
+ }).then((v) => {
590
+ if (v) {
591
+ setFile(v);
592
+ if (v.mimeType.includes("image")) {
593
+ getBlobFromCoStream({
594
+ items
595
+ }).then((s) => {
596
+ if (s) setFile(s);
597
+ });
598
+ }
599
+ }
600
+ }).finally(() => setIsLoading(false));
601
+ }, [items]);
602
+ if (!isLoading && !file) return /* @__PURE__ */ jsx8("div", { children: "No blob" });
603
+ if (isLoading) return /* @__PURE__ */ jsx8("div", { children: "Loading..." });
604
+ if (!file) return /* @__PURE__ */ jsx8("div", { children: "No blob" });
605
+ const { blob, mimeType } = file;
606
+ const sizeInKB = (file.totalSize || 0) / 1024;
607
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
608
+ /* @__PURE__ */ jsxs(BinaryStreamGrid, { children: [
609
+ /* @__PURE__ */ jsx8(
610
+ LabelContentPair,
611
+ {
612
+ label: "Mime Type",
613
+ content: /* @__PURE__ */ jsx8(Badge, { children: mimeType || "No mime type" })
614
+ }
615
+ ),
616
+ /* @__PURE__ */ jsx8(
617
+ LabelContentPair,
618
+ {
619
+ label: "Size",
620
+ content: /* @__PURE__ */ jsxs("span", { children: [
621
+ sizeInKB.toFixed(2),
622
+ " KB"
623
+ ] })
624
+ }
625
+ ),
626
+ /* @__PURE__ */ jsx8(
627
+ LabelContentPair,
628
+ {
629
+ label: "Download",
630
+ content: /* @__PURE__ */ jsx8(
631
+ BinaryDownloadButton,
632
+ {
633
+ fileName: value.id.toString(),
634
+ pdfBlob: blob,
635
+ mimeType,
636
+ label: mimeType === "application/pdf" ? "Download PDF" : "Download file"
637
+ }
638
+ )
639
+ }
640
+ )
641
+ ] }),
642
+ mimeType === "image/png" || mimeType === "image/jpeg" ? /* @__PURE__ */ jsx8(
643
+ LabelContentPair,
644
+ {
645
+ label: "Preview",
646
+ content: /* @__PURE__ */ jsx8(ImagePreviewContainer, { children: /* @__PURE__ */ jsx8(RenderBlobImage, { blob }) })
647
+ }
648
+ ) : null
649
+ ] });
650
+ }
651
+ function RenderCoStream({
652
+ value,
653
+ node
654
+ }) {
655
+ const streamPerUser = Object.keys(value.items);
656
+ const userCoIds = streamPerUser.map((stream) => stream.split("_session")[0]);
657
+ return /* @__PURE__ */ jsx8(CoStreamGrid, { children: userCoIds.map((id, idx) => /* @__PURE__ */ jsxs(CoStreamItemContainer, { children: [
658
+ /* @__PURE__ */ jsx8(AccountOrGroupText, { coId: id, node }),
659
+ value.items[streamPerUser[idx]]?.map(
660
+ (item) => /* @__PURE__ */ jsxs("div", { children: [
661
+ new Date(item.madeAt).toLocaleString(),
662
+ " ",
663
+ JSON.stringify(item.value)
664
+ ] }, item.tx.txIndex + item.tx.sessionID)
665
+ )
666
+ ] }, id)) });
667
+ }
668
+ function CoStreamView({
669
+ value,
670
+ node
671
+ }) {
672
+ const streamType = detectCoStreamType(value);
673
+ if (streamType.type === "binary") {
674
+ if (streamType.items === void 0) {
675
+ return /* @__PURE__ */ jsx8("div", { children: "No binary stream" });
676
+ }
677
+ return /* @__PURE__ */ jsx8(
678
+ RenderCoBinaryStream,
679
+ {
680
+ value,
681
+ items: streamType.items
682
+ }
683
+ );
684
+ }
685
+ if (streamType.type === "coStream") {
686
+ return /* @__PURE__ */ jsx8(RenderCoStream, { value, node });
687
+ }
688
+ if (streamType.type === "unknown") return /* @__PURE__ */ jsx8("div", { children: "Unknown stream type" });
689
+ return /* @__PURE__ */ jsx8("div", { children: "Unknown stream type" });
690
+ }
691
+ function RenderBlobImage({ blob }) {
692
+ const urlCreator = window.URL || window.webkitURL;
693
+ return /* @__PURE__ */ jsx8("img", { src: urlCreator.createObjectURL(blob) });
694
+ }
695
+
696
+ // src/inspector/viewer/use-resolve-covalue.ts
697
+ var isBrowserImage = (coValue) => {
698
+ return "originalSize" in coValue && "placeholderDataURL" in coValue;
699
+ };
700
+ async function resolveCoValue(coValueId, node) {
701
+ const value = await node.load(coValueId);
702
+ if (value === "unavailable") {
703
+ return {
704
+ value: void 0,
705
+ snapshot: "unavailable",
706
+ type: null,
707
+ extendedType: void 0
708
+ };
709
+ }
710
+ const snapshot = value.toJSON();
711
+ const type = value.type;
712
+ let extendedType;
713
+ if (type === "comap") {
714
+ if (isBrowserImage(snapshot)) {
715
+ extendedType = "image";
716
+ } else if (value.headerMeta?.type === "account") {
717
+ extendedType = "account";
718
+ } else if (value.core.isGroup()) {
719
+ extendedType = "group";
720
+ }
721
+ }
722
+ return {
723
+ value,
724
+ snapshot,
725
+ type,
726
+ extendedType
727
+ };
728
+ }
729
+ function subscribeToCoValue(coValueId, node, callback) {
730
+ return node.subscribe(coValueId, (value) => {
731
+ if (value === "unavailable") {
732
+ callback({
733
+ value: void 0,
734
+ snapshot: "unavailable",
735
+ type: null,
736
+ extendedType: void 0
737
+ });
738
+ } else {
739
+ const snapshot = value.toJSON();
740
+ const type = value.type;
741
+ let extendedType;
742
+ if (type === "comap") {
743
+ if (isBrowserImage(snapshot)) {
744
+ extendedType = "image";
745
+ } else if (value.headerMeta?.type === "account") {
746
+ extendedType = "account";
747
+ } else if (value.core.isGroup()) {
748
+ extendedType = "group";
749
+ }
750
+ } else if (type === "costream") {
751
+ const coStream = detectCoStreamType(value);
752
+ if (coStream.type === "binary") {
753
+ extendedType = "file";
754
+ }
755
+ }
756
+ callback({
757
+ value,
758
+ snapshot,
759
+ type,
760
+ extendedType
761
+ });
762
+ }
763
+ });
764
+ }
765
+ function useResolvedCoValue(coValueId, node) {
766
+ const [result, setResult] = useState5();
767
+ useEffect5(() => {
768
+ let isMounted = true;
769
+ const unsubscribe = subscribeToCoValue(coValueId, node, (newResult) => {
770
+ if (isMounted) {
771
+ setResult(newResult);
772
+ }
773
+ });
774
+ return () => {
775
+ isMounted = false;
776
+ unsubscribe();
777
+ };
778
+ }, [coValueId, node]);
779
+ return result || {
780
+ value: void 0,
781
+ snapshot: void 0,
782
+ type: void 0,
783
+ extendedType: void 0
784
+ };
785
+ }
786
+ function useResolvedCoValues(coValueIds, node) {
787
+ const [results, setResults] = useState5([]);
788
+ useEffect5(() => {
789
+ let isMounted = true;
790
+ const unsubscribes = [];
791
+ coValueIds.forEach((coValueId, index) => {
792
+ const unsubscribe = subscribeToCoValue(coValueId, node, (newResult) => {
793
+ if (isMounted) {
794
+ setResults((prevResults) => {
795
+ const newResults = prevResults.slice(0, coValueIds.length);
796
+ newResults[index] = newResult;
797
+ return newResults;
798
+ });
799
+ }
800
+ });
801
+ unsubscribes.push(unsubscribe);
802
+ });
803
+ return () => {
804
+ isMounted = false;
805
+ unsubscribes.forEach((unsubscribe) => unsubscribe());
806
+ };
807
+ }, [coValueIds, node]);
808
+ return results;
809
+ }
810
+
811
+ // src/inspector/viewer/account-or-group-text.tsx
812
+ import { Fragment as Fragment2, jsx as jsx9 } from "react/jsx-runtime";
813
+ function AccountOrGroupText({
814
+ coId,
815
+ node,
816
+ showId = false,
817
+ onClick
818
+ }) {
819
+ const { snapshot, extendedType } = useResolvedCoValue(coId, node);
820
+ const [name, setName] = useState6(null);
821
+ useEffect6(() => {
822
+ if (snapshot && typeof snapshot === "object" && "profile" in snapshot) {
823
+ const profileId = snapshot.profile;
824
+ resolveCoValue(profileId, node).then((profileResult) => {
825
+ if (profileResult.snapshot && typeof profileResult.snapshot === "object" && "name" in profileResult.snapshot) {
826
+ setName(profileResult.snapshot.name);
827
+ }
828
+ });
829
+ }
830
+ }, [snapshot, node, extendedType]);
831
+ if (!snapshot) return /* @__PURE__ */ jsx9("span", { children: "Loading..." });
832
+ if (extendedType !== "account" && extendedType !== "group") {
833
+ return /* @__PURE__ */ jsx9("span", { children: "CoID is not an account or group" });
834
+ }
835
+ const displayName = extendedType === "account" ? name || "Account" : "Group";
836
+ const displayText = showId ? `${displayName} <${coId}>` : displayName;
837
+ if (onClick) {
838
+ return /* @__PURE__ */ jsx9(Button, { variant: "link", onClick: () => onClick(displayName), children: displayText });
839
+ }
840
+ return /* @__PURE__ */ jsx9(Fragment2, { children: displayText });
841
+ }
842
+
843
+ // src/inspector/viewer/grid-view.tsx
844
+ import { useState as useState9 } from "react";
845
+ import { styled as styled12 } from "goober";
846
+
847
+ // src/inspector/viewer/type-icon.tsx
848
+ import { styled as styled6 } from "goober";
849
+ import { jsx as jsx10 } from "react/jsx-runtime";
850
+ var IconText = styled6("span")`
851
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
852
+ `;
853
+ var UnavailableText = styled6("div")`
854
+ font-weight: 500;
855
+ `;
856
+ var EmptySpace = styled6("div")`
857
+ white-space: pre;
858
+ width: 3.5rem;
859
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
860
+ `;
861
+ var TypeIcon = ({
862
+ type,
863
+ extendedType
864
+ }) => {
865
+ const iconMap = {
866
+ record: "{} Record",
867
+ image: "\u{1F5BC}\uFE0F Image",
868
+ comap: "{} CoMap",
869
+ costream: "\u224B CoFeed",
870
+ colist: "\u2630 CoList",
871
+ account: "\u{1F464} Account",
872
+ group: "\u{1F465} Group",
873
+ file: "\u{1F4C3} FileStream",
874
+ coplaintext: "\u{1F4C4} CoPlainText"
875
+ };
876
+ const iconKey = extendedType || type;
877
+ const icon2 = iconMap[iconKey];
878
+ return icon2 ? /* @__PURE__ */ jsx10(IconText, { children: icon2 }) : null;
879
+ };
880
+ var ResolveIcon = ({
881
+ coId,
882
+ node
883
+ }) => {
884
+ const { type, extendedType, snapshot } = useResolvedCoValue(coId, node);
885
+ if (snapshot === "unavailable" && !type) {
886
+ return /* @__PURE__ */ jsx10(UnavailableText, { children: "Unavailable" });
887
+ }
888
+ if (!type) return /* @__PURE__ */ jsx10(EmptySpace, { children: " " });
889
+ return /* @__PURE__ */ jsx10(TypeIcon, { type, extendedType });
890
+ };
891
+
892
+ // src/inspector/viewer/types.ts
893
+ var isCoId = (coId) => typeof coId === "string" && coId.startsWith("co_") && !coId.includes("inviteSecret");
894
+
895
+ // src/inspector/viewer/value-renderer.tsx
896
+ import { styled as styled7 } from "goober";
897
+ import React3, { useState as useState7 } from "react";
898
+
899
+ // src/inspector/ui/icons/caution.tsx
900
+ import { jsx as jsx11, jsxs as jsxs2 } from "react/jsx-runtime";
901
+ function CautionIcon(props) {
902
+ return /* @__PURE__ */ jsxs2(
903
+ "svg",
904
+ {
905
+ ...props,
906
+ xmlns: "http://www.w3.org/2000/svg",
907
+ width: "24",
908
+ height: "24",
909
+ viewBox: "0 0 24 24",
910
+ fill: "none",
911
+ stroke: "currentColor",
912
+ strokeLinejoin: "round",
913
+ children: [
914
+ /* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "12", fill: "currentColor" }),
915
+ /* @__PURE__ */ jsx11("rect", { x: "10.5", y: "6", width: "3", height: "7.5", rx: "1.5", fill: "#fff" }),
916
+ /* @__PURE__ */ jsx11("rect", { x: "10.5", y: "16.5", width: "3", height: "3", rx: "1.5", fill: "#fff" })
917
+ ]
918
+ }
919
+ );
920
+ }
921
+
922
+ // src/inspector/ui/icons/chevron-down-icon.tsx
923
+ import { jsx as jsx12 } from "react/jsx-runtime";
924
+ function ChevronDownIcon(props) {
925
+ return /* @__PURE__ */ jsx12(
926
+ "svg",
927
+ {
928
+ ...props,
929
+ xmlns: "http://www.w3.org/2000/svg",
930
+ width: "24",
931
+ height: "24",
932
+ viewBox: "0 0 24 24",
933
+ fill: "none",
934
+ stroke: "currentColor",
935
+ strokeLinejoin: "round",
936
+ children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" })
937
+ }
938
+ );
939
+ }
940
+
941
+ // src/inspector/ui/icons/delete-icon.tsx
942
+ import { jsx as jsx13, jsxs as jsxs3 } from "react/jsx-runtime";
943
+ function DeleteIcon(props) {
944
+ return /* @__PURE__ */ jsxs3(
945
+ "svg",
946
+ {
947
+ ...props,
948
+ xmlns: "http://www.w3.org/2000/svg",
949
+ width: "24",
950
+ height: "24",
951
+ viewBox: "0 0 24 24",
952
+ fill: "none",
953
+ stroke: "currentColor",
954
+ strokeLinejoin: "round",
955
+ className: "lucide lucide-trash-icon lucide-trash",
956
+ children: [
957
+ /* @__PURE__ */ jsx13("path", { d: "M3 6h18" }),
958
+ /* @__PURE__ */ jsx13("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
959
+ /* @__PURE__ */ jsx13("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
960
+ ]
961
+ }
962
+ );
963
+ }
964
+
965
+ // src/inspector/ui/icons/edit-icon.tsx
966
+ import { jsx as jsx14 } from "react/jsx-runtime";
967
+ function EditIcon(props) {
968
+ return /* @__PURE__ */ jsx14(
969
+ "svg",
970
+ {
971
+ ...props,
972
+ xmlns: "http://www.w3.org/2000/svg",
973
+ width: "24",
974
+ height: "24",
975
+ viewBox: "0 0 24 24",
976
+ fill: "none",
977
+ stroke: "currentColor",
978
+ strokeLinejoin: "round",
979
+ className: "lucide lucide-edit lucide-pencil",
980
+ children: /* @__PURE__ */ jsx14("path", { d: "M13.2942 7.95881C13.5533 7.63559 13.5013 7.16358 13.178 6.90453C12.8548 6.64549 12.3828 6.6975 12.1238 7.02072L13.2942 7.95881ZM6.811 14.8488L7.37903 15.3385C7.38489 15.3317 7.39062 15.3248 7.39623 15.3178L6.811 14.8488ZM6.64 15.2668L5.89146 15.2179L5.8908 15.2321L6.64 15.2668ZM6.5 18.2898L5.7508 18.2551C5.74908 18.2923 5.75013 18.3296 5.75396 18.3667L6.5 18.2898ZM7.287 18.9768L7.31152 19.7264C7.36154 19.7247 7.41126 19.7181 7.45996 19.7065L7.287 18.9768ZM10.287 18.2658L10.46 18.9956L10.4716 18.9927L10.287 18.2658ZM10.672 18.0218L11.2506 18.4991L11.2571 18.491L10.672 18.0218ZM17.2971 10.959C17.5562 10.6358 17.5043 10.1638 17.1812 9.90466C16.8581 9.64552 16.386 9.69742 16.1269 10.0206L17.2971 10.959ZM12.1269 7.02052C11.8678 7.34365 11.9196 7.81568 12.2428 8.07484C12.5659 8.33399 13.0379 8.28213 13.2971 7.95901L12.1269 7.02052ZM14.3 5.50976L14.8851 5.97901C14.8949 5.96672 14.9044 5.95412 14.9135 5.94123L14.3 5.50976ZM15.929 5.18976L16.4088 4.61332C16.3849 4.59344 16.3598 4.57507 16.3337 4.5583L15.929 5.18976ZM18.166 7.05176L18.6968 6.52192C18.6805 6.50561 18.6635 6.49007 18.6458 6.47532L18.166 7.05176ZM18.5029 7.87264L19.2529 7.87676V7.87676L18.5029 7.87264ZM18.157 8.68976L17.632 8.15412C17.6108 8.17496 17.5908 8.19704 17.5721 8.22025L18.157 8.68976ZM16.1271 10.0203C15.8678 10.3433 15.9195 10.8153 16.2425 11.0746C16.5655 11.3339 17.0376 11.2823 17.2969 10.9593L16.1271 10.0203ZM13.4537 7.37862C13.3923 6.96898 13.0105 6.68666 12.6009 6.74805C12.1912 6.80943 11.9089 7.19127 11.9703 7.60091L13.4537 7.37862ZM16.813 11.2329C17.2234 11.1772 17.5109 10.7992 17.4552 10.3888C17.3994 9.97834 17.0215 9.69082 16.611 9.74659L16.813 11.2329ZM12.1238 7.02072L6.22577 14.3797L7.39623 15.3178L13.2942 7.95881L12.1238 7.02072ZM6.24297 14.359C6.03561 14.5995 5.91226 14.9011 5.89159 15.218L7.38841 15.3156C7.38786 15.324 7.38457 15.3321 7.37903 15.3385L6.24297 14.359ZM5.8908 15.2321L5.7508 18.2551L7.2492 18.3245L7.3892 15.3015L5.8908 15.2321ZM5.75396 18.3667C5.83563 19.1586 6.51588 19.7524 7.31152 19.7264L7.26248 18.2272C7.25928 18.2273 7.25771 18.2268 7.25669 18.2264C7.25526 18.2259 7.25337 18.2249 7.25144 18.2232C7.2495 18.2215 7.24825 18.2198 7.24754 18.2185C7.24703 18.2175 7.24637 18.216 7.24604 18.2128L5.75396 18.3667ZM7.45996 19.7065L10.46 18.9955L10.114 17.536L7.11404 18.247L7.45996 19.7065ZM10.4716 18.9927C10.7771 18.9151 11.05 18.7422 11.2506 18.499L10.0934 17.5445C10.0958 17.5417 10.0989 17.5397 10.1024 17.5388L10.4716 18.9927ZM11.2571 18.491L17.2971 10.959L16.1269 10.0206L10.0869 17.5526L11.2571 18.491ZM13.2971 7.95901L14.8851 5.97901L13.7149 5.04052L12.1269 7.02052L13.2971 7.95901ZM14.9135 5.94123C15.0521 5.74411 15.3214 5.6912 15.5243 5.82123L16.3337 4.5583C15.4544 3.99484 14.2873 4.2241 13.6865 5.0783L14.9135 5.94123ZM15.4492 5.7662L17.6862 7.6282L18.6458 6.47532L16.4088 4.61332L15.4492 5.7662ZM17.6352 7.58161C17.7111 7.6577 17.7535 7.761 17.7529 7.86852L19.2529 7.87676C19.2557 7.36905 19.0555 6.88127 18.6968 6.52192L17.6352 7.58161ZM17.7529 7.86852C17.7524 7.97604 17.7088 8.07886 17.632 8.15412L18.682 9.22541C19.0446 8.87002 19.2501 8.38447 19.2529 7.87676L17.7529 7.86852ZM17.5721 8.22025L16.1271 10.0203L17.2969 10.9593L18.7419 9.15928L17.5721 8.22025ZM11.9703 7.60091C12.3196 9.93221 14.4771 11.5503 16.813 11.2329L16.611 9.74659C15.0881 9.95352 13.6815 8.89855 13.4537 7.37862L11.9703 7.60091Z" })
981
+ }
982
+ );
983
+ }
984
+
985
+ // src/inspector/ui/icons/link-icon.tsx
986
+ import { jsx as jsx15 } from "react/jsx-runtime";
987
+ function LinkIcon(props) {
988
+ return /* @__PURE__ */ jsx15(
989
+ "svg",
990
+ {
991
+ xmlns: "http://www.w3.org/2000/svg",
992
+ fill: "none",
993
+ viewBox: "0 0 24 24",
994
+ strokeWidth: 1.5,
995
+ stroke: "currentColor",
996
+ ...props,
997
+ children: /* @__PURE__ */ jsx15(
998
+ "path",
999
+ {
1000
+ strokeLinecap: "round",
1001
+ strokeLinejoin: "round",
1002
+ d: "M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
1003
+ }
1004
+ )
1005
+ }
1006
+ );
1007
+ }
1008
+
1009
+ // src/inspector/ui/icons/history.tsx
1010
+ import { jsx as jsx16, jsxs as jsxs4 } from "react/jsx-runtime";
1011
+ function HistoryIcon(props) {
1012
+ return /* @__PURE__ */ jsxs4(
1013
+ "svg",
1014
+ {
1015
+ ...props,
1016
+ xmlns: "http://www.w3.org/2000/svg",
1017
+ width: "24",
1018
+ height: "24",
1019
+ viewBox: "0 0 24 24",
1020
+ fill: "none",
1021
+ stroke: "currentColor",
1022
+ strokeLinejoin: "round",
1023
+ className: "lucide lucide-trash-icon lucide-trash",
1024
+ children: [
1025
+ /* @__PURE__ */ jsx16(
1026
+ "path",
1027
+ {
1028
+ d: "M5.52786 16.7023C6.6602 18.2608 8.3169 19.3584 10.1936 19.7934C12.0703 20.2284 14.0409 19.9716 15.7434 19.0701C17.446 18.1687 18.766 16.6832 19.4611 14.8865C20.1562 13.0898 20.1796 11.1027 19.527 9.29011C18.8745 7.47756 17.5898 5.96135 15.909 5.02005C14.2282 4.07875 12.2641 3.77558 10.3777 4.16623C8.49129 4.55689 6.80919 5.61514 5.64045 7.14656C4.47171 8.67797 3.89482 10.5797 4.01579 12.5023M4.01579 12.5023L2.51579 11.0023M4.01579 12.5023L5.51579 11.0023",
1029
+ strokeWidth: "2",
1030
+ strokeLinecap: "round",
1031
+ strokeLinejoin: "round"
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ jsx16(
1035
+ "path",
1036
+ {
1037
+ d: "M12 8V12L15 15",
1038
+ strokeWidth: "2",
1039
+ strokeLinecap: "round",
1040
+ strokeLinejoin: "round"
1041
+ }
1042
+ )
1043
+ ]
1044
+ }
1045
+ );
1046
+ }
1047
+
1048
+ // src/inspector/ui/icons/add-icon.tsx
1049
+ import { jsx as jsx17 } from "react/jsx-runtime";
1050
+ function AddIcon(props) {
1051
+ return /* @__PURE__ */ jsx17(
1052
+ "svg",
1053
+ {
1054
+ ...props,
1055
+ xmlns: "http://www.w3.org/2000/svg",
1056
+ width: "24",
1057
+ height: "24",
1058
+ viewBox: "0 0 24 24",
1059
+ fill: "none",
1060
+ stroke: "currentColor",
1061
+ strokeLinejoin: "round",
1062
+ children: /* @__PURE__ */ jsx17(
1063
+ "path",
1064
+ {
1065
+ d: "M4 12H20M12 4V20",
1066
+ strokeWidth: "2",
1067
+ strokeLinecap: "round",
1068
+ strokeLinejoin: "round"
1069
+ }
1070
+ )
1071
+ }
1072
+ );
1073
+ }
1074
+
1075
+ // src/inspector/ui/icon.tsx
1076
+ import { jsx as jsx18 } from "react/jsx-runtime";
1077
+ var icons = {
1078
+ caution: CautionIcon,
1079
+ chevronDown: ChevronDownIcon,
1080
+ delete: DeleteIcon,
1081
+ edit: EditIcon,
1082
+ link: LinkIcon,
1083
+ history: HistoryIcon,
1084
+ add: AddIcon
1085
+ };
1086
+ var sizes = {
1087
+ "2xs": 14,
1088
+ xs: 16,
1089
+ sm: 20,
1090
+ md: 24,
1091
+ lg: 28,
1092
+ xl: 28,
1093
+ "2xl": 32,
1094
+ "3xl": 36,
1095
+ "4xl": 40,
1096
+ "5xl": 48,
1097
+ "6xl": 60,
1098
+ "7xl": 72,
1099
+ "8xl": 96,
1100
+ "9xl": 128
1101
+ };
1102
+ var strokeWidths = {
1103
+ "2xs": 2.5,
1104
+ xs: 2,
1105
+ sm: 2,
1106
+ md: 1.5,
1107
+ lg: 1.5,
1108
+ xl: 1.5,
1109
+ "2xl": 1.25,
1110
+ "3xl": 1.25,
1111
+ "4xl": 1.25,
1112
+ "5xl": 1,
1113
+ "6xl": 1,
1114
+ "7xl": 1,
1115
+ "8xl": 1,
1116
+ "9xl": 1
1117
+ };
1118
+ function Icon({
1119
+ name,
1120
+ size = "md",
1121
+ className,
1122
+ ...svgProps
1123
+ }) {
1124
+ if (!name || !icons.hasOwnProperty(name)) {
1125
+ throw new Error(`Icon not found: ${name}`);
1126
+ }
1127
+ const IconComponent = icons?.hasOwnProperty(name) ? icons[name] : icon;
1128
+ return /* @__PURE__ */ jsx18(
1129
+ IconComponent,
1130
+ {
1131
+ "aria-hidden": "true",
1132
+ size: sizes[size],
1133
+ strokeWidth: strokeWidths[size],
1134
+ strokeLinecap: "round",
1135
+ ...svgProps
1136
+ }
1137
+ );
1138
+ }
1139
+
1140
+ // src/inspector/viewer/value-renderer.tsx
1141
+ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs5 } from "react/jsx-runtime";
1142
+ var LinkContainer = styled7("span")`
1143
+ display: inline-flex;
1144
+ gap: 0.25rem;
1145
+ align-items: center;
1146
+ `;
1147
+ var BooleanText = styled7("span")`
1148
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1149
+ ${(props) => props.value ? `
1150
+ color: var(--j-success-color);
1151
+ ` : `
1152
+ color: var(--j-destructive-color);
1153
+ `}
1154
+ `;
1155
+ var ObjectContent = styled7("pre")`
1156
+ margin-top: 0.375rem;
1157
+ font-size: 0.875rem;
1158
+ white-space: pre-wrap;
1159
+ `;
1160
+ var PreviewContainer = styled7("div")`
1161
+ font-size: 0.875rem;
1162
+ display: flex;
1163
+ flex-direction: column;
1164
+ gap: 0.5rem;
1165
+ align-items: flex-start;
1166
+ `;
1167
+ var PreviewGrid = styled7("div")`
1168
+ display: grid;
1169
+ grid-template-columns: auto 1fr;
1170
+ gap: 0.5rem;
1171
+ `;
1172
+ var PreviewMoreText = styled7(Text)`
1173
+ text-align: left;
1174
+ margin-top: 0.5rem;
1175
+ `;
1176
+ var ImagePreviewContainer2 = styled7("div")`
1177
+ display: flex;
1178
+ flex-direction: column;
1179
+ align-items: flex-start;
1180
+ `;
1181
+ var PreviewImage = styled7("img")`
1182
+ width: 2rem;
1183
+ height: 2rem;
1184
+ border: 2px solid white;
1185
+ box-shadow: var(--j-shadow-sm);
1186
+ margin: 0.5rem 0;
1187
+ `;
1188
+ var RecordText = styled7("div")`
1189
+ display: flex;
1190
+ align-items: center;
1191
+ gap: 0.25rem;
1192
+ `;
1193
+ var ListText = styled7("div")`
1194
+ display: flex;
1195
+ align-items: center;
1196
+ gap: 0.25rem;
1197
+ `;
1198
+ function ValueRenderer({
1199
+ json,
1200
+ onCoIDClick,
1201
+ compact
1202
+ }) {
1203
+ const [isExpanded, setIsExpanded] = useState7(false);
1204
+ if (typeof json === "undefined" || json === void 0) {
1205
+ return /* @__PURE__ */ jsx19(Text, { muted: true, children: "undefined" });
1206
+ }
1207
+ if (json === null) {
1208
+ return /* @__PURE__ */ jsx19(Text, { muted: true, children: "null" });
1209
+ }
1210
+ if (typeof json === "string" && isCoId(json)) {
1211
+ const content = /* @__PURE__ */ jsxs5(Fragment3, { children: [
1212
+ json,
1213
+ onCoIDClick && /* @__PURE__ */ jsx19(Icon, { name: "link" })
1214
+ ] });
1215
+ if (onCoIDClick) {
1216
+ return /* @__PURE__ */ jsx19(
1217
+ Button,
1218
+ {
1219
+ variant: "link",
1220
+ onClick: () => {
1221
+ onCoIDClick?.(json);
1222
+ },
1223
+ children: content
1224
+ }
1225
+ );
1226
+ }
1227
+ return /* @__PURE__ */ jsx19(LinkContainer, { children: content });
1228
+ }
1229
+ if (typeof json === "string") {
1230
+ return /* @__PURE__ */ jsx19(Text, { children: json });
1231
+ }
1232
+ if (typeof json === "number") {
1233
+ return /* @__PURE__ */ jsx19(Text, { mono: true, children: json });
1234
+ }
1235
+ if (typeof json === "boolean") {
1236
+ return /* @__PURE__ */ jsx19(BooleanText, { value: json, children: json.toString() });
1237
+ }
1238
+ const longJson = JSON.stringify(json, null, 2);
1239
+ const shortJson = longJson.split("\n").slice(0, compact ? 3 : 8).join("\n");
1240
+ const hasDifference = longJson !== shortJson;
1241
+ if (typeof json === "object") {
1242
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
1243
+ /* @__PURE__ */ jsx19("p", { children: Array.isArray(json) ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1244
+ "Array (",
1245
+ json.length,
1246
+ ")"
1247
+ ] }) : /* @__PURE__ */ jsx19(Fragment3, { children: "Object" }) }),
1248
+ /* @__PURE__ */ jsxs5(ObjectContent, { children: [
1249
+ isExpanded ? longJson : shortJson,
1250
+ hasDifference && !isExpanded ? "\n ..." : null
1251
+ ] }),
1252
+ !compact && hasDifference ? /* @__PURE__ */ jsx19(Button, { variant: "link", onClick: () => setIsExpanded(!isExpanded), children: isExpanded ? "Show less" : "Show more" }) : null
1253
+ ] });
1254
+ }
1255
+ return /* @__PURE__ */ jsx19("span", { children: String(json) });
1256
+ }
1257
+ var CoMapPreview = ({
1258
+ coId,
1259
+ node,
1260
+ limit = 6
1261
+ }) => {
1262
+ const { value, snapshot, type, extendedType } = useResolvedCoValue(
1263
+ coId,
1264
+ node
1265
+ );
1266
+ if (!snapshot) {
1267
+ return /* @__PURE__ */ jsx19(
1268
+ "div",
1269
+ {
1270
+ style: {
1271
+ borderRadius: "0.25rem",
1272
+ backgroundColor: "var(--j-foreground)",
1273
+ animation: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
1274
+ whiteSpace: "pre",
1275
+ width: "6rem"
1276
+ },
1277
+ children: " "
1278
+ }
1279
+ );
1280
+ }
1281
+ if (snapshot === "unavailable" && !value) {
1282
+ return /* @__PURE__ */ jsx19(Text, { inline: true, muted: true, children: "Unavailable" });
1283
+ }
1284
+ if (type === "coplaintext") {
1285
+ return /* @__PURE__ */ jsx19(Fragment3, { children: value.toString() });
1286
+ }
1287
+ if (extendedType === "image" && isBrowserImage(snapshot)) {
1288
+ return /* @__PURE__ */ jsxs5(ImagePreviewContainer2, { children: [
1289
+ /* @__PURE__ */ jsx19(PreviewImage, { src: snapshot.placeholderDataURL }),
1290
+ /* @__PURE__ */ jsxs5(Text, { inline: true, small: true, muted: true, children: [
1291
+ snapshot.originalSize[0],
1292
+ " x ",
1293
+ snapshot.originalSize[1]
1294
+ ] })
1295
+ ] });
1296
+ }
1297
+ if (extendedType === "record") {
1298
+ return /* @__PURE__ */ jsxs5(RecordText, { children: [
1299
+ "Record",
1300
+ " ",
1301
+ /* @__PURE__ */ jsxs5(Text, { inline: true, muted: true, children: [
1302
+ "(",
1303
+ Object.keys(snapshot).length,
1304
+ ")"
1305
+ ] })
1306
+ ] });
1307
+ }
1308
+ if (type === "colist") {
1309
+ return /* @__PURE__ */ jsxs5(ListText, { children: [
1310
+ "List",
1311
+ " ",
1312
+ /* @__PURE__ */ jsxs5(Text, { inline: true, muted: true, children: [
1313
+ "(",
1314
+ snapshot.length,
1315
+ ")"
1316
+ ] })
1317
+ ] });
1318
+ }
1319
+ const properties = Object.entries(snapshot);
1320
+ const limitedProperties = extendedType === "account" ? properties.filter(
1321
+ ([key]) => !key.startsWith("key_z") && !key.startsWith("sealer_z") && key !== "readKey"
1322
+ ).slice(0, limit) : properties.slice(0, limit);
1323
+ return /* @__PURE__ */ jsxs5(PreviewContainer, { children: [
1324
+ /* @__PURE__ */ jsx19(PreviewGrid, { children: limitedProperties.map(([key, value2]) => /* @__PURE__ */ jsxs5(React3.Fragment, { children: [
1325
+ /* @__PURE__ */ jsxs5(Text, { strong: true, children: [
1326
+ key,
1327
+ ": "
1328
+ ] }),
1329
+ /* @__PURE__ */ jsx19(ValueRenderer, { compact: true, json: value2 })
1330
+ ] }, key)) }),
1331
+ properties.length > limit && /* @__PURE__ */ jsxs5(PreviewMoreText, { muted: true, small: true, children: [
1332
+ properties.length - limit,
1333
+ " more"
1334
+ ] })
1335
+ ] });
1336
+ };
1337
+
1338
+ // src/inspector/viewer/co-value-editor.tsx
1339
+ import { useState as useState8 } from "react";
1340
+ import { styled as styled9 } from "goober";
1341
+
1342
+ // src/inspector/ui/select.tsx
1343
+ import { styled as styled8 } from "goober";
1344
+ import { useId } from "react";
1345
+ import { jsx as jsx20, jsxs as jsxs6 } from "react/jsx-runtime";
1346
+ var SelectContainer = styled8("div")`
1347
+ display: grid;
1348
+ gap: 0.25rem;
1349
+ `;
1350
+ var SelectWrapper = styled8("div")`
1351
+ position: relative;
1352
+ display: flex;
1353
+ align-items: center;
1354
+ `;
1355
+ var StyledSelect = styled8("select")`
1356
+ width: 100%;
1357
+ border-radius: var(--j-radius-md);
1358
+ border: 1px solid var(--j-border-color);
1359
+ padding: 0.5rem 0.875rem 0.5rem 0.875rem;
1360
+ padding-right: 2rem;
1361
+ box-shadow: var(--j-shadow-sm);
1362
+ font-weight: 500;
1363
+ color: var(--j-text-color-strong);
1364
+ appearance: none;
1365
+ overflow: hidden;
1366
+ text-overflow: ellipsis;
1367
+ white-space: nowrap;
1368
+
1369
+ @media (prefers-color-scheme: dark) {
1370
+ background-color: var(--j-foreground);
1371
+ }
1372
+ `;
1373
+ var SelectIcon = styled8("span")`
1374
+ position: absolute;
1375
+ right: 0.5em;
1376
+ color: var(--j-neutral-400);
1377
+ pointer-events: none;
1378
+
1379
+ @media (prefers-color-scheme: dark) {
1380
+ color: var(--j-neutral-900);
1381
+ }
1382
+ `;
1383
+ function Select(props) {
1384
+ const { label, hideLabel, id: customId, className, ...selectProps } = props;
1385
+ const generatedId = useId();
1386
+ const id = customId || generatedId;
1387
+ return /* @__PURE__ */ jsxs6(SelectContainer, { className, children: [
1388
+ /* @__PURE__ */ jsx20("label", { htmlFor: id, className: hideLabel ? "j-sr-only" : "", children: label }),
1389
+ /* @__PURE__ */ jsxs6(SelectWrapper, { children: [
1390
+ /* @__PURE__ */ jsx20(StyledSelect, { ...selectProps, id, children: props.children }),
1391
+ /* @__PURE__ */ jsx20(SelectIcon, { children: /* @__PURE__ */ jsx20(Icon, { name: "chevronDown", size: "sm" }) })
1392
+ ] })
1393
+ ] });
1394
+ }
1395
+
1396
+ // src/inspector/viewer/co-value-editor.tsx
1397
+ import { jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
1398
+ function CoValueEditor({
1399
+ node,
1400
+ property,
1401
+ value,
1402
+ coValue,
1403
+ onCancel
1404
+ }) {
1405
+ const getInitialType = () => {
1406
+ if (value === null) return "null";
1407
+ if (value === void 0) return "undefined";
1408
+ if (typeof value === "number") return "number";
1409
+ if (typeof value === "string") return "string";
1410
+ if (typeof value === "boolean") return value ? "true" : "false";
1411
+ if (typeof value === "object") return "object";
1412
+ return "undefined";
1413
+ };
1414
+ const [selectedType, setSelectedType] = useState8(getInitialType());
1415
+ const [editValue, setEditValue] = useState8(
1416
+ value === void 0 || value === null ? "" : typeof value === "object" ? JSON.stringify(value, null, 2) : String(value)
1417
+ );
1418
+ const handleSubmit = (e) => {
1419
+ e.preventDefault();
1420
+ e.stopPropagation();
1421
+ let newValue;
1422
+ switch (selectedType) {
1423
+ case "null":
1424
+ newValue = null;
1425
+ break;
1426
+ case "undefined":
1427
+ newValue = void 0;
1428
+ break;
1429
+ case "true":
1430
+ newValue = true;
1431
+ break;
1432
+ case "false":
1433
+ newValue = false;
1434
+ break;
1435
+ case "number":
1436
+ newValue = parseFloat(editValue);
1437
+ break;
1438
+ case "string":
1439
+ newValue = editValue;
1440
+ break;
1441
+ case "object":
1442
+ newValue = JSON.parse(editValue);
1443
+ break;
1444
+ default:
1445
+ throw new Error(`Invalid type: ${selectedType}`);
1446
+ }
1447
+ coValue.core.makeTransaction(
1448
+ [
1449
+ {
1450
+ op: "set",
1451
+ key: property,
1452
+ value: newValue
1453
+ }
1454
+ ],
1455
+ "private"
1456
+ );
1457
+ onCancel();
1458
+ };
1459
+ const showTextarea = selectedType === "number" || selectedType === "string" || selectedType === "object";
1460
+ return /* @__PURE__ */ jsxs7(EditForm, { onSubmit: handleSubmit, children: [
1461
+ /* @__PURE__ */ jsxs7(
1462
+ Select,
1463
+ {
1464
+ label: "Type",
1465
+ value: selectedType,
1466
+ onChange: (e) => {
1467
+ setSelectedType(e.target.value);
1468
+ },
1469
+ onClick: (e) => e.stopPropagation(),
1470
+ children: [
1471
+ /* @__PURE__ */ jsx21("option", { value: "number", children: "number" }),
1472
+ /* @__PURE__ */ jsx21("option", { value: "string", children: "string" }),
1473
+ /* @__PURE__ */ jsx21("option", { value: "true", children: "true" }),
1474
+ /* @__PURE__ */ jsx21("option", { value: "false", children: "false" }),
1475
+ /* @__PURE__ */ jsx21("option", { value: "object", children: "object" }),
1476
+ /* @__PURE__ */ jsx21("option", { value: "null", children: "null" }),
1477
+ /* @__PURE__ */ jsx21("option", { value: "undefined", children: "undefined" })
1478
+ ]
1479
+ }
1480
+ ),
1481
+ showTextarea && /* @__PURE__ */ jsx21(
1482
+ StyledTextarea,
1483
+ {
1484
+ value: editValue,
1485
+ onChange: (e) => setEditValue(e.target.value),
1486
+ onClick: (e) => e.stopPropagation()
1487
+ }
1488
+ ),
1489
+ /* @__PURE__ */ jsxs7(FormActions, { children: [
1490
+ /* @__PURE__ */ jsx21(Button, { type: "button", variant: "secondary", onClick: onCancel, children: "Cancel" }),
1491
+ /* @__PURE__ */ jsx21(Button, { type: "submit", variant: "primary", children: "Submit" })
1492
+ ] })
1493
+ ] });
1494
+ }
1495
+ var EditForm = styled9("form")`
1496
+ display: flex;
1497
+ flex-direction: column;
1498
+ gap: 0.75rem;
1499
+ `;
1500
+ var StyledTextarea = styled9("textarea")`
1501
+ width: 100%;
1502
+ min-height: 120px;
1503
+ border-radius: var(--j-radius-md);
1504
+ border: 1px solid var(--j-border-color);
1505
+ padding: 0.5rem 0.875rem;
1506
+ box-shadow: var(--j-shadow-sm);
1507
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1508
+ font-size: 0.875rem;
1509
+ background-color: white;
1510
+ color: var(--j-text-color-strong);
1511
+ resize: vertical;
1512
+
1513
+ @media (prefers-color-scheme: dark) {
1514
+ background-color: var(--j-foreground);
1515
+ }
1516
+ `;
1517
+ var FormActions = styled9("div")`
1518
+ display: flex;
1519
+ gap: 0.5rem;
1520
+ justify-content: flex-end;
1521
+ `;
1522
+
1523
+ // src/inspector/ui/card.tsx
1524
+ import { styled as styled10 } from "goober";
1525
+ var Card = styled10("div")`
1526
+ background-color: var(--j-background);
1527
+ border-radius: var(--j-radius-lg);
1528
+ box-shadow: var(--j-shadow-sm);
1529
+ border: 1px solid var(--j-border-color);
1530
+ padding: 1rem;
1531
+ text-align: left;
1532
+ overflow-x: hidden;
1533
+ display: flex;
1534
+ flex-direction: column;
1535
+ gap: 0.5rem;
1536
+ `;
1537
+ var CardHeader = styled10("div")`
1538
+ display: flex;
1539
+ justify-content: space-between;
1540
+ align-items: center;
1541
+ `;
1542
+ var CardBody = styled10("div")`
1543
+ flex: 1;
1544
+ `;
1545
+
1546
+ // src/inspector/ui/grid.tsx
1547
+ import { styled as styled11 } from "goober";
1548
+ import { jsx as jsx22 } from "react/jsx-runtime";
1549
+ var GridThreeColumns = styled11("div")`
1550
+ display: grid;
1551
+ grid-template-columns: repeat(1, minmax(0, 1fr));
1552
+ gap: 1rem;
1553
+
1554
+ @media (min-width: 768px) {
1555
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1556
+ }
1557
+
1558
+ @media (min-width: 1280px) {
1559
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1560
+ }
1561
+ `;
1562
+ var GridTwoColumns = styled11("div")`
1563
+ display: grid;
1564
+ grid-template-columns: repeat(1, minmax(0, 1fr));
1565
+ gap: 1rem;
1566
+
1567
+ @media (min-width: 768px) {
1568
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1569
+ }
1570
+ `;
1571
+ var GridOneColumn = styled11("div")`
1572
+ display: grid;
1573
+ grid-template-columns: repeat(1, minmax(0, 1fr));
1574
+ gap: 1rem;
1575
+ `;
1576
+ function Grid(props) {
1577
+ const { cols, children, ...rest } = props;
1578
+ switch (cols) {
1579
+ case 1:
1580
+ return /* @__PURE__ */ jsx22(GridOneColumn, { ...rest, children });
1581
+ case 2:
1582
+ return /* @__PURE__ */ jsx22(GridTwoColumns, { ...rest, children });
1583
+ case 3:
1584
+ return /* @__PURE__ */ jsx22(GridThreeColumns, { ...rest, children });
1585
+ default:
1586
+ throw new Error(`Invalid number of columns: ${cols}`);
1587
+ }
1588
+ }
1589
+
1590
+ // src/inspector/utils/permissions.ts
1591
+ function isWriter(role) {
1592
+ return role === "writer" || role === "admin" || role === "manager" || role === "writeOnly";
1593
+ }
1594
+
1595
+ // src/inspector/viewer/grid-view.tsx
1596
+ import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs8 } from "react/jsx-runtime";
1597
+ function GridItem({
1598
+ entry,
1599
+ onNavigate,
1600
+ node,
1601
+ coValue
1602
+ }) {
1603
+ const [key, value] = entry;
1604
+ const isCoValue = isCoId(value);
1605
+ const [isEditing, setIsEditing] = useState9(false);
1606
+ const handleEditClick = (e) => {
1607
+ e.stopPropagation();
1608
+ setIsEditing(true);
1609
+ };
1610
+ const handleCancel = () => {
1611
+ setIsEditing(false);
1612
+ };
1613
+ const handleDelete = (e) => {
1614
+ e.stopPropagation();
1615
+ if (confirm(`Are you sure you want to delete the property "${key}"?`)) {
1616
+ coValue?.core.makeTransaction(
1617
+ [
1618
+ {
1619
+ op: "del",
1620
+ key
1621
+ }
1622
+ ],
1623
+ "private"
1624
+ );
1625
+ }
1626
+ };
1627
+ if (isEditing) {
1628
+ return /* @__PURE__ */ jsxs8(
1629
+ Card,
1630
+ {
1631
+ style: {
1632
+ backgroundColor: "var(--j-foreground)",
1633
+ borderColor: "var(--j-foreground)"
1634
+ },
1635
+ children: [
1636
+ /* @__PURE__ */ jsx23(CardHeader, { children: /* @__PURE__ */ jsx23("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: isCoValue ? /* @__PURE__ */ jsxs8(Fragment4, { children: [
1637
+ /* @__PURE__ */ jsx23(Text, { strong: true, children: key }),
1638
+ /* @__PURE__ */ jsx23(Badge, { children: /* @__PURE__ */ jsx23(ResolveIcon, { coId: value, node }) })
1639
+ ] }) : /* @__PURE__ */ jsx23(Text, { strong: true, children: key }) }) }),
1640
+ /* @__PURE__ */ jsx23(CardBody, { style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx23(
1641
+ CoValueEditor,
1642
+ {
1643
+ node,
1644
+ property: key,
1645
+ value,
1646
+ coValue,
1647
+ onCancel: handleCancel
1648
+ }
1649
+ ) })
1650
+ ]
1651
+ }
1652
+ );
1653
+ }
1654
+ const cardProps = isCoValue ? {
1655
+ onClick: () => onNavigate([{ coId: value, name: key }]),
1656
+ as: "button"
1657
+ } : {
1658
+ style: {
1659
+ backgroundColor: "var(--j-foreground)",
1660
+ borderColor: "var(--j-foreground)"
1661
+ }
1662
+ };
1663
+ return /* @__PURE__ */ jsxs8(Card, { ...cardProps, children: [
1664
+ /* @__PURE__ */ jsxs8(CardHeader, { children: [
1665
+ /* @__PURE__ */ jsx23("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: isCoValue ? /* @__PURE__ */ jsxs8(Fragment4, { children: [
1666
+ /* @__PURE__ */ jsx23(Text, { strong: true, children: key }),
1667
+ /* @__PURE__ */ jsx23(Badge, { children: /* @__PURE__ */ jsx23(ResolveIcon, { coId: value, node }) })
1668
+ ] }) : /* @__PURE__ */ jsx23(Text, { strong: true, children: key }) }),
1669
+ coValue && isWriter(coValue.group.myRole()) && /* @__PURE__ */ jsxs8(ActionButtons, { children: [
1670
+ /* @__PURE__ */ jsx23(
1671
+ EditButton,
1672
+ {
1673
+ onClick: handleEditClick,
1674
+ type: "button",
1675
+ "aria-label": "Edit",
1676
+ children: /* @__PURE__ */ jsx23(Icon, { name: "edit", size: "sm" })
1677
+ }
1678
+ ),
1679
+ /* @__PURE__ */ jsx23(
1680
+ DeleteButton,
1681
+ {
1682
+ onClick: handleDelete,
1683
+ type: "button",
1684
+ "aria-label": "Delete",
1685
+ children: /* @__PURE__ */ jsx23(Icon, { name: "delete", size: "sm" })
1686
+ }
1687
+ )
1688
+ ] })
1689
+ ] }),
1690
+ /* @__PURE__ */ jsx23(CardBody, { style: { wordBreak: "break-word" }, children: isCoValue ? /* @__PURE__ */ jsx23(CoMapPreview, { coId: value, node }) : /* @__PURE__ */ jsx23(
1691
+ ValueRenderer,
1692
+ {
1693
+ json: value,
1694
+ onCoIDClick: (coId) => {
1695
+ onNavigate([{ coId, name: key }]);
1696
+ }
1697
+ }
1698
+ ) })
1699
+ ] });
1700
+ }
1701
+ function GridView({
1702
+ data,
1703
+ onNavigate,
1704
+ node,
1705
+ coValue
1706
+ }) {
1707
+ const entries = Object.entries(data);
1708
+ return /* @__PURE__ */ jsx23(Grid, { cols: entries.length === 1 ? 1 : 3, children: entries.map((entry, childIndex) => /* @__PURE__ */ jsx23(
1709
+ GridItem,
1710
+ {
1711
+ entry,
1712
+ onNavigate,
1713
+ node,
1714
+ coValue
1715
+ },
1716
+ childIndex
1717
+ )) });
1718
+ }
1719
+ var EditButton = styled12("button")`
1720
+ display: inline-flex;
1721
+ align-items: center;
1722
+ justify-content: center;
1723
+ padding: 0.25rem;
1724
+ border: none;
1725
+ background: transparent;
1726
+ cursor: pointer;
1727
+ color: var(--j-text-color);
1728
+ border-radius: var(--j-radius-sm);
1729
+ transition: background-color 0.2s;
1730
+
1731
+ &:hover {
1732
+ background-color: var(--j-foreground);
1733
+ }
1734
+ `;
1735
+ var DeleteButton = styled12("button")`
1736
+ display: inline-flex;
1737
+ align-items: center;
1738
+ justify-content: center;
1739
+ padding: 0.25rem;
1740
+ border: none;
1741
+ background: transparent;
1742
+ cursor: pointer;
1743
+ color: var(--j-text-color);
1744
+ border-radius: var(--j-radius-sm);
1745
+ transition: background-color 0.2s;
1746
+
1747
+ &:hover {
1748
+ background-color: var(--j-foreground);
1749
+ }
1750
+ `;
1751
+ var ActionButtons = styled12("div")`
1752
+ display: flex;
1753
+ align-items: center;
1754
+ gap: 0.25rem;
1755
+ `;
1756
+
1757
+ // src/inspector/viewer/raw-data-card.tsx
1758
+ import { useEffect as useEffect10, useState as useState12 } from "react";
1759
+
1760
+ // src/inspector/ui/modal.tsx
1761
+ import { styled as styled13 } from "goober";
1762
+ import { forwardRef as forwardRef2, useEffect as useEffect7, useRef } from "react";
1763
+ import { jsx as jsx24, jsxs as jsxs9 } from "react/jsx-runtime";
1764
+ var ModalContent = styled13("dialog")`
1765
+ background-color: var(--j-background);
1766
+ border-radius: var(--j-radius-lg);
1767
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
1768
+ border: 1px solid var(--j-border-color);
1769
+ ${(props) => props.wide ? "max-width: 60vw;" : "max-width: 32rem;"}
1770
+ margin-block: auto;
1771
+ margin-inline: auto;
1772
+ &::backdrop {
1773
+ background-color: rgba(0, 0, 0, 0.7);
1774
+ }
1775
+
1776
+ `;
1777
+ var ModalHeader = styled13("div")`
1778
+ display: flex;
1779
+ justify-content: space-between;
1780
+ align-items: flex-start;
1781
+ padding: 1.5rem 1.5rem 0 1.5rem;
1782
+ gap: 1rem;
1783
+ `;
1784
+ var ModalBody = styled13("div")`
1785
+ padding: 1rem 1.5rem;
1786
+ flex: 1;
1787
+ `;
1788
+ var ModalFooter = styled13("div")`
1789
+ display: flex;
1790
+ justify-content: flex-end;
1791
+ gap: 0.75rem;
1792
+ padding: 0 1.5rem 1.5rem 1.5rem;
1793
+ `;
1794
+ var CloseButton = styled13("button")`
1795
+ background: none;
1796
+ border: none;
1797
+ cursor: pointer;
1798
+ padding: 0.25rem;
1799
+ border-radius: var(--j-radius-sm);
1800
+ color: var(--j-text-color);
1801
+ font-size: 1.25rem;
1802
+ line-height: 1;
1803
+ display: flex;
1804
+ align-items: center;
1805
+ justify-content: center;
1806
+ min-width: 2rem;
1807
+ min-height: 2rem;
1808
+
1809
+ &:hover {
1810
+ background-color: var(--j-foreground);
1811
+ }
1812
+
1813
+ &:focus-visible {
1814
+ outline: 2px solid var(--j-border-focus);
1815
+ outline-offset: 2px;
1816
+ }
1817
+ `;
1818
+ var Modal = forwardRef2(
1819
+ ({
1820
+ isOpen,
1821
+ onClose,
1822
+ heading,
1823
+ text,
1824
+ children,
1825
+ confirmText = "Confirm",
1826
+ cancelText = "Cancel",
1827
+ onConfirm,
1828
+ onCancel,
1829
+ showButtons = true,
1830
+ className,
1831
+ wide = false
1832
+ }, ref) => {
1833
+ const modalRef = useRef(null);
1834
+ useEffect7(() => {
1835
+ if (isOpen) {
1836
+ modalRef.current?.showModal();
1837
+ } else {
1838
+ onClose();
1839
+ modalRef.current?.close();
1840
+ }
1841
+ }, [isOpen, onClose]);
1842
+ const handleConfirm = () => {
1843
+ onConfirm?.();
1844
+ onClose();
1845
+ };
1846
+ const handleCancel = () => {
1847
+ onCancel?.();
1848
+ onClose();
1849
+ };
1850
+ if (!isOpen) return null;
1851
+ return /* @__PURE__ */ jsxs9(
1852
+ ModalContent,
1853
+ {
1854
+ ref: ref || modalRef,
1855
+ className,
1856
+ role: "dialog",
1857
+ "aria-labelledby": "modal-heading",
1858
+ onClose,
1859
+ wide,
1860
+ children: [
1861
+ /* @__PURE__ */ jsxs9(ModalHeader, { children: [
1862
+ /* @__PURE__ */ jsx24(Heading, { id: "modal-heading", children: heading }),
1863
+ /* @__PURE__ */ jsx24(CloseButton, { onClick: onClose, "aria-label": "Close modal", type: "button", children: "\xD7" })
1864
+ ] }),
1865
+ /* @__PURE__ */ jsxs9(ModalBody, { children: [
1866
+ text && /* @__PURE__ */ jsx24("p", { style: { margin: "0 0 1rem 0", color: "var(--j-text-color)" }, children: text }),
1867
+ children
1868
+ ] }),
1869
+ showButtons && /* @__PURE__ */ jsxs9(ModalFooter, { children: [
1870
+ /* @__PURE__ */ jsx24(Button, { variant: "secondary", onClick: handleCancel, children: cancelText }),
1871
+ /* @__PURE__ */ jsx24(Button, { variant: "primary", onClick: handleConfirm, children: confirmText })
1872
+ ] })
1873
+ ]
1874
+ }
1875
+ );
1876
+ }
1877
+ );
1878
+ Modal.displayName = "Modal";
1879
+
1880
+ // src/inspector/ui/input.tsx
1881
+ import { styled as styled14 } from "goober";
1882
+ import { forwardRef as forwardRef3, useId as useId2 } from "react";
1883
+ import { jsx as jsx25, jsxs as jsxs10 } from "react/jsx-runtime";
1884
+ var Container = styled14("div")`
1885
+ display: grid;
1886
+ gap: 0.25rem;
1887
+ `;
1888
+ var StyledInput = styled14("input")`
1889
+ width: 100%;
1890
+ border-radius: var(--j-radius-md);
1891
+ border: 1px solid var(--j-border-color);
1892
+ padding: 0.5rem 0.875rem;
1893
+ box-shadow: var(--j-shadow-sm);
1894
+ font-weight: 500;
1895
+ background-color: white;
1896
+ color: var(--j-text-color-strong);
1897
+
1898
+ @media (prefers-color-scheme: dark) {
1899
+ background-color: var(--j-foreground);
1900
+ }
1901
+ `;
1902
+ var Input = forwardRef3(
1903
+ ({ label, className, hideLabel, id: customId, ...inputProps }, ref) => {
1904
+ const generatedId = useId2();
1905
+ const id = customId || generatedId;
1906
+ return /* @__PURE__ */ jsxs10(Container, { className, children: [
1907
+ /* @__PURE__ */ jsx25(
1908
+ "label",
1909
+ {
1910
+ htmlFor: id,
1911
+ className: hideLabel ? "j-sr-only" : "",
1912
+ style: { color: "var(--j-text-color)" },
1913
+ children: label
1914
+ }
1915
+ ),
1916
+ /* @__PURE__ */ jsx25(StyledInput, { ref, ...inputProps, id })
1917
+ ] });
1918
+ }
1919
+ );
1920
+
1921
+ // src/inspector/ui/data-table.tsx
1922
+ import { useEffect as useEffect8, useMemo as useMemo3, useState as useState10 } from "react";
1923
+
1924
+ // src/inspector/ui/table.tsx
1925
+ import { styled as styled15 } from "goober";
1926
+ import React4 from "react";
1927
+ import { jsx as jsx26 } from "react/jsx-runtime";
1928
+ var StyledTable = styled15("table")`
1929
+ width: 100%;
1930
+ `;
1931
+ var StyledThead = styled15("thead")`
1932
+ text-align: left;
1933
+ border-bottom: 1px solid var(--j-border-color);
1934
+ background-color: var(--j-neutral-100);
1935
+
1936
+ @media (prefers-color-scheme: dark) {
1937
+ background-color: var(--j-neutral-925);
1938
+ }
1939
+ `;
1940
+ var StyledTbody = styled15("tbody")`
1941
+ tr {
1942
+ border-bottom: 1px solid var(--j-border-color);
1943
+
1944
+ &:last-child {
1945
+ border-bottom: none;
1946
+ }
1947
+ }
1948
+ `;
1949
+ var StyledTh = styled15("th")`
1950
+ font-weight: 500;
1951
+ padding: 0.5rem 0.75rem;
1952
+ color: var(--j-text-color-strong);
1953
+ `;
1954
+ var StyledTd = styled15("td")`
1955
+ padding: 0.5rem 0.75rem;
1956
+ `;
1957
+ var Table = React4.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx26(StyledTable, { ref, ...props, children }));
1958
+ var TableHead = React4.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx26(StyledThead, { ref, ...props, children }));
1959
+ var TableBody = React4.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx26(StyledTbody, { ref, ...props, children }));
1960
+ var TableRow = React4.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx26("tr", { ref, ...props, children }));
1961
+ var TableHeader = React4.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx26(StyledTh, { ref, ...props, children }));
1962
+ var TableCell = React4.forwardRef(({ children, ...props }, ref) => /* @__PURE__ */ jsx26(StyledTd, { ref, ...props, children }));
1963
+
1964
+ // src/inspector/ui/data-table.tsx
1965
+ import { Fragment as Fragment5, jsx as jsx27, jsxs as jsxs11 } from "react/jsx-runtime";
1966
+ function DataTable({
1967
+ columns,
1968
+ data,
1969
+ pageSize = 10,
1970
+ initialSort = null,
1971
+ getRowKey,
1972
+ emptyMessage = "No data available"
1973
+ }) {
1974
+ const [currentPage, setCurrentPage] = useState10(1);
1975
+ const [sortConfig, setSortConfig] = useState10(initialSort);
1976
+ const [filters, setFilters] = useState10({});
1977
+ const filteredData = useMemo3(() => {
1978
+ return data.filter((row) => {
1979
+ return Object.entries(filters).every(([columnId, filterValue]) => {
1980
+ if (!filterValue) return true;
1981
+ const column = columns.find((col) => col.id === columnId);
1982
+ if (!column?.filterable) return true;
1983
+ if (column.filterFn) {
1984
+ return column.filterFn(row, filterValue);
1985
+ }
1986
+ const cellValue = String(column.accessor(row));
1987
+ return cellValue.toLowerCase().includes(filterValue.toLowerCase());
1988
+ });
1989
+ });
1990
+ }, [data, filters, columns]);
1991
+ const sortedData = useMemo3(() => {
1992
+ if (!sortConfig) return filteredData;
1993
+ const column = columns.find((col) => col.id === sortConfig.columnId);
1994
+ if (!column?.sortable) return filteredData;
1995
+ const sorted = [...filteredData].sort((a, b) => {
1996
+ if (column.sortFn) {
1997
+ return column.sortFn(a, b);
1998
+ }
1999
+ const aValue = String(column.accessor(a));
2000
+ const bValue = String(column.accessor(b));
2001
+ return aValue.localeCompare(bValue);
2002
+ });
2003
+ return sortConfig.direction === "desc" ? sorted.reverse() : sorted;
2004
+ }, [filteredData, sortConfig, columns]);
2005
+ const totalPages = Math.ceil(sortedData.length / pageSize);
2006
+ const showPagination = sortedData.length > pageSize;
2007
+ const startIndex = (currentPage - 1) * pageSize;
2008
+ const endIndex = startIndex + pageSize;
2009
+ const paginatedData = sortedData.slice(startIndex, endIndex);
2010
+ useEffect8(() => {
2011
+ setCurrentPage(1);
2012
+ }, [filters]);
2013
+ const handleSort = (columnId) => {
2014
+ const column = columns.find((col) => col.id === columnId);
2015
+ if (!column?.sortable) return;
2016
+ setSortConfig((current) => {
2017
+ if (current?.columnId === columnId) {
2018
+ if (current.direction === "asc") {
2019
+ return { columnId, direction: "desc" };
2020
+ }
2021
+ return null;
2022
+ }
2023
+ return { columnId, direction: "asc" };
2024
+ });
2025
+ };
2026
+ const handleFilterChange = (columnId, value) => {
2027
+ setFilters((current) => ({
2028
+ ...current,
2029
+ [columnId]: value
2030
+ }));
2031
+ };
2032
+ const handlePageChange = (page) => {
2033
+ setCurrentPage(Math.max(1, Math.min(page, totalPages)));
2034
+ };
2035
+ return /* @__PURE__ */ jsxs11(Fragment5, { children: [
2036
+ /* @__PURE__ */ jsxs11(Table, { children: [
2037
+ /* @__PURE__ */ jsx27(TableHead, { children: /* @__PURE__ */ jsx27(TableRow, { children: columns.map((column) => /* @__PURE__ */ jsx27(TableHeader, { children: /* @__PURE__ */ jsxs11(
2038
+ "div",
2039
+ {
2040
+ style: {
2041
+ display: "flex",
2042
+ alignItems: "center",
2043
+ gap: "8px",
2044
+ cursor: column.sortable ? "pointer" : "default"
2045
+ },
2046
+ onClick: () => handleSort(column.id),
2047
+ children: [
2048
+ /* @__PURE__ */ jsx27("span", { children: column.header }),
2049
+ column.sortable && /* @__PURE__ */ jsx27(
2050
+ "span",
2051
+ {
2052
+ style: {
2053
+ fontSize: "12px",
2054
+ opacity: 0.7
2055
+ },
2056
+ children: sortConfig?.columnId === column.id ? sortConfig.direction === "asc" ? "\u2191" : "\u2193" : "\u2195"
2057
+ }
2058
+ )
2059
+ ]
2060
+ }
2061
+ ) }, column.id)) }) }),
2062
+ /* @__PURE__ */ jsxs11(TableBody, { children: [
2063
+ columns.some((column) => column.filterable) && /* @__PURE__ */ jsx27(TableRow, { children: columns.map((column) => /* @__PURE__ */ jsx27(TableCell, { children: column.filterable && /* @__PURE__ */ jsx27(
2064
+ Input,
2065
+ {
2066
+ label: "Filter",
2067
+ hideLabel: true,
2068
+ type: "search",
2069
+ placeholder: `Filter ${column.header.toLowerCase()}`,
2070
+ value: filters[column.id] || "",
2071
+ onChange: (e) => handleFilterChange(column.id, e.target.value),
2072
+ onClick: (e) => e.stopPropagation()
2073
+ }
2074
+ ) }, column.id)) }),
2075
+ paginatedData.length === 0 ? /* @__PURE__ */ jsx27(TableRow, { children: /* @__PURE__ */ jsx27(TableCell, { colSpan: columns.length, children: /* @__PURE__ */ jsx27(
2076
+ "div",
2077
+ {
2078
+ style: {
2079
+ textAlign: "center",
2080
+ padding: "20px",
2081
+ opacity: 0.6
2082
+ },
2083
+ children: emptyMessage
2084
+ }
2085
+ ) }) }) : paginatedData.map((row, index) => /* @__PURE__ */ jsx27(TableRow, { children: columns.map((column) => /* @__PURE__ */ jsx27(TableCell, { children: column.accessor(row) }, column.id)) }, getRowKey(row, startIndex + index)))
2086
+ ] })
2087
+ ] }),
2088
+ showPagination && /* @__PURE__ */ jsxs11(
2089
+ "div",
2090
+ {
2091
+ style: {
2092
+ display: "flex",
2093
+ justifyContent: "space-between",
2094
+ alignItems: "center",
2095
+ marginTop: "16px",
2096
+ padding: "8px 0"
2097
+ },
2098
+ children: [
2099
+ /* @__PURE__ */ jsxs11("div", { style: { fontSize: "14px", opacity: 0.7 }, children: [
2100
+ "Showing ",
2101
+ startIndex + 1,
2102
+ " to ",
2103
+ Math.min(endIndex, sortedData.length),
2104
+ " ",
2105
+ "of ",
2106
+ sortedData.length,
2107
+ " entries",
2108
+ Object.keys(filters).some((key) => filters[key]) && ` (filtered from ${data.length})`
2109
+ ] }),
2110
+ /* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
2111
+ /* @__PURE__ */ jsx27(
2112
+ Button,
2113
+ {
2114
+ variant: "secondary",
2115
+ onClick: () => handlePageChange(1),
2116
+ disabled: currentPage === 1,
2117
+ children: "\xAB\xAB"
2118
+ }
2119
+ ),
2120
+ /* @__PURE__ */ jsx27(
2121
+ Button,
2122
+ {
2123
+ variant: "secondary",
2124
+ onClick: () => handlePageChange(currentPage - 1),
2125
+ disabled: currentPage === 1,
2126
+ children: "\xAB"
2127
+ }
2128
+ ),
2129
+ /* @__PURE__ */ jsxs11("span", { style: { fontSize: "14px" }, children: [
2130
+ "Page ",
2131
+ currentPage,
2132
+ " of ",
2133
+ totalPages
2134
+ ] }),
2135
+ /* @__PURE__ */ jsx27(
2136
+ Button,
2137
+ {
2138
+ variant: "secondary",
2139
+ onClick: () => handlePageChange(currentPage + 1),
2140
+ disabled: currentPage === totalPages,
2141
+ children: "\xBB"
2142
+ }
2143
+ ),
2144
+ /* @__PURE__ */ jsx27(
2145
+ Button,
2146
+ {
2147
+ variant: "secondary",
2148
+ onClick: () => handlePageChange(totalPages),
2149
+ disabled: currentPage === totalPages,
2150
+ children: "\xBB\xBB"
2151
+ }
2152
+ )
2153
+ ] })
2154
+ ]
2155
+ }
2156
+ )
2157
+ ] });
2158
+ }
2159
+
2160
+ // src/inspector/ui/accordion.tsx
2161
+ import { styled as styled16 } from "goober";
2162
+ import { useEffect as useEffect9, useState as useState11 } from "react";
2163
+ import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
2164
+ function Accordion({ title, children, storageKey }) {
2165
+ const [open, setOpen] = useStoragedState(storageKey, false);
2166
+ return /* @__PURE__ */ jsxs12(
2167
+ "details",
2168
+ {
2169
+ open,
2170
+ style: { display: "flex", flexDirection: "column", gap: "1rem" },
2171
+ children: [
2172
+ /* @__PURE__ */ jsx28(
2173
+ StyledSummary,
2174
+ {
2175
+ onClick: (e) => {
2176
+ e.preventDefault();
2177
+ setOpen((v) => !v);
2178
+ },
2179
+ children: title
2180
+ }
2181
+ ),
2182
+ children
2183
+ ]
2184
+ }
2185
+ );
2186
+ }
2187
+ function useStoragedState(key, defaultValue) {
2188
+ const [state, setState] = useState11(() => {
2189
+ if (typeof window === "undefined") return defaultValue;
2190
+ const stored = localStorage.getItem(key);
2191
+ return stored ? JSON.parse(stored) : defaultValue;
2192
+ });
2193
+ useEffect9(() => {
2194
+ localStorage.setItem(key, JSON.stringify(state));
2195
+ }, [state]);
2196
+ return [state, setState];
2197
+ }
2198
+ var StyledSummary = styled16("summary")`
2199
+ font-size: 1.125rem;
2200
+ cursor: pointer;
2201
+ font-weight: 500;
2202
+ color: var(--j-text-color-strong);
2203
+ `;
2204
+
2205
+ // src/inspector/viewer/raw-data-card.tsx
2206
+ import { jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
2207
+ function CopyButton({ data }) {
2208
+ const [copyCount, setCopyCount] = useState12(0);
2209
+ const copied = copyCount > 0;
2210
+ const stringifiedData = JSON.stringify(data);
2211
+ useEffect10(() => {
2212
+ if (copyCount > 0) {
2213
+ const timeout = setTimeout(() => setCopyCount(0), 1e3);
2214
+ return () => {
2215
+ clearTimeout(timeout);
2216
+ };
2217
+ }
2218
+ }, [copyCount]);
2219
+ return /* @__PURE__ */ jsx29(
2220
+ Button,
2221
+ {
2222
+ style: {
2223
+ position: "absolute",
2224
+ top: "10px",
2225
+ right: "10px"
2226
+ },
2227
+ onClick: () => {
2228
+ window.navigator.clipboard.writeText(stringifiedData).then(() => {
2229
+ setCopyCount((count) => count + 1);
2230
+ });
2231
+ },
2232
+ variant: "secondary",
2233
+ children: copied ? "Copied" : "Copy"
2234
+ }
2235
+ );
2236
+ }
2237
+ function RawDataCard({ data }) {
2238
+ return /* @__PURE__ */ jsx29(Accordion, { title: "Raw data", storageKey: "jazz-inspector-show-raw-data", children: /* @__PURE__ */ jsxs13(Card, { style: { position: "relative" }, children: [
2239
+ /* @__PURE__ */ jsx29(CardHeader, { children: /* @__PURE__ */ jsx29(CopyButton, { data }) }),
2240
+ /* @__PURE__ */ jsx29(CardBody, { children: /* @__PURE__ */ jsx29(ValueRenderer, { json: data }) })
2241
+ ] }) });
2242
+ }
2243
+
2244
+ // src/inspector/viewer/account-view.tsx
2245
+ import { Fragment as Fragment6, jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
2246
+ function AccountView({
2247
+ data,
2248
+ onNavigate,
2249
+ node
2250
+ }) {
2251
+ const readableData = { ...data };
2252
+ for (const key in readableData) {
2253
+ if (key === "readKey" || key.startsWith("sealer_z") || key.startsWith("key_z")) {
2254
+ delete readableData[key];
2255
+ }
2256
+ }
2257
+ return /* @__PURE__ */ jsxs14(Fragment6, { children: [
2258
+ /* @__PURE__ */ jsx30(GridView, { data: readableData, onNavigate, node }),
2259
+ /* @__PURE__ */ jsx30(RawDataCard, { data })
2260
+ ] });
2261
+ }
2262
+
2263
+ // src/inspector/viewer/co-plain-text-view.tsx
2264
+ import { useState as useState13 } from "react";
2265
+ import { styled as styled17 } from "goober";
2266
+ import { CoPlainText } from "jazz-tools";
2267
+ import { Fragment as Fragment7, jsx as jsx31, jsxs as jsxs15 } from "react/jsx-runtime";
2268
+ function CoPlainTextView({
2269
+ data,
2270
+ coValue
2271
+ }) {
2272
+ const currentText = Object.values(data).join("");
2273
+ const [isEditing, setIsEditing] = useState13(false);
2274
+ const [editValue, setEditValue] = useState13("");
2275
+ const canEdit2 = isWriter(coValue.group.myRole());
2276
+ const handleEditClick = () => {
2277
+ setIsEditing(true);
2278
+ setEditValue(currentText);
2279
+ };
2280
+ const handleCancel = () => {
2281
+ setIsEditing(false);
2282
+ setEditValue(currentText);
2283
+ };
2284
+ const handleSave = (e) => {
2285
+ e.preventDefault();
2286
+ e.stopPropagation();
2287
+ const coPlainText = CoPlainText.fromRaw(coValue);
2288
+ coPlainText.$jazz.applyDiff(editValue);
2289
+ setIsEditing(false);
2290
+ };
2291
+ if (!data) return;
2292
+ if (isEditing) {
2293
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
2294
+ /* @__PURE__ */ jsxs15(EditForm2, { onSubmit: handleSave, children: [
2295
+ /* @__PURE__ */ jsx31(
2296
+ StyledTextarea2,
2297
+ {
2298
+ value: editValue,
2299
+ onChange: (e) => setEditValue(e.target.value),
2300
+ onClick: (e) => e.stopPropagation()
2301
+ }
2302
+ ),
2303
+ /* @__PURE__ */ jsxs15(FormActions2, { children: [
2304
+ /* @__PURE__ */ jsx31(Button, { type: "button", variant: "secondary", onClick: handleCancel, children: "Cancel" }),
2305
+ /* @__PURE__ */ jsx31(Button, { type: "submit", variant: "primary", children: "Save" })
2306
+ ] })
2307
+ ] }),
2308
+ /* @__PURE__ */ jsx31(RawDataCard, { data })
2309
+ ] });
2310
+ }
2311
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
2312
+ /* @__PURE__ */ jsx31("p", { children: currentText }),
2313
+ /* @__PURE__ */ jsx31("div", { children: canEdit2 && /* @__PURE__ */ jsx31(Button, { variant: "secondary", onClick: handleEditClick, title: "Edit", children: /* @__PURE__ */ jsx31(Icon, { name: "edit" }) }) }),
2314
+ /* @__PURE__ */ jsx31(RawDataCard, { data })
2315
+ ] });
2316
+ }
2317
+ var EditForm2 = styled17("form")`
2318
+ display: flex;
2319
+ flex-direction: column;
2320
+ gap: 0.75rem;
2321
+ margin-bottom: 1rem;
2322
+ `;
2323
+ var StyledTextarea2 = styled17("textarea")`
2324
+ width: 100%;
2325
+ min-height: 120px;
2326
+ border-radius: var(--j-radius-md);
2327
+ border: 1px solid var(--j-border-color);
2328
+ padding: 0.5rem 0.875rem;
2329
+ box-shadow: var(--j-shadow-sm);
2330
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2331
+ font-size: 0.875rem;
2332
+ background-color: white;
2333
+ color: var(--j-text-color-strong);
2334
+ resize: vertical;
2335
+
2336
+ @media (prefers-color-scheme: dark) {
2337
+ background-color: var(--j-foreground);
2338
+ }
2339
+ `;
2340
+ var FormActions2 = styled17("div")`
2341
+ display: flex;
2342
+ gap: 0.5rem;
2343
+ justify-content: flex-end;
2344
+ `;
2345
+
2346
+ // src/inspector/viewer/group-view.tsx
2347
+ import { useState as useState14 } from "react";
2348
+ import { Fragment as Fragment8, jsx as jsx32, jsxs as jsxs16 } from "react/jsx-runtime";
2349
+ function partitionMembers(data) {
2350
+ const everyone = Object.entries(data).filter(([key]) => key === "everyone").map(([key, value]) => ({
2351
+ id: key,
2352
+ role: value
2353
+ }));
2354
+ const members = Object.entries(data).filter(([key]) => isCoId(key)).map(([key, value]) => ({
2355
+ id: key,
2356
+ role: value
2357
+ }));
2358
+ const parentGroups = Object.entries(data).filter(([key]) => key.startsWith("parent_co_")).map(([key, value]) => ({
2359
+ id: key.slice(7),
2360
+ role: value
2361
+ }));
2362
+ const childGroups = Object.entries(data).filter(
2363
+ ([key, value]) => key.startsWith("child_co_") && value !== "revoked"
2364
+ ).map(([key, value]) => ({
2365
+ id: key.slice(6),
2366
+ role: value
2367
+ }));
2368
+ return { everyone, members, parentGroups, childGroups };
2369
+ }
2370
+ function GroupView({
2371
+ coValue,
2372
+ data,
2373
+ onNavigate,
2374
+ node
2375
+ }) {
2376
+ const [addMemberType, setAddMemberType] = useState14(null);
2377
+ const { everyone, members, parentGroups, childGroups } = partitionMembers(
2378
+ data
2379
+ );
2380
+ const onRemoveMember = async (id) => {
2381
+ if (confirm("Are you sure you want to remove this member?") === false) {
2382
+ return;
2383
+ }
2384
+ try {
2385
+ const group = await node.load(coValue.id);
2386
+ if (group === "unavailable") {
2387
+ throw new Error("Group not found");
2388
+ }
2389
+ const rawGroup = group;
2390
+ rawGroup.removeMember(id);
2391
+ } catch (error) {
2392
+ console.error(error);
2393
+ throw error;
2394
+ }
2395
+ };
2396
+ const onRemoveGroup = async (id) => {
2397
+ if (confirm("Are you sure you want to remove this group?") === false) {
2398
+ return;
2399
+ }
2400
+ try {
2401
+ const group = await node.load(coValue.id);
2402
+ if (group === "unavailable") {
2403
+ throw new Error("Group not found");
2404
+ }
2405
+ const rawGroup = group;
2406
+ const targetGroup = await node.load(id);
2407
+ if (targetGroup === "unavailable") {
2408
+ throw new Error("Group not found");
2409
+ }
2410
+ const rawTargetGroup = targetGroup;
2411
+ rawGroup.revokeExtend(rawTargetGroup);
2412
+ } catch (error) {
2413
+ console.error(error);
2414
+ throw error;
2415
+ }
2416
+ };
2417
+ const handleAddMemberSubmit = async (event) => {
2418
+ event.preventDefault();
2419
+ const form = event.currentTarget;
2420
+ const memberId = form.elements.namedItem("memberId")?.value;
2421
+ const role = form.elements.namedItem("role")?.value;
2422
+ try {
2423
+ const group = await node.load(coValue.id);
2424
+ if (group === "unavailable") {
2425
+ throw new Error("Group not found");
2426
+ }
2427
+ const rawGroup = group;
2428
+ if (addMemberType === "account") {
2429
+ let rawAccount = "everyone";
2430
+ if (memberId !== "everyone") {
2431
+ const account = await node.load(memberId);
2432
+ if (account === "unavailable") {
2433
+ throw new Error("Account not found");
2434
+ }
2435
+ rawAccount = account;
2436
+ }
2437
+ rawGroup.addMember(rawAccount, role);
2438
+ } else if (addMemberType === "group") {
2439
+ const targetGroup = await node.load(memberId);
2440
+ if (targetGroup === "unavailable") {
2441
+ throw new Error("Group not found");
2442
+ }
2443
+ const rawTargetGroup = targetGroup;
2444
+ rawGroup.extend(
2445
+ rawTargetGroup,
2446
+ role
2447
+ );
2448
+ }
2449
+ setAddMemberType(null);
2450
+ } catch (error) {
2451
+ console.error(error);
2452
+ alert(`Failed to add ${addMemberType}: ${error.message}`);
2453
+ }
2454
+ };
2455
+ return /* @__PURE__ */ jsxs16(Fragment8, { children: [
2456
+ /* @__PURE__ */ jsxs16(Table, { children: [
2457
+ /* @__PURE__ */ jsx32(TableHead, { children: /* @__PURE__ */ jsxs16(TableRow, { children: [
2458
+ /* @__PURE__ */ jsx32(TableHeader, { children: "Member" }),
2459
+ /* @__PURE__ */ jsx32(TableHeader, { children: "Permission" }),
2460
+ /* @__PURE__ */ jsx32(TableHeader, {})
2461
+ ] }) }),
2462
+ /* @__PURE__ */ jsxs16(TableBody, { children: [
2463
+ everyone.map((member) => /* @__PURE__ */ jsxs16(TableRow, { children: [
2464
+ /* @__PURE__ */ jsx32(TableCell, { children: member.id }),
2465
+ /* @__PURE__ */ jsx32(TableCell, { children: member.role }),
2466
+ /* @__PURE__ */ jsx32(TableCell, { children: member.role !== "revoked" && /* @__PURE__ */ jsx32(
2467
+ Button,
2468
+ {
2469
+ variant: "secondary",
2470
+ onClick: () => onRemoveMember(member.id),
2471
+ children: /* @__PURE__ */ jsx32(Icon, { name: "delete" })
2472
+ }
2473
+ ) })
2474
+ ] }, member.id)),
2475
+ members.map((member) => /* @__PURE__ */ jsxs16(TableRow, { children: [
2476
+ /* @__PURE__ */ jsx32(TableCell, { children: /* @__PURE__ */ jsx32(
2477
+ AccountOrGroupText,
2478
+ {
2479
+ coId: member.id,
2480
+ node,
2481
+ showId: true,
2482
+ onClick: () => {
2483
+ onNavigate([{ coId: member.id, name: member.id }]);
2484
+ }
2485
+ }
2486
+ ) }),
2487
+ /* @__PURE__ */ jsx32(TableCell, { children: member.role }),
2488
+ /* @__PURE__ */ jsx32(TableCell, { children: member.role !== "revoked" && /* @__PURE__ */ jsx32(
2489
+ Button,
2490
+ {
2491
+ variant: "secondary",
2492
+ onClick: () => onRemoveMember(member.id),
2493
+ children: /* @__PURE__ */ jsx32(Icon, { name: "delete" })
2494
+ }
2495
+ ) })
2496
+ ] }, member.id)),
2497
+ parentGroups.map((group) => /* @__PURE__ */ jsxs16(TableRow, { children: [
2498
+ /* @__PURE__ */ jsx32(TableCell, { children: /* @__PURE__ */ jsx32(
2499
+ AccountOrGroupText,
2500
+ {
2501
+ coId: group.id,
2502
+ node,
2503
+ showId: true,
2504
+ onClick: () => {
2505
+ onNavigate([{ coId: group.id, name: group.id }]);
2506
+ }
2507
+ }
2508
+ ) }),
2509
+ /* @__PURE__ */ jsx32(TableCell, { children: group.role }),
2510
+ /* @__PURE__ */ jsx32(TableCell, { children: group.role !== "revoked" && /* @__PURE__ */ jsx32(
2511
+ Button,
2512
+ {
2513
+ variant: "secondary",
2514
+ onClick: () => onRemoveGroup(group.id),
2515
+ children: /* @__PURE__ */ jsx32(Icon, { name: "delete" })
2516
+ }
2517
+ ) })
2518
+ ] }, group.id))
2519
+ ] })
2520
+ ] }),
2521
+ /* @__PURE__ */ jsxs16(
2522
+ "div",
2523
+ {
2524
+ style: {
2525
+ display: "flex",
2526
+ justifyContent: "flex-end",
2527
+ gap: "0.75rem",
2528
+ marginTop: "1rem"
2529
+ },
2530
+ children: [
2531
+ /* @__PURE__ */ jsx32(Button, { variant: "primary", onClick: () => setAddMemberType("account"), children: "Add Account" }),
2532
+ /* @__PURE__ */ jsx32(Button, { variant: "primary", onClick: () => setAddMemberType("group"), children: "Add Group" })
2533
+ ]
2534
+ }
2535
+ ),
2536
+ childGroups.length > 0 && /* @__PURE__ */ jsxs16(Table, { children: [
2537
+ /* @__PURE__ */ jsx32(TableHead, { children: /* @__PURE__ */ jsx32(TableRow, { children: /* @__PURE__ */ jsx32(TableHeader, { children: "Member of" }) }) }),
2538
+ /* @__PURE__ */ jsx32(TableBody, { children: childGroups.map((group) => /* @__PURE__ */ jsx32(TableRow, { children: /* @__PURE__ */ jsx32(TableCell, { children: /* @__PURE__ */ jsx32(
2539
+ AccountOrGroupText,
2540
+ {
2541
+ coId: group.id,
2542
+ node,
2543
+ showId: true,
2544
+ onClick: () => {
2545
+ onNavigate([{ coId: group.id, name: group.id }]);
2546
+ }
2547
+ }
2548
+ ) }) }, group.id)) })
2549
+ ] }),
2550
+ /* @__PURE__ */ jsx32(RawDataCard, { data }),
2551
+ /* @__PURE__ */ jsx32(
2552
+ Modal,
2553
+ {
2554
+ isOpen: addMemberType !== null,
2555
+ onClose: () => setAddMemberType(null),
2556
+ heading: addMemberType === "account" ? "Add Account" : "Add Group",
2557
+ showButtons: false,
2558
+ children: /* @__PURE__ */ jsx32("form", { onSubmit: handleAddMemberSubmit, children: /* @__PURE__ */ jsxs16(
2559
+ "div",
2560
+ {
2561
+ style: { display: "flex", flexDirection: "column", gap: "1rem" },
2562
+ children: [
2563
+ /* @__PURE__ */ jsx32(
2564
+ Input,
2565
+ {
2566
+ name: "memberId",
2567
+ label: addMemberType === "account" ? "Account ID" : "Group ID",
2568
+ placeholder: addMemberType === "account" ? "Enter account ID" : "Enter group ID",
2569
+ required: true
2570
+ }
2571
+ ),
2572
+ /* @__PURE__ */ jsxs16(Select, { name: "role", label: "Role", children: [
2573
+ /* @__PURE__ */ jsx32("option", { value: "reader", children: "Reader" }),
2574
+ /* @__PURE__ */ jsx32("option", { value: "writer", children: "Writer" }),
2575
+ /* @__PURE__ */ jsx32("option", { value: "admin", children: "Admin" }),
2576
+ addMemberType === "account" ? /* @__PURE__ */ jsx32(Fragment8, { children: /* @__PURE__ */ jsx32("option", { value: "writeOnly", children: "Write Only" }) }) : /* @__PURE__ */ jsx32(Fragment8, { children: /* @__PURE__ */ jsx32("option", { value: "inherit", children: "Inherit" }) })
2577
+ ] }),
2578
+ /* @__PURE__ */ jsxs16(
2579
+ "div",
2580
+ {
2581
+ style: {
2582
+ display: "flex",
2583
+ gap: "0.75rem",
2584
+ justifyContent: "flex-end",
2585
+ marginTop: "0.5rem"
2586
+ },
2587
+ children: [
2588
+ /* @__PURE__ */ jsx32(
2589
+ Button,
2590
+ {
2591
+ type: "button",
2592
+ variant: "secondary",
2593
+ onClick: () => setAddMemberType(null),
2594
+ children: "Cancel"
2595
+ }
2596
+ ),
2597
+ /* @__PURE__ */ jsx32(Button, { type: "submit", variant: "primary", children: "Add" })
2598
+ ]
2599
+ }
2600
+ )
2601
+ ]
2602
+ }
2603
+ ) })
2604
+ }
2605
+ )
2606
+ ] });
2607
+ }
2608
+
2609
+ // src/inspector/viewer/role-display.tsx
2610
+ import { jsxs as jsxs17 } from "react/jsx-runtime";
2611
+ function RoleDisplay({
2612
+ node,
2613
+ value
2614
+ }) {
2615
+ const { snapshot } = useResolvedCoValue(value.group.id, node);
2616
+ if (!snapshot || snapshot === "unavailable") {
2617
+ return null;
2618
+ }
2619
+ let role;
2620
+ if (value.group.id == node.getCurrentAgent().id) {
2621
+ role = "owner";
2622
+ } else if (snapshot[node.getCurrentAgent().id]) {
2623
+ role = snapshot[node.getCurrentAgent().id];
2624
+ } else if (snapshot.everyone) {
2625
+ role = snapshot.everyone;
2626
+ } else {
2627
+ role = "unauthorized";
2628
+ }
2629
+ return /* @__PURE__ */ jsxs17(Text, { children: [
2630
+ "Role: ",
2631
+ role
2632
+ ] });
2633
+ }
2634
+
2635
+ // src/inspector/viewer/table-viewer.tsx
2636
+ import { styled as styled18 } from "goober";
2637
+ import { useMemo as useMemo4, useState as useState15 } from "react";
2638
+ import { Fragment as Fragment9, jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
2639
+ var PaginationContainer = styled18("div")`
2640
+ padding: 1rem 0;
2641
+ display: flex;
2642
+ align-items: center;
2643
+ justify-content: space-between;
2644
+ gap: 0.5rem;
2645
+ `;
2646
+ var RedTooltip = styled18("span")`
2647
+ position:relative; /* making the .tooltip span a container for the tooltip text */
2648
+ border-bottom:1px dashed #000; /* little indicater to indicate it's hoverable */
2649
+
2650
+ &:before {
2651
+ content: attr(data-text);
2652
+ background-color: red;
2653
+ position:absolute;
2654
+
2655
+ /* vertically center */
2656
+ top:50%;
2657
+ transform:translateY(-50%);
2658
+
2659
+ /* move to right */
2660
+ left:100%;
2661
+ margin-left:15px; /* and add a small left margin */
2662
+
2663
+ /* basic styles */
2664
+ width:200px;
2665
+ padding:10px;
2666
+ border-radius:10px;
2667
+ color: #fff;
2668
+ text-align:center;
2669
+
2670
+ display:none; /* hide by default */
2671
+ }
2672
+
2673
+ &:hover:before {
2674
+ display:block;
2675
+ }
2676
+ `;
2677
+ function CoValuesTableView({
2678
+ data,
2679
+ node,
2680
+ onNavigate,
2681
+ onRemove
2682
+ }) {
2683
+ const [visibleRowsCount, setVisibleRowsCount] = useState15(10);
2684
+ const [coIdArray, visibleRows] = useMemo4(() => {
2685
+ const coIdArray2 = Array.isArray(data) ? data : Object.values(data).every((k) => typeof k === "string" && isCoId(k)) ? Object.values(data).map((k) => k) : [];
2686
+ const visibleRows2 = coIdArray2.slice(0, visibleRowsCount);
2687
+ return [coIdArray2, visibleRows2];
2688
+ }, [data, visibleRowsCount]);
2689
+ const resolvedRows = useResolvedCoValues(visibleRows, node);
2690
+ const hasMore = visibleRowsCount < coIdArray.length;
2691
+ if (!coIdArray.length) {
2692
+ return /* @__PURE__ */ jsx33("div", { children: "No data to display" });
2693
+ }
2694
+ if (resolvedRows.length === 0) {
2695
+ return /* @__PURE__ */ jsx33("div", { children: "Loading..." });
2696
+ }
2697
+ const keys = Array.from(
2698
+ new Set(
2699
+ resolvedRows.filter((item) => item.snapshot !== "unavailable").flatMap((item) => Object.keys(item.snapshot || {}))
2700
+ )
2701
+ );
2702
+ const loadMore = () => {
2703
+ setVisibleRowsCount((prevVisibleRows) => prevVisibleRows + 10);
2704
+ };
2705
+ return /* @__PURE__ */ jsxs18(Fragment9, { children: [
2706
+ /* @__PURE__ */ jsxs18(Table, { children: [
2707
+ /* @__PURE__ */ jsx33(TableHead, { children: /* @__PURE__ */ jsxs18(TableRow, { children: [
2708
+ ["ID", ...keys, "Action"].map((key) => /* @__PURE__ */ jsx33(TableHeader, { children: key }, key)),
2709
+ onRemove && /* @__PURE__ */ jsx33(TableHeader, {})
2710
+ ] }) }),
2711
+ /* @__PURE__ */ jsx33(TableBody, { children: resolvedRows.slice(0, visibleRowsCount).map((item, index) => /* @__PURE__ */ jsxs18(TableRow, { children: [
2712
+ /* @__PURE__ */ jsx33(TableCell, { children: /* @__PURE__ */ jsx33(Text, { mono: true, children: item.snapshot === "unavailable" ? /* @__PURE__ */ jsxs18(RedTooltip, { "data-text": "Unavailable", children: [
2713
+ /* @__PURE__ */ jsx33(
2714
+ Icon,
2715
+ {
2716
+ name: "caution",
2717
+ color: "red",
2718
+ style: {
2719
+ display: "inline-block",
2720
+ marginRight: "0.5rem"
2721
+ }
2722
+ }
2723
+ ),
2724
+ visibleRows[index]
2725
+ ] }) : visibleRows[index] }) }),
2726
+ keys.map((key) => /* @__PURE__ */ jsx33(TableCell, { children: item.snapshot !== "unavailable" && /* @__PURE__ */ jsx33(
2727
+ ValueRenderer,
2728
+ {
2729
+ json: item.snapshot[key],
2730
+ onCoIDClick: (coId) => {
2731
+ async function handleClick() {
2732
+ onNavigate([
2733
+ {
2734
+ coId: item.value.id,
2735
+ name: index.toString()
2736
+ },
2737
+ {
2738
+ coId,
2739
+ name: key
2740
+ }
2741
+ ]);
2742
+ }
2743
+ handleClick();
2744
+ }
2745
+ }
2746
+ ) }, key)),
2747
+ /* @__PURE__ */ jsx33(TableCell, { children: /* @__PURE__ */ jsx33(
2748
+ Button,
2749
+ {
2750
+ variant: "secondary",
2751
+ onClick: () => onNavigate([
2752
+ {
2753
+ coId: item.value.id,
2754
+ name: index.toString()
2755
+ }
2756
+ ]),
2757
+ children: "View"
2758
+ }
2759
+ ) }),
2760
+ onRemove && /* @__PURE__ */ jsx33(TableCell, { children: /* @__PURE__ */ jsx33(Button, { variant: "secondary", onClick: () => onRemove(index), children: "Remove" }) })
2761
+ ] }, index)) })
2762
+ ] }),
2763
+ /* @__PURE__ */ jsxs18(PaginationContainer, { children: [
2764
+ /* @__PURE__ */ jsxs18(Text, { muted: true, small: true, children: [
2765
+ "Showing ",
2766
+ Math.min(visibleRowsCount, coIdArray.length),
2767
+ " of",
2768
+ " ",
2769
+ coIdArray.length
2770
+ ] }),
2771
+ hasMore && /* @__PURE__ */ jsx33(Button, { variant: "secondary", onClick: loadMore, children: "Load more" })
2772
+ ] })
2773
+ ] });
2774
+ }
2775
+ function TableView({
2776
+ data,
2777
+ node,
2778
+ onNavigate,
2779
+ onRemove
2780
+ }) {
2781
+ const isListOfCoValues = useMemo4(() => {
2782
+ return Array.isArray(data) && data.every((k) => isCoId(k));
2783
+ }, [data]);
2784
+ if (isListOfCoValues) {
2785
+ return /* @__PURE__ */ jsx33(
2786
+ CoValuesTableView,
2787
+ {
2788
+ data,
2789
+ node,
2790
+ onNavigate,
2791
+ onRemove
2792
+ }
2793
+ );
2794
+ }
2795
+ return /* @__PURE__ */ jsxs18(Table, { children: [
2796
+ /* @__PURE__ */ jsx33(TableHead, { children: /* @__PURE__ */ jsxs18(TableRow, { children: [
2797
+ /* @__PURE__ */ jsx33(TableHeader, { style: { width: "5rem" }, children: "Index" }),
2798
+ /* @__PURE__ */ jsx33(TableHeader, { children: "Value" }),
2799
+ onRemove && /* @__PURE__ */ jsx33(TableHeader, { children: "Action" })
2800
+ ] }) }),
2801
+ /* @__PURE__ */ jsx33(TableBody, { children: Array.isArray(data) && data?.map((value, index) => /* @__PURE__ */ jsxs18(TableRow, { children: [
2802
+ /* @__PURE__ */ jsx33(TableCell, { children: /* @__PURE__ */ jsx33(Text, { mono: true, children: index }) }),
2803
+ /* @__PURE__ */ jsx33(TableCell, { children: /* @__PURE__ */ jsx33(ValueRenderer, { json: value }) }),
2804
+ onRemove && /* @__PURE__ */ jsx33(TableCell, { children: /* @__PURE__ */ jsx33(Button, { variant: "secondary", onClick: () => onRemove(index), children: "Remove" }) })
2805
+ ] }, index)) })
2806
+ ] });
2807
+ }
2808
+
2809
+ // src/inspector/viewer/history-view.tsx
2810
+ import { useMemo as useMemo5 } from "react";
2811
+ import { styled as styled19 } from "goober";
2812
+
2813
+ // src/inspector/utils/transactions-changes.ts
2814
+ var isGroupExtension = (coValue, change) => {
2815
+ if (coValue.core.isGroup() === false) return false;
2816
+ return change?.op === "set" && change?.value === "extend";
2817
+ };
2818
+ var isGroupExtendRevocation = (coValue, change) => {
2819
+ if (coValue.core.isGroup() === false) return false;
2820
+ return change?.op === "set" && change?.value === "revoked";
2821
+ };
2822
+ var isGroupPromotion = (coValue, change) => {
2823
+ if (coValue.core.isGroup() === false) return false;
2824
+ return change?.op === "set" && change?.key.startsWith("parent_co_");
2825
+ };
2826
+ var isUserPromotion = (coValue, change) => {
2827
+ if (coValue.core.isGroup() === false) return false;
2828
+ return change?.op === "set" && (isCoId(change?.key) || change?.key === "everyone");
2829
+ };
2830
+ var isKeyRevelation = (coValue, change) => {
2831
+ if (coValue.core.isGroup() === false && coValue.headerMeta?.type !== "account")
2832
+ return false;
2833
+ return change?.op === "set" && change?.key.includes("_for_");
2834
+ };
2835
+ var isPropertySet = (coValue, change) => {
2836
+ return change?.op === "set" && "key" in change && "value" in change;
2837
+ };
2838
+ var isPropertyDeletion = (coValue, change) => {
2839
+ return change?.op === "del" && "key" in change;
2840
+ };
2841
+ var isItemAppend = (coValue, change) => {
2842
+ if (coValue.type !== "colist" && coValue.type !== "coplaintext") return false;
2843
+ return change?.op === "app" && "after" in change && "value" in change;
2844
+ };
2845
+ var isItemPrepend = (coValue, change) => {
2846
+ if (coValue.type !== "colist" && coValue.type !== "coplaintext") return false;
2847
+ return change?.op === "pre" && "before" in change && "value" in change;
2848
+ };
2849
+ var isItemDeletion = (coValue, change) => {
2850
+ if (coValue.type !== "colist" && coValue.type !== "coplaintext") return false;
2851
+ return change?.op === "del" && "insertion" in change;
2852
+ };
2853
+ var isStreamStart = (coValue, change) => {
2854
+ if (coValue.type !== "coStream") return false;
2855
+ return change?.type === "start" && "mimeType" in change;
2856
+ };
2857
+ var isStreamChunk = (coValue, change) => {
2858
+ if (coValue.type !== "coStream") return false;
2859
+ return change?.type === "chunk" && "chunk" in change;
2860
+ };
2861
+ var isStreamEnd = (coValue, change) => {
2862
+ if (coValue.type !== "coStream") return false;
2863
+ return change?.type === "end";
2864
+ };
2865
+
2866
+ // src/inspector/utils/history.ts
2867
+ import { stringifyOpID } from "cojson";
2868
+ function areSameOpIds(opId1, opId2) {
2869
+ if (typeof opId1 === "string" || typeof opId2 === "string") {
2870
+ return opId1 === opId2;
2871
+ }
2872
+ return opId1.sessionID === opId2.sessionID && opId1.txIndex === opId2.txIndex && opId1.changeIdx === opId2.changeIdx;
2873
+ }
2874
+ function isCoPlainText(coValue) {
2875
+ return coValue.type === "coplaintext";
2876
+ }
2877
+ function getTransactionChanges(tx, coValue) {
2878
+ if (tx.isValid === false && tx.tx.privacy === "private") {
2879
+ const readKey = coValue.core.getReadKey(tx.tx.keyUsed);
2880
+ if (!readKey) {
2881
+ return [
2882
+ `Unable to decrypt transaction: read key ${tx.tx.keyUsed} not found.`
2883
+ ];
2884
+ }
2885
+ return coValue.core.verified.decryptTransaction(
2886
+ tx.txID.sessionID,
2887
+ tx.txID.txIndex,
2888
+ readKey
2889
+ ) ?? [];
2890
+ }
2891
+ if (isCoPlainText(coValue)) {
2892
+ if (tx.changes === void 0 || tx.changes.length === 0) return [];
2893
+ const firstChange = tx.changes[0];
2894
+ if (isItemAppend(coValue, firstChange) && tx.changes.every(
2895
+ (c) => isItemAppend(coValue, c) && areSameOpIds(c.after, firstChange.after)
2896
+ )) {
2897
+ const changes = tx.changes;
2898
+ if (firstChange.after !== "start") {
2899
+ changes.reverse();
2900
+ }
2901
+ return [
2902
+ {
2903
+ op: "app",
2904
+ value: changes.map((c) => c.value).join(""),
2905
+ after: firstChange.after
2906
+ }
2907
+ ];
2908
+ }
2909
+ if (isItemPrepend(coValue, firstChange) && tx.changes.every(
2910
+ (c) => isItemPrepend(coValue, c) && areSameOpIds(c.before, firstChange.before)
2911
+ )) {
2912
+ const changes = tx.changes;
2913
+ if (firstChange.before !== "end") {
2914
+ changes.reverse();
2915
+ }
2916
+ return [
2917
+ {
2918
+ op: "pre",
2919
+ value: changes.map((c) => c.value).join(""),
2920
+ before: firstChange.before
2921
+ }
2922
+ ];
2923
+ }
2924
+ if (isItemDeletion(coValue, firstChange) && tx.changes.every((c) => isItemDeletion(coValue, c))) {
2925
+ let changesAreConsecutive2 = function(changes) {
2926
+ if (changes.length < 2) return false;
2927
+ const mapping = coValueBeforeDeletions.mapping.idxAfterOpID;
2928
+ for (let i = 1; i < changes.length; ++i) {
2929
+ const prevIdx = mapping[stringifyOpID(changes[i - 1].insertion)];
2930
+ const currIdx = mapping[stringifyOpID(changes[i].insertion)];
2931
+ if (currIdx !== prevIdx && currIdx !== (prevIdx ?? -2) + 1) {
2932
+ return false;
2933
+ }
2934
+ }
2935
+ return true;
2936
+ };
2937
+ var changesAreConsecutive = changesAreConsecutive2;
2938
+ const coValueBeforeDeletions = coValue.atTime(tx.madeAt - 1);
2939
+ if (changesAreConsecutive2(tx.changes)) {
2940
+ const groupedBySession = /* @__PURE__ */ new Map();
2941
+ for (const change of tx.changes) {
2942
+ const group = `${change.insertion.sessionID}-${change.insertion.txIndex}`;
2943
+ if (!groupedBySession.has(group)) groupedBySession.set(group, []);
2944
+ groupedBySession.get(group).push(change);
2945
+ }
2946
+ return Array.from(groupedBySession.values()).map((changes) => {
2947
+ const stringDeleted = changes.toSorted((a, b) => {
2948
+ if (a.insertion.txIndex === b.insertion.txIndex) {
2949
+ return a.insertion.changeIdx - b.insertion.changeIdx;
2950
+ }
2951
+ return a.insertion.txIndex - b.insertion.txIndex;
2952
+ }).map(
2953
+ (c) => coValueBeforeDeletions.get(
2954
+ coValueBeforeDeletions.mapping.idxAfterOpID[stringifyOpID(c.insertion)]
2955
+ )
2956
+ ).join("");
2957
+ return {
2958
+ op: "custom",
2959
+ action: `"${stringDeleted}" has been deleted`
2960
+ };
2961
+ });
2962
+ }
2963
+ }
2964
+ }
2965
+ return tx.changes ?? tx.tx.changes ?? [];
2966
+ }
2967
+ function restoreCoMapToTimestamp(coValue, timestamp, removeUnknownProperties) {
2968
+ const myRole = coValue.group.myRole();
2969
+ if (myRole === void 0 || !["admin", "manager", "writer", "writerOnly"].includes(myRole)) {
2970
+ return;
2971
+ }
2972
+ const newCoValue = coValue.atTime(timestamp).toJSON();
2973
+ const oldCoValue = coValue.toJSON();
2974
+ if (newCoValue === null) return;
2975
+ let changes = [];
2976
+ if (removeUnknownProperties) {
2977
+ for (const key in oldCoValue) {
2978
+ if (!(key in newCoValue)) {
2979
+ changes.push({
2980
+ op: "del",
2981
+ key
2982
+ });
2983
+ }
2984
+ }
2985
+ }
2986
+ for (const key in newCoValue) {
2987
+ if (newCoValue[key] !== oldCoValue[key]) {
2988
+ changes.push({
2989
+ op: "set",
2990
+ key,
2991
+ value: newCoValue[key]
2992
+ });
2993
+ }
2994
+ }
2995
+ if (changes.length > 0) {
2996
+ coValue.core.makeTransaction(changes, "private");
2997
+ }
2998
+ }
2999
+
3000
+ // src/inspector/viewer/history-view.tsx
3001
+ import { Fragment as Fragment10, jsx as jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
3002
+ function HistoryView({
3003
+ coValue,
3004
+ node
3005
+ }) {
3006
+ const transactions = useMemo5(
3007
+ () => getHistory(coValue),
3008
+ [coValue.core.verifiedTransactions.length]
3009
+ );
3010
+ const columns = [
3011
+ {
3012
+ id: "author",
3013
+ header: "Author",
3014
+ accessor: (row) => /* @__PURE__ */ jsxs19(Fragment10, { children: [
3015
+ row.isValid || /* @__PURE__ */ jsx34(RedTooltip2, { "data-text": "This transaction is invalid and is not used", children: /* @__PURE__ */ jsx34(
3016
+ Icon,
3017
+ {
3018
+ name: "caution",
3019
+ size: "xs",
3020
+ color: "red",
3021
+ style: {
3022
+ display: "inline-block",
3023
+ verticalAlign: "middle",
3024
+ marginRight: "0.25rem"
3025
+ }
3026
+ }
3027
+ ) }),
3028
+ row.author.startsWith("co_") ? /* @__PURE__ */ jsx34(
3029
+ AccountOrGroupText,
3030
+ {
3031
+ coId: row.author,
3032
+ node,
3033
+ showId: true
3034
+ }
3035
+ ) : row.author
3036
+ ] }),
3037
+ sortable: false,
3038
+ filterable: true,
3039
+ sortFn: (a, b) => a.author.localeCompare(b.author),
3040
+ filterFn: (row, filterValue) => row.author.toLowerCase().includes(filterValue.toLowerCase())
3041
+ },
3042
+ {
3043
+ id: "action",
3044
+ header: "Action",
3045
+ accessor: (row) => {
3046
+ if (row.isValid) return row.action;
3047
+ return /* @__PURE__ */ jsxs19(Fragment10, { children: [
3048
+ row.action,
3049
+ /* @__PURE__ */ jsxs19("span", { style: { color: "red", display: "block" }, children: [
3050
+ "Invalid transaction: ",
3051
+ row.validationErrorMessage
3052
+ ] })
3053
+ ] });
3054
+ },
3055
+ sortable: false,
3056
+ filterable: true,
3057
+ sortFn: (a, b) => a.action.localeCompare(b.action)
3058
+ },
3059
+ {
3060
+ id: "timestamp",
3061
+ header: "Timestamp",
3062
+ accessor: (row) => row.timestamp.toISOString(),
3063
+ sortable: true,
3064
+ filterable: true,
3065
+ sortFn: (a, b) => a.timestamp.getTime() - b.timestamp.getTime()
3066
+ }
3067
+ ];
3068
+ return /* @__PURE__ */ jsx34(Accordion, { title: "CoValue history", storageKey: "jazz-inspector-show-history", children: /* @__PURE__ */ jsx34(
3069
+ DataTable,
3070
+ {
3071
+ columns,
3072
+ data: transactions,
3073
+ pageSize: 10,
3074
+ initialSort: { columnId: "timestamp", direction: "desc" },
3075
+ getRowKey: (row) => row.id,
3076
+ emptyMessage: "No history available"
3077
+ }
3078
+ ) });
3079
+ }
3080
+ function getHistory(coValue) {
3081
+ return coValue.core.verifiedTransactions.flatMap((tx, index) => {
3082
+ const changes = getTransactionChanges(tx, coValue);
3083
+ return changes.map((change, changeIndex) => ({
3084
+ id: `${tx.txID.sessionID.toString()}-${tx.txID.txIndex}-${index}-${changeIndex}`,
3085
+ author: tx.author,
3086
+ action: mapTransactionToAction(change, coValue),
3087
+ timestamp: new Date(tx.currentMadeAt),
3088
+ isValid: tx.isValid,
3089
+ validationErrorMessage: tx.validationErrorMessage
3090
+ }));
3091
+ });
3092
+ }
3093
+ function mapTransactionToAction(change, coValue) {
3094
+ if (isUserPromotion(coValue, change)) {
3095
+ if (change.value === "revoked") {
3096
+ return `${change.key} has been revoked`;
3097
+ }
3098
+ return `${change.key} has been promoted to ${change.value}`;
3099
+ }
3100
+ if (isGroupExtension(coValue, change)) {
3101
+ const child = change.key.slice(6);
3102
+ return `Group became a member of ${child}`;
3103
+ }
3104
+ if (isGroupExtendRevocation(coValue, change)) {
3105
+ const child = change.key.slice(6);
3106
+ return `Group's membership of ${child} has been revoked.`;
3107
+ }
3108
+ if (isGroupPromotion(coValue, change)) {
3109
+ const parent = change.key.slice(7);
3110
+ return `Group ${parent} has been promoted to ${change.value}`;
3111
+ }
3112
+ if (isKeyRevelation(coValue, change)) {
3113
+ const [key, target] = change.key.split("_for_");
3114
+ return `Key "${key}" has been revealed to "${target}"`;
3115
+ }
3116
+ if (isItemAppend(coValue, change)) {
3117
+ if (change.after === "start") {
3118
+ return `"${change.value}" has been appended`;
3119
+ }
3120
+ const after = findListChange(change.after, coValue);
3121
+ if (after === void 0) {
3122
+ return `"${change.value}" has been inserted after undefined item`;
3123
+ }
3124
+ return `"${change.value}" has been inserted after "${after.value}"`;
3125
+ }
3126
+ if (isItemPrepend(coValue, change)) {
3127
+ if (change.before === "end") {
3128
+ return `"${change.value}" has been prepended`;
3129
+ }
3130
+ const before = findListChange(change.before, coValue);
3131
+ if (before === void 0) {
3132
+ return `"${change.value}" has been inserted before undefined item`;
3133
+ }
3134
+ return `"${change.value}" has been inserted before "${before.value}"`;
3135
+ }
3136
+ if (isItemDeletion(coValue, change)) {
3137
+ const insertion = findListChange(change.insertion, coValue);
3138
+ if (insertion === void 0) {
3139
+ return `An undefined item has been deleted`;
3140
+ }
3141
+ return `"${insertion.value}" has been deleted`;
3142
+ }
3143
+ if (isStreamStart(coValue, change)) {
3144
+ return `Stream started with mime type "${change.mimeType}" and file name "${change.fileName}"`;
3145
+ }
3146
+ if (isStreamChunk(coValue, change)) {
3147
+ return `Stream chunk added`;
3148
+ }
3149
+ if (isStreamEnd(coValue, change)) {
3150
+ return `Stream ended`;
3151
+ }
3152
+ if (isPropertySet(coValue, change)) {
3153
+ return `Property "${change.key}" has been set to ${JSON.stringify(change.value)}`;
3154
+ }
3155
+ if (isPropertyDeletion(coValue, change)) {
3156
+ return `Property "${change.key}" has been deleted`;
3157
+ }
3158
+ if (change.op === "custom") {
3159
+ return change.action;
3160
+ }
3161
+ return "Unknown action: " + JSON.stringify(change);
3162
+ }
3163
+ var findListChange = (opId, coValue) => {
3164
+ return coValue.core.verifiedTransactions.find(
3165
+ (tx) => tx.txID.sessionID === opId.sessionID && tx.txID.txIndex === opId.txIndex
3166
+ )?.changes?.[opId.changeIdx];
3167
+ };
3168
+ var RedTooltip2 = styled19("span")`
3169
+ position:relative; /* making the .tooltip span a container for the tooltip text */
3170
+ border-bottom:1px dashed #000; /* little indicater to indicate it's hoverable */
3171
+
3172
+ &:before {
3173
+ content: attr(data-text);
3174
+ background-color: red;
3175
+ position:absolute;
3176
+
3177
+ /* vertically center */
3178
+ top:50%;
3179
+ transform:translateY(-50%);
3180
+
3181
+ /* move to right */
3182
+ left:100%;
3183
+ margin-left:15px; /* and add a small left margin */
3184
+
3185
+ /* basic styles */
3186
+ width:200px;
3187
+ padding:10px;
3188
+ border-radius:10px;
3189
+ color: #fff;
3190
+ text-align:center;
3191
+
3192
+ display:none; /* hide by default */
3193
+ }
3194
+
3195
+ &:hover:before {
3196
+ display:block;
3197
+ }
3198
+ `;
3199
+
3200
+ // src/inspector/viewer/co-map-view.tsx
3201
+ import { useState as useState16, useMemo as useMemo6 } from "react";
3202
+ import { styled as styled20 } from "goober";
3203
+ import { Fragment as Fragment11, jsx as jsx35, jsxs as jsxs20 } from "react/jsx-runtime";
3204
+ function CoMapView({
3205
+ coValue,
3206
+ data,
3207
+ node,
3208
+ onNavigate
3209
+ }) {
3210
+ return /* @__PURE__ */ jsxs20(Fragment11, { children: [
3211
+ /* @__PURE__ */ jsx35(
3212
+ GridView,
3213
+ {
3214
+ data,
3215
+ onNavigate,
3216
+ node,
3217
+ coValue
3218
+ }
3219
+ ),
3220
+ /* @__PURE__ */ jsxs20("div", { children: [
3221
+ /* @__PURE__ */ jsx35(
3222
+ AddPropertyModal,
3223
+ {
3224
+ disabled: !isWriter(coValue.group.myRole()),
3225
+ coValue,
3226
+ node
3227
+ }
3228
+ ),
3229
+ " ",
3230
+ /* @__PURE__ */ jsx35(RestoreSnapshotModal, { coValue })
3231
+ ] })
3232
+ ] });
3233
+ }
3234
+ function AddPropertyModal({
3235
+ coValue,
3236
+ node,
3237
+ disabled
3238
+ }) {
3239
+ const [isAddPropertyModalOpen, setIsAddPropertyModalOpen] = useState16(false);
3240
+ const [propertyName, setPropertyName] = useState16("");
3241
+ const openAddPropertyModal = () => {
3242
+ setIsAddPropertyModalOpen(true);
3243
+ setPropertyName("");
3244
+ };
3245
+ const handleCancel = () => {
3246
+ setIsAddPropertyModalOpen(false);
3247
+ setPropertyName("");
3248
+ };
3249
+ return /* @__PURE__ */ jsxs20(Fragment11, { children: [
3250
+ /* @__PURE__ */ jsx35(
3251
+ Button,
3252
+ {
3253
+ title: "Add Property",
3254
+ variant: "secondary",
3255
+ disabled,
3256
+ onClick: openAddPropertyModal,
3257
+ children: /* @__PURE__ */ jsx35(Icon, { name: "add" })
3258
+ }
3259
+ ),
3260
+ /* @__PURE__ */ jsxs20(
3261
+ Modal,
3262
+ {
3263
+ isOpen: isAddPropertyModalOpen,
3264
+ onClose: handleCancel,
3265
+ heading: "Add Property",
3266
+ showButtons: false,
3267
+ children: [
3268
+ /* @__PURE__ */ jsx35(
3269
+ Input,
3270
+ {
3271
+ label: "Property Name",
3272
+ value: propertyName,
3273
+ onChange: (e) => setPropertyName(e.target.value),
3274
+ placeholder: "Enter property name"
3275
+ }
3276
+ ),
3277
+ propertyName && /* @__PURE__ */ jsx35(EditorContainer, { children: /* @__PURE__ */ jsx35(
3278
+ CoValueEditor,
3279
+ {
3280
+ node,
3281
+ property: propertyName,
3282
+ value: void 0,
3283
+ coValue,
3284
+ onCancel: handleCancel
3285
+ }
3286
+ ) })
3287
+ ]
3288
+ }
3289
+ )
3290
+ ] });
3291
+ }
3292
+ function RestoreSnapshotModal({ coValue }) {
3293
+ const [isRestoreModalOpen, setIsRestoreModalOpen] = useState16(false);
3294
+ const [selectedIndex, setSelectedIndex] = useState16(-1);
3295
+ const [removeUnknownProperties, setRemoveUnknownProperties] = useState16(false);
3296
+ const timestamps = useMemo6(
3297
+ () => coValue.core.verifiedTransactions.map((tx) => tx.madeAt),
3298
+ [coValue.core.verifiedTransactions.length]
3299
+ );
3300
+ const coMapAtSelectedIndex = useMemo6(() => {
3301
+ if (selectedIndex === -1) return null;
3302
+ return coValue.atTime(timestamps[selectedIndex]).toJSON();
3303
+ }, [coValue, timestamps, selectedIndex]);
3304
+ const openRestoreModal = () => {
3305
+ setIsRestoreModalOpen(true);
3306
+ setSelectedIndex(timestamps.length - 1);
3307
+ };
3308
+ const handleRestore = () => {
3309
+ if (timestamps.length < 2) return;
3310
+ if (timestamps.length === 0) return;
3311
+ const selectedTimestamp = timestamps[selectedIndex];
3312
+ if (selectedTimestamp === void 0) return;
3313
+ restoreCoMapToTimestamp(
3314
+ coValue,
3315
+ selectedTimestamp,
3316
+ removeUnknownProperties
3317
+ );
3318
+ setIsRestoreModalOpen(false);
3319
+ };
3320
+ const handleClose = () => {
3321
+ setIsRestoreModalOpen(false);
3322
+ };
3323
+ const canRestore = isWriter(coValue.group.myRole());
3324
+ return /* @__PURE__ */ jsxs20(Fragment11, { children: [
3325
+ /* @__PURE__ */ jsx35(Button, { title: "Timeline", variant: "secondary", onClick: openRestoreModal, children: /* @__PURE__ */ jsx35(Icon, { name: "history" }) }),
3326
+ /* @__PURE__ */ jsxs20(
3327
+ Modal,
3328
+ {
3329
+ isOpen: isRestoreModalOpen,
3330
+ onClose: handleClose,
3331
+ heading: "Timeline",
3332
+ confirmText: "Restore",
3333
+ cancelText: "Cancel",
3334
+ onConfirm: handleRestore,
3335
+ onCancel: handleClose,
3336
+ showButtons: timestamps.length > 1 && canRestore,
3337
+ children: [
3338
+ timestamps.length > 1 && /* @__PURE__ */ jsxs20(Fragment11, { children: [
3339
+ /* @__PURE__ */ jsxs20(RangeContainer, { children: [
3340
+ /* @__PURE__ */ jsx35(RangeLabel, { children: "Select Timestamp" }),
3341
+ /* @__PURE__ */ jsx35(
3342
+ RangeInput,
3343
+ {
3344
+ type: "range",
3345
+ min: 0,
3346
+ max: Math.max(0, timestamps.length - 1),
3347
+ value: selectedIndex,
3348
+ onChange: (e) => setSelectedIndex(Number(e.target.value)),
3349
+ disabled: timestamps.length === 0
3350
+ }
3351
+ ),
3352
+ /* @__PURE__ */ jsx35(TimestampDisplay, { children: timestamps[selectedIndex] !== void 0 ? new Date(timestamps[selectedIndex]).toISOString() : "No timestamps available" })
3353
+ ] }),
3354
+ canRestore && /* @__PURE__ */ jsxs20(CheckboxContainer, { children: [
3355
+ /* @__PURE__ */ jsx35(
3356
+ CheckboxInput,
3357
+ {
3358
+ type: "checkbox",
3359
+ id: "remove-unknown-properties",
3360
+ checked: removeUnknownProperties,
3361
+ onChange: (e) => setRemoveUnknownProperties(e.target.checked)
3362
+ }
3363
+ ),
3364
+ /* @__PURE__ */ jsx35(CheckboxLabel, { htmlFor: "remove-unknown-properties", children: "Remove unknown properties (properties that don't exist in the selected snapshot)" })
3365
+ ] })
3366
+ ] }),
3367
+ timestamps.length > 0 && timestamps[selectedIndex] !== void 0 && /* @__PURE__ */ jsxs20(PreviewSection, { children: [
3368
+ /* @__PURE__ */ jsx35(PreviewLabel, { children: "State at that time:" }),
3369
+ /* @__PURE__ */ jsx35(PreviewPre, { children: JSON.stringify(coMapAtSelectedIndex, null, 2) })
3370
+ ] }),
3371
+ timestamps.length < 2 && /* @__PURE__ */ jsx35("div", { style: { color: "var(--j-text-color)" }, children: "At least 2 timestamps are required to restore a snapshot." })
3372
+ ]
3373
+ }
3374
+ )
3375
+ ] });
3376
+ }
3377
+ var PreviewSection = styled20("div")`
3378
+ margin-top: 1.5rem;
3379
+ `;
3380
+ var PreviewLabel = styled20("div")`
3381
+ font-weight: 500;
3382
+ margin-bottom: 0.5rem;
3383
+ color: var(--j-text-color-strong);
3384
+ `;
3385
+ var PreviewPre = styled20("pre")`
3386
+ background-color: var(--j-foreground);
3387
+ border: 1px solid var(--j-border-color);
3388
+ border-radius: var(--j-radius-md);
3389
+ padding: 1rem;
3390
+ overflow-x: auto;
3391
+ font-size: 0.875rem;
3392
+ max-height: 400px;
3393
+ overflow-y: auto;
3394
+ color: var(--j-text-color);
3395
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
3396
+ `;
3397
+ var RangeContainer = styled20("div")`
3398
+ display: flex;
3399
+ flex-direction: column;
3400
+ gap: 0.75rem;
3401
+ `;
3402
+ var RangeLabel = styled20("label")`
3403
+ font-weight: 500;
3404
+ color: var(--j-text-color-strong);
3405
+ font-size: 0.875rem;
3406
+ `;
3407
+ var RangeInput = styled20("input")`
3408
+ width: 100%;
3409
+ height: 0.5rem;
3410
+ border-radius: var(--j-radius-sm);
3411
+ outline: none;
3412
+ -webkit-appearance: none;
3413
+ appearance: none;
3414
+ background: var(--j-foreground);
3415
+ cursor: pointer;
3416
+
3417
+ &::-webkit-slider-thumb {
3418
+ -webkit-appearance: none;
3419
+ appearance: none;
3420
+ width: 1.25rem;
3421
+ height: 1.25rem;
3422
+ border-radius: 50%;
3423
+ background: var(--j-primary-color);
3424
+ cursor: pointer;
3425
+ border: 2px solid var(--j-background);
3426
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
3427
+ }
3428
+
3429
+ &::-moz-range-thumb {
3430
+ width: 1.25rem;
3431
+ height: 1.25rem;
3432
+ border-radius: 50%;
3433
+ background: var(--j-primary-color);
3434
+ cursor: pointer;
3435
+ border: 2px solid var(--j-background);
3436
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
3437
+ }
3438
+
3439
+ &:disabled {
3440
+ opacity: 0.5;
3441
+ cursor: not-allowed;
3442
+ }
3443
+ `;
3444
+ var TimestampDisplay = styled20("div")`
3445
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
3446
+ font-size: 0.875rem;
3447
+ color: var(--j-text-color);
3448
+ padding: 0.5rem;
3449
+ background-color: var(--j-foreground);
3450
+ border: 1px solid var(--j-border-color);
3451
+ border-radius: var(--j-radius-md);
3452
+ text-align: center;
3453
+ `;
3454
+ var CheckboxContainer = styled20("div")`
3455
+ display: flex;
3456
+ align-items: flex-start;
3457
+ gap: 0.5rem;
3458
+ margin-top: 1rem;
3459
+ `;
3460
+ var CheckboxInput = styled20("input")`
3461
+ width: 1rem;
3462
+ height: 1rem;
3463
+ margin-top: 0.125rem;
3464
+ cursor: pointer;
3465
+ accent-color: var(--j-primary-color);
3466
+ `;
3467
+ var CheckboxLabel = styled20("label")`
3468
+ font-size: 0.875rem;
3469
+ color: var(--j-text-color);
3470
+ cursor: pointer;
3471
+ line-height: 1.25rem;
3472
+ `;
3473
+ var EditorContainer = styled20("div")`
3474
+ margin-top: 1rem;
3475
+ `;
3476
+
3477
+ // src/inspector/viewer/page.tsx
3478
+ import { Fragment as Fragment12, jsx as jsx36, jsxs as jsxs21 } from "react/jsx-runtime";
3479
+ var BasePageContainer = React5.forwardRef(
3480
+ ({ isTopLevel, ...rest }, ref) => /* @__PURE__ */ jsx36("div", { ref, ...rest })
3481
+ );
3482
+ var PageContainer = styled21(BasePageContainer)`
3483
+ position: absolute;
3484
+ z-index: 10;
3485
+ inset: 0;
3486
+ width: 100%;
3487
+ height: 100%;
3488
+ padding: 0 0.75rem;
3489
+ `;
3490
+ var BackButton = styled21("div")`
3491
+ position: absolute;
3492
+ left: 0;
3493
+ right: 0;
3494
+ top: 0;
3495
+ height: 2.5rem;
3496
+ `;
3497
+ var HeaderContainer = styled21("div")`
3498
+ display: flex;
3499
+ justify-content: space-between;
3500
+ align-items: center;
3501
+ margin-bottom: 1rem;
3502
+ `;
3503
+ var TitleContainer = styled21("div")`
3504
+ display: flex;
3505
+ align-items: center;
3506
+ gap: 0.75rem;
3507
+ `;
3508
+ var Title = styled21(Heading)`
3509
+ display: flex;
3510
+ flex-direction: column;
3511
+ align-items: flex-start;
3512
+ gap: 0.25rem;
3513
+ `;
3514
+ var BadgeContainer = styled21("div")`
3515
+ display: flex;
3516
+ align-items: center;
3517
+ gap: 0.75rem;
3518
+ `;
3519
+ var ContentContainer = styled21("div")`
3520
+ overflow: auto;
3521
+ display: flex;
3522
+ flex-direction: column;
3523
+ gap: 1rem;
3524
+ padding-bottom: 2rem;
3525
+ `;
3526
+ function canEdit(value) {
3527
+ try {
3528
+ const myRole = value.group.myRole();
3529
+ return myRole === "writer" || myRole === "admin";
3530
+ } catch (e) {
3531
+ return false;
3532
+ }
3533
+ }
3534
+ function View(props) {
3535
+ const { type, extendedType } = props.coValue;
3536
+ const { snapshot, value } = props.coValue;
3537
+ const { node, onNavigate } = props;
3538
+ if (!snapshot || snapshot === "unavailable") return;
3539
+ if (type === "costream") {
3540
+ return /* @__PURE__ */ jsx36(
3541
+ CoStreamView,
3542
+ {
3543
+ data: snapshot,
3544
+ onNavigate,
3545
+ node,
3546
+ value
3547
+ }
3548
+ );
3549
+ }
3550
+ if (extendedType === "group") {
3551
+ return /* @__PURE__ */ jsx36(
3552
+ GroupView,
3553
+ {
3554
+ coValue: value,
3555
+ data: snapshot,
3556
+ node,
3557
+ onNavigate
3558
+ }
3559
+ );
3560
+ }
3561
+ if (extendedType === "account") {
3562
+ return /* @__PURE__ */ jsx36(AccountView, { data: snapshot, node, onNavigate });
3563
+ }
3564
+ if (type === "coplaintext") {
3565
+ return /* @__PURE__ */ jsx36(
3566
+ CoPlainTextView,
3567
+ {
3568
+ data: snapshot,
3569
+ coValue: value,
3570
+ node
3571
+ }
3572
+ );
3573
+ }
3574
+ if (type === "colist") {
3575
+ const handleRemove = (index) => {
3576
+ if (confirm("Are you sure you want to remove this item?")) {
3577
+ const list = value;
3578
+ list.delete(index);
3579
+ }
3580
+ };
3581
+ return /* @__PURE__ */ jsx36(
3582
+ TableView,
3583
+ {
3584
+ data: snapshot,
3585
+ node,
3586
+ onNavigate,
3587
+ onRemove: canEdit(value) ? handleRemove : void 0
3588
+ }
3589
+ );
3590
+ }
3591
+ if (extendedType === "record") {
3592
+ return /* @__PURE__ */ jsx36(TableView, { data: snapshot, node, onNavigate });
3593
+ }
3594
+ if (type === "comap") {
3595
+ return /* @__PURE__ */ jsx36(
3596
+ CoMapView,
3597
+ {
3598
+ coValue: value,
3599
+ data: snapshot,
3600
+ node,
3601
+ onNavigate
3602
+ }
3603
+ );
3604
+ }
3605
+ return /* @__PURE__ */ jsx36(GridView, { data: snapshot, onNavigate, node });
3606
+ }
3607
+ function Page(props) {
3608
+ const {
3609
+ coId,
3610
+ node,
3611
+ name,
3612
+ onNavigate,
3613
+ onHeaderClick,
3614
+ style,
3615
+ className = "",
3616
+ isTopLevel
3617
+ } = props;
3618
+ const coValue = useResolvedCoValue(coId, node);
3619
+ const { value, snapshot, type, extendedType } = coValue;
3620
+ if (snapshot === "unavailable") {
3621
+ return /* @__PURE__ */ jsx36("div", { style, children: "Data unavailable" });
3622
+ }
3623
+ if (!snapshot) {
3624
+ return /* @__PURE__ */ jsx36("div", { style });
3625
+ }
3626
+ return /* @__PURE__ */ jsxs21(PageContainer, { style, className, isTopLevel, children: [
3627
+ !isTopLevel && /* @__PURE__ */ jsx36(
3628
+ BackButton,
3629
+ {
3630
+ "aria-label": "Back",
3631
+ onClick: () => {
3632
+ onHeaderClick?.();
3633
+ },
3634
+ "aria-hidden": "true"
3635
+ }
3636
+ ),
3637
+ /* @__PURE__ */ jsx36(HeaderContainer, { children: /* @__PURE__ */ jsxs21(TitleContainer, { children: [
3638
+ /* @__PURE__ */ jsx36(Title, { children: /* @__PURE__ */ jsxs21("span", { children: [
3639
+ name,
3640
+ typeof snapshot === "object" && "name" in snapshot ? /* @__PURE__ */ jsxs21("span", { style: { color: "#57534e", fontWeight: 500 }, children: [
3641
+ " ",
3642
+ snapshot.name
3643
+ ] }) : null
3644
+ ] }) }),
3645
+ /* @__PURE__ */ jsxs21(BadgeContainer, { children: [
3646
+ /* @__PURE__ */ jsx36(Badge, { children: type && /* @__PURE__ */ jsx36(TypeIcon, { type, extendedType }) }),
3647
+ /* @__PURE__ */ jsx36(Badge, { children: coId })
3648
+ ] })
3649
+ ] }) }),
3650
+ /* @__PURE__ */ jsxs21(ContentContainer, { children: [
3651
+ /* @__PURE__ */ jsx36(View, { ...props, coValue }),
3652
+ extendedType !== "account" && extendedType !== "group" && /* @__PURE__ */ jsxs21(Fragment12, { children: [
3653
+ /* @__PURE__ */ jsx36(RoleDisplay, { node, value }),
3654
+ /* @__PURE__ */ jsxs21(Text, { muted: true, children: [
3655
+ "Owned by",
3656
+ " ",
3657
+ /* @__PURE__ */ jsx36(
3658
+ AccountOrGroupText,
3659
+ {
3660
+ coId: value.group.id,
3661
+ node,
3662
+ showId: true,
3663
+ onClick: () => {
3664
+ onNavigate([{ coId: value.group.id, name: "owner" }]);
3665
+ }
3666
+ }
3667
+ )
3668
+ ] })
3669
+ ] }),
3670
+ value && /* @__PURE__ */ jsx36(HistoryView, { coValue: value, node })
3671
+ ] })
3672
+ ] });
3673
+ }
3674
+
3675
+ // src/inspector/ui/error-boundary.tsx
3676
+ import React6 from "react";
3677
+ import { styled as styled22 } from "goober";
3678
+ import { jsx as jsx37, jsxs as jsxs22 } from "react/jsx-runtime";
3679
+ var ErrorBoundary = class extends React6.Component {
3680
+ constructor(props) {
3681
+ super(props);
3682
+ this.state = { hasError: false };
3683
+ }
3684
+ static getDerivedStateFromError(error) {
3685
+ return { hasError: true, error };
3686
+ }
3687
+ componentDidCatch(error, errorInfo) {
3688
+ console.error(error);
3689
+ }
3690
+ render() {
3691
+ if (this.state.hasError) {
3692
+ return /* @__PURE__ */ jsxs22("div", { style: { padding: "1rem" }, children: [
3693
+ /* @__PURE__ */ jsx37(StyledHeading2, { children: this.props.title }),
3694
+ /* @__PURE__ */ jsx37(Text, { mono: true, style: { marginTop: "0.5rem", color: "#ef4444" }, children: this.state.error?.message || "An unexpected error occurred" }),
3695
+ /* @__PURE__ */ jsx37("pre", { style: { paddingLeft: "1rem", color: "#ef4444" }, children: this.state.error?.stack })
3696
+ ] });
3697
+ }
3698
+ return this.props.children;
3699
+ }
3700
+ };
3701
+ var StyledHeading2 = styled22("h1")`
3702
+ font-size: 1.125rem;
3703
+ font-weight: 500;
3704
+ color: var(--j-text-color-strong);
3705
+ `;
3706
+
3707
+ // src/inspector/pages/home.tsx
3708
+ import { styled as styled23 } from "goober";
3709
+ import { useState as useState17 } from "react";
3710
+ import { Fragment as Fragment13, jsx as jsx38, jsxs as jsxs23 } from "react/jsx-runtime";
3711
+ function HomePage() {
3712
+ const { localNode, accountID } = useNode();
3713
+ const { path, setPage } = useRouter();
3714
+ const [coValueId, setCoValueId] = useState17("");
3715
+ if (!localNode || !accountID) {
3716
+ return /* @__PURE__ */ jsx38("div", { children: "Loading..." });
3717
+ }
3718
+ const handleCoValueIdSubmit = (e) => {
3719
+ e.preventDefault();
3720
+ if (coValueId) {
3721
+ setPage(coValueId);
3722
+ }
3723
+ setCoValueId("");
3724
+ };
3725
+ return /* @__PURE__ */ jsx38(Fragment13, { children: /* @__PURE__ */ jsxs23(
3726
+ CenteredForm,
3727
+ {
3728
+ onSubmit: handleCoValueIdSubmit,
3729
+ "aria-hidden": path.length !== 0,
3730
+ children: [
3731
+ /* @__PURE__ */ jsx38(Heading, { children: "Jazz CoValue Inspector" }),
3732
+ /* @__PURE__ */ jsx38(
3733
+ Input,
3734
+ {
3735
+ label: "CoValue ID",
3736
+ className: "font-mono",
3737
+ hideLabel: true,
3738
+ placeholder: "co_z1234567890abcdef123456789",
3739
+ value: coValueId,
3740
+ onChange: (e) => setCoValueId(e.target.value)
3741
+ }
3742
+ ),
3743
+ /* @__PURE__ */ jsx38(Button, { type: "submit", variant: "primary", children: "Inspect CoValue" }),
3744
+ /* @__PURE__ */ jsx38(OrText, { children: "or" }),
3745
+ /* @__PURE__ */ jsx38(
3746
+ Button,
3747
+ {
3748
+ variant: "secondary",
3749
+ onClick: () => {
3750
+ setPage(accountID);
3751
+ },
3752
+ children: "Inspect my account"
3753
+ }
3754
+ )
3755
+ ]
3756
+ }
3757
+ ) });
3758
+ }
3759
+ var CenteredForm = styled23("form")`
3760
+ display: flex;
3761
+ flex-direction: column;
3762
+ position: relative;
3763
+ top: -1.5rem;
3764
+ justify-content: center;
3765
+ gap: 0.5rem;
3766
+ height: 100%;
3767
+ width: 100%;
3768
+ max-width: 24rem;
3769
+ margin: 0 auto;
3770
+ `;
3771
+ var OrText = styled23("p")`
3772
+ text-align: center;
3773
+ `;
3774
+
3775
+ // src/inspector/viewer/page-stack.tsx
3776
+ import { Fragment as Fragment14, jsx as jsx39 } from "react/jsx-runtime";
3777
+ var PageStackContainer = styled24("article")`
3778
+ position: relative;
3779
+ padding: 0 0.75rem;
3780
+ overflow-y: auto;
3781
+ flex: 1;
3782
+ color: var(--j-text-color);
3783
+ font-size: 16px;
3784
+ `;
3785
+ function PageStack({ homePage }) {
3786
+ const { path, addPages, goBack } = useRouter();
3787
+ const { localNode } = useNode();
3788
+ const page = path[path.length - 1];
3789
+ const index = path.length - 1;
3790
+ if (path.length <= 0) {
3791
+ return /* @__PURE__ */ jsx39(PageStackContainer, { children: homePage ?? /* @__PURE__ */ jsx39(HomePage, {}) });
3792
+ }
3793
+ return /* @__PURE__ */ jsx39(Fragment14, { children: /* @__PURE__ */ jsx39(PageStackContainer, { children: localNode && page && /* @__PURE__ */ jsx39(ErrorBoundary, { title: "An error occurred while rendering this CoValue", children: /* @__PURE__ */ jsx39(
3794
+ Page,
3795
+ {
3796
+ coId: page.coId,
3797
+ node: localNode,
3798
+ name: page.name || page.coId,
3799
+ onHeaderClick: goBack,
3800
+ onNavigate: addPages,
3801
+ isTopLevel: index === path.length - 1
3802
+ }
3803
+ ) }) }) });
3804
+ }
3805
+
3806
+ // src/inspector/ui/global-styles.tsx
3807
+ import { styled as styled25 } from "goober";
3808
+ var GlobalStyles = styled25("div")`
3809
+ /* Colors */
3810
+ --j-primary-color: #146AFF;
3811
+ --j-link-color: var(--j-primary-color);
3812
+ --j-success-color: oklch(52.7% 0.154 150.069);
3813
+ --j-destructive-color: oklch(50.5% 0.213 27.518);
3814
+
3815
+ /* Neutral Colors */
3816
+ --j-neutral-100: #faf8f8;
3817
+ --j-neutral-200: #e5e3e4;
3818
+ --j-neutral-300: #d0cecf;
3819
+ --j-neutral-400: #bbbaba;
3820
+ --j-neutral-500: #a8a6a6;
3821
+ --j-neutral-600: #858484;
3822
+ --j-neutral-700: #6b696a;
3823
+ --j-neutral-900: #2f2e2e;
3824
+ --j-neutral-925: #1b1a1a;
3825
+ --j-neutral-950: #151414;
3826
+
3827
+ /* Text Colors */
3828
+ --j-text-color: var(--j-neutral-700);
3829
+ --j-text-color-strong: var(--j-neutral-900);
3830
+
3831
+ /* Border Colors */
3832
+ --j-border-color: var(--j-neutral-200);
3833
+ --j-border-color-hover: var(--j-neutral-300);
3834
+ --j-border-dark: var(--j-neutral-900);
3835
+ --j-border-focus: var(--j-primary-color);
3836
+
3837
+ /* Background Colors */
3838
+ --j-background: #FFFFFF;
3839
+ --j-foreground: var(--j-neutral-100);
3840
+
3841
+ /* Border Radius */
3842
+ --j-radius-sm: 0.25rem;
3843
+ --j-radius-md: 0.375rem;
3844
+ --j-radius-lg: 0.5rem;
3845
+
3846
+ /* Shadows */
3847
+ --j-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
3848
+
3849
+ @media (prefers-color-scheme: dark) {
3850
+ --j-text-color: var(--j-neutral-400);
3851
+ --j-border-color: var(--j-neutral-900);
3852
+ --j-background: var(--j-neutral-950);
3853
+ --j-foreground: var(--j-neutral-925);
3854
+ --j-border-color-hover: var(--j-neutral-700);
3855
+ --j-text-color-strong: var(--j-neutral-100);
3856
+ --j-success-color: oklch(72.3% 0.219 149.579);
3857
+ --j-destructive-color: oklch(63.7% 0.237 25.331);
3858
+ }
3859
+
3860
+ *:focus {
3861
+ outline: none;
3862
+ }
3863
+
3864
+ *:focus-visible {
3865
+ box-shadow: 0 0 0 2px var(--j-link-color);
3866
+ }
3867
+
3868
+ .j-sr-only {
3869
+ position: absolute;
3870
+ width: 1px;
3871
+ height: 1px;
3872
+ padding: 0;
3873
+ margin: -1px;
3874
+ overflow: hidden;
3875
+ clip: rect(0, 0, 0, 0);
3876
+ white-space: nowrap;
3877
+ border: 0;
3878
+ }
3879
+ `;
3880
+
3881
+ // src/inspector/viewer/header.tsx
3882
+ import { styled as styled27 } from "goober";
3883
+ import { useState as useState19 } from "react";
3884
+
3885
+ // src/inspector/viewer/breadcrumbs.tsx
3886
+ import { styled as styled26 } from "goober";
3887
+ import React7 from "react";
3888
+ import { jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
3889
+ var BreadcrumbsContainer = styled26("div")`
3890
+ position: relative;
3891
+ z-index: 20;
3892
+ flex: 1;
3893
+ display: flex;
3894
+ align-items: center;
3895
+ `;
3896
+ var Separator = styled26("span")`
3897
+ padding: 0 0.125rem;
3898
+ `;
3899
+ var Breadcrumbs = () => {
3900
+ const { path, goToIndex } = useRouter();
3901
+ return /* @__PURE__ */ jsxs24(BreadcrumbsContainer, { children: [
3902
+ /* @__PURE__ */ jsx40(
3903
+ Button,
3904
+ {
3905
+ variant: "link",
3906
+ style: { padding: "0 0.25rem" },
3907
+ onClick: () => goToIndex(-1),
3908
+ children: "Home"
3909
+ }
3910
+ ),
3911
+ path.map((page, index) => {
3912
+ return /* @__PURE__ */ jsxs24(React7.Fragment, { children: [
3913
+ /* @__PURE__ */ jsx40(Separator, { "aria-hidden": true, children: "/" }),
3914
+ /* @__PURE__ */ jsx40(
3915
+ Button,
3916
+ {
3917
+ variant: "link",
3918
+ style: { padding: "0 0.25rem" },
3919
+ onClick: () => goToIndex(index),
3920
+ children: index === 0 ? page.name || "Root" : page.name
3921
+ }
3922
+ )
3923
+ ] }, page.coId);
3924
+ })
3925
+ ] });
3926
+ };
3927
+
3928
+ // src/inspector/viewer/delete-local-data.tsx
3929
+ import { useState as useState18 } from "react";
3930
+ import { Fragment as Fragment15, jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
3931
+ var DELETE_LOCAL_DATA_STRING = "delete my local data";
3932
+ function DeleteLocalData() {
3933
+ const [showDeleteModal, setShowDeleteModal] = useState18(false);
3934
+ const [confirmDeleteString, setConfirmDeleteString] = useState18("");
3935
+ return /* @__PURE__ */ jsxs25(Fragment15, { children: [
3936
+ /* @__PURE__ */ jsx41(Button, { variant: "destructive", onClick: () => setShowDeleteModal(true), children: "Delete my local data" }),
3937
+ /* @__PURE__ */ jsxs25(
3938
+ Modal,
3939
+ {
3940
+ isOpen: showDeleteModal,
3941
+ onClose: () => setShowDeleteModal(false),
3942
+ heading: "Delete Local Data",
3943
+ showButtons: false,
3944
+ children: [
3945
+ /* @__PURE__ */ jsxs25(
3946
+ "div",
3947
+ {
3948
+ style: {
3949
+ margin: "0 0 1rem 0",
3950
+ color: "var(--j-text-color)",
3951
+ display: "flex",
3952
+ flexDirection: "column",
3953
+ gap: "0.5rem"
3954
+ },
3955
+ children: [
3956
+ /* @__PURE__ */ jsxs25("p", { children: [
3957
+ "This action ",
3958
+ /* @__PURE__ */ jsx41("strong", { children: "cannot" }),
3959
+ " be undone."
3960
+ ] }),
3961
+ /* @__PURE__ */ jsxs25("p", { children: [
3962
+ "Be aware that the following data will be",
3963
+ " ",
3964
+ /* @__PURE__ */ jsx41("strong", { children: "permanently" }),
3965
+ " deleted:"
3966
+ ] }),
3967
+ /* @__PURE__ */ jsxs25("ul", { style: { listStyleType: "disc", paddingLeft: "1rem" }, children: [
3968
+ /* @__PURE__ */ jsxs25("li", { children: [
3969
+ "Unsynced data for ",
3970
+ /* @__PURE__ */ jsx41("strong", { children: "all apps" }),
3971
+ " on",
3972
+ " ",
3973
+ /* @__PURE__ */ jsx41("code", { children: window.location.origin })
3974
+ ] }),
3975
+ /* @__PURE__ */ jsx41("li", { children: "Accounts" }),
3976
+ /* @__PURE__ */ jsx41("li", { children: "Logged in sessions" })
3977
+ ] }),
3978
+ /* @__PURE__ */ jsx41("p", {})
3979
+ ]
3980
+ }
3981
+ ),
3982
+ /* @__PURE__ */ jsx41(
3983
+ Input,
3984
+ {
3985
+ label: `Type "${DELETE_LOCAL_DATA_STRING}" to confirm`,
3986
+ placeholder: DELETE_LOCAL_DATA_STRING,
3987
+ value: confirmDeleteString,
3988
+ onChange: (e) => {
3989
+ setConfirmDeleteString(e.target.value);
3990
+ }
3991
+ }
3992
+ ),
3993
+ /* @__PURE__ */ jsx41(
3994
+ "p",
3995
+ {
3996
+ style: {
3997
+ margin: "0 0 1rem 0",
3998
+ color: "var(--j-text-color)",
3999
+ display: "flex",
4000
+ flexDirection: "column",
4001
+ gap: "0.5rem"
4002
+ },
4003
+ children: /* @__PURE__ */ jsxs25("small", { children: [
4004
+ "Data synced to a sync server will ",
4005
+ /* @__PURE__ */ jsx41("strong", { children: "not" }),
4006
+ " be deleted, and will be synced when you log in again."
4007
+ ] })
4008
+ }
4009
+ ),
4010
+ /* @__PURE__ */ jsxs25(
4011
+ "div",
4012
+ {
4013
+ style: {
4014
+ display: "flex",
4015
+ marginTop: "0.5rem",
4016
+ justifyContent: "flex-end",
4017
+ gap: "0.5rem"
4018
+ },
4019
+ children: [
4020
+ /* @__PURE__ */ jsx41(Button, { variant: "secondary", onClick: () => setShowDeleteModal(false), children: "Cancel" }),
4021
+ /* @__PURE__ */ jsx41(
4022
+ Button,
4023
+ {
4024
+ variant: "destructive",
4025
+ disabled: confirmDeleteString !== DELETE_LOCAL_DATA_STRING,
4026
+ onClick: () => {
4027
+ const jazzKeys = Object.keys(localStorage).filter(
4028
+ (key) => key.startsWith("jazz-") || key.startsWith("co_z")
4029
+ );
4030
+ jazzKeys.forEach((key) => localStorage.removeItem(key));
4031
+ indexedDB.deleteDatabase("jazz-storage");
4032
+ window.location.reload();
4033
+ setShowDeleteModal(false);
4034
+ },
4035
+ children: "I'm sure, delete my local data"
4036
+ }
4037
+ )
4038
+ ]
4039
+ }
4040
+ )
4041
+ ]
4042
+ }
4043
+ )
4044
+ ] });
4045
+ }
4046
+
4047
+ // src/inspector/viewer/header.tsx
4048
+ import { jsx as jsx42, jsxs as jsxs26 } from "react/jsx-runtime";
4049
+ function Header({
4050
+ showDeleteLocalData = false,
4051
+ showClose = false,
4052
+ onClose,
4053
+ children
4054
+ }) {
4055
+ const [coValueId, setCoValueId] = useState19("");
4056
+ const { path, setPage } = useRouter();
4057
+ const handleCoValueIdSubmit = (e) => {
4058
+ e.preventDefault();
4059
+ if (coValueId) {
4060
+ setPage(coValueId);
4061
+ }
4062
+ setCoValueId("");
4063
+ };
4064
+ return /* @__PURE__ */ jsxs26(HeaderContainer2, { children: [
4065
+ /* @__PURE__ */ jsx42(Breadcrumbs, {}),
4066
+ path.length !== 0 && /* @__PURE__ */ jsx42(Form, { onSubmit: handleCoValueIdSubmit, children: /* @__PURE__ */ jsx42(
4067
+ Input,
4068
+ {
4069
+ label: "CoValue ID",
4070
+ style: { fontFamily: "monospace" },
4071
+ hideLabel: true,
4072
+ placeholder: "co_z1234567890abcdef123456789",
4073
+ value: coValueId,
4074
+ onChange: (e) => setCoValueId(e.target.value)
4075
+ }
4076
+ ) }),
4077
+ children,
4078
+ showDeleteLocalData && /* @__PURE__ */ jsx42(DeleteLocalData, {}),
4079
+ showClose && /* @__PURE__ */ jsx42(Button, { variant: "plain", type: "button", onClick: onClose, children: "Close" })
4080
+ ] });
4081
+ }
4082
+ var HeaderContainer2 = styled27("div")`
4083
+ display: flex;
4084
+ align-items: center;
4085
+ gap: 1rem;
4086
+ padding: 0 0.75rem;
4087
+ margin: 0.75rem 0;
4088
+ `;
4089
+ var Form = styled27("form")`
4090
+ width: 24rem;
4091
+ `;
4092
+
4093
+ export {
4094
+ Button,
4095
+ AccountOrGroupText,
4096
+ Icon,
4097
+ Modal,
4098
+ Input,
4099
+ NodeContext,
4100
+ NodeProvider,
4101
+ useNode,
4102
+ InMemoryRouterProvider,
4103
+ HashRouterProvider,
4104
+ PageStack,
4105
+ GlobalStyles,
4106
+ Header
4107
+ };
4108
+ //# sourceMappingURL=chunk-YQNK5Y7B.js.map