diff --git a/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue b/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue index fd3ca930b..f673ce6ea 100644 --- a/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue +++ b/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue @@ -55,6 +55,18 @@ void geometryCanvas; void detailsEditorContainer; void sideJsonPreviewContainer; +function startJsonEditFromBlankArea(event: MouseEvent) { + const target = event.target as { closest?: (selector: string) => Element | null } | null; + const line = target?.closest?.(".cm-line"); + if (line) { + const range = line.ownerDocument.createRange(); + range.selectNodeContents(line); + const textHit = Array.from(range.getClientRects()).some((rect) => event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom); + if (textHit) return; + } + emit("startEdit"); +} + defineExpose({ openSearch }); @@ -159,9 +171,10 @@ defineExpose({ openSearch }); v-else-if="detail.formattedJson" ref="sideJsonPreviewContainer" data-cell-detail-editor-root + data-cell-detail-json-preview class="overflow-hidden rounded border bg-muted/20 p-2" :class="[{ 'cursor-text': detail.isEditable }, valueFillsHeight ? 'min-h-0 flex-1' : 'h-72 max-h-[42vh]']" - @dblclick.capture="emit('startEdit')" + @dblclick.capture="startJsonEditFromBlankArea" />
 {
     expect(updateOpen).toHaveBeenCalledWith(false);
   });
 
-  it("forwards panel edit/copy/cancel actions and exposes search", () => {
+  it("forwards panel actions and only starts JSON editing from preview whitespace", async () => {
     const startEdit = vi.fn();
     const copyValue = vi.fn();
     const cancel = vi.fn();
@@ -959,6 +959,26 @@ describe("cell detail surfaces", () => {
     expect(cancel).toHaveBeenCalledOnce();
     mounted.exposed.value.openSearch();
     expect(mocks.panelOpenSearch).toHaveBeenCalledOnce();
+
+    await mounted.setProps({ detail: detail() });
+    const jsonPreview = findOne(mounted.root, (node) => node.props["data-cell-detail-json-preview"] === "");
+    const doubleClickCapture = jsonPreview.props.onDblclickCapture;
+    const textLine = {
+      ownerDocument: {
+        createRange: () => ({
+          selectNodeContents: vi.fn(),
+          getClientRects: () => [{ left: 10, right: 110, top: 20, bottom: 40 }],
+        }),
+      },
+    };
+    const lineTarget = { closest: (selector: string) => (selector === ".cm-line" ? textLine : null) };
+
+    doubleClickCapture({ target: lineTarget, clientX: 60, clientY: 30 });
+    expect(startEdit).toHaveBeenCalledTimes(2);
+
+    doubleClickCapture({ target: lineTarget, clientX: 160, clientY: 30 });
+    doubleClickCapture({ target: { closest: () => null }, clientX: 60, clientY: 80 });
+    expect(startEdit).toHaveBeenCalledTimes(4);
   });
 });