Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion plugins/VelaShell.Plugin.Mongo/Loc.Design.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private static readonly (string Key, string En, string Zh)[] DesignTexts =

// ── 新建索引 ──
("Design_Fields", "Fields", "字段"),
("Design_DragHint", "Drag to reorder", "拖动调整顺序"),
("Design_DragHint", "Key order is the prefix order · ↑ ↓ or drag ⠿", "键序即前缀顺序 · ↑ ↓ 或拖动 ⠿ 调整"),
("Design_FieldPlaceholder", "field.path", "字段路径"),
("Design_AddField", "Add field", "添加字段"),
("Design_Kind", "Type", "类型"),
Expand All @@ -130,6 +130,21 @@ private static readonly (string Key, string En, string Zh)[] DesignTexts =
("Design_RoleSort", "sort", "排序"),
("Design_RoleRange", "range", "范围"),
("Design_CreateIndex", "Create index", "创建索引"),
("Design_EditorTarget", "Runs createIndexes on {0}", "将在 {0} 上执行 createIndexes"),
("Design_DraftBadge", "new", "新建"),
("Design_DraftUsage", "not created yet", "待创建"),
("Design_ColOrder", "Order / type", "排序 / 类型"),
("Design_ColRole", "ESR role", "ESR 角色"),
("Design_ColSampled", "Sampled type", "抽样类型"),
("Design_MoveUp", "Move up", "上移"),
("Design_MoveDown", "Move down", "下移"),
("Design_RemoveField", "Remove field", "移除字段"),
("Design_TypeOptions", "Type & options", "类型与选项"),
("Design_Preview", "Command preview", "命令预览"),
("Design_OptUniqueHint", "rejects duplicate keys", "拒绝重复键值"),
("Design_OptSparseHint", "skips docs without the field", "跳过缺该字段的文档"),
("Design_OptPartialHint", "only docs matching the filter", "只索引满足条件的文档"),
("Design_CloseEditor", "Close the editor", "关闭编辑器"),
("Design_NoSampleFields", "No sampled fields — type a field path", "没有抽样字段 · 直接输入字段路径"),
("Design_ErrNoFields", "Add at least one field", "至少添加一个字段"),
("Design_ErrUniqueKind", "Text, hashed, wildcard and 2dsphere indexes can't be unique", "文本、哈希、通配符与 2dsphere 索引不能设为唯一"),
Expand Down
5 changes: 4 additions & 1 deletion plugins/VelaShell.Plugin.Mongo/Loc.GridFs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ private static readonly (string Key, string En, string Zh)[] GridFsTexts =
("Fs_NoMatch", "No files match “{0}”", "没有匹配 “{0}” 的文件"),
("Fs_Truncated", "Showing the first {0} items — narrow it down with search", "仅显示前 {0} 项 · 用搜索缩小范围"),
("Fs_OpenFolder", "Open", "打开"),
("Fs_DropHere", "Drop files here to upload to {0}", "拖拽文件到此处上传到 {0}"),
("Fs_DropHere", "Drop files here, or click to choose files — they go to {0}", "拖拽文件到此处,或点击选择文件 · 上传到 {0}"),
("Fs_DropPickFiles", "Choose files…", "选择文件…"),
("Fs_DropPickFolder", "Choose a folder…", "选择文件夹…"),
("Fs_DropClickTip", "Click to choose files to upload", "点击选择要上传的文件"),
("Fs_DropHint", "No 16 MB per-file limit · chunkSize 255 KB · metadata can be edited before upload",
"单文件上限不受 16 MB 限制 · chunkSize 255 KB · 可在上传前编辑 metadata"),

Expand Down
3 changes: 3 additions & 0 deletions plugins/VelaShell.Plugin.Mongo/Loc.Monitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ private static readonly (string Key, string En, string Zh)[] MonitorTexts =
("Mon_StorageIndex", "Indexes", "索引"),
("Mon_StorageTip", "data {0} · indexes {1}", "数据 {0} · 索引 {1}"),
("Mon_StorageEmpty", "No user collections yet", "还没有用户集合"),
("Mon_StorageColName", "Collection", "集合"),
("Mon_StorageColBar", "Data + indexes", "数据 + 索引"),
("Mon_StorageColSize", "Size", "占用"),

