дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
fix(ui): Make read-only content selectable with a Copy context menu (#48)
Root cause: body is user-select: none and each feature had to opt back in; the Markdown preview (and other read-only views) never did, and the app-wide contextmenu guard left no Copy item. Fix: shared selectableText class (src/ui) for read-only content, and a fallback Copy context menu from browserGuards when right-clicking a selection or a selectable area (component menus still win). Siblings covered: Quick Documentation / hover / completion docs, hover errors, Quick Definition, debugger value tooltip, inspection messages, message dialogs (no drag on body text), notification titles, About dialog; existing selectable consoles now get Copy too. Verified in-app on macOS: select in Markdown preview, right-click Copy puts the text on the clipboard; tab context menu unchanged. Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
Цей коміт міститься в:
1 джерело
6dfd82de4c
коміт
de84a643aa
17 files changed
+453
-55
No files matched your search
@@ -25,6 +25,7 @@ import {
|
||||
type TreeHandle,
|
||||
type TreeRowState,
|
||||
T,
|
||||
selectableText,
|
||||
} from "@/ui";
|
||||
import {
|
||||
cancelInspection,
|
||||
@@ -438,7 +439,7 @@ function ProblemDetails({ node, root }: { node: Node | null; root: string }) {
|
||||
<Icon name={sev} size={14} className={styles[`sev-${sev}`]} />
|
||||
<span>{p.d.source ? `${p.d.source}${p.d.code ? ` (${p.d.code})` : ""}` : <T>Language server</T>}</span>
|
||||
</div>
|
||||
<p className={styles.detailsMessage}>{p.d.message}</p>
|
||||
<p className={clsx(styles.detailsMessage, selectableText)}>{p.d.message}</p>
|
||||
<div className={styles.muted}>
|
||||
{relativeDir(root, p.path) ? `${relativeDir(root, p.path)}/` : ""}
|
||||
{basename(p.path)}:{p.d.range.start.line + 1}:{p.d.range.start.col + 1}
|
||||
|
||||
@@ -3,7 +3,8 @@ import { create } from "zustand";
|
||||
import { executeAction } from "@/platform/actions";
|
||||
import { t as translate, useT } from "@/platform/i18n";
|
||||
import { commands, isTauri, type AppInfo, type BuildInfo } from "@/platform/ipc";
|
||||
import { Dialog, IconButton } from "@/ui";
|
||||
import clsx from "clsx";
|
||||
import { Dialog, IconButton, selectableText } from "@/ui";
|
||||
import logo from "../../../src-tauri/icons/128x128.png";
|
||||
import { buildInfo, guessOs } from "./host";
|
||||
import styles from "./app.module.css";
|
||||
@@ -82,13 +83,13 @@ function AboutDialog({ onClose }: { onClose: () => void }) {
|
||||
<div className={styles.about}>
|
||||
<img className={styles.logo} src={logo} alt="" width={64} height={64} />
|
||||
<div className={styles.aboutBody}>
|
||||
<div className={styles.productName}>OpenIDEA {build?.version ?? ""}</div>
|
||||
<div className={clsx(styles.productName, selectableText)}>OpenIDEA {build?.version ?? ""}</div>
|
||||
{build && (
|
||||
<div className={styles.aboutLine}>
|
||||
<div className={clsx(styles.aboutLine, selectableText)}>
|
||||
{t("app.about.build")} #{build.build} ({build.profile})
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.tagline}>{t("app.about.tagline")}</div>
|
||||
<div className={clsx(styles.tagline, selectableText)}>{t("app.about.tagline")}</div>
|
||||
<dl className={styles.facts}>
|
||||
{rows
|
||||
.filter(([k]) => k !== t("app.about.version") && k !== t("app.about.build"))
|
||||
@@ -116,12 +117,12 @@ function AboutDialog({ onClose }: { onClose: () => void }) {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.license}>
|
||||
<div className={clsx(styles.license, selectableText)}>
|
||||
{t("app.about.copyright")}
|
||||
<br />
|
||||
{t("app.about.license")}
|
||||
</div>
|
||||
<div className={styles.disclaimer}>
|
||||
<div className={clsx(styles.disclaimer, selectableText)}>
|
||||
{t("app.about.thirdParty")} {t("app.about.disclaimer")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { workflowSettings } from "@/platform/workflowSettings";
|
||||
import { activeSession, evaluate } from "../state";
|
||||
import { inlineValue } from "../values";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import { iconMarkup } from "@/ui";
|
||||
import { iconMarkup, selectableText } from "@/ui";
|
||||
import { mountValueTree } from "../ui/ValueHintTree";
|
||||
import styles from "./editor.module.css";
|
||||
|
||||
@@ -59,7 +59,7 @@ export function valueTooltip(): Extension {
|
||||
above: true,
|
||||
create: () => {
|
||||
const dom = document.createElement("div");
|
||||
dom.className = styles.valueTooltip!;
|
||||
dom.className = `${styles.valueTooltip} ${selectableText}`;
|
||||
let tree: { unmount(): void } | null = null;
|
||||
if (value.variablesReference > 0) {
|
||||
// IDEA's value hint: the "+" expands the object into a variables tree in place.
|
||||
@@ -73,7 +73,7 @@ export function valueTooltip(): Extension {
|
||||
expand.addEventListener("click", () => {
|
||||
if (tree) return;
|
||||
expand.remove();
|
||||
dom.classList.add(styles.valueTooltipTree!);
|
||||
dom.classList.replace(selectableText, styles.valueTooltipTree!);
|
||||
dom.replaceChildren();
|
||||
tree = mountValueTree(dom, expr.text, value, session.id);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import clsx from "clsx";
|
||||
import { call, commands, isTauri } from "@/platform/ipc";
|
||||
import { maybeEditorService } from "@/workbench/contributions";
|
||||
import { highlightInto } from "@/editor/lsp/markdown";
|
||||
import { IconButton } from "@/ui";
|
||||
import { IconButton, selectableText } from "@/ui";
|
||||
import { useLiveMarkdown } from "./live";
|
||||
import { modeOf, useMarkdownModes } from "./modes";
|
||||
import { ANCHOR_PREFIX, previewOffsetForLine, renderMarkdown, resolveLink, slugify } from "./render";
|
||||
@@ -140,7 +140,7 @@ function MarkdownPreview({ path, sync }: { path: string; sync: boolean }) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={styles.preview}
|
||||
className={clsx(styles.preview, selectableText)}
|
||||
tabIndex={-1}
|
||||
role="document"
|
||||
aria-label="Markdown preview"
|
||||
|
||||
Reference in new issue
Заблокувати користувача