mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 17:02:17 +00:00
Run oxfmt once over the whole repository (formatting only, no logic changes) and add `pnpm format:check` to the frontend CI job so unformatted code no longer builds up. `.oxfmtrc.json` now skips the generated theme docs, test fixtures and code blocks inside Markdown. Co-authored-by: jaw52 <[email protected]>
127 lines
4.1 KiB
TypeScript
127 lines
4.1 KiB
TypeScript
/**
|
||
* Web browsers (IDEA's Open In › Browser and the editor's browser bar): HTML
|
||
* and SVG files open in an installed browser through the built-in server, and
|
||
* HTML files get a built-in preview beside the editor. ⌃⇧R in an HTML editor
|
||
* opens the default browser, like IDEA's Run for HTML files.
|
||
*/
|
||
import { registerActions } from "@/platform/actions";
|
||
import { getContext } from "@/platform/context";
|
||
import { basename } from "@/platform/project";
|
||
import { lazyComponent, type MenuItem } from "@/ui";
|
||
import { maybeEditorService, registerMenuGroup, registerTextEditorWrapper } from "@/workbench/contributions";
|
||
import {
|
||
browserIcon,
|
||
isHtmlPath,
|
||
isWebPath,
|
||
loadBrowsers,
|
||
openInBrowser,
|
||
togglePreview,
|
||
useBrowsers,
|
||
useWebPreview,
|
||
} from "./browsers";
|
||
import { tx } from "@/platform/i18n";
|
||
|
||
interface OpenArg {
|
||
path?: string;
|
||
browser?: string;
|
||
}
|
||
|
||
function targetPath(arg: unknown): string | null {
|
||
if (typeof arg === "string") return arg;
|
||
const path = (arg as OpenArg | undefined)?.path ?? getContext().editorPath;
|
||
return typeof path === "string" ? path : null;
|
||
}
|
||
|
||
async function showPreview(path: string) {
|
||
await maybeEditorService()?.openFile(path, { focus: true });
|
||
togglePreview(path, true);
|
||
}
|
||
|
||
function browserMenu(path: string): MenuItem {
|
||
const html = isHtmlPath(path);
|
||
return {
|
||
label: "Browser",
|
||
icon: "web",
|
||
submenu: [
|
||
{ kind: "action", action: "web.openInBrowser", arg: { path }, label: "Default Browser" },
|
||
{ kind: "separator" },
|
||
...(html ? [{ label: "Built-in Preview", icon: "eye", run: () => void showPreview(path) }] : []),
|
||
...useBrowsers.getState().list.map((b): MenuItem => ({
|
||
label: b.name,
|
||
verbatim: true,
|
||
icon: browserIcon(b.id),
|
||
run: () => void openInBrowser(path, b.id),
|
||
})),
|
||
],
|
||
};
|
||
}
|
||
|
||
export function activate(): () => void {
|
||
void loadBrowsers();
|
||
const disposers = [
|
||
registerTextEditorWrapper({
|
||
id: "web.html",
|
||
matches: isHtmlPath,
|
||
component: lazyComponent(() => import("./WebEditor").then((m) => m.WebEditor)),
|
||
}),
|
||
registerActions([
|
||
{
|
||
id: "web.openInBrowser",
|
||
title: "Open in Browser",
|
||
description: "Open the HTML or SVG file in the default browser through the built-in server",
|
||
category: "View",
|
||
icon: "web",
|
||
when: "projectOpen",
|
||
keyWhen: "textEditorActive && editorLangId == html",
|
||
keywords: ["browser", "html", "preview", "chrome", "firefox", "safari", "run"],
|
||
run: ({ arg }) => {
|
||
const path = targetPath(arg);
|
||
if (path && isWebPath(path)) return openInBrowser(path, (arg as OpenArg | undefined)?.browser ?? null);
|
||
},
|
||
},
|
||
{
|
||
id: "web.togglePreview",
|
||
title: "Built-in Preview",
|
||
description: "Show the HTML file rendered beside the editor",
|
||
category: "View",
|
||
icon: "eye",
|
||
when: "textEditorActive && editorLangId == html",
|
||
keywords: ["browser", "html", "preview", "live"],
|
||
toggled: () => {
|
||
const path = getContext().editorPath;
|
||
return typeof path === "string" && !!useWebPreview.getState()[path];
|
||
},
|
||
run: ({ arg }) => {
|
||
const path = targetPath(arg);
|
||
if (path && isHtmlPath(path)) togglePreview(path);
|
||
},
|
||
},
|
||
]),
|
||
registerMenuGroup({
|
||
id: "web.open",
|
||
menus: ["editor", "editorTab", "projectView"],
|
||
order: 5,
|
||
items: (_menu, { paths, dirs }) => {
|
||
const path = paths.length === 1 && !dirs?.[0] ? paths[0]! : null;
|
||
if (!path || !isHtmlPath(path)) return [];
|
||
return [
|
||
{
|
||
kind: "action",
|
||
action: "web.openInBrowser",
|
||
arg: { path },
|
||
label: tx`Open '${basename(path)}' in Browser`,
|
||
icon: "web",
|
||
},
|
||
];
|
||
},
|
||
}),
|
||
registerMenuGroup({
|
||
id: "web.openIn",
|
||
menus: ["openIn"],
|
||
items: (_menu, { paths, dirs }) =>
|
||
paths.length === 1 && !dirs?.[0] && isWebPath(paths[0]!) ? [browserMenu(paths[0]!)] : [],
|
||
}),
|
||
];
|
||
return () => disposers.reverse().forEach((d) => d());
|
||
}
|