@buoy-gg/shared-ui 7.0.34 → 7.0.36
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.
- package/lib/commonjs/dataViewer/CyberpunkInput.js +3 -2
- package/lib/commonjs/dataViewer/DataTreeActionBar.js +3 -2
- package/lib/commonjs/dataViewer/DataTreeActionDock.js +2 -1
- package/lib/commonjs/dataViewer/DiffSummary.js +4 -3
- package/lib/commonjs/dataViewer/LiveExplorer.js +11 -10
- package/lib/commonjs/dataViewer/SplitDiffViewer.js +10 -9
- package/lib/commonjs/dataViewer/ThemedSplitView.js +12 -11
- package/lib/commonjs/dataViewer/ValueEditModal.js +6 -5
- package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +4 -3
- package/lib/commonjs/dataViewer/tree/TreeDiffViewer.js +15 -14
- package/lib/commonjs/hooks/safe-area-impl.js +1 -1
- package/lib/commonjs/icons/index.js +540 -1
- package/lib/commonjs/index.js +48 -0
- package/lib/commonjs/license/FreeAccountPrompt.js +2 -1
- package/lib/commonjs/license/LicenseEntryModal.js +2 -1
- package/lib/commonjs/sync/keyedArrays.js +209 -0
- package/lib/commonjs/sync/toolAttention.test.js +51 -0
- package/lib/commonjs/sync/toolUiBridge.js +58 -0
- package/lib/commonjs/sync/typedEdit.js +228 -0
- package/lib/commonjs/sync/valuePath.js +337 -0
- package/lib/commonjs/sync/valueShape.js +161 -0
- package/lib/commonjs/ui/components/ChangeRow.js +2 -1
- package/lib/commonjs/ui/components/ClipboardHintBanner.js +2 -1
- package/lib/commonjs/ui/components/CompactRow.js +3 -2
- package/lib/commonjs/ui/components/DataInspector.js +3 -2
- package/lib/commonjs/ui/components/DetailView.js +2 -1
- package/lib/commonjs/ui/components/DynamicFilterView.js +8 -7
- package/lib/commonjs/ui/components/EntityFilterView.js +9 -8
- package/lib/commonjs/ui/components/EventHistoryViewer/CompareBar.js +5 -4
- package/lib/commonjs/ui/components/EventHistoryViewer/DiffModeTabs.js +2 -1
- package/lib/commonjs/ui/components/EventHistoryViewer/EventPickerModal.js +5 -4
- package/lib/commonjs/ui/components/EventStepperFooter.js +4 -3
- package/lib/commonjs/ui/components/ExpandablePopover.js +48 -8
- package/lib/commonjs/ui/components/ExpandedInfoRow.js +3 -2
- package/lib/commonjs/ui/components/FilterComponents.js +2 -1
- package/lib/commonjs/ui/components/InfoRow.js +2 -1
- package/lib/commonjs/ui/components/Pill.js +2 -1
- package/lib/commonjs/ui/components/ToolFooter.js +3 -2
- package/lib/commonjs/ui/console/CyberpunkSectionButton.js +3 -2
- package/lib/commonjs/ui/fonts.js +12 -0
- package/lib/commonjs/ui/gameUI/components/GameUICollapsibleSection.js +4 -3
- package/lib/commonjs/ui/gameUI/components/GameUICompactStats.js +12 -11
- package/lib/commonjs/ui/gameUI/components/GameUIIssuesList.js +9 -8
- package/lib/commonjs/ui/gameUI/components/GameUIStatusHeader.js +4 -3
- package/lib/commonjs/ui/index.js +8 -0
- package/lib/commonjs/ui/toolTheme.js +4 -3
- package/lib/module/dataViewer/CyberpunkInput.js +3 -2
- package/lib/module/dataViewer/DataTreeActionBar.js +3 -2
- package/lib/module/dataViewer/DataTreeActionDock.js +2 -1
- package/lib/module/dataViewer/DiffSummary.js +4 -3
- package/lib/module/dataViewer/LiveExplorer.js +11 -11
- package/lib/module/dataViewer/SplitDiffViewer.js +10 -9
- package/lib/module/dataViewer/ThemedSplitView.js +12 -11
- package/lib/module/dataViewer/ValueEditModal.js +6 -5
- package/lib/module/dataViewer/VirtualizedDataExplorer.js +5 -4
- package/lib/module/dataViewer/tree/TreeDiffViewer.js +15 -15
- package/lib/module/hooks/safe-area-impl.js +1 -1
- package/lib/module/icons/index.js +29 -2
- package/lib/module/index.js +4 -0
- package/lib/module/license/FreeAccountPrompt.js +2 -2
- package/lib/module/license/LicenseEntryModal.js +3 -3
- package/lib/module/sync/keyedArrays.js +204 -0
- package/lib/module/sync/toolAttention.test.js +50 -0
- package/lib/module/sync/toolUiBridge.js +55 -0
- package/lib/module/sync/typedEdit.js +220 -0
- package/lib/module/sync/valuePath.js +330 -0
- package/lib/module/sync/valueShape.js +153 -0
- package/lib/module/ui/components/ChangeRow.js +2 -1
- package/lib/module/ui/components/ClipboardHintBanner.js +2 -1
- package/lib/module/ui/components/CompactRow.js +3 -2
- package/lib/module/ui/components/DataInspector.js +3 -2
- package/lib/module/ui/components/DetailView.js +2 -1
- package/lib/module/ui/components/DynamicFilterView.js +8 -7
- package/lib/module/ui/components/EntityFilterView.js +9 -8
- package/lib/module/ui/components/EventHistoryViewer/CompareBar.js +6 -5
- package/lib/module/ui/components/EventHistoryViewer/DiffModeTabs.js +3 -2
- package/lib/module/ui/components/EventHistoryViewer/EventPickerModal.js +6 -5
- package/lib/module/ui/components/EventStepperFooter.js +4 -3
- package/lib/module/ui/components/ExpandablePopover.js +48 -8
- package/lib/module/ui/components/ExpandedInfoRow.js +3 -2
- package/lib/module/ui/components/FilterComponents.js +2 -2
- package/lib/module/ui/components/InfoRow.js +2 -1
- package/lib/module/ui/components/Pill.js +2 -1
- package/lib/module/ui/components/ToolFooter.js +3 -2
- package/lib/module/ui/console/CyberpunkSectionButton.js +3 -2
- package/lib/module/ui/fonts.js +8 -0
- package/lib/module/ui/gameUI/components/GameUICollapsibleSection.js +4 -3
- package/lib/module/ui/gameUI/components/GameUICompactStats.js +12 -11
- package/lib/module/ui/gameUI/components/GameUIIssuesList.js +9 -8
- package/lib/module/ui/gameUI/components/GameUIStatusHeader.js +4 -3
- package/lib/module/ui/index.js +3 -0
- package/lib/module/ui/toolTheme.js +4 -4
- package/lib/typescript/commonjs/dataViewer/CyberpunkInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/DataTreeActionBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/DataTreeActionDock.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/DiffSummary.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/LiveExplorer.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/SplitDiffViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/ValueEditModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
- package/lib/typescript/commonjs/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
- package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/commonjs/icons/index.d.ts +4 -1
- package/lib/typescript/commonjs/icons/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts.map +1 -1
- package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/sync/keyedArrays.d.ts +35 -0
- package/lib/typescript/commonjs/sync/keyedArrays.d.ts.map +1 -0
- package/lib/typescript/commonjs/sync/toolAttention.test.d.ts +2 -0
- package/lib/typescript/commonjs/sync/toolAttention.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/sync/toolUiBridge.d.ts +25 -0
- package/lib/typescript/commonjs/sync/toolUiBridge.d.ts.map +1 -1
- package/lib/typescript/commonjs/sync/typedEdit.d.ts +70 -0
- package/lib/typescript/commonjs/sync/typedEdit.d.ts.map +1 -0
- package/lib/typescript/commonjs/sync/valuePath.d.ts +52 -0
- package/lib/typescript/commonjs/sync/valuePath.d.ts.map +1 -0
- package/lib/typescript/commonjs/sync/valueShape.d.ts +77 -0
- package/lib/typescript/commonjs/sync/valueShape.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/ChangeRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ClipboardHintBanner.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/CompactRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/DataInspector.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/DetailView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/DynamicFilterView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EntityFilterView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EventHistoryViewer/CompareBar.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EventHistoryViewer/DiffModeTabs.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EventHistoryViewer/EventPickerModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/EventStepperFooter.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts +18 -1
- package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ExpandedInfoRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/FilterComponents.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/InfoRow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/Pill.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ToolFooter.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/console/CyberpunkSectionButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/fonts.d.ts +7 -0
- package/lib/typescript/commonjs/ui/fonts.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/gameUI/components/GameUICollapsibleSection.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/gameUI/components/GameUICompactStats.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/gameUI/components/GameUIIssuesList.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/gameUI/components/GameUIStatusHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -0
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/toolTheme.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/CyberpunkInput.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/DataTreeActionBar.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/DataTreeActionDock.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/DiffSummary.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/LiveExplorer.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/SplitDiffViewer.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/ValueEditModal.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
- package/lib/typescript/module/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
- package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
- package/lib/typescript/module/icons/index.d.ts +4 -1
- package/lib/typescript/module/icons/index.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/license/FreeAccountPrompt.d.ts.map +1 -1
- package/lib/typescript/module/license/LicenseEntryModal.d.ts.map +1 -1
- package/lib/typescript/module/sync/keyedArrays.d.ts +35 -0
- package/lib/typescript/module/sync/keyedArrays.d.ts.map +1 -0
- package/lib/typescript/module/sync/toolAttention.test.d.ts +2 -0
- package/lib/typescript/module/sync/toolAttention.test.d.ts.map +1 -0
- package/lib/typescript/module/sync/toolUiBridge.d.ts +25 -0
- package/lib/typescript/module/sync/toolUiBridge.d.ts.map +1 -1
- package/lib/typescript/module/sync/typedEdit.d.ts +70 -0
- package/lib/typescript/module/sync/typedEdit.d.ts.map +1 -0
- package/lib/typescript/module/sync/valuePath.d.ts +52 -0
- package/lib/typescript/module/sync/valuePath.d.ts.map +1 -0
- package/lib/typescript/module/sync/valueShape.d.ts +77 -0
- package/lib/typescript/module/sync/valueShape.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/ChangeRow.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ClipboardHintBanner.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/CompactRow.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/DataInspector.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/DetailView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/DynamicFilterView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EntityFilterView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EventHistoryViewer/CompareBar.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EventHistoryViewer/DiffModeTabs.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EventHistoryViewer/EventPickerModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/EventStepperFooter.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ExpandablePopover.d.ts +18 -1
- package/lib/typescript/module/ui/components/ExpandablePopover.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ExpandedInfoRow.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/FilterComponents.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/InfoRow.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/Pill.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ToolFooter.d.ts.map +1 -1
- package/lib/typescript/module/ui/console/CyberpunkSectionButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/fonts.d.ts +7 -0
- package/lib/typescript/module/ui/fonts.d.ts.map +1 -0
- package/lib/typescript/module/ui/gameUI/components/GameUICollapsibleSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/gameUI/components/GameUICompactStats.d.ts.map +1 -1
- package/lib/typescript/module/ui/gameUI/components/GameUIIssuesList.d.ts.map +1 -1
- package/lib/typescript/module/ui/gameUI/components/GameUIStatusHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -0
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/toolTheme.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -19,7 +19,7 @@ import React, { useMemo, useState, useEffect, useRef } from "react";
|
|
|
19
19
|
import { View, Text, ScrollView, StyleSheet, TouchableOpacity } from "react-native";
|
|
20
20
|
import { gameUIColors, night } from "@buoy-gg/shared-ui";
|
|
21
21
|
import { isAbsent } from "../lineDiff.js";
|
|
22
|
-
|
|
22
|
+
import { monoFont } from "../../ui/fonts.js";
|
|
23
23
|
// ============================================
|
|
24
24
|
// TYPES & INTERFACES
|
|
25
25
|
// ============================================
|
|
@@ -785,16 +785,16 @@ const styles = StyleSheet.create({
|
|
|
785
785
|
summaryIcon: {
|
|
786
786
|
fontSize: 14,
|
|
787
787
|
fontWeight: "700",
|
|
788
|
-
fontFamily:
|
|
788
|
+
fontFamily: monoFont
|
|
789
789
|
},
|
|
790
790
|
summaryCount: {
|
|
791
791
|
fontSize: 13,
|
|
792
792
|
fontWeight: "600",
|
|
793
|
-
fontFamily:
|
|
793
|
+
fontFamily: monoFont
|
|
794
794
|
},
|
|
795
795
|
summaryLabel: {
|
|
796
796
|
fontSize: 11,
|
|
797
|
-
fontFamily:
|
|
797
|
+
fontFamily: monoFont,
|
|
798
798
|
opacity: 0.9
|
|
799
799
|
},
|
|
800
800
|
scrollView: {
|
|
@@ -818,7 +818,7 @@ const styles = StyleSheet.create({
|
|
|
818
818
|
},
|
|
819
819
|
lineNumberText: {
|
|
820
820
|
fontSize: 11,
|
|
821
|
-
fontFamily:
|
|
821
|
+
fontFamily: monoFont,
|
|
822
822
|
textAlign: "right"
|
|
823
823
|
},
|
|
824
824
|
marker: {
|
|
@@ -830,7 +830,7 @@ const styles = StyleSheet.create({
|
|
|
830
830
|
},
|
|
831
831
|
markerText: {
|
|
832
832
|
fontSize: 12,
|
|
833
|
-
fontFamily:
|
|
833
|
+
fontFamily: monoFont,
|
|
834
834
|
fontWeight: "600"
|
|
835
835
|
},
|
|
836
836
|
content: {
|
|
@@ -851,33 +851,33 @@ const styles = StyleSheet.create({
|
|
|
851
851
|
},
|
|
852
852
|
expandIcon: {
|
|
853
853
|
fontSize: 11,
|
|
854
|
-
fontFamily:
|
|
854
|
+
fontFamily: monoFont,
|
|
855
855
|
fontWeight: "600"
|
|
856
856
|
},
|
|
857
857
|
colon: {
|
|
858
858
|
fontSize: 12,
|
|
859
|
-
fontFamily:
|
|
859
|
+
fontFamily: monoFont,
|
|
860
860
|
marginHorizontal: 4
|
|
861
861
|
},
|
|
862
862
|
key: {
|
|
863
863
|
fontSize: 11,
|
|
864
|
-
fontFamily:
|
|
864
|
+
fontFamily: monoFont,
|
|
865
865
|
fontWeight: "500",
|
|
866
866
|
opacity: 0.9
|
|
867
867
|
},
|
|
868
868
|
value: {
|
|
869
869
|
fontSize: 11,
|
|
870
|
-
fontFamily:
|
|
870
|
+
fontFamily: monoFont,
|
|
871
871
|
maxWidth: "80%"
|
|
872
872
|
},
|
|
873
873
|
arrow: {
|
|
874
874
|
fontSize: 12,
|
|
875
|
-
fontFamily:
|
|
875
|
+
fontFamily: monoFont,
|
|
876
876
|
fontWeight: "600"
|
|
877
877
|
},
|
|
878
878
|
badge: {
|
|
879
879
|
fontSize: 11,
|
|
880
|
-
fontFamily:
|
|
880
|
+
fontFamily: monoFont,
|
|
881
881
|
fontWeight: "600",
|
|
882
882
|
marginLeft: 8
|
|
883
883
|
},
|
|
@@ -889,19 +889,19 @@ const styles = StyleSheet.create({
|
|
|
889
889
|
},
|
|
890
890
|
emptyIcon: {
|
|
891
891
|
fontSize: 48,
|
|
892
|
-
fontFamily:
|
|
892
|
+
fontFamily: monoFont,
|
|
893
893
|
opacity: 0.2,
|
|
894
894
|
marginBottom: 12
|
|
895
895
|
},
|
|
896
896
|
emptyTitle: {
|
|
897
897
|
fontSize: 14,
|
|
898
|
-
fontFamily:
|
|
898
|
+
fontFamily: monoFont,
|
|
899
899
|
fontWeight: "600",
|
|
900
900
|
marginBottom: 4
|
|
901
901
|
},
|
|
902
902
|
emptySubtitle: {
|
|
903
903
|
fontSize: 12,
|
|
904
|
-
fontFamily:
|
|
904
|
+
fontFamily: monoFont,
|
|
905
905
|
opacity: 0.6
|
|
906
906
|
}
|
|
907
907
|
});
|
|
@@ -28,8 +28,35 @@ export { WifiCircuitIcon } from "./WifiCircuitIcon.js";
|
|
|
28
28
|
export { RouteMapIcon, RouteIcon, NavigationIcon } from "./RouteMapIcon.js";
|
|
29
29
|
export { StackPulseIcon, RenderPulseIcon, HighlightUpdatesIcon } from "./StackPulseIcon.js";
|
|
30
30
|
export { RenderCountIcon } from "./RenderCountIcon.js";
|
|
31
|
-
|
|
31
|
+
// The hand-drawn stopwatch is legacy; `BenchmarkIcon` is the BIF artwork (same on every port).
|
|
32
|
+
export { BenchmarkIcon as BenchmarkLegacyIcon } from "./BenchmarkIcon.js";
|
|
32
33
|
export { IconBackground } from "./IconBackground.js";
|
|
33
34
|
|
|
34
35
|
// Export lucide icons
|
|
35
|
-
export * from "./lucide-icons.js";
|
|
36
|
+
export * from "./lucide-icons.js";
|
|
37
|
+
|
|
38
|
+
// The lucide glyph tier, drawn from shared/icons/glyphs/*.json — the SAME data
|
|
39
|
+
// the Flutter and Swift renderers read. React Native used to carry a second,
|
|
40
|
+
// hand-drawn copy of these icons (`./lucide-icons`), so `Globe` was one drawing
|
|
41
|
+
// on RN and another on every port; the cross-framework parity sheet measured
|
|
42
|
+
// `icons/globe-24` 10.8 % apart on both. These explicit exports shadow the
|
|
43
|
+
// `export * from "./lucide-icons"` above (the same trick `WifiBifIcon` uses),
|
|
44
|
+
// so one edit to a JSON now moves all three frameworks. The hand-drawn
|
|
45
|
+
// originals stay reachable as `<Name>Icon` from `./lucide-icons`.
|
|
46
|
+
//
|
|
47
|
+
// `Pin` is deliberately NOT here: RN's pin carries an extra `active` prop that
|
|
48
|
+
// stands it upright, which the glyph data has no way to express (the ports draw
|
|
49
|
+
// the resting pose only). `WifiIcon` stays lucide for the same kind of reason —
|
|
50
|
+
// see the `WifiBifIcon` note below.
|
|
51
|
+
export { ActivityGlyph as Activity, AlertCircleGlyph as AlertCircle, AlertTriangleGlyph as AlertTriangle, BookmarkGlyph as Bookmark, BoxGlyph as Box, BugGlyph as Bug, CheckCircleGlyph as CheckCircle, CheckGlyph as Check, ChevronDownGlyph as ChevronDown, ChevronLeftGlyph as ChevronLeft, ChevronRightGlyph as ChevronRight, ChevronUpGlyph as ChevronUp, ClockGlyph as Clock, CopyGlyph as Copy, DatabaseGlyph as Database, DockBottomGlyph as DockBottom, DownloadGlyph as Download, Edit3Glyph as Edit3, EyeOffGlyph as EyeOff, EyeGlyph as Eye, FileJsonGlyph as FileJson, FileTextGlyph as FileText, FilmGlyph as Film, FilterGlyph as Filter, FlaskConicalGlyph as FlaskConical, FloatWindowGlyph as FloatWindow, GitBranchGlyph as GitBranch, GlobeGlyph as Globe, HashGlyph as Hash, HomeGlyph as Home, ImageGlyph as Image, InfoGlyph as Info, KeyGlyph as Key, LayersGlyph as Layers, LinkGlyph as Link, LockGlyph as Lock, MinusGlyph as Minus, MusicGlyph as Music, NavigationGlyph as Navigation, PaletteGlyph as Palette, PauseGlyph as Pause, PlayGlyph as Play, PlusGlyph as Plus, PowerGlyph as Power, RefreshCwGlyph as RefreshCw, SearchGlyph as Search, ServerGlyph as Server, SettingsGlyph as Settings,
|
|
52
|
+
// Solid gear (brand tier, so a caller's strokeWidth can't blow the hole open).
|
|
53
|
+
SettingsSolidIcon as SettingsSolid, ShieldGlyph as Shield, Trash2Glyph as Trash2,
|
|
54
|
+
// RN aliases `Trash` onto `Trash2Icon`; the glyph has to shadow both.
|
|
55
|
+
Trash2Glyph as Trash, UnlockGlyph as Unlock, UploadGlyph as Upload, UserGlyph as User, WifiGlyph as Wifi, XCircleGlyph as XCircle, XGlyph as X, ZapGlyph as Zap } from "@buoy-gg/floating-tools-core";
|
|
56
|
+
|
|
57
|
+
// Every BIF brand icon the dial can show, re-exported so the parity sheet (and tools) render
|
|
58
|
+
// the same artwork by name — see shared/parity/specimens.json `icons/bif-*`.
|
|
59
|
+
export { SentryIcon, EventsIcon, RoutesIcon, RoutePinIcon, RoutePathIcon, RouteDotIcon, RouteTwoPinsIcon, CameraIcon, HighlightIcon, ReduxIcon, StorageIcon, AskBuoyIcon, HighlighterIcon, QueryIcon, ScenariosIcon } from "@buoy-gg/floating-tools-core";
|
|
60
|
+
// `WifiIcon` here stays the lucide glyph (react-query's online toggle); the BIF wifi ships as
|
|
61
|
+
// `WifiBifIcon`. `BenchmarkIcon` IS the BIF stopwatch since 2026-09-04 (perf-monitor's dial icon).
|
|
62
|
+
export { WifiIcon as WifiBifIcon, BenchmarkIcon, BENCHMARK_ICON_COLOR } from "@buoy-gg/floating-tools-core";
|
package/lib/module/index.js
CHANGED
|
@@ -5,6 +5,10 @@ export * from "./ui/index.js";
|
|
|
5
5
|
|
|
6
6
|
// Store base classes
|
|
7
7
|
export * from "./stores/index.js";
|
|
8
|
+
export * from "./sync/keyedArrays.js";
|
|
9
|
+
export * from "./sync/valueShape.js";
|
|
10
|
+
export * from "./sync/valuePath.js";
|
|
11
|
+
export * from "./sync/typedEdit.js";
|
|
8
12
|
export * from "./sync/wireBudget.js";
|
|
9
13
|
export * from "./sync/wireForm.js";
|
|
10
14
|
export * from "./sync/toolSyncAdapter.js";
|
|
@@ -19,7 +19,7 @@ import { buoyColors } from "../ui/gameUI/constants/gameUIColors.js";
|
|
|
19
19
|
import { Check, Copy, X } from "../icons/lucide-icons.js";
|
|
20
20
|
import { copyToClipboard } from "../clipboard/index.js";
|
|
21
21
|
import { openPricing } from "./openPricing.js";
|
|
22
|
-
|
|
22
|
+
import { monoFont } from "../ui/fonts.js";
|
|
23
23
|
/** The one thing a reader has to be able to act on. */
|
|
24
24
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
25
25
|
export const LOGIN_COMMAND = "npx buoy login";
|
|
@@ -155,7 +155,7 @@ const styles = StyleSheet.create({
|
|
|
155
155
|
fontSize: 14,
|
|
156
156
|
fontWeight: "600",
|
|
157
157
|
color: buoyColors.text,
|
|
158
|
-
fontFamily:
|
|
158
|
+
fontFamily: monoFont
|
|
159
159
|
},
|
|
160
160
|
reassure: {
|
|
161
161
|
fontSize: 12,
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import React, { useCallback } from "react";
|
|
12
|
-
import { View, Text, TouchableOpacity, StyleSheet, Modal
|
|
12
|
+
import { View, Text, TouchableOpacity, StyleSheet, Modal } from "react-native";
|
|
13
13
|
import { absoluteFill } from "../utils/absoluteFill.js";
|
|
14
14
|
import { macOSColors } from "../ui/gameUI/constants/macOSDesignSystemColors.js";
|
|
15
15
|
import { openPricing, isWeb } from "./openPricing.js";
|
|
16
16
|
import { X, Key, Link, FileCode, Copy } from "../icons/lucide-icons.js";
|
|
17
|
-
|
|
17
|
+
import { monoFont } from "../ui/fonts.js";
|
|
18
18
|
// For clipboard functionality
|
|
19
19
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
20
|
let Clipboard = null;
|
|
@@ -339,7 +339,7 @@ const styles = StyleSheet.create({
|
|
|
339
339
|
},
|
|
340
340
|
codeText: {
|
|
341
341
|
fontSize: 11,
|
|
342
|
-
fontFamily:
|
|
342
|
+
fontFamily: monoFont,
|
|
343
343
|
lineHeight: 18,
|
|
344
344
|
color: macOSColors.text.primary
|
|
345
345
|
},
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Id-addressed list edits — one implementation, shared by every adapter that
|
|
5
|
+
* takes a merge patch.
|
|
6
|
+
*
|
|
7
|
+
* WHY. Resending a whole array is correct only when the caller has SEEN the
|
|
8
|
+
* whole array, and reads are capped. Measured with a 20-item list truncated to
|
|
9
|
+
* 5 (`pnpm ai:array --truncate=5`): the whole-array form scored 0/9 across three
|
|
10
|
+
* models — two refused outright, and one silently sent back only the 5 items it
|
|
11
|
+
* had been shown, destroying the other 15. Addressed edits scored 9/9.
|
|
12
|
+
*
|
|
13
|
+
* It also makes mass deletion impossible BY OMISSION: an addressed edit can only
|
|
14
|
+
* remove ids it names. Sending a real array still replaces the list, which is
|
|
15
|
+
* how a caller deliberately empties one.
|
|
16
|
+
*
|
|
17
|
+
* This lives here rather than in one adapter because react-query, zustand and
|
|
18
|
+
* network all take merge patches, and a second copy of a data-destroying safety
|
|
19
|
+
* rule is a copy that drifts.
|
|
20
|
+
*/
|
|
21
|
+
const isPlainObject = v => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
22
|
+
|
|
23
|
+
/** Fields we will treat as an item's stable identity, best first. */
|
|
24
|
+
const ID_FIELDS = ["id", "lineId", "key", "uuid", "_id", "slug", "name"];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The field that identifies items in this list, or undefined if there isn't one.
|
|
28
|
+
*
|
|
29
|
+
* Every item must carry it, and every value must be a distinct string/number —
|
|
30
|
+
* a duplicated id would make an addressed edit ambiguous, and silently picking
|
|
31
|
+
* the first match is exactly the kind of "helpful" guess that loses data.
|
|
32
|
+
*/
|
|
33
|
+
export function identityField(items) {
|
|
34
|
+
if (!items.length || !items.every(isPlainObject)) return undefined;
|
|
35
|
+
const first = items[0];
|
|
36
|
+
for (const f of ID_FIELDS) {
|
|
37
|
+
if (!(f in first)) continue;
|
|
38
|
+
const values = items.map(it => it[f]);
|
|
39
|
+
const usable = values.every(v => typeof v === "string" || typeof v === "number");
|
|
40
|
+
if (!usable) continue;
|
|
41
|
+
if (new Set(values.map(String)).size !== values.length) continue;
|
|
42
|
+
return f;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The identity an ADDED item should carry.
|
|
49
|
+
*
|
|
50
|
+
* `Object.entries` hands back STRING keys, so `{"9901": {id: 9901, …}}` was
|
|
51
|
+
* written as `{id: "9901"}` — the caller's own numeric id replaced by text, and
|
|
52
|
+
* then refused by the typed-edit guard as `[2].id: number → string`. The caller
|
|
53
|
+
* recovers by resending the WHOLE array, which is exactly the truncation-unsafe
|
|
54
|
+
* move this module exists to prevent. Measured on a live QA run: one added
|
|
55
|
+
* offer cost a refusal, a full-list retransmission and 35 seconds.
|
|
56
|
+
*
|
|
57
|
+
* So the id the caller WROTE wins. With no id in the body, the key is coerced
|
|
58
|
+
* to the type the existing items use, and only when the text round-trips
|
|
59
|
+
* exactly: "007" is not 7, and inventing that identity is worse than a refusal
|
|
60
|
+
* the caller can read.
|
|
61
|
+
*/
|
|
62
|
+
function identityForAdd(current, idField, key, edit) {
|
|
63
|
+
const supplied = edit[idField];
|
|
64
|
+
if (typeof supplied === "string" || typeof supplied === "number") {
|
|
65
|
+
// An item whose id disagrees with the address it was sent under is
|
|
66
|
+
// ambiguous — either could be the one the caller meant. Say so rather than
|
|
67
|
+
// silently picking one and writing an item nobody asked for.
|
|
68
|
+
if (String(supplied) !== key) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
error: `The new item is addressed as \`${key}\` but carries \`${idField}\`: ${JSON.stringify(supplied)}. Use the same value for both — address it by the id it carries.`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
ok: true,
|
|
76
|
+
value: supplied
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const numericList = current.every(it => typeof it[idField] === "number");
|
|
80
|
+
if (numericList && key.trim() !== "" && String(Number(key)) === key) {
|
|
81
|
+
return {
|
|
82
|
+
ok: true,
|
|
83
|
+
value: Number(key)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
value: key
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Let a caller address list items by their own id instead of resending the list.
|
|
94
|
+
*
|
|
95
|
+
* { lines: { "seed-1": { qty: 5 } } } change one field of one item
|
|
96
|
+
* { lines: { "seed-2": null } } remove that item
|
|
97
|
+
* { lines: { "new-1": { …all fields… } } } append (id not already present)
|
|
98
|
+
*
|
|
99
|
+
* WHY THIS EXISTS. Resending the whole array is correct only when the caller has
|
|
100
|
+
* SEEN the whole array, and reads are capped — so on a long list the honest
|
|
101
|
+
* agents refuse and the careless ones send back only the items they were shown,
|
|
102
|
+
* silently destroying the rest. Measured: with a 20-item bag truncated to 5, the
|
|
103
|
+
* whole-array form scored 0/9 across three models and one of them deleted 15
|
|
104
|
+
* lines without a word. Addressed edits scored 9/9. (`pnpm ai:array`.)
|
|
105
|
+
*
|
|
106
|
+
* It also makes mass deletion impossible BY OMISSION: `lines: []` empties a bag,
|
|
107
|
+
* but a keyed edit can only remove ids it names.
|
|
108
|
+
*
|
|
109
|
+
* Returns the patch with keyed objects expanded into real arrays, so everything
|
|
110
|
+
* downstream — the typed-edit guard, the receipt, the store write — is unchanged
|
|
111
|
+
* and keeps all of its protections.
|
|
112
|
+
*/
|
|
113
|
+
export function expandKeyedArrays(current, patch, path = "", notes = []) {
|
|
114
|
+
if (Array.isArray(current) && isPlainObject(patch)) {
|
|
115
|
+
const idField = identityField(current);
|
|
116
|
+
if (!idField) {
|
|
117
|
+
// No stable identity: leave it alone and let the normal guard report the
|
|
118
|
+
// object-vs-array type change, which says something truer than we could.
|
|
119
|
+
return {
|
|
120
|
+
patch,
|
|
121
|
+
notes
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const byId = new Map(current.map(it => [String(it[idField]), it]));
|
|
125
|
+
const next = current.slice();
|
|
126
|
+
let removed = 0;
|
|
127
|
+
let changed = 0;
|
|
128
|
+
let added = 0;
|
|
129
|
+
for (const [id, edit] of Object.entries(patch)) {
|
|
130
|
+
const i = next.findIndex(it => String(it[idField]) === id);
|
|
131
|
+
if (edit === null) {
|
|
132
|
+
if (i === -1) return {
|
|
133
|
+
patch,
|
|
134
|
+
notes
|
|
135
|
+
};
|
|
136
|
+
next.splice(i, 1);
|
|
137
|
+
removed++;
|
|
138
|
+
} else if (i === -1) {
|
|
139
|
+
if (!isPlainObject(edit)) return {
|
|
140
|
+
patch,
|
|
141
|
+
notes
|
|
142
|
+
};
|
|
143
|
+
const identity = identityForAdd(current, idField, id, edit);
|
|
144
|
+
if (!identity.ok) {
|
|
145
|
+
return {
|
|
146
|
+
patch,
|
|
147
|
+
notes,
|
|
148
|
+
error: identity.error
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
next.push({
|
|
152
|
+
...edit,
|
|
153
|
+
[idField]: identity.value
|
|
154
|
+
});
|
|
155
|
+
added++;
|
|
156
|
+
} else {
|
|
157
|
+
const merged = expandKeyedArrays(byId.get(id), edit, `${path}[${id}]`, notes);
|
|
158
|
+
if (merged.error) return {
|
|
159
|
+
patch,
|
|
160
|
+
notes,
|
|
161
|
+
error: merged.error
|
|
162
|
+
};
|
|
163
|
+
const base = next[i];
|
|
164
|
+
next[i] = isPlainObject(base) && isPlainObject(merged.patch) ? {
|
|
165
|
+
...base,
|
|
166
|
+
...merged.patch
|
|
167
|
+
} : merged.patch;
|
|
168
|
+
changed++;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
notes.push(`${path || "(root)"}: addressed by \`${idField}\` — ${changed} changed, ${added} added, ${removed} removed, ${next.length} item(s) remain`);
|
|
172
|
+
return {
|
|
173
|
+
patch: next,
|
|
174
|
+
notes
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (isPlainObject(current) && isPlainObject(patch)) {
|
|
178
|
+
const outPatch = {};
|
|
179
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
180
|
+
const p = path ? `${path}.${k}` : k;
|
|
181
|
+
if (!(k in current)) {
|
|
182
|
+
outPatch[k] = v;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const inner = expandKeyedArrays(current[k], v, p, notes);
|
|
186
|
+
// A refusal deep in the tree is the whole patch's refusal: writing the
|
|
187
|
+
// rest of it would apply half of what the caller asked for.
|
|
188
|
+
if (inner.error) return {
|
|
189
|
+
patch,
|
|
190
|
+
notes,
|
|
191
|
+
error: inner.error
|
|
192
|
+
};
|
|
193
|
+
outPatch[k] = inner.patch;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
patch: outPatch,
|
|
197
|
+
notes
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
patch,
|
|
202
|
+
notes
|
|
203
|
+
};
|
|
204
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The attention badge seam: a tool says "I need you" and the rail hears it.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { getToolAttention, setToolAttention, subscribeToolAttention } from "./toolUiBridge.js";
|
|
8
|
+
describe("toolAttention", () => {
|
|
9
|
+
it("stores, replaces and clears a badge, notifying once per change", () => {
|
|
10
|
+
const fn = vi.fn();
|
|
11
|
+
const off = subscribeToolAttention(fn);
|
|
12
|
+
setToolAttention("ask-buoy", {
|
|
13
|
+
kind: "needsInput",
|
|
14
|
+
label: "Needs your approval"
|
|
15
|
+
});
|
|
16
|
+
expect(getToolAttention("ask-buoy")).toEqual({
|
|
17
|
+
kind: "needsInput",
|
|
18
|
+
label: "Needs your approval"
|
|
19
|
+
});
|
|
20
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
21
|
+
// Same value again is not a change.
|
|
22
|
+
setToolAttention("ask-buoy", {
|
|
23
|
+
kind: "needsInput",
|
|
24
|
+
label: "Needs your approval"
|
|
25
|
+
});
|
|
26
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
27
|
+
setToolAttention("ask-buoy", {
|
|
28
|
+
kind: "done",
|
|
29
|
+
label: "Finished"
|
|
30
|
+
});
|
|
31
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
32
|
+
setToolAttention("ask-buoy", null);
|
|
33
|
+
expect(getToolAttention("ask-buoy")).toBeNull();
|
|
34
|
+
expect(fn).toHaveBeenCalledTimes(3);
|
|
35
|
+
// Clearing what is already clear is silent.
|
|
36
|
+
setToolAttention("ask-buoy", null);
|
|
37
|
+
expect(fn).toHaveBeenCalledTimes(3);
|
|
38
|
+
off();
|
|
39
|
+
});
|
|
40
|
+
it("keeps tools apart", () => {
|
|
41
|
+
setToolAttention("a", {
|
|
42
|
+
kind: "failed",
|
|
43
|
+
label: "x"
|
|
44
|
+
});
|
|
45
|
+
setToolAttention("b", null);
|
|
46
|
+
expect(getToolAttention("a")?.kind).toBe("failed");
|
|
47
|
+
expect(getToolAttention("b")).toBeNull();
|
|
48
|
+
setToolAttention("a", null);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -65,4 +65,59 @@ export function openBuoyTool(toolId) {
|
|
|
65
65
|
/** True when some host is listening — for deciding whether to offer the button. */
|
|
66
66
|
export function canOpenBuoyTools() {
|
|
67
67
|
return openers().size > 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Attention ───────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* "This tool needs you" — shown as a badge on its chip in the minimized rail.
|
|
74
|
+
*
|
|
75
|
+
* The case: Ask Buoy is minimized so a tester can watch the app, and the
|
|
76
|
+
* agent hits a step that needs a tap. Nobody can see the card. The sheet used
|
|
77
|
+
* to fail safe by DECLINING the change and ending the turn — which made
|
|
78
|
+
* minimize mean "stop", the opposite of what it is for. Now the approval
|
|
79
|
+
* holds, the chip gets a badge, and tapping the chip (the existing restore
|
|
80
|
+
* path) brings the card back. Same seam shape as the opener above: the rail
|
|
81
|
+
* lives inside the host, the tool cannot import the host, so it publishes
|
|
82
|
+
* through a global.
|
|
83
|
+
*
|
|
84
|
+
* `done` and `failed` cover the other thing a hidden tool cannot say — that
|
|
85
|
+
* the work you minimized it to wait for has finished.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
const ATTENTION_KEY = "__buoyToolAttention";
|
|
89
|
+
function attention() {
|
|
90
|
+
const g = globalThis;
|
|
91
|
+
if (!g[ATTENTION_KEY]) g[ATTENTION_KEY] = {
|
|
92
|
+
byTool: new Map(),
|
|
93
|
+
listeners: new Set()
|
|
94
|
+
};
|
|
95
|
+
return g[ATTENTION_KEY];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Set, replace or (with null) clear a tool's badge. No-op when nothing changed. */
|
|
99
|
+
export function setToolAttention(toolId, next) {
|
|
100
|
+
const store = attention();
|
|
101
|
+
const prev = store.byTool.get(toolId) ?? null;
|
|
102
|
+
if (prev === next || prev && next && prev.kind === next.kind && prev.label === next.label) return;
|
|
103
|
+
if (next) store.byTool.set(toolId, next);else store.byTool.delete(toolId);
|
|
104
|
+
for (const fn of store.listeners) {
|
|
105
|
+
try {
|
|
106
|
+
fn();
|
|
107
|
+
} catch {
|
|
108
|
+
// A throwing subscriber must not stop the others from hearing.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function getToolAttention(toolId) {
|
|
113
|
+
return attention().byTool.get(toolId) ?? null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** For `useSyncExternalStore` in the rail. */
|
|
117
|
+
export function subscribeToolAttention(fn) {
|
|
118
|
+
const store = attention();
|
|
119
|
+
store.listeners.add(fn);
|
|
120
|
+
return () => {
|
|
121
|
+
store.listeners.delete(fn);
|
|
122
|
+
};
|
|
68
123
|
}
|