@peers-app/peers-ui 0.15.5 → 0.16.0
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/dist/components/markdown-editor/editor.js +2 -1
- package/dist/components/markdown-editor/move-line-plugin.d.ts +11 -1
- package/dist/components/markdown-editor/move-line-plugin.js +83 -18
- package/dist/components/markdown-editor/select-line-boundary-plugin.d.ts +8 -0
- package/dist/components/markdown-editor/select-line-boundary-plugin.js +71 -0
- package/dist/screens/data-explorer/data-explorer.js +64 -16
- package/package.json +3 -3
- package/src/components/markdown-editor/editor.tsx +2 -0
- package/src/components/markdown-editor/move-line-plugin.tsx +89 -19
- package/src/components/markdown-editor/select-line-boundary-plugin.tsx +95 -0
- package/src/screens/data-explorer/data-explorer.tsx +271 -76
- package/docs/conversation-tab.md +0 -201
- package/docs/getting-started.md +0 -284
- package/docs/knowledge.md +0 -187
- package/docs/tabs-ui.md +0 -681
- package/docs/user-contacts-ui.md +0 -384
|
@@ -62,6 +62,7 @@ const markdown_plugin_1 = require("./markdown-plugin");
|
|
|
62
62
|
const mention_node_1 = require("./mention-node");
|
|
63
63
|
const mentions_plugin_1 = require("./mentions-plugin");
|
|
64
64
|
const move_line_plugin_1 = require("./move-line-plugin");
|
|
65
|
+
const select_line_boundary_plugin_1 = require("./select-line-boundary-plugin");
|
|
65
66
|
const theme_1 = __importDefault(require("./theme"));
|
|
66
67
|
const toolbar_1 = require("./toolbar");
|
|
67
68
|
const editorConfig = {
|
|
@@ -111,7 +112,7 @@ function MarkdownEditor(props) {
|
|
|
111
112
|
};
|
|
112
113
|
}
|
|
113
114
|
const _mentionConfigs = props.mentionConfigs ?? mention_configs_1.mentionConfigs;
|
|
114
|
-
return ((0, jsx_runtime_1.jsx)(LexicalComposer_1.LexicalComposer, { initialConfig: { ...editorConfig }, children: (0, jsx_runtime_1.jsxs)("div", { className: "editor-container", children: [props.hideToolbar ? null : (0, jsx_runtime_1.jsx)(toolbar_1.ToolbarPlugin, { topRightControls: props.topRightControls }), (0, jsx_runtime_1.jsxs)("div", { className: "editor-inner", ref: editorRef, style: { maxHeight: props.maxHeight, overflowY: "auto" }, children: [(0, jsx_runtime_1.jsx)(LexicalRichTextPlugin_1.RichTextPlugin, { contentEditable: (0, jsx_runtime_1.jsx)(LexicalContentEditable_1.ContentEditable, { className: "editor-input p-2" }), placeholder: null, ErrorBoundary: LexicalErrorBoundary_1.LexicalErrorBoundary }), (0, jsx_runtime_1.jsx)(LexicalHistoryPlugin_1.HistoryPlugin, {}), props.autoFocus && (0, jsx_runtime_1.jsx)(LexicalAutoFocusPlugin_1.AutoFocusPlugin, { defaultSelection: "rootEnd" }), (0, jsx_runtime_1.jsx)(LexicalListPlugin_1.ListPlugin, {}), _mentionConfigs.length > 0 && ((0, jsx_runtime_1.jsx)(mentions_plugin_1.MentionsPlugin, { mentionConfigs: _mentionConfigs, mentionsOpen: mentionsOpen })), (0, jsx_runtime_1.jsx)(autolink_plugin_1.LexicalAutoLinkPlugin, {}), (0, jsx_runtime_1.jsx)(markdown_plugin_1.MarkdownPlugin, { markdownObs: props.value }), (0, jsx_runtime_1.jsx)(LexicalMarkdownShortcutPlugin_1.MarkdownShortcutPlugin, { transformers: markdown_plugin_1.customMarkdownTransformers }), (0, jsx_runtime_1.jsx)(LexicalCheckListPlugin_1.CheckListPlugin, {}), (0, jsx_runtime_1.jsx)(LexicalTabIndentationPlugin_1.TabIndentationPlugin, {}), (0, jsx_runtime_1.jsx)(move_line_plugin_1.MoveLineWithAltArrowsPlugin, { mentionsOpen: mentionsOpen }), (0, jsx_runtime_1.jsx)(OnKeyDownPlugin, { effects: props.effects, mentionsOpen: mentionsOpen })] })] }) }));
|
|
115
|
+
return ((0, jsx_runtime_1.jsx)(LexicalComposer_1.LexicalComposer, { initialConfig: { ...editorConfig }, children: (0, jsx_runtime_1.jsxs)("div", { className: "editor-container", children: [props.hideToolbar ? null : (0, jsx_runtime_1.jsx)(toolbar_1.ToolbarPlugin, { topRightControls: props.topRightControls }), (0, jsx_runtime_1.jsxs)("div", { className: "editor-inner", ref: editorRef, style: { maxHeight: props.maxHeight, overflowY: "auto" }, children: [(0, jsx_runtime_1.jsx)(LexicalRichTextPlugin_1.RichTextPlugin, { contentEditable: (0, jsx_runtime_1.jsx)(LexicalContentEditable_1.ContentEditable, { className: "editor-input p-2" }), placeholder: null, ErrorBoundary: LexicalErrorBoundary_1.LexicalErrorBoundary }), (0, jsx_runtime_1.jsx)(LexicalHistoryPlugin_1.HistoryPlugin, {}), props.autoFocus && (0, jsx_runtime_1.jsx)(LexicalAutoFocusPlugin_1.AutoFocusPlugin, { defaultSelection: "rootEnd" }), (0, jsx_runtime_1.jsx)(LexicalListPlugin_1.ListPlugin, {}), _mentionConfigs.length > 0 && ((0, jsx_runtime_1.jsx)(mentions_plugin_1.MentionsPlugin, { mentionConfigs: _mentionConfigs, mentionsOpen: mentionsOpen })), (0, jsx_runtime_1.jsx)(autolink_plugin_1.LexicalAutoLinkPlugin, {}), (0, jsx_runtime_1.jsx)(markdown_plugin_1.MarkdownPlugin, { markdownObs: props.value }), (0, jsx_runtime_1.jsx)(LexicalMarkdownShortcutPlugin_1.MarkdownShortcutPlugin, { transformers: markdown_plugin_1.customMarkdownTransformers }), (0, jsx_runtime_1.jsx)(LexicalCheckListPlugin_1.CheckListPlugin, {}), (0, jsx_runtime_1.jsx)(LexicalTabIndentationPlugin_1.TabIndentationPlugin, {}), (0, jsx_runtime_1.jsx)(move_line_plugin_1.MoveLineWithAltArrowsPlugin, { mentionsOpen: mentionsOpen }), (0, jsx_runtime_1.jsx)(select_line_boundary_plugin_1.SelectToLineBoundaryPlugin, {}), (0, jsx_runtime_1.jsx)(OnKeyDownPlugin, { effects: props.effects, mentionsOpen: mentionsOpen })] })] }) }));
|
|
115
116
|
}
|
|
116
117
|
const OnKeyDownPlugin = (props) => {
|
|
117
118
|
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { Observable } from "@peers-app/peers-sdk";
|
|
2
|
+
import { type LexicalNode } from "lexical";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Walks up from `node` to find the reorderable block for keyboard shortcuts that
|
|
5
|
+
* operate on "lines" (blocks). Returns a list item when inside a list, or the
|
|
6
|
+
* top-level block directly under the root.
|
|
7
|
+
*/
|
|
8
|
+
export declare function $getMovableBlock(node: LexicalNode): LexicalNode | null;
|
|
9
|
+
/**
|
|
10
|
+
* Registers Alt/Option + ArrowUp/ArrowDown to move the selected block(s) — or the
|
|
11
|
+
* block at the caret — past an adjacent sibling. Supports both collapsed cursors and
|
|
12
|
+
* multi-block selections: the single neighbor block is relocated to the other side of
|
|
13
|
+
* the selection range, which effectively shifts the selected blocks up or down by one.
|
|
4
14
|
* @param props.mentionsOpen — when true, the plugin defers so mention typeahead keeps keyboard focus.
|
|
5
15
|
*/
|
|
6
16
|
export declare function MoveLineWithAltArrowsPlugin(props: {
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.$getMovableBlock = $getMovableBlock;
|
|
3
4
|
exports.MoveLineWithAltArrowsPlugin = MoveLineWithAltArrowsPlugin;
|
|
4
5
|
const list_1 = require("@lexical/list");
|
|
5
6
|
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
|
|
6
7
|
const lexical_1 = require("lexical");
|
|
7
8
|
const react_1 = require("react");
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* Walks up from `node` to find the reorderable block for keyboard shortcuts that
|
|
11
|
+
* operate on "lines" (blocks). Returns a list item when inside a list, or the
|
|
12
|
+
* top-level block directly under the root.
|
|
11
13
|
*/
|
|
12
14
|
function $getMovableBlock(node) {
|
|
13
15
|
let n = node;
|
|
@@ -27,7 +29,25 @@ function $getMovableBlock(node) {
|
|
|
27
29
|
return null;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
32
|
+
* Returns true when `node` sits at the absolute start of `block` — i.e. it is
|
|
33
|
+
* the block itself or lies along the first-child path from block down to node.
|
|
34
|
+
* Used to detect a selection endpoint that bled into the next block at offset 0.
|
|
35
|
+
*/
|
|
36
|
+
function $isFirstPositionInBlock(node, block) {
|
|
37
|
+
let n = node;
|
|
38
|
+
while (n !== null && n !== block) {
|
|
39
|
+
if (n.getPreviousSibling() !== null) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
n = n.getParent();
|
|
43
|
+
}
|
|
44
|
+
return n === block;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Registers Alt/Option + ArrowUp/ArrowDown to move the selected block(s) — or the
|
|
48
|
+
* block at the caret — past an adjacent sibling. Supports both collapsed cursors and
|
|
49
|
+
* multi-block selections: the single neighbor block is relocated to the other side of
|
|
50
|
+
* the selection range, which effectively shifts the selected blocks up or down by one.
|
|
31
51
|
* @param props.mentionsOpen — when true, the plugin defers so mention typeahead keeps keyboard focus.
|
|
32
52
|
*/
|
|
33
53
|
function MoveLineWithAltArrowsPlugin(props) {
|
|
@@ -49,33 +69,78 @@ function MoveLineWithAltArrowsPlugin(props) {
|
|
|
49
69
|
// so handled would stay false, preventDefault would never run, and the
|
|
50
70
|
// built-in arrow handler + browser would move the caret on top of our swap.
|
|
51
71
|
const selection = (0, lexical_1.$getSelection)();
|
|
52
|
-
if (!(0, lexical_1.$isRangeSelection)(selection)
|
|
72
|
+
if (!(0, lexical_1.$isRangeSelection)(selection)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const anchorBlock = $getMovableBlock(selection.anchor.getNode());
|
|
76
|
+
const focusBlock = $getMovableBlock(selection.focus.getNode());
|
|
77
|
+
if (anchorBlock === null || focusBlock === null) {
|
|
53
78
|
return false;
|
|
54
79
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (block === null) {
|
|
80
|
+
// Both endpoints must share the same parent (same nesting level).
|
|
81
|
+
if (anchorBlock.getParent()?.getKey() !== focusBlock.getParent()?.getKey()) {
|
|
58
82
|
return false;
|
|
59
83
|
}
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
84
|
+
const anchorIdx = anchorBlock.getIndexWithinParent();
|
|
85
|
+
const focusIdx = focusBlock.getIndexWithinParent();
|
|
86
|
+
const firstBlock = anchorIdx <= focusIdx ? anchorBlock : focusBlock;
|
|
87
|
+
let lastBlock = anchorIdx <= focusIdx ? focusBlock : anchorBlock;
|
|
88
|
+
let anchorKey = selection.anchor.key;
|
|
89
|
+
let anchorOffset = selection.anchor.offset;
|
|
90
|
+
let anchorType = selection.anchor.type;
|
|
91
|
+
let focusKey = selection.focus.key;
|
|
92
|
+
let focusOffset = selection.focus.offset;
|
|
93
|
+
let focusType = selection.focus.type;
|
|
94
|
+
// When a selection extends to offset 0 of the next block (common when
|
|
95
|
+
// selecting to "end of line"), that trailing block has no selected
|
|
96
|
+
// content — exclude it so it isn't dragged along. Also clamp the
|
|
97
|
+
// trailing selection endpoint to the end of the new lastBlock so the
|
|
98
|
+
// restored highlight doesn't overextend into the excluded block.
|
|
99
|
+
if (lastBlock !== firstBlock) {
|
|
100
|
+
const lastPoint = anchorIdx > focusIdx ? selection.anchor : selection.focus;
|
|
101
|
+
if (lastPoint.offset === 0 && $isFirstPositionInBlock(lastPoint.getNode(), lastBlock)) {
|
|
102
|
+
const stepped = lastBlock.getPreviousSibling();
|
|
103
|
+
if (stepped === null) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
lastBlock = stepped;
|
|
107
|
+
const lastDesc = (0, lexical_1.$isElementNode)(lastBlock) ? lastBlock.getLastDescendant() : null;
|
|
108
|
+
const clampKey = lastDesc ? lastDesc.getKey() : lastBlock.getKey();
|
|
109
|
+
const clampOffset = lastDesc
|
|
110
|
+
? lastDesc.getTextContentSize()
|
|
111
|
+
: (0, lexical_1.$isElementNode)(lastBlock)
|
|
112
|
+
? lastBlock.getChildrenSize()
|
|
113
|
+
: lastBlock.getTextContentSize();
|
|
114
|
+
const clampType = lastDesc
|
|
115
|
+
? "text"
|
|
116
|
+
: (0, lexical_1.$isElementNode)(lastBlock)
|
|
117
|
+
? "element"
|
|
118
|
+
: "text";
|
|
119
|
+
if (anchorIdx > focusIdx) {
|
|
120
|
+
anchorKey = clampKey;
|
|
121
|
+
anchorOffset = clampOffset;
|
|
122
|
+
anchorType = clampType;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
focusKey = clampKey;
|
|
126
|
+
focusOffset = clampOffset;
|
|
127
|
+
focusType = clampType;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
66
131
|
if (code === "ArrowUp") {
|
|
67
|
-
const prev =
|
|
132
|
+
const prev = firstBlock.getPreviousSibling();
|
|
68
133
|
if (prev === null) {
|
|
69
134
|
return false;
|
|
70
135
|
}
|
|
71
|
-
|
|
136
|
+
lastBlock.insertAfter(prev, false);
|
|
72
137
|
}
|
|
73
138
|
else {
|
|
74
|
-
const next =
|
|
139
|
+
const next = lastBlock.getNextSibling();
|
|
75
140
|
if (next === null) {
|
|
76
141
|
return false;
|
|
77
142
|
}
|
|
78
|
-
|
|
143
|
+
firstBlock.insertBefore(next, false);
|
|
79
144
|
}
|
|
80
145
|
if ((0, lexical_1.$getNodeByKey)(anchorKey) !== null) {
|
|
81
146
|
const restored = (0, lexical_1.$createRangeSelection)();
|
|
@@ -86,7 +151,7 @@ function MoveLineWithAltArrowsPlugin(props) {
|
|
|
86
151
|
(0, lexical_1.$setSelection)(restored);
|
|
87
152
|
}
|
|
88
153
|
else {
|
|
89
|
-
|
|
154
|
+
lastBlock.selectEnd();
|
|
90
155
|
}
|
|
91
156
|
event.preventDefault();
|
|
92
157
|
return true;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intercepts Cmd+Shift+ArrowLeft/Right (Mac line-boundary selection) and explicitly
|
|
3
|
+
* sets anchor/focus so that Lexical's reconciliation cannot flip the selection direction.
|
|
4
|
+
* Uses the browser's native `Selection.modify` with `'lineboundary'` granularity to
|
|
5
|
+
* find the correct **visual** line boundary (respecting text wrapping), then converts
|
|
6
|
+
* the result back to Lexical coordinates.
|
|
7
|
+
*/
|
|
8
|
+
export declare function SelectToLineBoundaryPlugin(): null;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SelectToLineBoundaryPlugin = SelectToLineBoundaryPlugin;
|
|
4
|
+
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
|
|
5
|
+
const lexical_1 = require("lexical");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
/**
|
|
8
|
+
* Intercepts Cmd+Shift+ArrowLeft/Right (Mac line-boundary selection) and explicitly
|
|
9
|
+
* sets anchor/focus so that Lexical's reconciliation cannot flip the selection direction.
|
|
10
|
+
* Uses the browser's native `Selection.modify` with `'lineboundary'` granularity to
|
|
11
|
+
* find the correct **visual** line boundary (respecting text wrapping), then converts
|
|
12
|
+
* the result back to Lexical coordinates.
|
|
13
|
+
*/
|
|
14
|
+
function SelectToLineBoundaryPlugin() {
|
|
15
|
+
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
|
|
16
|
+
(0, react_1.useEffect)(() => {
|
|
17
|
+
return editor.registerCommand(lexical_1.KEY_DOWN_COMMAND, (event) => {
|
|
18
|
+
if (!event.shiftKey || !event.metaKey || event.altKey || event.ctrlKey) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const code = event.code;
|
|
22
|
+
if (code !== "ArrowLeft" && code !== "ArrowRight") {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
26
|
+
if (!(0, lexical_1.$isRangeSelection)(selection)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const savedAnchorKey = selection.anchor.key;
|
|
30
|
+
const savedAnchorOffset = selection.anchor.offset;
|
|
31
|
+
const savedAnchorType = selection.anchor.type;
|
|
32
|
+
const focusElement = editor.getElementByKey(selection.focus.key);
|
|
33
|
+
if (!focusElement) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const focusDomNode = selection.focus.type === "text" ? focusElement.firstChild : focusElement;
|
|
37
|
+
if (!focusDomNode) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const domSelection = window.getSelection();
|
|
41
|
+
if (!domSelection) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
domSelection.collapse(focusDomNode, selection.focus.offset);
|
|
45
|
+
domSelection.modify("move", code === "ArrowLeft" ? "left" : "right", "lineboundary");
|
|
46
|
+
const boundaryDomNode = domSelection.anchorNode;
|
|
47
|
+
const boundaryDomOffset = domSelection.anchorOffset;
|
|
48
|
+
if (!boundaryDomNode) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const boundaryLexicalNode = (0, lexical_1.$getNearestNodeFromDOMNode)(boundaryDomNode);
|
|
52
|
+
if (!boundaryLexicalNode) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const newFocusKey = boundaryLexicalNode.getKey();
|
|
56
|
+
const newFocusOffset = boundaryDomOffset;
|
|
57
|
+
const newFocusType = (0, lexical_1.$isTextNode)(boundaryLexicalNode)
|
|
58
|
+
? "text"
|
|
59
|
+
: "element";
|
|
60
|
+
const restored = (0, lexical_1.$createRangeSelection)();
|
|
61
|
+
restored.format = selection.format;
|
|
62
|
+
restored.style = selection.style;
|
|
63
|
+
restored.anchor.set(savedAnchorKey, savedAnchorOffset, savedAnchorType);
|
|
64
|
+
restored.focus.set(newFocusKey, newFocusOffset, newFocusType);
|
|
65
|
+
(0, lexical_1.$setSelection)(restored);
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
return true;
|
|
68
|
+
}, lexical_1.COMMAND_PRIORITY_HIGH);
|
|
69
|
+
}, [editor]);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
@@ -9,11 +9,46 @@ const ui_loader_1 = require("../../ui-router/ui-loader");
|
|
|
9
9
|
function getDataExplorerApi() {
|
|
10
10
|
return window.electronAPI?.dataExplorer;
|
|
11
11
|
}
|
|
12
|
+
function formatRelativeTime(isoString) {
|
|
13
|
+
const date = new Date(isoString);
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
const diffMs = now - date.getTime();
|
|
16
|
+
const diffMin = Math.floor(diffMs / 60_000);
|
|
17
|
+
if (diffMin < 1)
|
|
18
|
+
return "just now";
|
|
19
|
+
if (diffMin < 60)
|
|
20
|
+
return `${diffMin}m ago`;
|
|
21
|
+
const diffHrs = Math.floor(diffMin / 60);
|
|
22
|
+
if (diffHrs < 24)
|
|
23
|
+
return `${diffHrs}h ago`;
|
|
24
|
+
const diffDays = Math.floor(diffHrs / 24);
|
|
25
|
+
if (diffDays < 30)
|
|
26
|
+
return `${diffDays}d ago`;
|
|
27
|
+
return date.toLocaleDateString();
|
|
28
|
+
}
|
|
29
|
+
function DeleteConfirmModal({ tableName, onConfirm, onCancel, }) {
|
|
30
|
+
const [confirmText, setConfirmText] = (0, react_1.useState)("");
|
|
31
|
+
const [deleting, setDeleting] = (0, react_1.useState)(false);
|
|
32
|
+
const handleConfirm = async () => {
|
|
33
|
+
setDeleting(true);
|
|
34
|
+
try {
|
|
35
|
+
await onConfirm();
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
setDeleting(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center", style: { zIndex: 1050, backgroundColor: "rgba(0,0,0,0.5)" }, onClick: onCancel, children: (0, jsx_runtime_1.jsxs)("div", { className: "card shadow-lg", style: { maxWidth: 480, width: "100%" }, onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("div", { className: "card-header bg-danger text-white d-flex justify-content-between align-items-center", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Delete Table" }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn-close btn-close-white", onClick: onCancel })] }), (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsxs)("div", { className: "alert alert-danger mb-3", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-exclamation-triangle-fill me-2" }), "This will permanently delete all data for ", (0, jsx_runtime_1.jsx)("strong", { children: tableName }), " on this device and all synced devices once synced."] }), (0, jsx_runtime_1.jsxs)("label", { className: "form-label", children: ["Type ", (0, jsx_runtime_1.jsx)("strong", { children: tableName }), " to confirm:"] }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "form-control", value: confirmText, onChange: (e) => setConfirmText(e.target.value), placeholder: tableName,
|
|
42
|
+
// biome-ignore lint/a11y/noAutofocus: confirmation input needs immediate focus
|
|
43
|
+
autoFocus: true })] }), (0, jsx_runtime_1.jsxs)("div", { className: "card-footer d-flex justify-content-end gap-2", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-secondary", onClick: onCancel, disabled: deleting, children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-danger", disabled: confirmText !== tableName || deleting, onClick: handleConfirm, children: deleting ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "spinner-border spinner-border-sm me-1" }), "Deleting..."] })) : ("Delete Table") })] })] }) }));
|
|
44
|
+
}
|
|
12
45
|
function DataExplorerList() {
|
|
13
46
|
const [tables, setTables] = (0, react_1.useState)([]);
|
|
47
|
+
const [deletedTables, setDeletedTables] = (0, react_1.useState)([]);
|
|
14
48
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
15
49
|
const [currentContext, setCurrentContext] = (0, react_1.useState)("");
|
|
16
50
|
const [compacting, setCompacting] = (0, react_1.useState)(false);
|
|
51
|
+
const [deleteTarget, setDeleteTarget] = (0, react_1.useState)(null);
|
|
17
52
|
const formatBytes = (bytes) => {
|
|
18
53
|
if (bytes === undefined)
|
|
19
54
|
return "-";
|
|
@@ -30,12 +65,12 @@ function DataExplorerList() {
|
|
|
30
65
|
const kb = 1024;
|
|
31
66
|
const mb = kb * 1024;
|
|
32
67
|
if (bytes < 10 * kb)
|
|
33
|
-
return "text-secondary";
|
|
68
|
+
return "text-secondary";
|
|
34
69
|
if (bytes < 10 * mb)
|
|
35
|
-
return "text-primary";
|
|
70
|
+
return "text-primary";
|
|
36
71
|
if (bytes < 100 * mb)
|
|
37
|
-
return "text-warning";
|
|
38
|
-
return "text-danger";
|
|
72
|
+
return "text-warning";
|
|
73
|
+
return "text-danger";
|
|
39
74
|
};
|
|
40
75
|
const loadTables = (0, react_1.useCallback)(async () => {
|
|
41
76
|
try {
|
|
@@ -46,12 +81,9 @@ function DataExplorerList() {
|
|
|
46
81
|
console.warn("Data Explorer API not available");
|
|
47
82
|
return;
|
|
48
83
|
}
|
|
49
|
-
// Get current context info
|
|
50
84
|
const contextInfo = await api.getCurrentContext();
|
|
51
85
|
setCurrentContext(contextInfo.contextName);
|
|
52
|
-
// Get all tables from IPC
|
|
53
86
|
const allTablesInfo = await api.getAllTables();
|
|
54
|
-
// Sort by registered status first, then by table name
|
|
55
87
|
allTablesInfo.sort((a, b) => {
|
|
56
88
|
if (a.isRegistered !== b.isRegistered) {
|
|
57
89
|
return a.isRegistered ? -1 : 1;
|
|
@@ -59,6 +91,9 @@ function DataExplorerList() {
|
|
|
59
91
|
return a.tableName.localeCompare(b.tableName);
|
|
60
92
|
});
|
|
61
93
|
setTables(allTablesInfo);
|
|
94
|
+
const deleted = await api.getDeletedTables();
|
|
95
|
+
deleted.sort((a, b) => a.tableName.localeCompare(b.tableName));
|
|
96
|
+
setDeletedTables(deleted);
|
|
62
97
|
}
|
|
63
98
|
catch (error) {
|
|
64
99
|
console.error("Error loading tables:", error);
|
|
@@ -67,6 +102,23 @@ function DataExplorerList() {
|
|
|
67
102
|
setLoading(false);
|
|
68
103
|
}
|
|
69
104
|
}, []);
|
|
105
|
+
const handleDeleteTable = async () => {
|
|
106
|
+
if (!deleteTarget)
|
|
107
|
+
return;
|
|
108
|
+
const api = getDataExplorerApi();
|
|
109
|
+
if (!api)
|
|
110
|
+
return;
|
|
111
|
+
try {
|
|
112
|
+
await api.deleteTable(deleteTarget);
|
|
113
|
+
setDeleteTarget(null);
|
|
114
|
+
setLoading(true);
|
|
115
|
+
await loadTables();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error("Error deleting table:", error);
|
|
119
|
+
alert(`Error deleting table: ${error instanceof Error ? error.message : String(error)}`);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
70
122
|
const compactDatabase = async (beforeTimestamp) => {
|
|
71
123
|
try {
|
|
72
124
|
setCompacting(true);
|
|
@@ -85,7 +137,6 @@ function DataExplorerList() {
|
|
|
85
137
|
}
|
|
86
138
|
await api.compactDatabase(beforeTimestamp);
|
|
87
139
|
alert(`Database compacted successfully!${isExtreme ? " (Extreme mode)" : ""}`);
|
|
88
|
-
// Reload tables to show updated sizes
|
|
89
140
|
setLoading(true);
|
|
90
141
|
await loadTables();
|
|
91
142
|
}
|
|
@@ -112,7 +163,6 @@ function DataExplorerList() {
|
|
|
112
163
|
}
|
|
113
164
|
await api.resetChangeTracking();
|
|
114
165
|
alert("Change tracking reset successfully!");
|
|
115
|
-
// Reload tables to show updated sizes
|
|
116
166
|
setLoading(true);
|
|
117
167
|
await loadTables();
|
|
118
168
|
}
|
|
@@ -136,7 +186,9 @@ function DataExplorerList() {
|
|
|
136
186
|
if (!currentContext && tables.length === 0) {
|
|
137
187
|
return ((0, jsx_runtime_1.jsx)("div", { className: "container-fluid p-4", children: (0, jsx_runtime_1.jsx)("div", { className: "alert alert-warning", children: "Unable to load database information." }) }));
|
|
138
188
|
}
|
|
139
|
-
|
|
189
|
+
const registeredTables = tables.filter((t) => t.isRegistered);
|
|
190
|
+
const unregisteredTables = tables.filter((t) => !t.isRegistered);
|
|
191
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "container-fluid p-4", children: [deleteTarget && ((0, jsx_runtime_1.jsx)(DeleteConfirmModal, { tableName: deleteTarget, onConfirm: handleDeleteTable, onCancel: () => setDeleteTarget(null) })), (0, jsx_runtime_1.jsxs)("ul", { className: "nav nav-tabs mb-4", children: [(0, jsx_runtime_1.jsx)("li", { className: "nav-item", children: (0, jsx_runtime_1.jsxs)("a", { className: "nav-link active", href: "#/data-explorer", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-table" }), " Tables"] }) }), (0, jsx_runtime_1.jsx)("li", { className: "nav-item", children: (0, jsx_runtime_1.jsxs)("a", { className: "nav-link", href: "#/data-explorer/query", children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-terminal" }), " Query"] }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between align-items-center mb-4", children: [(0, jsx_runtime_1.jsx)("h2", { children: "Data Explorer" }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex gap-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "btn-group", role: "group", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-warning", onClick: () => compactDatabase(), disabled: compacting || loading, title: "Compact database (cleanup changes older than 2 weeks)", children: compacting ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "spinner-border spinner-border-sm me-1", role: "status", "aria-hidden": "true" }), "Compacting..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-disc" }), " Compact Database"] })) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-warning dropdown-toggle dropdown-toggle-split", "data-bs-toggle": "dropdown", "aria-expanded": "false", disabled: compacting || loading, title: "More compaction options", children: (0, jsx_runtime_1.jsx)("span", { className: "visually-hidden", children: "Toggle Dropdown" }) }), (0, jsx_runtime_1.jsxs)("ul", { className: "dropdown-menu", children: [(0, jsx_runtime_1.jsx)("li", { children: (0, jsx_runtime_1.jsxs)("a", { className: "dropdown-item", href: "#", onClick: (e) => {
|
|
140
192
|
e.preventDefault();
|
|
141
193
|
compactDatabase(Date.now());
|
|
142
194
|
}, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-exclamation-triangle text-danger me-2" }), "Extreme Compaction", (0, jsx_runtime_1.jsx)("div", { className: "small text-muted", children: "Remove ALL change history" })] }) }), (0, jsx_runtime_1.jsx)("li", { children: (0, jsx_runtime_1.jsx)("hr", { className: "dropdown-divider" }) }), (0, jsx_runtime_1.jsx)("li", { children: (0, jsx_runtime_1.jsxs)("a", { className: "dropdown-item", href: "#", onClick: (e) => {
|
|
@@ -145,15 +197,11 @@ function DataExplorerList() {
|
|
|
145
197
|
}, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-arrow-clockwise text-warning me-2" }), "Reset Change Tracking", (0, jsx_runtime_1.jsx)("div", { className: "small text-muted", children: "Clear and rebuild all change history" })] }) })] })] }), (0, jsx_runtime_1.jsxs)("button", { className: "btn btn-sm btn-primary", onClick: () => {
|
|
146
198
|
setLoading(true);
|
|
147
199
|
loadTables();
|
|
148
|
-
}, disabled: compacting, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-arrow-clockwise" }), " Refresh"] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "card mb-4", children: (0, jsx_runtime_1.jsx)("div", { className: "card-body", children: (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between align-items-center", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h5", { className: "card-title mb-2", children: "Current Data Context" }), (0, jsx_runtime_1.jsx)("p", { className: "mb-0", children: currentContext })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-end", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-muted small", children: "Total DB Size" }), (0, jsx_runtime_1.jsx)("div", { className: "fs-4 fw-bold", children: formatBytes(tables.reduce((sum, t) => sum + (t.bytes || 0), 0)) })] })] }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "card mb-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsxs)("h5", { className: "card-title", children: ["Registered Tables (",
|
|
149
|
-
.filter((t) => t.isRegistered)
|
|
150
|
-
.map((table) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("strong", { children: table.tableName }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: table.tableId || "system" }) }), (0, jsx_runtime_1.jsx)("td", { className: "text-end", children: table.rowCount !== undefined ? table.rowCount.toLocaleString() : "-" }), (0, jsx_runtime_1.jsx)("td", { className: `text-end fw-bold ${getBytesColorClass(table.bytes)}`, children: formatBytes(table.bytes) })] }, table.tableName))) })] }) }))] }) }), tables.some((t) => !t.isRegistered) && ((0, jsx_runtime_1.jsx)("div", { className: "card", children: (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsxs)("h5", { className: "card-title", children: ["Unregistered Tables (", tables.filter((t) => !t.isRegistered).length, ")", (0, jsx_runtime_1.jsx)("span", { className: "badge bg-warning text-dark ms-2", children: "Raw SQLite" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-muted small", children: "These tables exist in the SQLite database but are not registered with the TableContainer. They may be system tables, change tracking tables, or legacy tables." }), (0, jsx_runtime_1.jsx)("div", { className: "table-responsive", children: (0, jsx_runtime_1.jsxs)("table", { className: "table table-hover table-sm", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "Table Name" }), (0, jsx_runtime_1.jsx)("th", { children: "Type" }), (0, jsx_runtime_1.jsx)("th", { className: "text-end", children: "Row Count" }), (0, jsx_runtime_1.jsx)("th", { className: "text-end", children: "Bytes" }), (0, jsx_runtime_1.jsx)("th", { children: "SQL Definition" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: tables
|
|
151
|
-
.filter((t) => !t.isRegistered)
|
|
152
|
-
.map((table) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("strong", { children: table.tableName }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("span", { className: "badge bg-secondary", children: table.type || "table" }) }), (0, jsx_runtime_1.jsx)("td", { className: "text-end", children: table.rowCount !== undefined ? table.rowCount.toLocaleString() : "-" }), (0, jsx_runtime_1.jsx)("td", { className: `text-end fw-bold ${getBytesColorClass(table.bytes)}`, children: formatBytes(table.bytes) }), (0, jsx_runtime_1.jsx)("td", { children: table.sql ? ((0, jsx_runtime_1.jsxs)("details", { children: [(0, jsx_runtime_1.jsx)("summary", { className: "cursor-pointer text-primary", style: { cursor: "pointer" }, children: (0, jsx_runtime_1.jsx)("small", { children: "View SQL" }) }), (0, jsx_runtime_1.jsx)("pre", { className: "mt-2 p-2 bg-body-secondary border rounded", style: {
|
|
200
|
+
}, disabled: compacting, children: [(0, jsx_runtime_1.jsx)("i", { className: "bi bi-arrow-clockwise" }), " Refresh"] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "card mb-4", children: (0, jsx_runtime_1.jsx)("div", { className: "card-body", children: (0, jsx_runtime_1.jsxs)("div", { className: "d-flex justify-content-between align-items-center", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h5", { className: "card-title mb-2", children: "Current Data Context" }), (0, jsx_runtime_1.jsx)("p", { className: "mb-0", children: currentContext })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-end", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-muted small", children: "Total DB Size" }), (0, jsx_runtime_1.jsx)("div", { className: "fs-4 fw-bold", children: formatBytes(tables.reduce((sum, t) => sum + (t.bytes || 0), 0)) })] })] }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "card mb-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsxs)("h5", { className: "card-title", children: ["Registered Tables (", registeredTables.length, ")", (0, jsx_runtime_1.jsx)("span", { className: "badge bg-success ms-2", children: "Active" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-muted small", children: "These tables are registered with the TableContainer and actively used by the application." }), registeredTables.length === 0 ? ((0, jsx_runtime_1.jsx)("p", { className: "text-muted", children: "No registered tables found" })) : ((0, jsx_runtime_1.jsx)("div", { className: "table-responsive", children: (0, jsx_runtime_1.jsxs)("table", { className: "table table-hover table-sm", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "Table Name" }), (0, jsx_runtime_1.jsx)("th", { children: "Table ID" }), (0, jsx_runtime_1.jsx)("th", { className: "text-end", children: "Row Count" }), (0, jsx_runtime_1.jsx)("th", { className: "text-end", children: "Bytes" }), (0, jsx_runtime_1.jsx)("th", { style: { width: "1%" } })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: registeredTables.map((table) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("strong", { children: table.tableName }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: table.tableId || "system" }) }), (0, jsx_runtime_1.jsx)("td", { className: "text-end", children: table.rowCount !== undefined ? table.rowCount.toLocaleString() : "-" }), (0, jsx_runtime_1.jsx)("td", { className: `text-end fw-bold ${getBytesColorClass(table.bytes)}`, children: formatBytes(table.bytes) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-outline-danger border-0", title: `Delete ${table.tableName}`, onClick: () => setDeleteTarget(table.tableName), children: (0, jsx_runtime_1.jsx)("i", { className: "bi bi-trash" }) }) })] }, table.tableName))) })] }) }))] }) }), unregisteredTables.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "card mb-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsxs)("h5", { className: "card-title", children: ["Unregistered Tables (", unregisteredTables.length, ")", (0, jsx_runtime_1.jsx)("span", { className: "badge bg-warning text-dark ms-2", children: "Raw SQLite" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-muted small", children: "These tables exist in the SQLite database but are not registered with the TableContainer. They may be system tables, change tracking tables, or legacy tables." }), (0, jsx_runtime_1.jsx)("div", { className: "table-responsive", children: (0, jsx_runtime_1.jsxs)("table", { className: "table table-hover table-sm", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "Table Name" }), (0, jsx_runtime_1.jsx)("th", { children: "Type" }), (0, jsx_runtime_1.jsx)("th", { className: "text-end", children: "Row Count" }), (0, jsx_runtime_1.jsx)("th", { className: "text-end", children: "Bytes" }), (0, jsx_runtime_1.jsx)("th", { children: "SQL Definition" }), (0, jsx_runtime_1.jsx)("th", { style: { width: "1%" } })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: unregisteredTables.map((table) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("strong", { children: table.tableName }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("span", { className: "badge bg-secondary", children: table.type || "table" }) }), (0, jsx_runtime_1.jsx)("td", { className: "text-end", children: table.rowCount !== undefined ? table.rowCount.toLocaleString() : "-" }), (0, jsx_runtime_1.jsx)("td", { className: `text-end fw-bold ${getBytesColorClass(table.bytes)}`, children: formatBytes(table.bytes) }), (0, jsx_runtime_1.jsx)("td", { children: table.sql ? ((0, jsx_runtime_1.jsxs)("details", { children: [(0, jsx_runtime_1.jsx)("summary", { className: "cursor-pointer text-primary", style: { cursor: "pointer" }, children: (0, jsx_runtime_1.jsx)("small", { children: "View SQL" }) }), (0, jsx_runtime_1.jsx)("pre", { className: "mt-2 p-2 bg-body-secondary border rounded", style: {
|
|
153
201
|
fontSize: "0.75rem",
|
|
154
202
|
maxHeight: "300px",
|
|
155
203
|
overflow: "auto",
|
|
156
|
-
}, children: (0, jsx_runtime_1.jsx)("code", { className: "text-body", children: table.sql }) })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-muted small", children: "-" })) })] }, table.tableName))) })] }) })] }) }))] }));
|
|
204
|
+
}, children: (0, jsx_runtime_1.jsx)("code", { className: "text-body", children: table.sql }) })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-muted small", children: "-" })) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "btn btn-sm btn-outline-danger border-0", title: `Delete ${table.tableName}`, onClick: () => setDeleteTarget(table.tableName), children: (0, jsx_runtime_1.jsx)("i", { className: "bi bi-trash" }) }) })] }, table.tableName))) })] }) })] }) })), deletedTables.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "card mb-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "card-body", children: [(0, jsx_runtime_1.jsxs)("h5", { className: "card-title", children: ["Deleted Tables (", deletedTables.length, ")", (0, jsx_runtime_1.jsx)("span", { className: "badge bg-danger ms-2", children: "Deleted" })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-muted small", children: "These tables have been deleted. Their definitions are retained so other devices know to stop syncing data for them." }), (0, jsx_runtime_1.jsx)("div", { className: "table-responsive", children: (0, jsx_runtime_1.jsxs)("table", { className: "table table-hover table-sm", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "Table Name" }), (0, jsx_runtime_1.jsx)("th", { children: "Table ID" }), (0, jsx_runtime_1.jsx)("th", { children: "Deleted At" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: deletedTables.map((table) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("strong", { className: "text-muted", children: table.tableName }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("code", { className: "text-muted small", children: table.tableId }) }), (0, jsx_runtime_1.jsx)("td", { children: (0, jsx_runtime_1.jsx)("span", { title: new Date(table.deletedAt).toLocaleString(), children: formatRelativeTime(table.deletedAt) }) })] }, table.tableId))) })] }) })] }) }))] }));
|
|
157
205
|
}
|
|
158
206
|
(0, ui_loader_1.registerInternalPeersUI)({
|
|
159
207
|
peersUIId: "data-explorer-list-ui",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peers-app/peers-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/peers-app/peers-ui.git"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"lint:fix": "biome check --write ."
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@peers-app/peers-sdk": "^0.
|
|
31
|
+
"@peers-app/peers-sdk": "^0.16.0",
|
|
32
32
|
"bootstrap": "^5.3.3",
|
|
33
33
|
"react": "^18.0.0",
|
|
34
34
|
"react-dom": "^18.0.0"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@babel/preset-env": "^7.24.5",
|
|
40
40
|
"@babel/preset-react": "^7.24.1",
|
|
41
41
|
"@babel/preset-typescript": "^7.27.1",
|
|
42
|
-
"@peers-app/peers-sdk": "0.
|
|
42
|
+
"@peers-app/peers-sdk": "0.16.0",
|
|
43
43
|
"@testing-library/dom": "^10.4.0",
|
|
44
44
|
"@testing-library/jest-dom": "^6.6.3",
|
|
45
45
|
"@testing-library/react": "^16.3.0",
|
|
@@ -22,6 +22,7 @@ import { customMarkdownTransformers, MarkdownPlugin } from "./markdown-plugin";
|
|
|
22
22
|
import { MentionNode } from "./mention-node";
|
|
23
23
|
import { MentionsPlugin } from "./mentions-plugin";
|
|
24
24
|
import { MoveLineWithAltArrowsPlugin } from "./move-line-plugin";
|
|
25
|
+
import { SelectToLineBoundaryPlugin } from "./select-line-boundary-plugin";
|
|
25
26
|
import theme from "./theme";
|
|
26
27
|
import { type IToolbarControl, ToolbarPlugin } from "./toolbar";
|
|
27
28
|
|
|
@@ -121,6 +122,7 @@ export function MarkdownEditor(props: IMarkdownEditorProps) {
|
|
|
121
122
|
<CheckListPlugin />
|
|
122
123
|
<TabIndentationPlugin />
|
|
123
124
|
<MoveLineWithAltArrowsPlugin mentionsOpen={mentionsOpen} />
|
|
125
|
+
<SelectToLineBoundaryPlugin />
|
|
124
126
|
<OnKeyDownPlugin effects={props.effects} mentionsOpen={mentionsOpen} />
|
|
125
127
|
</div>
|
|
126
128
|
</div>
|