jazz-tools 0.19.3 → 0.19.5

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 (75) hide show
  1. package/.svelte-kit/__package__/jazz.class.svelte.d.ts +2 -2
  2. package/.svelte-kit/__package__/jazz.class.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/jazz.class.svelte.js +15 -17
  4. package/.turbo/turbo-build.log +65 -65
  5. package/CHANGELOG.md +23 -0
  6. package/dist/{chunk-JPWM4CS2.js → chunk-DFFRRRRF.js} +137 -77
  7. package/dist/chunk-DFFRRRRF.js.map +1 -0
  8. package/dist/index.js +14 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/inspector/{custom-element-3JAYHXWQ.js → custom-element-P76EIWEV.js} +301 -142
  11. package/dist/inspector/{custom-element-3JAYHXWQ.js.map → custom-element-P76EIWEV.js.map} +1 -1
  12. package/dist/inspector/index.js +281 -122
  13. package/dist/inspector/index.js.map +1 -1
  14. package/dist/inspector/register-custom-element.js +1 -1
  15. package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts +2 -0
  16. package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts.map +1 -0
  17. package/dist/inspector/utils/history.d.ts +5 -1
  18. package/dist/inspector/utils/history.d.ts.map +1 -1
  19. package/dist/inspector/viewer/co-plain-text-view.d.ts +4 -2
  20. package/dist/inspector/viewer/co-plain-text-view.d.ts.map +1 -1
  21. package/dist/inspector/viewer/page.d.ts.map +1 -1
  22. package/dist/inspector/viewer/use-resolve-covalue.d.ts +0 -1
  23. package/dist/inspector/viewer/use-resolve-covalue.d.ts.map +1 -1
  24. package/dist/react-core/hooks.d.ts.map +1 -1
  25. package/dist/react-core/index.js +4 -17
  26. package/dist/react-core/index.js.map +1 -1
  27. package/dist/svelte/jazz.class.svelte.d.ts +2 -2
  28. package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
  29. package/dist/svelte/jazz.class.svelte.js +15 -17
  30. package/dist/testing.js +1 -1
  31. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  32. package/dist/tools/coValues/group.d.ts.map +1 -1
  33. package/dist/tools/coValues/interfaces.d.ts +7 -6
  34. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  35. package/dist/tools/coValues/request.d.ts.map +1 -1
  36. package/dist/tools/exports.d.ts +1 -1
  37. package/dist/tools/exports.d.ts.map +1 -1
  38. package/dist/tools/implementation/refs.d.ts +1 -1
  39. package/dist/tools/implementation/refs.d.ts.map +1 -1
  40. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +3 -1
  41. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  42. package/dist/tools/subscribe/SubscriptionScope.d.ts +5 -2
  43. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  44. package/dist/tools/subscribe/index.d.ts +1 -1
  45. package/dist/tools/subscribe/index.d.ts.map +1 -1
  46. package/dist/tools/subscribe/types.d.ts +2 -1
  47. package/dist/tools/subscribe/types.d.ts.map +1 -1
  48. package/dist/tools/tests/SubscriptionScope.test.d.ts +2 -0
  49. package/dist/tools/tests/SubscriptionScope.test.d.ts.map +1 -0
  50. package/package.json +4 -4
  51. package/src/inspector/tests/utils/history.test.ts +233 -2
  52. package/src/inspector/tests/viewer/co-plain-text-view.test.tsx +125 -0
  53. package/src/inspector/tests/viewer/history-view.test.tsx +134 -2
  54. package/src/inspector/utils/history.ts +168 -1
  55. package/src/inspector/viewer/co-plain-text-view.tsx +102 -3
  56. package/src/inspector/viewer/history-view.tsx +5 -25
  57. package/src/inspector/viewer/page.tsx +8 -1
  58. package/src/inspector/viewer/use-resolve-covalue.ts +2 -6
  59. package/src/react-core/hooks.ts +5 -29
  60. package/src/svelte/jazz.class.svelte.ts +16 -34
  61. package/src/tools/coValues/coFeed.ts +10 -7
  62. package/src/tools/coValues/coMap.ts +10 -7
  63. package/src/tools/coValues/group.ts +6 -2
  64. package/src/tools/coValues/interfaces.ts +48 -28
  65. package/src/tools/coValues/request.ts +12 -8
  66. package/src/tools/exports.ts +1 -0
  67. package/src/tools/implementation/refs.ts +9 -17
  68. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +62 -30
  69. package/src/tools/subscribe/SubscriptionScope.ts +38 -2
  70. package/src/tools/subscribe/index.ts +28 -13
  71. package/src/tools/subscribe/types.ts +5 -2
  72. package/src/tools/tests/SubscriptionScope.test.ts +397 -0
  73. package/src/tools/tests/deepLoading.test.ts +22 -0
  74. package/src/tools/tests/subscribe.test.ts +69 -0
  75. package/dist/chunk-JPWM4CS2.js.map +0 -1
@@ -4,8 +4,8 @@
4
4
  import React8 from "react";
5
5
 
6
6
  // src/inspector/viewer/new-app.tsx
7
- import { styled as styled26 } from "goober";
8
- import { useState as useState16 } from "react";
7
+ import { styled as styled27 } from "goober";
8
+ import { useState as useState17 } from "react";
9
9
 
