mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-13 04:45:19 +00:00
Quick action tweaks (#218)
This commit is contained in:
parent
cdfcb5a88b
commit
7685d9cf66
4 changed files with 317 additions and 69 deletions
|
|
@ -14,18 +14,20 @@ import {
|
||||||
jsonSchemaLinter,
|
jsonSchemaLinter,
|
||||||
stateExtensions
|
stateExtensions
|
||||||
} from "codemirror-json-schema";
|
} from "codemirror-json-schema";
|
||||||
import { useRef, forwardRef, useImperativeHandle, Ref, ReactNode } from "react";
|
import { useRef, forwardRef, useImperativeHandle, Ref, ReactNode, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Schema } from "ajv";
|
import { Schema } from "ajv";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||||
import { CodeHostType } from "@/lib/utils";
|
import { CodeHostType } from "@/lib/utils";
|
||||||
|
|
||||||
export type QuickActionFn<T> = (previous: T) => T;
|
export type QuickActionFn<T> = (previous: T) => T;
|
||||||
export type QuickAction<T> = {
|
export type QuickAction<T> = {
|
||||||
name: string;
|
name: string;
|
||||||
fn: QuickActionFn<T>;
|
fn: QuickActionFn<T>;
|
||||||
description?: string | ReactNode;
|
description?: string | ReactNode;
|
||||||
|
selectionText?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ConfigEditorProps<T> {
|
interface ConfigEditorProps<T> {
|
||||||
|
|
@ -57,11 +59,13 @@ export function onQuickAction<T>(
|
||||||
options?: {
|
options?: {
|
||||||
focusEditor?: boolean;
|
focusEditor?: boolean;
|
||||||
moveCursor?: boolean;
|
moveCursor?: boolean;
|
||||||
|
selectionText?: string;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
focusEditor = false,
|
focusEditor = false,
|
||||||
moveCursor = true,
|
moveCursor = true,
|
||||||
|
selectionText = `""`,
|
||||||
} = options ?? {};
|
} = options ?? {};
|
||||||
|
|
||||||
let previousConfig: T;
|
let previousConfig: T;
|
||||||
|
|
@ -78,7 +82,6 @@ export function onQuickAction<T>(
|
||||||
view.focus();
|
view.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
const cursorPos = next.lastIndexOf(`""`) + 1;
|
|
||||||
view.dispatch({
|
view.dispatch({
|
||||||
changes: {
|
changes: {
|
||||||
from: 0,
|
from: 0,
|
||||||
|
|
@ -87,10 +90,16 @@ export function onQuickAction<T>(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moveCursor) {
|
if (moveCursor && selectionText) {
|
||||||
view.dispatch({
|
const cursorPos = next.lastIndexOf(selectionText);
|
||||||
selection: { anchor: cursorPos, head: cursorPos }
|
if (cursorPos >= 0) {
|
||||||
});
|
view.dispatch({
|
||||||
|
selection: {
|
||||||
|
anchor: cursorPos,
|
||||||
|
head: cursorPos + selectionText.length
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,10 +112,15 @@ export const isConfigValidJson = (config: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_ACTIONS_VISIBLE = 4;
|
||||||
|
|
||||||
const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCodeMirrorRef>) => {
|
const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCodeMirrorRef>) => {
|
||||||
const { value, type, onChange, actions, schema } = props;
|
const { value, type, onChange, actions, schema } = props;
|
||||||
const captureEvent = useCaptureEvent();
|
const captureEvent = useCaptureEvent();
|
||||||
const editorRef = useRef<ReactCodeMirrorRef>(null);
|
const editorRef = useRef<ReactCodeMirrorRef>(null);
|
||||||
|
const [isViewMoreActionsEnabled, setIsViewMoreActionsEnabled] = useState(false);
|
||||||
|
const [height, setHeight] = useState(224);
|
||||||
|
|
||||||
useImperativeHandle(
|
useImperativeHandle(
|
||||||
forwardedRef,
|
forwardedRef,
|
||||||
() => editorRef.current as ReactCodeMirrorRef
|
() => editorRef.current as ReactCodeMirrorRef
|
||||||
|
|
@ -117,7 +131,79 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border rounded-md">
|
<div className="border rounded-md">
|
||||||
<ScrollArea className="p-1 overflow-auto flex-1 h-56">
|
<div className="flex flex-row items-center flex-wrap p-1">
|
||||||
|
<TooltipProvider>
|
||||||
|
{actions
|
||||||
|
.slice(0, isViewMoreActionsEnabled ? actions.length : DEFAULT_ACTIONS_VISIBLE)
|
||||||
|
.map(({ name, fn, description, selectionText }, index, truncatedActions) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex flex-row items-center"
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
delayDuration={100}
|
||||||
|
>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="disabled:opacity-100 disabled:pointer-events-auto disabled:cursor-not-allowed text-sm font-mono tracking-tight"
|
||||||
|
size="sm"
|
||||||
|
disabled={!isConfigValidJson(value)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
captureEvent('wa_config_editor_quick_action_pressed', {
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
if (editorRef.current?.view) {
|
||||||
|
onQuickAction(fn, value, editorRef.current.view, {
|
||||||
|
focusEditor: true,
|
||||||
|
selectionText,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent
|
||||||
|
hidden={!description}
|
||||||
|
className="max-w-xs"
|
||||||
|
>
|
||||||
|
{description}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
{index !== truncatedActions.length - 1 && (
|
||||||
|
<Separator
|
||||||
|
orientation="vertical" className="h-4 mx-1"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{index === truncatedActions.length - 1 && truncatedActions.length < actions.length && (
|
||||||
|
<>
|
||||||
|
<Separator
|
||||||
|
orientation="vertical" className="h-4 mx-1"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
size="sm"
|
||||||
|
className="text-xs text-muted-foreground"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsViewMoreActionsEnabled(!isViewMoreActionsEnabled);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+{actions.length - truncatedActions.length} more
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<ScrollArea className="p-1 overflow-auto flex-1" style={{ height }}>
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
value={value}
|
value={value}
|
||||||
|
|
@ -142,55 +228,27 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
|
||||||
theme={theme === "dark" ? "dark" : "light"}
|
theme={theme === "dark" ? "dark" : "light"}
|
||||||
/>
|
/>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
<Separator />
|
<div
|
||||||
<div className="flex flex-row items-center flex-wrap w-full p-1">
|
className="h-1 cursor-ns-resize bg-border rounded-md hover:bg-primary/50 transition-colors"
|
||||||
<TooltipProvider>
|
onMouseDown={(e) => {
|
||||||
{actions.map(({ name, fn, description }, index) => (
|
e.preventDefault();
|
||||||
<div
|
const startY = e.clientY;
|
||||||
key={index}
|
const startHeight = height;
|
||||||
className="flex flex-row items-center"
|
|
||||||
>
|
function onMouseMove(e: MouseEvent) {
|
||||||
<Tooltip
|
const delta = e.clientY - startY;
|
||||||
delayDuration={100}
|
setHeight(Math.max(112, startHeight + delta));
|
||||||
>
|
}
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
function onMouseUp() {
|
||||||
variant="ghost"
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
className="disabled:opacity-100 disabled:pointer-events-auto disabled:cursor-not-allowed text-sm font-mono tracking-tight"
|
document.removeEventListener('mouseup', onMouseUp);
|
||||||
size="sm"
|
}
|
||||||
disabled={!isConfigValidJson(value)}
|
|
||||||
onClick={(e) => {
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
e.preventDefault();
|
document.addEventListener('mouseup', onMouseUp);
|
||||||
captureEvent('wa_config_editor_quick_action_pressed', {
|
}}
|
||||||
name,
|
/>
|
||||||
type,
|
|
||||||
});
|
|
||||||
if (editorRef.current?.view) {
|
|
||||||
onQuickAction(fn, value, editorRef.current.view, {
|
|
||||||
focusEditor: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent
|
|
||||||
hidden={!description}
|
|
||||||
className="max-w-xs"
|
|
||||||
>
|
|
||||||
{description}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
{index !== actions.length - 1 && (
|
|
||||||
<Separator
|
|
||||||
orientation="vertical" className="h-4 mx-1"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</TooltipProvider>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default function Layout({
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="min-h-screen flex flex-col">
|
||||||
<NavigationMenu domain={domain} />
|
<NavigationMenu domain={domain} />
|
||||||
<main className="flex-grow flex justify-center p-4 bg-backgroundSecondary relative">
|
<main className="flex-grow flex justify-center p-4 bg-backgroundSecondary relative">
|
||||||
<div className="w-full max-w-6xl rounded-lg p-6">{children}</div>
|
<div className="w-full max-w-6xl p-6">{children}</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ export const githubQuickActions: QuickAction<GithubConnectionConfig>[] = [
|
||||||
...previous,
|
...previous,
|
||||||
repos: [
|
repos: [
|
||||||
...(previous.repos ?? []),
|
...(previous.repos ?? []),
|
||||||
""
|
"<owner>/<repo name>"
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
name: "Add a repo",
|
name: "Add a single repo",
|
||||||
|
selectionText: "<owner>/<repo name>",
|
||||||
description: (
|
description: (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span>Add a individual repository to sync with. Ensure the repository is visible to the provided <Code>token</Code> (if any).</span>
|
<span>Add a individual repository to sync with. Ensure the repository is visible to the provided <Code>token</Code> (if any).</span>
|
||||||
|
|
@ -47,10 +48,11 @@ export const githubQuickActions: QuickAction<GithubConnectionConfig>[] = [
|
||||||
...previous,
|
...previous,
|
||||||
orgs: [
|
orgs: [
|
||||||
...(previous.orgs ?? []),
|
...(previous.orgs ?? []),
|
||||||
""
|
"<organization name>"
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
name: "Add an organization",
|
name: "Add an organization",
|
||||||
|
selectionText: "<organization name>",
|
||||||
description: (
|
description: (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span>Add an organization to sync with. All repositories in the organization visible to the provided <Code>token</Code> (if any) will be synced.</span>
|
<span>Add an organization to sync with. All repositories in the organization visible to the provided <Code>token</Code> (if any) will be synced.</span>
|
||||||
|
|
@ -72,19 +74,138 @@ export const githubQuickActions: QuickAction<GithubConnectionConfig>[] = [
|
||||||
...previous,
|
...previous,
|
||||||
users: [
|
users: [
|
||||||
...(previous.users ?? []),
|
...(previous.users ?? []),
|
||||||
""
|
"<username>"
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
name: "Add a user",
|
name: "Add a user",
|
||||||
description: <span>Add a user to sync with. All repositories that the user owns visible to the provided <Code>token</Code> (if any) will be synced.</span>
|
selectionText: "<username>",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Add a user to sync with. All repositories that the user owns visible to the provided <Code>token</Code> (if any) will be synced.</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-row gap-1 items-center">
|
||||||
|
{[
|
||||||
|
"jane-doe",
|
||||||
|
"torvalds",
|
||||||
|
"octocat"
|
||||||
|
].map((org) => (
|
||||||
|
<Code key={org}>{org}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (previous: GithubConnectionConfig) => ({
|
fn: (previous: GithubConnectionConfig) => ({
|
||||||
...previous,
|
...previous,
|
||||||
url: previous.url ?? "",
|
url: previous.url ?? "https://github.example.com",
|
||||||
}),
|
}),
|
||||||
name: "Set a custom url",
|
name: "Set a custom url",
|
||||||
|
selectionText: "https://github.example.com",
|
||||||
description: <span>Set a custom GitHub host. Defaults to <Code>https://github.com</Code>.</span>
|
description: <span>Set a custom GitHub host. Defaults to <Code>https://github.com</Code>.</span>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fn: (previous: GithubConnectionConfig) => ({
|
||||||
|
...previous,
|
||||||
|
exclude: {
|
||||||
|
...previous.exclude,
|
||||||
|
repos: [
|
||||||
|
...(previous.exclude?.repos ?? []),
|
||||||
|
"<glob pattern>"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
name: "Exclude by repo name",
|
||||||
|
selectionText: "<glob pattern>",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Exclude repositories from syncing by name. Glob patterns are supported.</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{[
|
||||||
|
"my-org/docs*",
|
||||||
|
"my-org/test*"
|
||||||
|
].map((repo) => (
|
||||||
|
<Code key={repo}>{repo}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fn: (previous: GithubConnectionConfig) => ({
|
||||||
|
...previous,
|
||||||
|
exclude: {
|
||||||
|
...previous.exclude,
|
||||||
|
topics: [
|
||||||
|
...(previous.exclude?.topics ?? []),
|
||||||
|
"<topic>"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
name: "Exclude by topic",
|
||||||
|
selectionText: "<topic>",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Exclude topics from syncing. Only repos that do not match any of the provided topics will be synced. Glob patterns are supported.</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{[
|
||||||
|
"docs",
|
||||||
|
"ci"
|
||||||
|
].map((repo) => (
|
||||||
|
<Code key={repo}>{repo}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fn: (previous: GithubConnectionConfig) => ({
|
||||||
|
...previous,
|
||||||
|
topics: [
|
||||||
|
...(previous.topics ?? []),
|
||||||
|
"<topic>"
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
name: "Include by topic",
|
||||||
|
selectionText: "<topic>",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Include repositories by topic. Only repos that match at least one of the provided topics will be synced. Glob patterns are supported.</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{[
|
||||||
|
"docs",
|
||||||
|
"ci"
|
||||||
|
].map((repo) => (
|
||||||
|
<Code key={repo}>{repo}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fn: (previous: GithubConnectionConfig) => ({
|
||||||
|
...previous,
|
||||||
|
exclude: {
|
||||||
|
...previous.exclude,
|
||||||
|
archived: true,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
name: "Exclude archived repos",
|
||||||
|
description: <span>Exclude archived repositories from syncing.</span>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fn: (previous: GithubConnectionConfig) => ({
|
||||||
|
...previous,
|
||||||
|
exclude: {
|
||||||
|
...previous.exclude,
|
||||||
|
forks: true,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
name: "Exclude forked repos",
|
||||||
|
description: <span>Exclude forked repositories from syncing.</span>
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -98,6 +219,20 @@ export const gitlabQuickActions: QuickAction<GitlabConnectionConfig>[] = [
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
name: "Add a project",
|
name: "Add a project",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Add a individual project to sync with. Ensure the project is visible to the provided <Code>token</Code> (if any).</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{[
|
||||||
|
"gitlab-org/gitlab",
|
||||||
|
"corp/team-project",
|
||||||
|
].map((repo) => (
|
||||||
|
<Code key={repo}>{repo}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (previous: GitlabConnectionConfig) => ({
|
fn: (previous: GitlabConnectionConfig) => ({
|
||||||
|
|
@ -108,6 +243,20 @@ export const gitlabQuickActions: QuickAction<GitlabConnectionConfig>[] = [
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
name: "Add a user",
|
name: "Add a user",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Add a user to sync with. All projects that the user owns visible to the provided <Code>token</Code> (if any) will be synced.</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-row gap-1 items-center">
|
||||||
|
{[
|
||||||
|
"jane-doe",
|
||||||
|
"torvalds"
|
||||||
|
].map((org) => (
|
||||||
|
<Code key={org}>{org}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (previous: GitlabConnectionConfig) => ({
|
fn: (previous: GitlabConnectionConfig) => ({
|
||||||
|
|
@ -118,6 +267,20 @@ export const gitlabQuickActions: QuickAction<GitlabConnectionConfig>[] = [
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
name: "Add a group",
|
name: "Add a group",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Add a group to sync with. All projects in the group (and recursive subgroups) visible to the provided <Code>token</Code> (if any) will be synced.</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{[
|
||||||
|
"my-group",
|
||||||
|
"path/to/subgroup"
|
||||||
|
].map((org) => (
|
||||||
|
<Code key={org}>{org}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (previous: GitlabConnectionConfig) => ({
|
fn: (previous: GitlabConnectionConfig) => ({
|
||||||
|
|
@ -125,16 +288,43 @@ export const gitlabQuickActions: QuickAction<GitlabConnectionConfig>[] = [
|
||||||
url: previous.url ?? "",
|
url: previous.url ?? "",
|
||||||
}),
|
}),
|
||||||
name: "Set a custom url",
|
name: "Set a custom url",
|
||||||
|
description: <span>Set a custom GitLab host. Defaults to <Code>https://gitlab.com</Code>.</span>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (previous: GitlabConnectionConfig) => ({
|
fn: (previous: GitlabConnectionConfig) => ({
|
||||||
...previous,
|
...previous,
|
||||||
token: previous.token ?? {
|
all: true,
|
||||||
secret: "",
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
name: "Add a secret",
|
name: "Sync all projects",
|
||||||
|
description: <span>Sync all projects visible to the provided <Code>token</Code> (if any). Only available when using a self-hosted GitLab instance.</span>
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fn: (previous: GitlabConnectionConfig) => ({
|
||||||
|
...previous,
|
||||||
|
exclude: {
|
||||||
|
...previous.exclude,
|
||||||
|
projects: [
|
||||||
|
...(previous.exclude?.projects ?? []),
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
name: "Exclude a project",
|
||||||
|
description: (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>List of projects to exclude from syncing. Glob patterns are supported.</span>
|
||||||
|
<span className="text-sm mt-2 mb-1">Examples:</span>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{[
|
||||||
|
"docs/**",
|
||||||
|
"**/tests/**",
|
||||||
|
].map((repo) => (
|
||||||
|
<Code key={repo}>{repo}</Code>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export const giteaQuickActions: QuickAction<GiteaConnectionConfig>[] = [
|
export const giteaQuickActions: QuickAction<GiteaConnectionConfig>[] = [
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ export default function SettingsLayout({
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="min-h-screen flex flex-col">
|
||||||
<NavigationMenu domain={domain} />
|
<NavigationMenu domain={domain} />
|
||||||
<div className="flex-grow flex justify-center p-4 bg-[#fafafa] dark:bg-background relative">
|
<div className="flex-grow flex justify-center p-4 bg-backgroundSecondary relative">
|
||||||
<div className="w-full max-w-6xl">
|
<div className="w-full max-w-6xl p-6">
|
||||||
<Header className="w-full">
|
<Header className="w-full">
|
||||||
<h1 className="text-3xl">Settings</h1>
|
<h1 className="text-3xl">Settings</h1>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue