дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
Project & files - Project view modes (Project Files / Open Files / All Changed Files / Scratches); External Libraries for Python, JDK (src.zip), Node, Cargo, Go, Maven/Gradle jars; read-only jar/zip browsing - ⌘N New popup with type search and more file kinds; New Project, Rename Project, Reload All from Disk, Export/Import Settings, Power Save Mode - File type associations, extra content roots and exclude patterns (indexed and watched without reopening the project) Editor & navigation - Find bar: search scope (comments/strings), Preserve Case, Exclude, multiline, history; Find in Files: Module scope, custom pattern scopes, multiline - Search Everywhere filters, Assign Shortcut, settings results; named bookmark lists, file/folder bookmarks; Structure filters and Show Inherited; Recent Locations timestamps and breadcrumbs; Code Vision author - Completion options, editor tab placement/layout, inline completion (LSP 3.18) - Spell checking (Typo inspection, ide-spell) and Inspections settings with custom profiles; Code Style schemes, formatter markers, Do not format Run, VCS, services, database - Typed run configurations (Shell, Program, npm, Gradle, Maven, Docker, Compound), Modify options, Before launch, env dialog, Current File, Emulate terminal (PTY) - Git log graph/view options, Go to Hash/Branch/Tag, date filter keeps the graph; Pull/Merge/Rebase dialogs, Manage Remotes, Worktrees - Generic Services host (Docker connections/registries, run sessions); TODO scope/changes tabs with changelists; Coverage report import - Data sources: wire-compatible vendors, URL import, SSL certs, Options, Schemas and Advanced tabs, color and comment Workbench - Run Anything, Customize Main Toolbar, named tool window layouts, menu completeness, Code Cleanup, macros, "Don't Show Again", editor banners - Chinese translations for the new UI Claude-Session: https://claude.ai/code/session_01SaPPsVPuk6ZTNsoUCNCbMv
118 рядки
3.9 KiB
TypeScript
118 рядки
3.9 KiB
TypeScript
/** Dialog stack of the analysis feature and the shared "Specify Scope" fields. */
|
|
import { Fragment, type ReactNode } from "react";
|
|
import { create } from "zustand";
|
|
import { executeAction } from "@/platform/actions";
|
|
import { isTauri } from "@/platform/ipc";
|
|
import { EDIT_SCOPES_ACTION, useCustomScopeList } from "@/platform/search";
|
|
import { Button, IconButton, Input, Select, T } from "@/ui";
|
|
import { SCOPE_LABELS, currentFile, type ScopeChoice, type ScopeKind } from "./scope";
|
|
import styles from "./Analysis.module.css";
|
|
|
|
interface Entry {
|
|
id: number;
|
|
render: (close: () => void) => ReactNode;
|
|
}
|
|
|
|
const useDialogs = create<{ stack: Entry[] }>(() => ({ stack: [] }));
|
|
let seq = 0;
|
|
|
|
/** Shows a dialog; `render` receives the function that closes it. */
|
|
export function openDialog(render: (close: () => void) => ReactNode): () => void {
|
|
const id = ++seq;
|
|
const close = () => useDialogs.setState((s) => ({ stack: s.stack.filter((e) => e.id !== id) }));
|
|
useDialogs.setState((s) => ({ stack: [...s.stack, { id, render }] }));
|
|
return close;
|
|
}
|
|
|
|
export function AnalysisDialogHost() {
|
|
const stack = useDialogs((s) => s.stack);
|
|
return (
|
|
<>
|
|
{stack.map((e) => (
|
|
<Fragment key={e.id}>
|
|
{e.render(() => useDialogs.setState((s) => ({ stack: s.stack.filter((x) => x.id !== e.id) })))}
|
|
</Fragment>
|
|
))}
|
|
</>
|
|
);
|
|
}
|
|
|
|
async function browseDirectory(initial: string): Promise<string | null> {
|
|
if (!isTauri()) return null;
|
|
const { open } = await import("@tauri-apps/plugin-dialog");
|
|
const picked = await open({ directory: true, multiple: false, defaultPath: initial || undefined });
|
|
return typeof picked === "string" ? picked : null;
|
|
}
|
|
|
|
const KINDS: ScopeKind[] = ["project", "directory", "file", "changed", "custom"];
|
|
|
|
/** IDEA's "Specify Scope" radio group. */
|
|
export function ScopeFields({
|
|
value,
|
|
onChange,
|
|
name,
|
|
kinds = KINDS,
|
|
}: {
|
|
value: ScopeChoice;
|
|
onChange: (v: ScopeChoice) => void;
|
|
name: string;
|
|
/** The choices offered (default: all). */
|
|
kinds?: readonly ScopeKind[];
|
|
}) {
|
|
const file = currentFile();
|
|
const scopes = useCustomScopeList();
|
|
return (
|
|
<fieldset className={styles.scope}>
|
|
<legend className={styles.fieldLabel}>
|
|
<T>Scope</T>
|
|
</legend>
|
|
{kinds.map((kind) => (
|
|
<label key={kind} className={styles.radioRow}>
|
|
<input
|
|
type="radio"
|
|
name={name}
|
|
checked={value.kind === kind}
|
|
disabled={kind === "file" && !file}
|
|
onChange={() =>
|
|
onChange({ ...value, kind, custom: kind === "custom" ? (value.custom ?? scopes[0]?.name) : value.custom })
|
|
}
|
|
/>
|
|
<span>{kind === "file" && file ? `File '${file.split("/").pop()}'` : SCOPE_LABELS[kind]}</span>
|
|
</label>
|
|
))}
|
|
{value.kind === "directory" && (
|
|
<div className={styles.dirRow}>
|
|
<Input
|
|
aria-label="Directory"
|
|
value={value.directory}
|
|
onChange={(e) => onChange({ ...value, directory: e.target.value })}
|
|
className={styles.grow}
|
|
/>
|
|
<Button
|
|
onClick={() => void browseDirectory(value.directory).then((d) => d && onChange({ ...value, directory: d }))}
|
|
>
|
|
<T>Browse…</T>
|
|
</Button>
|
|
</div>
|
|
)}
|
|
{value.kind === "custom" && (
|
|
<div className={styles.dirRow}>
|
|
<Select<string>
|
|
className={styles.grow}
|
|
value={value.custom ?? null}
|
|
options={scopes.map((s) => ({ value: s.name, label: s.name, detail: s.pattern }))}
|
|
placeholder="No custom scopes"
|
|
onChange={(custom) => onChange({ ...value, custom })}
|
|
aria-label="Custom scope"
|
|
/>
|
|
<IconButton
|
|
icon="more-horizontal"
|
|
title="Edit Scopes…"
|
|
onClick={() => void executeAction(EDIT_SCOPES_ACTION, value.custom ? { select: value.custom } : undefined)}
|
|
/>
|
|
</div>
|
|
)}
|
|
</fieldset>
|
|
);
|
|
}
|