// ── 事件 ──
("Mon_EventsTitle", "Events", "事件"),
Expand Down
14 changes: 13 additions & 1 deletion plugins/VelaShell.Plugin.Mongo/Loc.Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,19 @@ private static readonly (string Key, string En, string Zh)[] QueryTexts =
("Query_Unlimited", "unlimited", "不限"),
("Query_MaxTimeTip", "Default server-side time limit for each statement (a .maxTimeMS() in the statement wins)",
"每条语句默认的服务器端超时(语句里写了 .maxTimeMS() 以语句为准)"),
("Query_DatabaseTip", "Current database — same as running use", "当前库 —— 切换等同于执行 use"),
("Query_DatabaseTip", "Database the statements run on — same as running use; db.<name> resolves here", "语句在哪个库上执行 —— 切换等同于执行 use;db.<集合> 在这个库里解析"),
("Query_ConnectionLabel", "Connection", "连接"),
("Query_DatabaseLabel", "Database", "数据库"),
("Query_ConnectionTip", "Connection the statements run on — switching keeps the script", "语句在哪条连接上执行 —— 切换时脚本原样带过去"),
("Query_ConnNotConnected", "not connected", "未连接"),
("Query_ConnFailed", "failed", "连接失败"),
("Query_SwitchingTo", "Connecting {0}…", "正在连接 {0}…"),
("Query_TargetHint", "Runs on {0} / {1}", "在 {0} / {1} 上执行"),
("Query_SwitchWhileRunning", "Stop the running statements before switching connections", "先停止正在运行的语句,再切换连接"),
("Query_SwitchFailed", "Couldn't connect {0}", "无法连接 {0}"),
("Query_SwitchedTo", "The query now runs on {0} · {1}", "查询已切换到 {0} · {1}"),
("Query_SwitchedDbMissing", "{0} has no database {1}; using {2}", "{0} 上没有库 {1},改用 {2}"),
("Query_CollectionMissing", "No collection {0} in database {1} — check the database picker or use()", "库 {1} 里没有集合 {0} —— 核对上方的数据库或 use()"),

// ── 状态 ──
("Query_StatusUnsaved", "Query {0} · unsaved", "查询 {0} · 未保存"),
Expand Down
6 changes: 3 additions & 3 deletions plugins/VelaShell.Plugin.Mongo/Loc.Workspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ private static readonly (string Key, string En, string Zh)[] WorkspaceTexts =
("Detail_Anonymous", "(no auth)", "(未认证)"),

// ── 标签页 ──
("Tab_CloseModifiedTitle", "Discard pending changes?", "放弃未提交的修改?"),
("Tab_CloseModifiedBody", "{0} has changes that were not applied. Closing the tab discards them.",
"{0} 有尚未应用的修改,关闭标签页会丢掉它们。"),
("Tab_CloseModifiedTitle", "Discard unsaved changes?", "放弃未保存的修改?"),
("Tab_CloseModifiedBody", "{0} has unsaved changes. Closing the tab discards them and can't be undone.",
"{0} 有未保存的修改,关闭标签页将放弃这些更改,且无法恢复。"),
("Tab_CloseDiscard", "Discard and close", "放弃并关闭"),
("Tab_Close", "Close", "关闭"),
("Tab_CloseOthers", "Close others", "关闭其他"),
Expand Down
22 changes: 20 additions & 2 deletions plugins/VelaShell.Plugin.Mongo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> id `velashell.mongo` · 进程内装载 · 命令面板入口(`contributes.commands`,与 Docker 面板同一个路数) · `minSdkVersion` 2.0.2

照设计稿 `velashell-plugin-mongo.pen`(仓库根,23 个画板)实现的 MongoDB 客户端:
照设计稿 `velashell-plugin-mongo.pen`(仓库根,24 个画板)实现的 MongoDB 客户端:
Navicat 式的对象树与大图标工具栏,补齐 Compass 的聚合管道、Schema 分析、索引建议与 GridFS;
网格 / 树 / JSON 三视图共用一份数据,所有编辑先进暂存区,提交前可预览将执行的命令。

