дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
Replaces the Pull Requests tool window with a full review workflow on top of gh's authentication. - List with search, quick filters and paging; a tab per pull request and a New Pull Request tab. - Details: commit picker, changes tree with viewed state, CI and reviewer status, merge, close and reopen. - Timeline editor: comments, review threads, reactions and suggested changes. - Review in the diff viewer and, for the checked-out branch, in the editor (review mode). - New generic vcs extension points: registerDiffViewerProvider, lineInlays, reviewGutter, branch widget extensions; new vcs_merge_base and gh_api commands. - CSP: img-src allows https://avatars.githubusercontent.com for avatars. Fixed while testing against a real repository: remotes behind an SSH host alias, mergeability stuck on UNKNOWN, Apply Locally staying disabled after a checkout, relative links navigating the app's page, and review inlays wider than the pane. Co-authored-by: imacross <[email protected]>
61 рядки
2.4 KiB
TypeScript
61 рядки
2.4 KiB
TypeScript
/**
|
|
* Extensions of the branch widget and its Branches popup contributed by other features
|
|
* (IDEA's `GitCurrentBranchPresenter` and the `Git.Experimental.Branch.Popup.Actions` group):
|
|
*
|
|
* - a presentation of the current branch (icon, text, tooltip) replacing the widget's own,
|
|
* as the GitHub plugin shows "#12 on feature" for a pull request's branch;
|
|
* - actions added to the popup's top, after its own (a separator before each extension's).
|
|
*
|
|
* An extension is provided under an id and replaced by providing it again (it follows its
|
|
* owner's state); the disposer removes it.
|
|
*
|
|
* const dispose = provideBranchWidgetExtension({ id: "github", presentation: { text: "#12 on feature" } });
|
|
*/
|
|
import { create } from "zustand";
|
|
import type { PopupAction } from "./branches";
|
|
|
|
export interface BranchPresentation {
|
|
/** Replaces the branch icon. */
|
|
icon?: string;
|
|
text: string;
|
|
/** Shown above the widget's own tooltip lines. */
|
|
tooltip?: string;
|
|
}
|
|
|
|
export interface BranchWidgetExtension {
|
|
id: string;
|
|
/** Lower first (presentation: the first one wins). Default 100. */
|
|
order?: number;
|
|
presentation?: BranchPresentation | null;
|
|
actions?: readonly PopupAction[];
|
|
}
|
|
|
|
export const useBranchWidgetExtensions = create<{ extensions: ReadonlyMap<string, BranchWidgetExtension> }>(() => ({
|
|
extensions: new Map(),
|
|
}));
|
|
|
|
/** Provides (or replaces) extension `ext.id`; returns its disposer. */
|
|
export function provideBranchWidgetExtension(ext: BranchWidgetExtension): () => void {
|
|
useBranchWidgetExtensions.setState((s) => ({ extensions: new Map(s.extensions).set(ext.id, ext) }));
|
|
return () =>
|
|
useBranchWidgetExtensions.setState((s) => {
|
|
if (s.extensions.get(ext.id) !== ext) return s;
|
|
const extensions = new Map(s.extensions);
|
|
extensions.delete(ext.id);
|
|
return { extensions };
|
|
});
|
|
}
|
|
|
|
const ordered = (m: ReadonlyMap<string, BranchWidgetExtension>) =>
|
|
[...m.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
|
|
/** The presentation replacing the widget's, if any. */
|
|
export function branchPresentation(m: ReadonlyMap<string, BranchWidgetExtension>): BranchPresentation | null {
|
|
return ordered(m).find((e) => e.presentation)?.presentation ?? null;
|
|
}
|
|
|
|
/** The extensions' popup actions, each extension in a group of its own. */
|
|
export function extensionPopupActions(m: ReadonlyMap<string, BranchWidgetExtension>): PopupAction[] {
|
|
return ordered(m).flatMap((e) => (e.actions ?? []).map((a) => ({ ...a, group: `ext:${e.id}` })));
|
|
}
|