10
10
  // src/inspector/ui/button.tsx
11
11
  import { styled } from "goober";
@@ -180,10 +180,10 @@ var Breadcrumbs = ({
180
180
  };
181
181
 
182
182
  // src/inspector/viewer/page-stack.tsx
183
- import { styled as styled23 } from "goober";
183
+ import { styled as styled24 } from "goober";
184
184
 
185
185
  // src/inspector/viewer/page.tsx
186
- import { styled as styled21 } from "goober";
186
+ import { styled as styled22 } from "goober";
187
187
  import React5 from "react";
188
188
 
189
189
  // src/inspector/ui/badge.tsx
@@ -532,12 +532,6 @@ function RenderBlobImage({ blob }) {
532
532
  var isBrowserImage = (coValue) => {
533
533
  return "originalSize" in coValue && "placeholderDataURL" in coValue;
534
534
  };
535
- var isGroup = (coValue) => {
536
- return "readKey" in coValue;
537
- };
538
- var isAccount = (coValue) => {
539
- return isGroup(coValue) && "profile" in coValue;
540
- };
541
535
  async function resolveCoValue(coValueId, node) {
542
536
  const value = await node.load(coValueId);
543
537
  if (value === "unavailable") {
@@ -554,7 +548,7 @@ async function resolveCoValue(coValueId, node) {
554
548
  if (type === "comap") {
555
549
  if (isBrowserImage(snapshot)) {
556
550
  extendedType = "image";
557
- } else if (isAccount(snapshot)) {
551
+ } else if (value.headerMeta?.type === "account") {
558
552
  extendedType = "account";
559
553
  } else if (value.core.isGroup()) {
560
554
  extendedType = "group";
@@ -583,7 +577,7 @@ function subscribeToCoValue(coValueId, node, callback) {
583
577
  if (type === "comap") {
584
578
  if (isBrowserImage(snapshot)) {
585
579
  extendedType = "image";
586
- } else if (isAccount(snapshot)) {
580
+ } else if (value.headerMeta?.type === "account") {
587
581
  extendedType = "account";
588
582
  } else if (value.core.isGroup()) {
589
583
  extendedType = "group";
@@ -2059,17 +2053,90 @@ function AccountView({
2059
2053
  }
2060
2054
 
2061
2055
  // src/inspector/viewer/co-plain-text-view.tsx
2056
+ import { useState as useState10 } from "react";
2057
+ import { styled as styled18 } from "goober";
2058
+ import { CoPlainText } from "jazz-tools";
2062
2059
  import { Fragment as Fragment7, jsx as jsx29, jsxs as jsxs16 } from "react/jsx-runtime";
2063
- function CoPlainTextView({ data }) {
2060
+ function CoPlainTextView({
2061
+ data,
2062
+ coValue
2063
+ }) {
2064
+ const currentText = Object.values(data).join("");
2065
+ const [isEditing, setIsEditing] = useState10(false);
2066
+ const [editValue, setEditValue] = useState10("");
2067
+ const canEdit2 = isWriter(coValue.group.myRole());
2068
+ const handleEditClick = () => {
2069
+ setIsEditing(true);
2070
+ setEditValue(currentText);
2071
+ };
2072
+ const handleCancel = () => {
2073
+ setIsEditing(false);
2074
+ setEditValue(currentText);
2075
+ };
2076
+ const handleSave = (e) => {
2077
+ e.preventDefault();
2078
+ e.stopPropagation();
2079
+ const coPlainText = CoPlainText.fromRaw(coValue);
2080
+ coPlainText.$jazz.applyDiff(editValue);
2081
+ setIsEditing(false);
2082
+ };
2064
2083
  if (!data) return;
2084
+ if (isEditing) {
2085
+ return /* @__PURE__ */ jsxs16(Fragment7, { children: [
2086
+ /* @__PURE__ */ jsxs16(EditForm2, { onSubmit: handleSave, children: [
2087
+ /* @__PURE__ */ jsx29(
2088
+ StyledTextarea2,
2089
+ {
2090
+ value: editValue,
2091
+ onChange: (e) => setEditValue(e.target.value),
2092
+ onClick: (e) => e.stopPropagation()
2093
+ }
2094
+ ),
2095
+ /* @__PURE__ */ jsxs16(FormActions2, { children: [
2096
+ /* @__PURE__ */ jsx29(Button, { type: "button", variant: "secondary", onClick: handleCancel, children: "Cancel" }),
2097
+ /* @__PURE__ */ jsx29(Button, { type: "submit", variant: "primary", children: "Save" })
2098
+ ] })
2099
+ ] }),
2100
+ /* @__PURE__ */ jsx29(RawDataCard, { data })
2101
+ ] });
2102
+ }
2065
2103
  return /* @__PURE__ */ jsxs16(Fragment7, { children: [
2066
- /* @__PURE__ */ jsx29("p", { children: Object.values(data).join("") }),
2104
+ /* @__PURE__ */ jsx29("p", { children: currentText }),
2105
+ /* @__PURE__ */ jsx29("div", { children: canEdit2 && /* @__PURE__ */ jsx29(Button, { variant: "secondary", onClick: handleEditClick, title: "Edit", children: /* @__PURE__ */ jsx29(Icon, { name: "edit" }) }) }),
2067
2106
  /* @__PURE__ */ jsx29(RawDataCard, { data })
2068
2107
  ] });
2069
2108
  }
2109
+ var EditForm2 = styled18("form")`
2110
+ display: flex;
2111
+ flex-direction: column;
2112
+ gap: 0.75rem;
2113
+ margin-bottom: 1rem;
2114
+ `;
2115
+ var StyledTextarea2 = styled18("textarea")`
2116
+ width: 100%;
2117
+ min-height: 120px;
2118
+ border-radius: var(--j-radius-md);
2119
+ border: 1px solid var(--j-border-color);
2120
+ padding: 0.5rem 0.875rem;
2121
+ box-shadow: var(--j-shadow-sm);
2122
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2123
+ font-size: 0.875rem;
2124
+ background-color: white;
2125
+ color: var(--j-text-color-strong);
2126
+ resize: vertical;
2127
+
2128
+ @media (prefers-color-scheme: dark) {
2129
+ background-color: var(--j-foreground);
2130
+ }
2131
+ `;
2132
+ var FormActions2 = styled18("div")`
2133
+ display: flex;
2134
+ gap: 0.5rem;
2135
+ justify-content: flex-end;
2136
+ `;
2070
2137
 
2071
2138
  // src/inspector/viewer/group-view.tsx
2072
- import { useState as useState10 } from "react";
2139
+ import { useState as useState11 } from "react";
2073
2140
  import { Fragment as Fragment8, jsx as jsx30, jsxs as jsxs17 } from "react/jsx-runtime";
2074
2141
  function partitionMembers(data) {
2075
2142
  const everyone = Object.entries(data).filter(([key]) => key === "everyone").map(([key, value]) => ({
@@ -2098,7 +2165,7 @@ function GroupView({
2098
2165
  onNavigate,
2099
2166
  node
2100
2167
  }) {
2101
- const [addMemberType, setAddMemberType] = useState10(null);
2168
+ const [addMemberType, setAddMemberType] = useState11(null);
2102
2169
  const { everyone, members, parentGroups, childGroups } = partitionMembers(
2103
2170
  data
2104
2171
  );
@@ -2358,17 +2425,17 @@ function RoleDisplay({
2358
2425
  }
2359
2426
 
2360
2427
  // src/inspector/viewer/table-viewer.tsx
2361
- import { styled as styled18 } from "goober";
2362
- import { useMemo as useMemo2, useState as useState11 } from "react";
2428
+ import { styled as styled19 } from "goober";
2429
+ import { useMemo as useMemo2, useState as useState12 } from "react";
2363
2430
  import { Fragment as Fragment9, jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
2364
- var PaginationContainer = styled18("div")`
2431
+ var PaginationContainer = styled19("div")`
2365
2432
  padding: 1rem 0;
2366
2433
  display: flex;
2367
2434
  align-items: center;
2368
2435
  justify-content: space-between;
2369
2436
  gap: 0.5rem;
2370
2437
  `;
2371
- var RedTooltip = styled18("span")`
2438
+ var RedTooltip = styled19("span")`
2372
2439
  position:relative; /* making the .tooltip span a container for the tooltip text */
2373
2440
  border-bottom:1px dashed #000; /* little indicater to indicate it's hoverable */
2374
2441
 
@@ -2405,7 +2472,7 @@ function CoValuesTableView({
2405
2472
  onNavigate,
2406
2473
  onRemove
2407
2474
  }) {
2408
- const [visibleRowsCount, setVisibleRowsCount] = useState11(10);
2475
+ const [visibleRowsCount, setVisibleRowsCount] = useState12(10);
2409
2476
  const [coIdArray, visibleRows] = useMemo2(() => {
2410
2477
  const coIdArray2 = Array.isArray(data) ? data : Object.values(data).every((k) => typeof k === "string" && isCoId(k)) ? Object.values(data).map((k) => k) : [];
2411
2478
  const visibleRows2 = coIdArray2.slice(0, visibleRowsCount);
@@ -2533,7 +2600,7 @@ function TableView({
2533
2600
 
2534
2601
  // src/inspector/viewer/history-view.tsx
2535
2602
  import { useMemo as useMemo3 } from "react";
2536
- import { styled as styled19 } from "goober";
2603
+ import { styled as styled20 } from "goober";
2537
2604
 
2538
2605
  // src/inspector/utils/transactions-changes.ts
2539
2606
  var isGroupExtension = (change) => {
@@ -2576,6 +2643,140 @@ var isStreamEnd = (change) => {
2576
2643
  return change?.type === "end";
2577
2644
  };
2578
2645
 
2646
+ // src/inspector/utils/history.ts
2647
+ import { stringifyOpID } from "cojson";
2648
+ function areSameOpIds(opId1, opId2) {
2649
+ if (typeof opId1 === "string" || typeof opId2 === "string") {
2650
+ return opId1 === opId2;
2651
+ }
2652
+ return opId1.sessionID === opId2.sessionID && opId1.txIndex === opId2.txIndex && opId1.changeIdx === opId2.changeIdx;
2653
+ }
2654
+ function isCoPlainText(coValue) {
2655
+ return coValue.type === "coplaintext";
2656
+ }
2657
+ function getTransactionChanges(tx, coValue) {
2658
+ if (tx.isValid === false && tx.tx.privacy === "private") {
2659
+ const readKey = coValue.core.getReadKey(tx.tx.keyUsed);
2660
+ if (!readKey) {
2661
+ return [
2662
+ `Unable to decrypt transaction: read key ${tx.tx.keyUsed} not found.`
2663
+ ];
2664
+ }
2665
+ return coValue.core.verified.decryptTransaction(
2666
+ tx.txID.sessionID,
2667
+ tx.txID.txIndex,
2668
+ readKey
2669
+ ) ?? [];
2670
+ }
2671
+ if (isCoPlainText(coValue)) {
2672
+ if (tx.changes === void 0 || tx.changes.length === 0) return [];
2673
+ const firstChange = tx.changes[0];
2674
+ if (isItemAppend(firstChange) && tx.changes.every(
2675
+ (c) => isItemAppend(c) && areSameOpIds(c.after, firstChange.after)
2676
+ )) {
2677
+ const changes = tx.changes;
2678
+ if (firstChange.after !== "start") {
2679
+ changes.reverse();
2680
+ }
2681
+ return [
2682
+ {
2683
+ op: "app",
2684
+ value: changes.map((c) => c.value).join(""),
2685
+ after: firstChange.after
2686
+ }
2687
+ ];
2688
+ }
2689
+ if (isItemPrepend(firstChange) && tx.changes.every(
2690
+ (c) => isItemPrepend(c) && areSameOpIds(c.before, firstChange.before)
2691
+ )) {
2692
+ const changes = tx.changes;
2693
+ if (firstChange.before !== "end") {
2694
+ changes.reverse();
2695
+ }
2696
+ return [
2697
+ {
2698
+ op: "pre",
2699
+ value: changes.map((c) => c.value).join(""),
2700
+ before: firstChange.before
2701
+ }
2702
+ ];
2703
+ }
2704
+ if (isItemDeletion(firstChange) && tx.changes.every((c) => isItemDeletion(c))) {
2705
+ let changesAreConsecutive2 = function(changes) {
2706
+ if (changes.length < 2) return false;
2707
+ const mapping = coValueBeforeDeletions.mapping.idxAfterOpID;
2708
+ for (let i = 1; i < changes.length; ++i) {
2709
+ const prevIdx = mapping[stringifyOpID(changes[i - 1].insertion)];
2710
+ const currIdx = mapping[stringifyOpID(changes[i].insertion)];
2711
+ if (currIdx !== prevIdx && currIdx !== (prevIdx ?? -2) + 1) {
2712
+ return false;
2713
+ }
2714
+ }
2715
+ return true;
2716
+ };
2717
+ var changesAreConsecutive = changesAreConsecutive2;
2718
+ const coValueBeforeDeletions = coValue.atTime(tx.madeAt - 1);
2719
+ if (changesAreConsecutive2(tx.changes)) {
2720
+ const groupedBySession = /* @__PURE__ */ new Map();
2721
+ for (const change of tx.changes) {
2722
+ const group = `${change.insertion.sessionID}-${change.insertion.txIndex}`;
2723
+ if (!groupedBySession.has(group)) groupedBySession.set(group, []);
2724
+ groupedBySession.get(group).push(change);
2725
+ }
2726
+ return Array.from(groupedBySession.values()).map((changes) => {
2727
+ const stringDeleted = changes.toSorted((a, b) => {
2728
+ if (a.insertion.txIndex === b.insertion.txIndex) {
2729
+ return a.insertion.changeIdx - b.insertion.changeIdx;
2730
+ }
2731
+ return a.insertion.txIndex - b.insertion.txIndex;
2732
+ }).map(
2733
+ (c) => coValueBeforeDeletions.get(
2734
+ coValueBeforeDeletions.mapping.idxAfterOpID[stringifyOpID(c.insertion)]
2735
+ )
2736
+ ).join("");
2737
+ return {
2738
+ op: "custom",
2739
+ action: `"${stringDeleted}" has been deleted`
2740
+ };
2741
+ });
2742
+ }
2743
+ }
2744
+ }
2745
+ return tx.changes ?? tx.tx.changes ?? [];
2746
+ }
2747
+ function restoreCoMapToTimestamp(coValue, timestamp, removeUnknownProperties) {
2748
+ const myRole = coValue.group.myRole();
2749
+ if (myRole === void 0 || !["admin", "manager", "writer", "writerOnly"].includes(myRole)) {
2750
+ return;
2751
+ }
2752
+ const newCoValue = coValue.atTime(timestamp).toJSON();
2753
+ const oldCoValue = coValue.toJSON();
2754
+ if (newCoValue === null) return;
2755
+ let changes = [];
2756
+ if (removeUnknownProperties) {
2757
+ for (const key in oldCoValue) {
2758
+ if (!(key in newCoValue)) {
2759
+ changes.push({
2760
+ op: "del",
2761
+ key
2762
+ });
2763
+ }
2764
+ }
2765
+ }
2766
+ for (const key in newCoValue) {
2767
+ if (newCoValue[key] !== oldCoValue[key]) {
2768
+ changes.push({
2769
+ op: "set",
2770
+ key,
2771
+ value: newCoValue[key]
2772
+ });
2773
+ }
2774
+ }
2775
+ if (changes.length > 0) {
2776
+ coValue.core.makeTransaction(changes, "private");
2777
+ }
2778
+ }
2779
+
2579
2780
  // src/inspector/viewer/history-view.tsx
2580
2781
  import { Fragment as Fragment10, jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
2581
2782
  function HistoryView({
@@ -2656,22 +2857,6 @@ function HistoryView({
2656
2857
  }
2657
2858
  ) });
2658
2859
  }
2659
- function getTransactionChanges(tx, coValue) {
2660
- if (tx.isValid === false && tx.tx.privacy === "private") {
2661
- const readKey = coValue.core.getReadKey(tx.tx.keyUsed);
2662
- if (!readKey) {
2663
- return [
2664
- `Unable to decrypt transaction: read key ${tx.tx.keyUsed} not found.`
2665
- ];
2666
- }
2667
- return coValue.core.verified.decryptTransaction(
2668
- tx.txID.sessionID,
2669
- tx.txID.txIndex,
2670
- readKey
2671
- ) ?? [];
2672
- }
2673
- return tx.changes ?? tx.tx.changes ?? [];
2674
- }
2675
2860
  function getHistory(coValue) {
2676
2861
  return coValue.core.verifiedTransactions.flatMap((tx, index) => {
2677
2862
  const changes = getTransactionChanges(tx, coValue);
@@ -2750,6 +2935,9 @@ function mapTransactionToAction(change, coValue) {
2750
2935
  if (isPropertyDeletion(change)) {
2751
2936
  return `Property "${change.key}" has been deleted`;
2752
2937
  }
2938
+ if (change.op === "custom") {
2939
+ return change.action;
2940
+ }
2753
2941
  return "Unknown action: " + JSON.stringify(change);
2754
2942
  }
2755
2943
  var findListChange = (opId, coValue) => {
@@ -2757,7 +2945,7 @@ var findListChange = (opId, coValue) => {
2757
2945
  (tx) => tx.txID.sessionID === opId.sessionID && tx.txID.txIndex === opId.txIndex
2758
2946
  )?.changes?.[opId.changeIdx];
2759
2947
  };
2760
- var RedTooltip2 = styled19("span")`
2948
+ var RedTooltip2 = styled20("span")`
2761
2949
  position:relative; /* making the .tooltip span a container for the tooltip text */
2762
2950
  border-bottom:1px dashed #000; /* little indicater to indicate it's hoverable */
2763
2951
 
@@ -2790,44 +2978,8 @@ var RedTooltip2 = styled19("span")`
2790
2978
  `;
2791
2979
 
2792
2980
  // src/inspector/viewer/co-map-view.tsx
2793
- import { useState as useState12, useMemo as useMemo4 } from "react";
2794
- import { styled as styled20 } from "goober";
2795
-
2796
- // src/inspector/utils/history.ts
2797
- function restoreCoMapToTimestamp(coValue, timestamp, removeUnknownProperties) {
2798
- const myRole = coValue.group.myRole();
2799
- if (myRole === void 0 || !["admin", "manager", "writer", "writerOnly"].includes(myRole)) {
2800
- return;
2801
- }
2802
- const newCoValue = coValue.atTime(timestamp).toJSON();
2803
- const oldCoValue = coValue.toJSON();
2804
- if (newCoValue === null) return;
2805
- let changes = [];
2806
- if (removeUnknownProperties) {
2807
- for (const key in oldCoValue) {
2808
- if (!(key in newCoValue)) {
2809
- changes.push({
2810
- op: "del",
2811
- key
2812
- });
2813
- }
2814
- }
2815
- }
2816
- for (const key in newCoValue) {
2817
- if (newCoValue[key] !== oldCoValue[key]) {
2818
- changes.push({
2819
- op: "set",
2820
- key,
2821
- value: newCoValue[key]
2822
- });
2823
- }
2824
- }
2825
- if (changes.length > 0) {
2826
- coValue.core.makeTransaction(changes, "private");
2827
- }
2828
- }
2829
-
2830
- // src/inspector/viewer/co-map-view.tsx
2981
+ import { useState as useState13, useMemo as useMemo4 } from "react";
2982
+ import { styled as styled21 } from "goober";
2831
2983
  import { Fragment as Fragment11, jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
2832
2984
  function CoMapView({
2833
2985
  coValue,
@@ -2864,8 +3016,8 @@ function AddPropertyModal({
2864
3016
  node,
2865
3017
  disabled
2866
3018
  }) {
2867
- const [isAddPropertyModalOpen, setIsAddPropertyModalOpen] = useState12(false);
2868
- const [propertyName, setPropertyName] = useState12("");
3019
+ const [isAddPropertyModalOpen, setIsAddPropertyModalOpen] = useState13(false);
3020
+ const [propertyName, setPropertyName] = useState13("");
2869
3021
  const openAddPropertyModal = () => {
2870
3022
  setIsAddPropertyModalOpen(true);
2871
3023
  setPropertyName("");
@@ -2918,9 +3070,9 @@ function AddPropertyModal({
2918
3070
  ] });
2919
3071
  }
2920
3072
  function RestoreSnapshotModal({ coValue }) {
2921
- const [isRestoreModalOpen, setIsRestoreModalOpen] = useState12(false);
2922
- const [selectedIndex, setSelectedIndex] = useState12(-1);
2923
- const [removeUnknownProperties, setRemoveUnknownProperties] = useState12(false);
3073
+ const [isRestoreModalOpen, setIsRestoreModalOpen] = useState13(false);
3074
+ const [selectedIndex, setSelectedIndex] = useState13(-1);
3075
+ const [removeUnknownProperties, setRemoveUnknownProperties] = useState13(false);
2924
3076
  const timestamps = useMemo4(
2925
3077
  () => coValue.core.verifiedTransactions.map((tx) => tx.madeAt),
2926
3078
  [coValue.core.verifiedTransactions.length]
@@ -3002,15 +3154,15 @@ function RestoreSnapshotModal({ coValue }) {
3002
3154
  )
3003
3155
  ] });
3004
3156
  }
3005
- var PreviewSection = styled20("div")`
3157
+ var PreviewSection = styled21("div")`
3006
3158
  margin-top: 1.5rem;
3007
3159
  `;
3008
- var PreviewLabel = styled20("div")`
3160
+ var PreviewLabel = styled21("div")`
3009
3161
  font-weight: 500;
3010
3162
  margin-bottom: 0.5rem;
3011
3163
  color: var(--j-text-color-strong);
3012
3164
  `;
3013
- var PreviewPre = styled20("pre")`
3165
+ var PreviewPre = styled21("pre")`
3014
3166
  background-color: var(--j-foreground);
3015
3167
  border: 1px solid var(--j-border-color);
3016
3168
  border-radius: var(--j-radius-md);
@@ -3022,17 +3174,17 @@ var PreviewPre = styled20("pre")`
3022
3174
  color: var(--j-text-color);
3023
3175
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
3024
3176
  `;
3025
- var RangeContainer = styled20("div")`
3177
+ var RangeContainer = styled21("div")`
3026
3178
  display: flex;
3027
3179
  flex-direction: column;
3028
3180
  gap: 0.75rem;
3029
3181
  `;
3030
- var RangeLabel = styled20("label")`
3182
+ var RangeLabel = styled21("label")`
3031
3183
  font-weight: 500;
3032
3184
  color: var(--j-text-color-strong);
3033
3185
  font-size: 0.875rem;
3034
3186
  `;
3035
- var RangeInput = styled20("input")`
3187
+ var RangeInput = styled21("input")`
3036
3188
  width: 100%;
3037
3189
  height: 0.5rem;
3038
3190
  border-radius: var(--j-radius-sm);
@@ -3069,7 +3221,7 @@ var RangeInput = styled20("input")`
3069
3221
  cursor: not-allowed;
3070
3222
  }
3071
3223
  `;
3072
- var TimestampDisplay = styled20("div")`
3224
+ var TimestampDisplay = styled21("div")`
3073
3225
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
3074
3226
  font-size: 0.875rem;
3075
3227
  color: var(--j-text-color);
@@ -3079,26 +3231,26 @@ var TimestampDisplay = styled20("div")`
3079
3231
  border-radius: var(--j-radius-md);
3080
3232
  text-align: center;
3081
3233
  `;
3082
- var CheckboxContainer = styled20("div")`
3234
+ var CheckboxContainer = styled21("div")`
3083
3235
  display: flex;
3084
3236
  align-items: flex-start;
3085
3237
  gap: 0.5rem;
3086
3238
  margin-top: 1rem;
3087
3239
  `;
3088
- var CheckboxInput = styled20("input")`
3240
+ var CheckboxInput = styled21("input")`
3089
3241
  width: 1rem;
3090
3242
  height: 1rem;
3091
3243
  margin-top: 0.125rem;
3092
3244
  cursor: pointer;
3093
3245
  accent-color: var(--j-primary-color);
3094
3246
  `;
3095
- var CheckboxLabel = styled20("label")`
3247
+ var CheckboxLabel = styled21("label")`
3096
3248
  font-size: 0.875rem;
3097
3249
  color: var(--j-text-color);
3098
3250
  cursor: pointer;
3099
3251
  line-height: 1.25rem;
3100
3252
  `;
3101
- var EditorContainer = styled20("div")`
3253
+ var EditorContainer = styled21("div")`
3102
3254
  margin-top: 1rem;
3103
3255
  `;
3104
3256
 
@@ -3107,7 +3259,7 @@ import { Fragment as Fragment12, jsx as jsx34, jsxs as jsxs22 } from "react/jsx-
3107
3259
  var BasePageContainer = React5.forwardRef(
3108
3260
  ({ isTopLevel, ...rest }, ref) => /* @__PURE__ */ jsx34("div", { ref, ...rest })
3109
3261
  );
3110
- var PageContainer = styled21(BasePageContainer)`
3262
+ var PageContainer = styled22(BasePageContainer)`
3111
3263
  position: absolute;
3112
3264
  z-index: 10;
3113
3265
  inset: 0;
@@ -3115,36 +3267,36 @@ var PageContainer = styled21(BasePageContainer)`
3115
3267
  height: 100%;
3116
3268
  padding: 0 0.75rem;
3117
3269
  `;
3118
- var BackButton = styled21("div")`
3270
+ var BackButton = styled22("div")`
3119
3271
  position: absolute;
3120
3272
  left: 0;
3121
3273
  right: 0;
3122
3274
  top: 0;
3123
3275
  height: 2.5rem;
3124
3276
  `;
3125
- var HeaderContainer = styled21("div")`
3277
+ var HeaderContainer = styled22("div")`
3126
3278
  display: flex;
3127
3279
  justify-content: space-between;
3128
3280
  align-items: center;
3129
3281
  margin-bottom: 1rem;
3130
3282
  `;
3131
- var TitleContainer = styled21("div")`
3283
+ var TitleContainer = styled22("div")`
3132
3284
  display: flex;
3133
3285
  align-items: center;
3134
3286
  gap: 0.75rem;
3135
3287
  `;
3136
- var Title = styled21(Heading)`
3288
+ var Title = styled22(Heading)`
3137
3289
  display: flex;
3138
3290
  flex-direction: column;
3139
3291
  align-items: flex-start;
3140
3292
  gap: 0.25rem;
3141
3293
  `;
3142
- var BadgeContainer = styled21("div")`
3294
+ var BadgeContainer = styled22("div")`
3143
3295
  display: flex;
3144
3296
  align-items: center;
3145
3297
  gap: 0.75rem;
3146
3298
  `;
3147
- var ContentContainer = styled21("div")`
3299
+ var ContentContainer = styled22("div")`
3148
3300
  overflow: auto;
3149
3301
  display: flex;
3150
3302
  flex-direction: column;
@@ -3190,7 +3342,14 @@ function View(props) {
3190
3342
  return /* @__PURE__ */ jsx34(AccountView, { data: snapshot, node, onNavigate });
3191
3343
  }
3192
3344
  if (type === "coplaintext") {
3193
- return /* @__PURE__ */ jsx34(CoPlainTextView, { data: snapshot });
3345
+ return /* @__PURE__ */ jsx34(
3346
+ CoPlainTextView,
3347
+ {
3348
+ data: snapshot,
3349
+ coValue: value,
3350
+ node
3351
+ }
3352
+ );
3194
3353
  }
3195
3354
  if (type === "colist") {
3196
3355
  const handleRemove = (index) => {
@@ -3295,7 +3454,7 @@ function Page(props) {
3295
3454
 
3296
3455
  // src/inspector/ui/error-boundary.tsx
3297
3456
  import React6 from "react";
3298
- import { styled as styled22 } from "goober";
3457
+ import { styled as styled23 } from "goober";
3299
3458
  import { jsx as jsx35, jsxs as jsxs23 } from "react/jsx-runtime";
3300
3459
  var ErrorBoundary = class extends React6.Component {
3301
3460
  constructor(props) {
@@ -3319,7 +3478,7 @@ var ErrorBoundary = class extends React6.Component {
3319
3478
  return this.props.children;
3320
3479
  }
3321
3480
  };
3322
- var StyledHeading2 = styled22("h1")`
3481
+ var StyledHeading2 = styled23("h1")`
3323
3482
  font-size: 1.125rem;
3324
3483
  font-weight: 500;
3325
3484
  color: var(--j-text-color-strong);
@@ -3327,7 +3486,7 @@ var StyledHeading2 = styled22("h1")`
3327
3486
 
3328
3487
  // src/inspector/viewer/page-stack.tsx
3329
3488
  import { Fragment as Fragment13, jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
3330
- var PageStackContainer = styled23("div")`
3489
+ var PageStackContainer = styled24("div")`
3331
3490
  position: relative;
3332
3491
  padding: 0 0.75rem;
3333
3492
  overflow-y: auto;
@@ -3361,10 +3520,10 @@ function PageStack({
3361
3520
  }
3362
3521
 
3363
3522
  // src/inspector/viewer/use-page-path.ts
3364
- import { useCallback, useEffect as useEffect8, useState as useState13 } from "react";
3523
+ import { useCallback, useEffect as useEffect8, useState as useState14 } from "react";
3365
3524
  var STORAGE_KEY = "jazz-inspector-paths";
3366
3525
  function usePagePath(defaultPath) {
3367
- const [path, setPath] = useState13(() => {
3526
+ const [path, setPath] = useState14(() => {
3368
3527
  if (typeof window === "undefined") return [];
3369
3528
  const stored = localStorage.getItem(STORAGE_KEY);
3370
3529
  if (stored) {
@@ -3418,8 +3577,8 @@ function usePagePath(defaultPath) {
3418
3577
  }
3419
3578
 
3420
3579
  // src/inspector/ui/global-styles.tsx
3421
- import { styled as styled24 } from "goober";
3422
- var GlobalStyles = styled24("div")`
3580
+ import { styled as styled25 } from "goober";
3581
+ var GlobalStyles = styled25("div")`
3423
3582
  /* Colors */
3424
3583
  --j-primary-color: #146AFF;
3425
3584
  --j-link-color: var(--j-primary-color);
@@ -3493,9 +3652,9 @@ var GlobalStyles = styled24("div")`
3493
3652
  `;
3494
3653
 
3495
3654
  // src/inspector/viewer/inspector-button.tsx
3496
- import { styled as styled25 } from "goober";
3655
+ import { styled as styled26 } from "goober";
3497
3656
  import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
3498
- var StyledInspectorButton = styled25("button")`
3657
+ var StyledInspectorButton = styled26("button")`
3499
3658
  position: fixed;
3500
3659
  width: 2.5rem;
3501
3660
  height: 2.5rem;
@@ -3526,7 +3685,7 @@ var StyledInspectorButton = styled25("button")`
3526
3685
  }
3527
3686
  }}
3528
3687
  `;
3529
- var JazzIcon = styled25("svg")`
3688
+ var JazzIcon = styled26("svg")`
3530
3689
  width: 100%;
3531
3690
  height: auto;
3532
3691
  position: relative;
@@ -3578,10 +3737,10 @@ function InspectorButton({
3578
3737
  }
3579
3738
 
3580
3739
  // src/inspector/viewer/use-open-inspector.ts
3581
- import { useEffect as useEffect9, useState as useState14 } from "react";
3740
+ import { useEffect as useEffect9, useState as useState15 } from "react";
3582
3741
  var STORAGE_KEY2 = "jazz-inspector-open";
3583
3742
  function useOpenInspector() {
3584
- const [open, setOpen] = useState14(() => {
3743
+ const [open, setOpen] = useState15(() => {
3585
3744
  if (typeof window === "undefined") return false;
3586
3745
  const stored = localStorage.getItem(STORAGE_KEY2);
3587
3746
  return stored ? JSON.parse(stored) : false;
@@ -3593,12 +3752,12 @@ function useOpenInspector() {
3593
3752
  }
3594
3753
 
3595
3754
  // src/inspector/viewer/delete-local-data.tsx
3596
- import { useState as useState15 } from "react";
3755
+ import { useState as useState16 } from "react";
3597
3756
  import { Fragment as Fragment14, jsx as jsx38, jsxs as jsxs26 } from "react/jsx-runtime";
3598
3757
  var DELETE_LOCAL_DATA_STRING = "delete my local data";
3599
3758
  function DeleteLocalData() {
3600
- const [showDeleteModal, setShowDeleteModal] = useState15(false);
3601
- const [confirmDeleteString, setConfirmDeleteString] = useState15("");
3759
+ const [showDeleteModal, setShowDeleteModal] = useState16(false);
3760
+ const [confirmDeleteString, setConfirmDeleteString] = useState16("");
3602
3761
  return /* @__PURE__ */ jsxs26(Fragment14, { children: [
3603
3762
  /* @__PURE__ */ jsx38(Button, { variant: "destructive", onClick: () => setShowDeleteModal(true), children: "Delete my local data" }),
3604
3763
  /* @__PURE__ */ jsxs26(
@@ -3713,7 +3872,7 @@ function DeleteLocalData() {
3713
3872
 
3714
3873
  // src/inspector/viewer/new-app.tsx
3715
3874
  import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
3716
- var InspectorContainer = styled26("div")`
3875
+ var InspectorContainer = styled27("div")`
3717
3876
  position: fixed;
3718
3877
  height: 50vh;
3719
3878
  max-height: 800px;
@@ -3730,17 +3889,17 @@ var InspectorContainer = styled26("div")`
3730
3889
  background-color: var(--j-background);
3731
3890
  }
3732
3891
  `;
3733
- var HeaderContainer2 = styled26("div")`
3892
+ var HeaderContainer2 = styled27("div")`
3734
3893
  display: flex;
3735
3894
  align-items: center;
3736
3895
  gap: 1rem;
3737
3896
  padding: 0 0.75rem;
3738
3897
  margin: 0.75rem 0;
3739
3898
  `;
3740
- var Form = styled26("form")`
3899
+ var Form = styled27("form")`
3741
3900
  width: 24rem;
3742
3901
  `;
3743
- var InitialForm = styled26("form")`
3902
+ var InitialForm = styled27("form")`
3744
3903
  display: flex;
3745
3904
  flex-direction: column;
3746
3905
  position: relative;
@@ -3752,7 +3911,7 @@ var InitialForm = styled26("form")`
3752
3911
  max-width: 24rem;
3753
3912
  margin: 0 auto;
3754
3913
  `;
3755
- var OrText = styled26("p")`
3914
+ var OrText = styled27("p")`
3756
3915
  text-align: center;
3757
3916
  `;
3758
3917
  function JazzInspectorInternal({
@@ -3761,7 +3920,7 @@ function JazzInspectorInternal({
3761
3920
  accountId
3762
3921
  }) {
3763
3922
  const [open, setOpen] = useOpenInspector();
3764
- const [coValueId, setCoValueId] = useState16("");
3923
+ const [coValueId, setCoValueId] = useState17("");
3765
3924
  const { path, addPages, goToIndex, goBack, setPage } = usePagePath();
3766
3925
  const handleCoValueIdSubmit = (e) => {
3767
3926
  e.preventDefault();