Expand Down Expand Up @@ -48,7 +48,7 @@ Navicat 式的对象树与大图标工具栏,补齐 Compass 的聚合管道、Sc
| (稿外)系统库 | admin / config / local 默认不列(`showSystemDatabases`,高级选项);对象树表头的眼睛按钮在本次会话里切换;连接里点名的默认库照列 | Navicat 的默认;日常几乎不进这三个库,列着只会把用户库往下挤 |
| 10 的「分组」 | 对话框里一栏(可选已有分组,也可新写);对象树里分了组的连接挂在可收起的组名节下面,不分组的在最上面 | — |
| 01 / 13 对象树根上只画了一条连接 | 根上是**全部**已保存的连接(Navicat 的习惯):灰点没连、绿点连着、红点没连上;双击连上,连着的那条下面挂库;右键里有连接 / 断开 / 编辑 / 复制 / 删除 | 连接由插件自己管之后,树根就是连接列表 |
| 14 执行计划时编辑器收矮到 230 | 不动分界:切到执行计划页,编辑器与结果区的高度照用户拖过的分隔条 | 结果页与执行计划页是同一块面板的两个页签;自动收矮再在切回时重置成默认值,用户拖好的高度就白拖了 |
| 14 执行计划时编辑器收矮到 194 | 不动分界:切到执行计划页,编辑器与结果区的高度照用户拖过的分隔条 | 结果页与执行计划页是同一块面板的两个页签;自动收矮再在切回时重置成默认值,用户拖好的高度就白拖了 |
| 01 对象树的分组文件夹右键只有刷新 | 「集合 / 视图 / GridFS 存储桶」分组(及其中对象、库行)右键可新建对应对象;桶行右键可上传文件 / 文件夹、删除存储桶 | 不打开对象列表也能就地新建;新建桶原先只能从已打开的桶标签里进 |
| 会话标签上的连接名(「mongo-inner-01」) | 宿主标签条上是一个「MongoDB」面板;连接名写在对象树根、对象标签与状态条上 | 一个面板装着全部连接(与 Docker 面板一样);对象标签按连接分开,同名集合在两条连接里是两个标签 |
| 22「连接中 / 连接失败」两张卡 | 照稿实现在插件里:连接时先开一个占位标签(「正在连接 X」+ 取消),连上了换成默认库的对象列表,没连上就原地变成「无法连接 X」+ 等宽字的原因 + 编辑连接 / 关闭标签页 / 重新连接;证书不受信任时多一个「信任此证书并重连」(只信这一张的指纹)。连接**中途断开**时内容区顶部一条横幅 | 证书信任原先是宿主的流程,连接自己管之后由插件给 |
Expand All @@ -75,6 +75,24 @@ Navicat 式的对象树与大图标工具栏,补齐 Compass 的聚合管道、Sc
日期不再给"现在"那一条文字候选。
- **筛选框的 Enter**:补全弹层开着时是「接受补全」,关着才是「查找」。筛选框在编辑区上另挂了隧道处理器,
而同一元素上的隧道处理器按**注册的逆序**调用 —— 它比 `CodeEditor` 自己的先拿到 Enter,所以要先看 `CodeEditor.IsCompletionOpen`。
- **新建索引**(设计稿 07b,`DesignIndexesView` + `DesignTabViewModel.NewIndex.cs`):Navicat 设计表的做法 —— 点「新建索引」后索引页下半部分
换成整宽编辑器(字段表 / 类型与选项 / 命令预览与预估),索引表末尾钉一行「新建」草稿(`DraftIndex`,与表里的行同一个 `IndexRow` 模型,
不进 `Indexes`,所以重名检查、删除、隐藏都碰不到它)。字段表的行距是 28,`DesignIndexesView.RowPitch` 跟着它算拖动落点;
上移 / 下移按钮走 `MoveKeyUpCommand / MoveKeyDownCommand`,与拖动是同一个 `MoveKey`。编辑器开着时索引表的滚动区
(`ScrollViewer#TableScroll.compact`)最多 180 高。
- **查询的执行目标**(`QueryTabViewModel.Target.cs`):工具行下行的「连接 ▾ 数据库 ▾」。标签的连接定死在构造里,
所以切换连接是 `MongoWorkspaceViewModel.ReplaceTab` 原位换一个新连接上的查询标签(`CarryOver` 带过文本、未保存基线、光标、
maxTimeMS);没连着的连接走 `ConnectAsync(entry, quiet: true)`(不开占位标签、不动树的选中、不开对象列表)。
诊断的第二段(`CheckCollectionsAsync`,异步)查 `db.集合` 在生效的库里有没有,没有就是一条 Warning;
脚本里建删了集合(`ChangesCatalog`)时 `ForgetCollections` 丢掉那个库的缓存。
- **GridFS 拖放区可点**:`DropZone` 的 Tapped 走 `UploadFilesCommand`;源头在框里那两个链接按钮里时不再重复弹(它们自己处理)。
- **服务器监控**(`MonitorTabView`):两行图表 `*` : `*` 分高度,中间 `hsplit` 可拖;两张柱状图 `MinSlots` = 满窗口的柱数
(`ChartSlots`),刚开始采样时柱子靠右、保持最终宽度。存储 Top 的集合名列是 `Width="Auto" SharedSizeGroup="StorageName"`
(各行对齐、默认放下最长的名字)再挂 `ui:TableColumns` 组 `storage`(拖宽、双击自适应);名字是两个 `Run`(库名淡色 + 集合名),
两个 `Run` 必须写在同一行,中间的换行会被画成一个空格。
- **有未保存修改的标签**:标签上是橙点,鼠标移到标签上换成 ×(`MongoWorkspaceView` 里 `Ellipse.dirty` / `Button.tabclose` 两组样式,
别再给它们绑 `IsVisible` —— 本地值会压过 `:pointerover` 样式,× 就永远出不来,有修改的标签就关不掉了)。
点 × 或 Ctrl+W 都先过 `WorkspaceTab.ConfirmCloseAsync`:「放弃未保存的修改?」,确认才关。
- **代码里建的右键菜单**一律走 `MenuKit.Command`:普通项不设 `Foreground` —— 哪怕设成 `null`,本地值也会压过宿主
`ContextMenu MenuItem` 样式,文字透明到悬停才露出来。
- **绑定不穿过可空的中段**:`{Binding Dialog.Title}`、`{Binding Editor.HasError}` 这类路径在中段为 null 时
Expand Down
64 changes: 50 additions & 14 deletions plugins/VelaShell.Plugin.Mongo/Ui/Charts/BarChart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,18 @@ public sealed class BarChart : Control
public static readonly StyledProperty<int> XLabelCountProperty =
AvaloniaProperty.Register<BarChart, int>(nameof(XLabelCount), 5);

