mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:08:16 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84abdc1885 |
No files matched your search
@@ -369,17 +369,6 @@
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.marketBar {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.marketBar > :first-child {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.categorySelect {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
EmptyState,
|
||||
Icon,
|
||||
IconButton,
|
||||
Input,
|
||||
SearchField,
|
||||
Select,
|
||||
Spinner,
|
||||
@@ -48,6 +47,7 @@ import {
|
||||
} from "./store";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { importIntellijTheme, showInstalledIntellijThemes } from "./intellijImport";
|
||||
import { promptInput } from "@/workbench/inputDialogs";
|
||||
import styles from "./PluginsPage.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
@@ -537,6 +537,21 @@ interface GearActions {
|
||||
install: (packageFile: boolean) => void;
|
||||
importIntellijFile: () => void;
|
||||
importInstalledIntellij: () => void;
|
||||
editRepository: () => void;
|
||||
}
|
||||
|
||||
/** Asks for the marketplace index URL; an empty one restores the default. */
|
||||
async function editMarketUrl() {
|
||||
const url = await promptInput({
|
||||
title: "Plugin Repository",
|
||||
label: "Marketplace index URL:",
|
||||
initial: usePlugins.getState().marketUrl ?? "",
|
||||
placeholder: "https://…/index.json or a folder path",
|
||||
okLabel: "Save",
|
||||
validate: () => null,
|
||||
hint: <T>Leave empty to use the default repository.</T>,
|
||||
});
|
||||
if (url !== null) await setMarketUrl(url);
|
||||
}
|
||||
|
||||
function gearMenu(plugins: readonly PluginInfo[], updates: number, actions: GearActions): MenuItem[] {
|
||||
@@ -550,6 +565,7 @@ function gearMenu(plugins: readonly PluginInfo[], updates: number, actions: Gear
|
||||
{ kind: "separator" },
|
||||
{ label: "Install Plugin from Disk…", icon: "package", run: () => actions.install(false) },
|
||||
{ label: "Install Plugin from Package File…", run: () => actions.install(true) },
|
||||
{ label: "Plugin Repository…", run: actions.editRepository },
|
||||
...(actions.canImport
|
||||
? [
|
||||
{ kind: "separator" as const },
|
||||
@@ -654,30 +670,6 @@ function CategoryFilter({
|
||||
);
|
||||
}
|
||||
|
||||
/** The marketplace index URL, editable in place. */
|
||||
function MarketUrlBar() {
|
||||
const current = usePlugins((s) => s.marketUrl);
|
||||
const [value, setValue] = useState(current ?? "");
|
||||
useEffect(() => setValue(current ?? ""), [current]);
|
||||
return (
|
||||
<form
|
||||
className={styles.marketBar}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
void setMarketUrl(value);
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
value={value}
|
||||
placeholder="Marketplace index URL (https://…/index.json or a folder path)"
|
||||
aria-label="Marketplace index URL"
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
<IconButton icon="refresh" title="Refresh Marketplace" type="submit" />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function MarketRow({ entry, selected, onSelect }: { entry: MarketPlugin; selected: boolean; onSelect: () => void }) {
|
||||
const update = isUpdate(entry);
|
||||
return (
|
||||
@@ -832,6 +824,7 @@ export function PluginsPage() {
|
||||
install: (packageFile) => void installFromDisk(undefined, packageFile).then(showInstalled),
|
||||
importIntellijFile: () => void importIntellijTheme().then(showInstalled),
|
||||
importInstalledIntellij: () => showInstalledIntellijThemes(showInstalled),
|
||||
editRepository: () => void editMarketUrl(),
|
||||
}),
|
||||
);
|
||||
};
|
||||
@@ -876,7 +869,7 @@ export function PluginsPage() {
|
||||
className={styles.search}
|
||||
value={query}
|
||||
onChange={setQuery}
|
||||
placeholder={tab === "installed" ? "Search installed plugins" : "Search local plugins"}
|
||||
placeholder={tab === "installed" ? "Search installed plugins" : "Search plugins"}
|
||||
aria-label="Filter plugins"
|
||||
/>
|
||||
{tab === "installed" ? (
|
||||
@@ -916,20 +909,11 @@ export function PluginsPage() {
|
||||
</ul>
|
||||
) : (
|
||||
<ul className={styles.list} aria-label={tx("Available plugins")}>
|
||||
<li className={styles.marketItem}>
|
||||
<MarketUrlBar />
|
||||
</li>
|
||||
{(market?.length ?? 0) > 0 && (
|
||||
<li className={styles.marketItem}>
|
||||
<CategoryFilter market={market ?? []} value={category} onChange={setCategory} />
|
||||
</li>
|
||||
)}
|
||||
{!marketUrl && !marketError && (
|
||||
<li className={styles.notice} role="note">
|
||||
<Icon name="info" size={14} />
|
||||
<span>{tx`${NO_REPOSITORY}. Showing plugin packages available on this machine.`}</span>
|
||||
</li>
|
||||
)}
|
||||
{marketError && <li className={clsx(styles.listEmpty, styles.errorText)}>{marketError}</li>}
|
||||
{marketShown.length > 0 && <ListHeader title="Marketplace" count={marketShown.length} />}
|
||||
{marketShown.map((m) => (
|
||||
@@ -983,6 +967,8 @@ export function PluginsPage() {
|
||||
/>
|
||||
) : selectedEntry ? (
|
||||
<CatalogDetails key={selectedEntry.id} entry={selectedEntry} />
|
||||
) : marketUrl ? (
|
||||
<EmptyState className={styles.detailsEmpty} title="No plugin selected" />
|
||||
) : (
|
||||
<EmptyState className={styles.detailsEmpty} title={NO_REPOSITORY}>
|
||||
<T>Install a plugin folder with</T> <code>plugin.toml</code>{" "}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { executeAction, executeActionFrom, getAction, registerAction } from "@/p
|
||||
import { useProject } from "@/platform/project";
|
||||
import { pluginThemeRef, useContributedIconThemes } from "@/platform/themes";
|
||||
import { ContextMenuHost, hideContextMenu } from "@/ui";
|
||||
import { InputDialogsHost } from "@/workbench/inputDialogs";
|
||||
import { provideNotificationService, type NotificationService } from "@/workbench/contributions";
|
||||
import { applyThemeAttributes, previewTheme, useThemeOverride } from "@/workbench/theme";
|
||||
import { createMemoryPluginsBackend, sampleCatalog, setPluginsBackend, type PluginsBackend } from "../backend";
|
||||
@@ -561,7 +562,6 @@ describe("PluginsPage", () => {
|
||||
render(<PluginsPage />);
|
||||
await user.click(screen.getByRole("tab", { name: /Available/ }));
|
||||
const list = await screen.findByRole("list", { name: "Available plugins" });
|
||||
expect(within(list).getByText(new RegExp(NO_REPOSITORY))).toBeInTheDocument();
|
||||
await waitFor(() => expect(within(list).getByText("Toy Language")).toBeInTheDocument());
|
||||
await user.click(within(list).getByText("Toy Language"));
|
||||
expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent("Toy Language");
|
||||
@@ -663,6 +663,53 @@ describe("marketplace", () => {
|
||||
expect(asked[0]).toMatchObject({ pluginId: "hello", kind: "install" });
|
||||
});
|
||||
|
||||
it("doesn't claim there is no repository while one is configured", async () => {
|
||||
setPluginsBackend({
|
||||
...createMemoryPluginsBackend([], []),
|
||||
marketUrl: async () => "https://plugins.example/index.json",
|
||||
market: async () => [],
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<PluginsPage />);
|
||||
await user.click(screen.getByRole("tab", { name: /Available/ }));
|
||||
expect(await screen.findByText("No plugin selected")).toBeInTheDocument();
|
||||
expect(screen.queryByText(new RegExp(NO_REPOSITORY))).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("textbox", { name: "Filter plugins" })).toHaveAttribute("placeholder", "Search plugins");
|
||||
});
|
||||
|
||||
it("edits the repository from the gear menu; an empty URL restores the default", async () => {
|
||||
const base = createMemoryPluginsBackend([], []);
|
||||
const set: (string | null)[] = [];
|
||||
setPluginsBackend({
|
||||
...base,
|
||||
marketUrl: async () => "https://plugins.example/index.json",
|
||||
market: async () => [],
|
||||
setMarketUrl: async (url) => void set.push(url),
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<>
|
||||
<PluginsPage />
|
||||
<ContextMenuHost />
|
||||
<InputDialogsHost />
|
||||
</>,
|
||||
);
|
||||
await user.click(screen.getByRole("tab", { name: /Available/ }));
|
||||
expect(screen.queryByRole("textbox", { name: /Marketplace index URL/ })).not.toBeInTheDocument();
|
||||
const edit = async (value: string) => {
|
||||
await user.click(screen.getByRole("button", { name: "Manage Plugins" }));
|
||||
await user.click(screen.getByRole("menuitem", { name: "Plugin Repository…" }));
|
||||
const field = await screen.findByRole("textbox", { name: /Marketplace index URL/ });
|
||||
await waitFor(() => expect(field).toHaveValue("https://plugins.example/index.json"));
|
||||
await user.clear(field);
|
||||
if (value) await user.type(field, value);
|
||||
await user.click(screen.getByRole("button", { name: "Save" }));
|
||||
};
|
||||
await edit("https://mirror.example/index.json");
|
||||
await edit("");
|
||||
await waitFor(() => expect(set).toEqual(["https://mirror.example/index.json", null]));
|
||||
});
|
||||
|
||||
it("filters the marketplace by category", async () => {
|
||||
const entry = (id: string, name: string, categories: string[]) => ({
|
||||
id,
|
||||
|
||||
@@ -1859,7 +1859,6 @@
|
||||
"Search frames": "Rechercher des cadres",
|
||||
"Search in comments and strings": "Rechercher dans les commentaires et les chaînes",
|
||||
"Search installed plugins": "Rechercher parmi les plugins installés",
|
||||
"Search local plugins": "Rechercher parmi les plugins locaux",
|
||||
"Search packages": "Rechercher des paquets",
|
||||
"Search projects": "Rechercher des projets",
|
||||
"Search pull requests": "Rechercher des pull requests",
|
||||
@@ -4406,7 +4405,6 @@
|
||||
"Memory & Diagnostics": "Mémoire et diagnostics",
|
||||
"Over budget ({budget})": "Budget dépassé ({budget})",
|
||||
"dev": "dev",
|
||||
"{label}. Showing plugin packages available on this machine.": "{label}. Affichage des packages de plugins disponibles sur cette machine.",
|
||||
"Zoomed into {name}": "Zoom sur {name}",
|
||||
"Rename directory '{current}' and its usages to:": "Renommer le répertoire '{current}' et ses utilisations en :",
|
||||
"Rename file '{current}' and its usages to:": "Renommer le fichier '{current}' et ses utilisations en :",
|
||||
@@ -5548,8 +5546,6 @@
|
||||
"Unsigned": "Non signé",
|
||||
"Roll Back to {previousVersion}": "Revenir à {previousVersion}",
|
||||
"Install Plugin from Package File…": "Installer un plugin depuis un fichier de paquet…",
|
||||
"Marketplace index URL": "URL de l'index de la marketplace",
|
||||
"Refresh Marketplace": "Actualiser la marketplace",
|
||||
"The plugin system is a preview: its API and the plugins may still change.": "Le système de plugins est en préversion : son API et les plugins peuvent encore changer.",
|
||||
"Linked for development": "Lié pour le développement",
|
||||
"Marketplace": "Marketplace",
|
||||
@@ -5782,7 +5778,6 @@
|
||||
"Weekly limit": "Limite hebdomadaire",
|
||||
"Weekly Opus limit": "Limite hebdomadaire Opus",
|
||||
"Weekly Sonnet limit": "Limite hebdomadaire Sonnet",
|
||||
"Marketplace index URL (https://…/index.json or a folder path)": "URL de l'index de la marketplace (https://…/index.json ou un chemin de dossier)",
|
||||
"Editor Providers": "Fournisseurs d'éditeurs",
|
||||
"Syntax errors": "Erreurs de syntaxe",
|
||||
"Pull Members Up…": "Remonter les membres…",
|
||||
|
||||
@@ -1937,7 +1937,6 @@
|
||||
"Search frames": "프레임 검색",
|
||||
"Search in comments and strings": "주석 및 문자열에서 검색",
|
||||
"Search installed plugins": "설치된 플러그인 검색",
|
||||
"Search local plugins": "로컬 플러그인 검색",
|
||||
"Search packages": "패키지 검색",
|
||||
"Search projects": "프로젝트 검색",
|
||||
"Search pull requests": "풀 리퀘스트 검색",
|
||||
@@ -4517,7 +4516,6 @@
|
||||
"Memory & Diagnostics": "메모리 및 진단",
|
||||
"Over budget ({budget})": "예산 초과({budget})",
|
||||
"dev": "개발",
|
||||
"{label}. Showing plugin packages available on this machine.": "{label}. 이 머신에서 사용 가능한 플러그인 패키지를 표시합니다.",
|
||||
"Zoomed into {name}": "{name} 확대됨",
|
||||
"Rename directory '{current}' and its usages to:": "디렉터리 '{current}' 및 해당 사용 위치의 새 이름:",
|
||||
"Rename file '{current}' and its usages to:": "파일 '{current}' 및 해당 사용 위치의 새 이름:",
|
||||
@@ -5708,14 +5706,11 @@
|
||||
"Unsigned": "서명되지 않음",
|
||||
"Roll Back to {previousVersion}": "{previousVersion}(으)로 롤백",
|
||||
"Install Plugin from Package File…": "패키지 파일에서 플러그인 설치…",
|
||||
"Marketplace index URL": "마켓플레이스 인덱스 URL",
|
||||
"Refresh Marketplace": "마켓플레이스 새로 고침",
|
||||
"Marketplace": "마켓플레이스",
|
||||
"Could not download plugin": "플러그인을 다운로드할 수 없습니다",
|
||||
"Rolled back {name} to {version}": "{name}을(를) {version}(으)로 롤백했습니다",
|
||||
"Could not roll back": "롤백할 수 없습니다",
|
||||
"Could not open the package": "패키지를 열 수 없습니다",
|
||||
"Marketplace index URL (https://…/index.json or a folder path)": "마켓플레이스 인덱스 URL(https://…/index.json 또는 폴더 경로)",
|
||||
"Settings of the plugin; projects can override them.": "플러그인 설정입니다. 프로젝트에서 재정의할 수 있습니다.",
|
||||
"Menus": "메뉴",
|
||||
"Shortcuts": "단축키",
|
||||
|
||||
@@ -1940,8 +1940,8 @@
|
||||
"Search frames": "搜索帧",
|
||||
"Search in comments and strings": "在注释和字符串中搜索",
|
||||
"Search installed plugins": "搜索已安装的插件",
|
||||
"Search local plugins": "搜索本地插件",
|
||||
"Search packages": "搜索软件包",
|
||||
"Search plugins": "搜索插件",
|
||||
"Search projects": "搜索项目",
|
||||
"Search pull requests": "搜索拉取请求",
|
||||
"Search query": "搜索查询",
|
||||
@@ -4535,7 +4535,6 @@
|
||||
"Memory & Diagnostics": "内存与诊断",
|
||||
"Over budget ({budget})": "超出预算({budget})",
|
||||
"dev": "开发",
|
||||
"{label}. Showing plugin packages available on this machine.": "{label}。显示此计算机上可用的插件包。",
|
||||
"Zoomed into {name}": "已缩放到 {name}",
|
||||
"Rename directory '{current}' and its usages to:": "将目录“{current}”及其用法重命名为:",
|
||||
"Rename file '{current}' and its usages to:": "将文件“{current}”及其用法重命名为:",
|
||||
@@ -5791,14 +5790,15 @@
|
||||
"1 theme": "1 个主题",
|
||||
"{count} themes": "{count} 个主题",
|
||||
"No theme plugins found in JetBrains IDEs on this computer. Choose a theme plugin downloaded from the JetBrains Marketplace (.jar or .zip) or a .theme.json file instead.": "在此计算机上的 JetBrains IDE 中未找到主题插件。请改为选择从 JetBrains Marketplace 下载的主题插件(.jar 或 .zip)或 .theme.json 文件。",
|
||||
"Marketplace index URL": "插件市场索引 URL",
|
||||
"Refresh Marketplace": "刷新插件市场",
|
||||
"Marketplace": "插件市场",
|
||||
"Plugin Repository…": "插件仓库…",
|
||||
"Plugin Repository": "插件仓库",
|
||||
"Marketplace index URL:": "插件市场索引 URL:",
|
||||
"Leave empty to use the default repository.": "留空则使用默认仓库。",
|
||||
"Could not download plugin": "无法下载插件",
|
||||
"Rolled back {name} to {version}": "已将 {name} 回滚到 {version}",
|
||||
"Could not roll back": "无法回滚",
|
||||
"Could not open the package": "无法打开插件包",
|
||||
"Marketplace index URL (https://…/index.json or a folder path)": "插件市场索引 URL(https://…/index.json 或文件夹路径)",
|
||||
"Settings of the plugin; projects can override them.": "插件的设置;项目可以覆盖。",
|
||||
"Menus": "菜单",
|
||||
"Shortcuts": "快捷键",
|
||||
|
||||
Reference in new issue
Block a user