дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +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
103 рядки
3.2 KiB
TypeScript
103 рядки
3.2 KiB
TypeScript
import { useMemo, useState, type KeyboardEvent } from "react";
|
|
import clsx from "clsx";
|
|
import { HighlightedText, Icon, Input, List, Popup, T, firstSelectable, listNavigate, type MenuItem } from "@/ui";
|
|
import { closeNewPopup, newPopupRows, useNewPopup, type NewPopupRow } from "./newPopupModel";
|
|
import styles from "./NewPopup.module.css";
|
|
|
|
const isSeparator = (r: NewPopupRow) => !!r.separator;
|
|
|
|
/** Overlay: IDEA's "New" popup — file kinds with a type-to-filter field. */
|
|
export function NewPopupHost() {
|
|
const request = useNewPopup((s) => s.request);
|
|
if (!request) return null;
|
|
return <NewPopup key={request.seq} at={request.at} items={request.items} />;
|
|
}
|
|
|
|
function NewPopup({ at, items }: { at: { x: number; y: number }; items: MenuItem[] }) {
|
|
const [query, setQuery] = useState("");
|
|
const [active, setActive] = useState(-1);
|
|
const rows = useMemo(() => newPopupRows(items, query), [items, query]);
|
|
const current =
|
|
active >= 0 && active < rows.length && !rows[active]!.separator ? active : firstSelectable(rows, isSeparator);
|
|
|
|
const pick = (row: NewPopupRow | undefined) => {
|
|
if (!row?.run) return;
|
|
closeNewPopup();
|
|
row.run();
|
|
};
|
|
|
|
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
const next = listNavigate(e.key === "ArrowUp" || e.key === "ArrowDown" ? e.key : "", {
|
|
items: rows,
|
|
active: current,
|
|
isSection: isSeparator,
|
|
wrap: true,
|
|
});
|
|
if (next !== null) {
|
|
e.preventDefault();
|
|
setActive(next);
|
|
} else if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
pick(rows[current]);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Popup
|
|
open
|
|
anchor={at}
|
|
placement="bottom-start"
|
|
onClose={closeNewPopup}
|
|
role="dialog"
|
|
aria-label="New"
|
|
className={styles.popup}
|
|
>
|
|
<div className={styles.search}>
|
|
<Icon name="search" size={14} className={styles.searchIcon} />
|
|
<Input
|
|
variant="bare"
|
|
data-autofocus
|
|
value={query}
|
|
placeholder="Search for file type"
|
|
aria-label="Search for file type"
|
|
className={styles.input}
|
|
onChange={(e) => {
|
|
setQuery(e.target.value);
|
|
setActive(-1);
|
|
}}
|
|
onKeyDown={onKeyDown}
|
|
/>
|
|
</div>
|
|
<List
|
|
items={rows}
|
|
getKey={(r) => r.key}
|
|
isSection={isSeparator}
|
|
renderSection={() => <div className={styles.separator} />}
|
|
sectionHeight={9}
|
|
activeIndex={current}
|
|
onActiveChange={setActive}
|
|
onActivate={(r) => pick(r)}
|
|
focusable={false}
|
|
hoverSelects
|
|
rowHeight={24}
|
|
className={styles.list}
|
|
aria-label="File types"
|
|
empty={
|
|
<div className={styles.empty}>
|
|
<T>Nothing found</T>
|
|
</div>
|
|
}
|
|
renderItem={(r, state) => (
|
|
<div className={clsx(styles.row, state.active && styles.active)}>
|
|
<span className={styles.icon}>{r.icon && <Icon name={r.icon} size={16} />}</span>
|
|
<span className={styles.label}>
|
|
<HighlightedText text={r.label} indices={r.matches ?? undefined} />
|
|
</span>
|
|
{r.shortcut && <span className={styles.shortcut}>{r.shortcut}</span>}
|
|
</div>
|
|
)}
|
|
/>
|
|
</Popup>
|
|
);
|
|
}
|