/// <summary>
/// 至少按这么多根柱分格(0 = 有几根分几格)。时间序列刚开始采样时只有寥寥几根:不设它,每根柱都被拉得和整张图一样宽;
/// 设成满窗口的根数,柱子从一开始就是最终的宽度,靠右排(最新的贴着「现在」),左边空着等数据填进来。
/// </summary>
public static readonly StyledProperty<int> MinSlotsProperty =
AvaloniaProperty.Register<BarChart, int>(nameof(MinSlots));

private int _hover = -1;

static BarChart()
{
AffectsRender<BarChart>(SeriesProperty, LabelsProperty, MaxValueProperty, ShowAxesProperty, GapRatioProperty);
AffectsRender<BarChart>(SeriesProperty, LabelsProperty, MaxValueProperty, ShowAxesProperty, GapRatioProperty, MinSlotsProperty);
}

/// <summary>数据。</summary>
Expand Down Expand Up @@ -91,6 +98,13 @@ public double GapRatio
set => SetValue(GapRatioProperty, value);
}

/// <summary>至少分几格。</summary>
public int MinSlots
{
get => GetValue(MinSlotsProperty);
set => SetValue(MinSlotsProperty, value);
}

/// <summary>横轴标签个数。</summary>
public int XLabelCount
{
Expand All @@ -114,14 +128,20 @@ public bool ShowTooltip

private int Count => Series is { Count: > 0 } s ? s.Max(static x => x.Values.Count) : 0;

/// <summary>分几格(柱数与 <see cref="MinSlots" /> 取大)。</summary>
private int Slots => Math.Max(Count, Math.Max(0, MinSlots));

/// <summary>第一根柱前面空着的格数(柱靠右排)。</summary>
private int Offset => Slots - Count;

/// <inheritdoc />
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
int count = Count;
Rect plot = Plot;
double x = e.GetPosition(this).X;
int index = count == 0 || plot.Width <= 0 ? -1 : (int)((x - plot.X) / (plot.Width / count));
int index = count == 0 || plot.Width <= 0 ? -1 : (int)Math.Floor((x - plot.X) / (plot.Width / Slots)) - Offset;
index = index < 0 || index >= count ? -1 : index;
if (index != _hover)
{
Expand Down Expand Up @@ -173,11 +193,13 @@ public override void Render(DrawingContext context)
}
}

double slot = plot.Width / count;
double slot = plot.Width / Slots;
// 柱靠右排:origin 是第 0 根柱所在格的左边。
double origin = plot.Left + (slot * Offset);
double barWidth = Math.Max(1, slot * (1 - Math.Clamp(GapRatio, 0, 0.9)));
for (int i = 0; i < count; i++)
{
double x = plot.Left + (slot * i) + ((slot - barWidth) / 2);
double x = origin + (slot * i) + ((slot - barWidth) / 2);
double bottom = plot.Bottom;
int top = -1;
for (int k = series.Count - 1; k >= 0; k--)
Expand Down Expand Up @@ -212,30 +234,44 @@ public override void Render(DrawingContext context)
}
if (i == _hover)
{
context.FillRectangle(ThemeBrushes.GetDim("VelaTextPrimary", 0.08), new Rect(plot.Left + (slot * i), plot.Top, slot, plot.Height));
context.FillRectangle(ThemeBrushes.GetDim("VelaTextPrimary", 0.08), new Rect(origin + (slot * i), plot.Top, slot, plot.Height));
}
}

if (ShowAxes && Labels is { Count: > 0 } labels)
{
int n = Math.Clamp(XLabelCount, 2, labels.Count);
IEnumerable<int> picks = Enumerable.Range(0, n).Select(k => (int)Math.Round((double)k * (labels.Count - 1) / (n - 1))).Distinct();
foreach (int i in picks)
var placed = new List<(FormattedText Text, double X)>();
foreach (int i in Enumerable.Range(0, n).Select(k => (int)Math.Round((double)k * (labels.Count - 1) / (n - 1))).Distinct())
{
var text = new FormattedText(labels[i], CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, 9, muted);
double cx = plot.Left + (slot * i) + (slot / 2) - (text.Width / 2);
cx = Math.Clamp(cx, plot.Left, Math.Max(plot.Left, plot.Right - text.Width));
context.DrawText(text, new Point(cx, plot.Bottom + 3));
double cx = origin + (slot * i) + (slot / 2) - (text.Width / 2);
placed.Add((text, Math.Clamp(cx, plot.Left, Math.Max(plot.Left, plot.Right - text.Width))));
}
// 图窄时标签会叠在一起(最后那个还带着「现在」):首尾一定画,中间的挨不下就跳过。
const double labelGap = 8;
double right = double.NegativeInfinity;
double lastLeft = placed.Count > 1 ? placed[^1].X : double.PositiveInfinity;
for (int k = 0; k < placed.Count; k++)
{
(FormattedText text, double x) = placed[k];
bool last = k == placed.Count - 1;
if (!last && k > 0 && (x < right + labelGap || x + text.Width > lastLeft - labelGap))
{
continue;
}
context.DrawText(text, new Point(x, plot.Bottom + 3));
right = x + text.Width;
}
}

if (ShowTooltip && _hover >= 0)
{
DrawTooltip(context, typeface, series, plot, slot);
DrawTooltip(context, typeface, series, plot, slot, origin);
}
}

private void DrawTooltip(DrawingContext context, Typeface typeface, IReadOnlyList<ChartSeries> series, Rect plot, double slot)
private void DrawTooltip(DrawingContext context, Typeface typeface, IReadOnlyList<ChartSeries> series, Rect plot, double slot, double origin)
{
IBrush primary = ThemeBrushes.Get("VelaTextPrimary", Brushes.White);
IBrush secondary = ThemeBrushes.Get("VelaTextSecondary", Brushes.LightGray);
Expand All @@ -249,10 +285,10 @@ private void DrawTooltip(DrawingContext context, Typeface typeface, IReadOnlyLis
const double row = 15;
double width = 150;
double height = 10 + (title.Length > 0 ? row : 0) + (lines.Count * row);
double x = plot.Left + (slot * _hover) + slot + 6;
double x = origin + (slot * _hover) + slot + 6;
if (x + width > Bounds.Width)
{
x = plot.Left + (slot * _hover) - width - 6;
x = origin + (slot * _hover) - width - 6;
}
double y = plot.Top + 4;
var box = new Rect(Math.Max(0, x), y, width, height);
Expand Down
Loading