mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-25 10:51:57 +01:00
Run oxfmt across repo, add format script and docs
Add .oxfmtignore to skip generated bindings and wasm-pack output. Add npm format script, update DEVELOPMENT.md for Vite+ toolchain, and format all non-generated files with oxfmt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '../core/Button';
|
||||
import { Select } from '../core/Select';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
import { useState } from "react";
|
||||
import { Button } from "../core/Button";
|
||||
import { Select } from "../core/Select";
|
||||
import { HStack, VStack } from "../core/Stacks";
|
||||
|
||||
interface Props {
|
||||
branches: string[];
|
||||
@@ -11,7 +11,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function BranchSelectionDialog({ branches, onCancel, onSelect, selectText }: Props) {
|
||||
const [branch, setBranch] = useState<string>('__NONE__');
|
||||
const [branch, setBranch] = useState<string>("__NONE__");
|
||||
return (
|
||||
<VStack
|
||||
className="mb-4"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
import type { GitStatusEntry } from '@yaakapp-internal/git';
|
||||
import { useGit } from '@yaakapp-internal/git';
|
||||
import type { GitStatusEntry } from "@yaakapp-internal/git";
|
||||
import { useGit } from "@yaakapp-internal/git";
|
||||
import type {
|
||||
Environment,
|
||||
Folder,
|
||||
@@ -8,26 +7,26 @@ import type {
|
||||
HttpRequest,
|
||||
WebsocketRequest,
|
||||
Workspace,
|
||||
} from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { modelToYaml } from '../../lib/diffYaml';
|
||||
import { resolvedModelName } from '../../lib/resolvedModelName';
|
||||
import { showErrorToast } from '../../lib/toast';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { Button } from '../core/Button';
|
||||
import type { CheckboxProps } from '../core/Checkbox';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import { DiffViewer } from '../core/Editor/DiffViewer';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { Input } from '../core/Input';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { SplitLayout } from '../core/SplitLayout';
|
||||
import { HStack } from '../core/Stacks';
|
||||
import { EmptyStateText } from '../EmptyStateText';
|
||||
import { gitCallbacks } from './callbacks';
|
||||
import { handlePushResult } from './git-util';
|
||||
} from "@yaakapp-internal/models";
|
||||
import classNames from "classnames";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { modelToYaml } from "../../lib/diffYaml";
|
||||
import { resolvedModelName } from "../../lib/resolvedModelName";
|
||||
import { showErrorToast } from "../../lib/toast";
|
||||
import { Banner } from "../core/Banner";
|
||||
import { Button } from "../core/Button";
|
||||
import type { CheckboxProps } from "../core/Checkbox";
|
||||
import { Checkbox } from "../core/Checkbox";
|
||||
import { DiffViewer } from "../core/Editor/DiffViewer";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { Input } from "../core/Input";
|
||||
import { Separator } from "../core/Separator";
|
||||
import { SplitLayout } from "../core/SplitLayout";
|
||||
import { HStack } from "../core/Stacks";
|
||||
import { EmptyStateText } from "../EmptyStateText";
|
||||
import { gitCallbacks } from "./callbacks";
|
||||
import { handlePushResult } from "./git-util";
|
||||
|
||||
interface Props {
|
||||
syncDir: string;
|
||||
@@ -49,7 +48,7 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) {
|
||||
);
|
||||
const [isPushing, setIsPushing] = useState(false);
|
||||
const [commitError, setCommitError] = useState<string | null>(null);
|
||||
const [message, setMessage] = useState<string>('');
|
||||
const [message, setMessage] = useState<string>("");
|
||||
const [selectedEntry, setSelectedEntry] = useState<GitStatusEntry | null>(null);
|
||||
|
||||
const handleCreateCommit = async () => {
|
||||
@@ -70,8 +69,8 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) {
|
||||
onDone();
|
||||
} catch (err) {
|
||||
showErrorToast({
|
||||
id: 'git-commit-and-push-error',
|
||||
title: 'Error committing and pushing',
|
||||
id: "git-commit-and-push-error",
|
||||
title: "Error committing and pushing",
|
||||
message: String(err),
|
||||
});
|
||||
} finally {
|
||||
@@ -96,11 +95,11 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) {
|
||||
}, [status.data?.entries]);
|
||||
|
||||
const hasAddedAnything = allEntries.find((e) => e.staged) != null;
|
||||
const hasAnythingToAdd = allEntries.find((e) => e.status !== 'current') != null;
|
||||
const hasAnythingToAdd = allEntries.find((e) => e.status !== "current") != null;
|
||||
|
||||
const tree: CommitTreeNode | null = useMemo(() => {
|
||||
const next = (
|
||||
model: CommitTreeNode['model'],
|
||||
model: CommitTreeNode["model"],
|
||||
ancestors: CommitTreeNode[],
|
||||
): CommitTreeNode | null => {
|
||||
const statusEntry = internalEntries?.find((s) => s.relaPath.includes(model.id));
|
||||
@@ -122,12 +121,12 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) {
|
||||
if (childModel == null) continue;
|
||||
|
||||
// TODO: Figure out why not all of these show up
|
||||
if ('folderId' in childModel && childModel.folderId != null) {
|
||||
if ("folderId" in childModel && childModel.folderId != null) {
|
||||
if (childModel.folderId === model.id) {
|
||||
const c = next(childModel, [...ancestors, node]);
|
||||
if (c != null) node.children.push(c);
|
||||
}
|
||||
} else if ('workspaceId' in childModel && childModel.workspaceId === model.id) {
|
||||
} else if ("workspaceId" in childModel && childModel.workspaceId === model.id) {
|
||||
const c = next(childModel, [...ancestors, node]);
|
||||
if (c != null) node.children.push(c);
|
||||
} else {
|
||||
@@ -144,7 +143,7 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) {
|
||||
const checkNode = useCallback(
|
||||
(treeNode: CommitTreeNode) => {
|
||||
const checked = nodeCheckedStatus(treeNode);
|
||||
const newChecked = checked === 'indeterminate' ? true : !checked;
|
||||
const newChecked = checked === "indeterminate" ? true : !checked;
|
||||
setCheckedAndChildren(treeNode, newChecked, unstage.mutate, add.mutate);
|
||||
// TODO: Also ensure parents are added properly
|
||||
},
|
||||
@@ -206,7 +205,7 @@ export function GitCommitDialog({ syncDir, onDone, workspace }: Props) {
|
||||
onSelect={handleSelectChild}
|
||||
selectedPath={selectedEntry?.relaPath ?? null}
|
||||
/>
|
||||
{externalEntries.find((e) => e.status !== 'current') && (
|
||||
{externalEntries.find((e) => e.status !== "current") && (
|
||||
<>
|
||||
<Separator className="mt-3 mb-1">External file changes</Separator>
|
||||
{externalEntries.map((entry) => (
|
||||
@@ -297,13 +296,13 @@ function TreeNodeChildren({
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
depth > 0 && 'pl-4 ml-2 border-l border-dashed border-border-subtle relative',
|
||||
depth > 0 && "pl-4 ml-2 border-l border-dashed border-border-subtle relative",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'relative flex gap-1 w-full h-xs items-center',
|
||||
isSelected ? 'text-text' : 'text-text-subtle',
|
||||
"relative flex gap-1 w-full h-xs items-center",
|
||||
isSelected ? "text-text" : "text-text-subtle",
|
||||
)}
|
||||
>
|
||||
{isSelected && (
|
||||
@@ -311,39 +310,39 @@ function TreeNodeChildren({
|
||||
)}
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
title={checked ? 'Unstage change' : 'Stage change'}
|
||||
title={checked ? "Unstage change" : "Stage change"}
|
||||
hideLabel
|
||||
onChange={(checked) => onCheck(node, checked)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className={classNames('flex-1 min-w-0 flex items-center gap-1 px-1 py-0.5 text-left')}
|
||||
onClick={() => node.status.status !== 'current' && onSelect(node.status)}
|
||||
className={classNames("flex-1 min-w-0 flex items-center gap-1 px-1 py-0.5 text-left")}
|
||||
onClick={() => node.status.status !== "current" && onSelect(node.status)}
|
||||
>
|
||||
{node.model.model !== 'http_request' &&
|
||||
node.model.model !== 'grpc_request' &&
|
||||
node.model.model !== 'websocket_request' ? (
|
||||
{node.model.model !== "http_request" &&
|
||||
node.model.model !== "grpc_request" &&
|
||||
node.model.model !== "websocket_request" ? (
|
||||
<Icon
|
||||
color="secondary"
|
||||
icon={
|
||||
node.model.model === 'folder'
|
||||
? 'folder'
|
||||
: node.model.model === 'environment'
|
||||
? 'variable'
|
||||
: 'house'
|
||||
node.model.model === "folder"
|
||||
? "folder"
|
||||
: node.model.model === "environment"
|
||||
? "variable"
|
||||
: "house"
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<span aria-hidden className="w-4" />
|
||||
)}
|
||||
<div className="truncate flex-1">{resolvedModelName(node.model)}</div>
|
||||
{node.status.status !== 'current' && (
|
||||
{node.status.status !== "current" && (
|
||||
<InlineCode
|
||||
className={classNames(
|
||||
'py-0 bg-transparent w-[6rem] text-center shrink-0',
|
||||
node.status.status === 'modified' && 'text-info',
|
||||
node.status.status === 'untracked' && 'text-success',
|
||||
node.status.status === 'removed' && 'text-danger',
|
||||
"py-0 bg-transparent w-[6rem] text-center shrink-0",
|
||||
node.status.status === "modified" && "text-info",
|
||||
node.status.status === "untracked" && "text-success",
|
||||
node.status.status === "removed" && "text-danger",
|
||||
)}
|
||||
>
|
||||
{node.status.status}
|
||||
@@ -375,7 +374,7 @@ function ExternalTreeNode({
|
||||
entry: GitStatusEntry;
|
||||
onCheck: (entry: GitStatusEntry) => void;
|
||||
}) {
|
||||
if (entry.status === 'current') {
|
||||
if (entry.status === "current") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -391,10 +390,10 @@ function ExternalTreeNode({
|
||||
<div className="truncate">{entry.relaPath}</div>
|
||||
<InlineCode
|
||||
className={classNames(
|
||||
'py-0 ml-auto bg-transparent w-[6rem] text-center',
|
||||
entry.status === 'modified' && 'text-info',
|
||||
entry.status === 'untracked' && 'text-success',
|
||||
entry.status === 'removed' && 'text-danger',
|
||||
"py-0 ml-auto bg-transparent w-[6rem] text-center",
|
||||
entry.status === "modified" && "text-info",
|
||||
entry.status === "untracked" && "text-success",
|
||||
entry.status === "removed" && "text-danger",
|
||||
)}
|
||||
>
|
||||
{entry.status}
|
||||
@@ -405,14 +404,14 @@ function ExternalTreeNode({
|
||||
);
|
||||
}
|
||||
|
||||
function nodeCheckedStatus(root: CommitTreeNode): CheckboxProps['checked'] {
|
||||
function nodeCheckedStatus(root: CommitTreeNode): CheckboxProps["checked"] {
|
||||
let numVisited = 0;
|
||||
let numChecked = 0;
|
||||
let numCurrent = 0;
|
||||
|
||||
const visitChildren = (n: CommitTreeNode) => {
|
||||
numVisited += 1;
|
||||
if (n.status.status === 'current') {
|
||||
if (n.status.status === "current") {
|
||||
numCurrent += 1;
|
||||
} else if (n.status.staged) {
|
||||
numChecked += 1;
|
||||
@@ -430,7 +429,7 @@ function nodeCheckedStatus(root: CommitTreeNode): CheckboxProps['checked'] {
|
||||
if (numChecked === 0) {
|
||||
return false;
|
||||
}
|
||||
return 'indeterminate';
|
||||
return "indeterminate";
|
||||
}
|
||||
|
||||
function setCheckedAndChildren(
|
||||
@@ -447,7 +446,7 @@ function setCheckedAndChildren(
|
||||
next(child);
|
||||
}
|
||||
|
||||
if (node.status.status === 'current') {
|
||||
if (node.status.status === "current") {
|
||||
// Nothing required
|
||||
} else if (checked && !node.status.staged) {
|
||||
toAdd.push(node.status.relaPath);
|
||||
@@ -463,7 +462,7 @@ function setCheckedAndChildren(
|
||||
}
|
||||
|
||||
function isNodeRelevant(node: CommitTreeNode): boolean {
|
||||
if (node.status.status !== 'current') {
|
||||
if (node.status.status !== "current") {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -480,7 +479,7 @@ function DiffPanel({ entry }: { entry: GitStatusEntry }) {
|
||||
<div className="text-sm text-text-subtle mb-2 px-1">
|
||||
{resolvedModelName(entry.next ?? entry.prev)} ({entry.status})
|
||||
</div>
|
||||
<DiffViewer original={prevYaml ?? ''} modified={nextYaml ?? ''} className="flex-1 min-h-0" />
|
||||
<DiffViewer original={prevYaml ?? ""} modified={nextYaml ?? ""} className="flex-1 min-h-0" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import { useGit } from '@yaakapp-internal/git';
|
||||
import type { WorkspaceMeta } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { openWorkspaceSettings } from '../../commands/openWorkspaceSettings';
|
||||
import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../../hooks/useActiveWorkspace';
|
||||
import { useKeyValue } from '../../hooks/useKeyValue';
|
||||
import { useRandomKey } from '../../hooks/useRandomKey';
|
||||
import { sync } from '../../init/sync';
|
||||
import { showConfirm, showConfirmDelete } from '../../lib/confirm';
|
||||
import { showDialog } from '../../lib/dialog';
|
||||
import { fireAndForget } from '../../lib/fireAndForget';
|
||||
import { showPrompt } from '../../lib/prompt';
|
||||
import { showErrorToast, showToast } from '../../lib/toast';
|
||||
import { Banner } from '../core/Banner';
|
||||
import type { DropdownItem } from '../core/Dropdown';
|
||||
import { Dropdown } from '../core/Dropdown';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { gitCallbacks } from './callbacks';
|
||||
import { GitCommitDialog } from './GitCommitDialog';
|
||||
import { GitRemotesDialog } from './GitRemotesDialog';
|
||||
import { handlePullResult, handlePushResult } from './git-util';
|
||||
import { HistoryDialog } from './HistoryDialog';
|
||||
import { useGit } from "@yaakapp-internal/git";
|
||||
import type { WorkspaceMeta } from "@yaakapp-internal/models";
|
||||
import classNames from "classnames";
|
||||
import { useAtomValue } from "jotai";
|
||||
import type { HTMLAttributes } from "react";
|
||||
import { forwardRef } from "react";
|
||||
import { openWorkspaceSettings } from "../../commands/openWorkspaceSettings";
|
||||
import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from "../../hooks/useActiveWorkspace";
|
||||
import { useKeyValue } from "../../hooks/useKeyValue";
|
||||
import { useRandomKey } from "../../hooks/useRandomKey";
|
||||
import { sync } from "../../init/sync";
|
||||
import { showConfirm, showConfirmDelete } from "../../lib/confirm";
|
||||
import { showDialog } from "../../lib/dialog";
|
||||
import { fireAndForget } from "../../lib/fireAndForget";
|
||||
import { showPrompt } from "../../lib/prompt";
|
||||
import { showErrorToast, showToast } from "../../lib/toast";
|
||||
import { Banner } from "../core/Banner";
|
||||
import type { DropdownItem } from "../core/Dropdown";
|
||||
import { Dropdown } from "../core/Dropdown";
|
||||
import { Icon } from "../core/Icon";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { gitCallbacks } from "./callbacks";
|
||||
import { GitCommitDialog } from "./GitCommitDialog";
|
||||
import { GitRemotesDialog } from "./GitRemotesDialog";
|
||||
import { handlePullResult, handlePushResult } from "./git-util";
|
||||
import { HistoryDialog } from "./HistoryDialog";
|
||||
|
||||
export function GitDropdown() {
|
||||
const workspaceMeta = useAtomValue(activeWorkspaceMetaAtom);
|
||||
@@ -58,13 +58,13 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
const localBranches = status.data?.localBranches ?? [];
|
||||
const remoteBranches = status.data?.remoteBranches ?? [];
|
||||
const remoteOnlyBranches = remoteBranches.filter(
|
||||
(b) => !localBranches.includes(b.replace(/^origin\//, '')),
|
||||
(b) => !localBranches.includes(b.replace(/^origin\//, "")),
|
||||
);
|
||||
if (workspace == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const noRepo = status.error?.includes('not found');
|
||||
const noRepo = status.error?.includes("not found");
|
||||
if (noRepo) {
|
||||
return <SetupGitDropdown workspaceId={workspace.id} initRepo={init.mutate} />;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
}
|
||||
|
||||
const currentBranch = status.data.headRefShorthand;
|
||||
const hasChanges = status.data.entries.some((e) => e.status !== 'current');
|
||||
const hasChanges = status.data.entries.some((e) => e.status !== "current");
|
||||
const _hasRemotes = (status.data.origins ?? []).length > 0;
|
||||
const { ahead, behind } = status.data;
|
||||
|
||||
@@ -88,12 +88,12 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
if (!force) {
|
||||
// Checkout failed so ask user if they want to force it
|
||||
const forceCheckout = await showConfirm({
|
||||
id: 'git-force-checkout',
|
||||
title: 'Conflicts Detected',
|
||||
id: "git-force-checkout",
|
||||
title: "Conflicts Detected",
|
||||
description:
|
||||
'Your branch has conflicts. Either make a commit or force checkout to discard changes.',
|
||||
confirmText: 'Force Checkout',
|
||||
color: 'warning',
|
||||
"Your branch has conflicts. Either make a commit or force checkout to discard changes.",
|
||||
confirmText: "Force Checkout",
|
||||
color: "warning",
|
||||
});
|
||||
if (forceCheckout) {
|
||||
tryCheckout(branch, true);
|
||||
@@ -101,21 +101,21 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
} else {
|
||||
// Checkout failed
|
||||
showErrorToast({
|
||||
id: 'git-checkout-error',
|
||||
title: 'Error checking out branch',
|
||||
id: "git-checkout-error",
|
||||
title: "Error checking out branch",
|
||||
message: String(err),
|
||||
});
|
||||
}
|
||||
},
|
||||
async onSuccess(branchName) {
|
||||
showToast({
|
||||
id: 'git-checkout-success',
|
||||
id: "git-checkout-success",
|
||||
message: (
|
||||
<>
|
||||
Switched branch <InlineCode>{branchName}</InlineCode>
|
||||
</>
|
||||
),
|
||||
color: 'success',
|
||||
color: "success",
|
||||
});
|
||||
await sync({ force: true });
|
||||
},
|
||||
@@ -125,33 +125,33 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
|
||||
const items: DropdownItem[] = [
|
||||
{
|
||||
label: 'View History...',
|
||||
label: "View History...",
|
||||
hidden: (log.data ?? []).length === 0,
|
||||
leftSlot: <Icon icon="history" />,
|
||||
onSelect: async () => {
|
||||
showDialog({
|
||||
id: 'git-history',
|
||||
size: 'md',
|
||||
title: 'Commit History',
|
||||
id: "git-history",
|
||||
size: "md",
|
||||
title: "Commit History",
|
||||
noPadding: true,
|
||||
render: () => <HistoryDialog log={log.data ?? []} />,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Manage Remotes...',
|
||||
label: "Manage Remotes...",
|
||||
leftSlot: <Icon icon="hard_drive_download" />,
|
||||
onSelect: () => GitRemotesDialog.show(syncDir),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: 'New Branch...',
|
||||
label: "New Branch...",
|
||||
leftSlot: <Icon icon="git_branch_plus" />,
|
||||
async onSelect() {
|
||||
const name = await showPrompt({
|
||||
id: 'git-branch-name',
|
||||
title: 'Create Branch',
|
||||
label: 'Branch Name',
|
||||
id: "git-branch-name",
|
||||
title: "Create Branch",
|
||||
label: "Branch Name",
|
||||
});
|
||||
if (!name) return;
|
||||
|
||||
@@ -161,8 +161,8 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onError: (err) => {
|
||||
showErrorToast({
|
||||
id: 'git-branch-error',
|
||||
title: 'Error creating branch',
|
||||
id: "git-branch-error",
|
||||
title: "Error creating branch",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -171,9 +171,9 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
tryCheckout(name, false);
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: 'Push',
|
||||
label: "Push",
|
||||
leftSlot: <Icon icon="arrow_up_from_line" />,
|
||||
waitForOnSelect: true,
|
||||
async onSelect() {
|
||||
@@ -182,8 +182,8 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
onSuccess: handlePushResult,
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-push-error',
|
||||
title: 'Error pushing changes',
|
||||
id: "git-push-error",
|
||||
title: "Error pushing changes",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -191,7 +191,7 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Pull',
|
||||
label: "Pull",
|
||||
leftSlot: <Icon icon="arrow_down_to_line" />,
|
||||
waitForOnSelect: true,
|
||||
async onSelect() {
|
||||
@@ -200,8 +200,8 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
onSuccess: handlePullResult,
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-pull-error',
|
||||
title: 'Error pulling changes',
|
||||
id: "git-pull-error",
|
||||
title: "Error pulling changes",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -209,14 +209,14 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Commit...',
|
||||
label: "Commit...",
|
||||
|
||||
leftSlot: <Icon icon="git_commit_vertical" />,
|
||||
onSelect() {
|
||||
showDialog({
|
||||
id: 'commit',
|
||||
title: 'Commit Changes',
|
||||
size: 'full',
|
||||
id: "commit",
|
||||
title: "Commit Changes",
|
||||
size: "full",
|
||||
noPadding: true,
|
||||
render: ({ hide }) => (
|
||||
<GitCommitDialog syncDir={syncDir} onDone={hide} workspace={workspace} />
|
||||
@@ -225,17 +225,17 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Reset Changes',
|
||||
label: "Reset Changes",
|
||||
hidden: !hasChanges,
|
||||
leftSlot: <Icon icon="rotate_ccw" />,
|
||||
color: 'danger',
|
||||
color: "danger",
|
||||
async onSelect() {
|
||||
const confirmed = await showConfirm({
|
||||
id: 'git-reset-changes',
|
||||
title: 'Reset Changes',
|
||||
description: 'This will discard all uncommitted changes. This cannot be undone.',
|
||||
confirmText: 'Reset',
|
||||
color: 'danger',
|
||||
id: "git-reset-changes",
|
||||
title: "Reset Changes",
|
||||
description: "This will discard all uncommitted changes. This cannot be undone.",
|
||||
confirmText: "Reset",
|
||||
color: "danger",
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
@@ -243,32 +243,32 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onSuccess() {
|
||||
showToast({
|
||||
id: 'git-reset-success',
|
||||
message: 'Changes have been reset',
|
||||
color: 'success',
|
||||
id: "git-reset-success",
|
||||
message: "Changes have been reset",
|
||||
color: "success",
|
||||
});
|
||||
fireAndForget(sync({ force: true }));
|
||||
},
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-reset-error',
|
||||
title: 'Error resetting changes',
|
||||
id: "git-reset-error",
|
||||
title: "Error resetting changes",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{ type: 'separator', label: 'Branches', hidden: localBranches.length < 1 },
|
||||
{ type: "separator", label: "Branches", hidden: localBranches.length < 1 },
|
||||
...localBranches.map((branch) => {
|
||||
const isCurrent = currentBranch === branch;
|
||||
return {
|
||||
label: branch,
|
||||
leftSlot: <Icon icon={isCurrent ? 'check' : 'empty'} />,
|
||||
leftSlot: <Icon icon={isCurrent ? "check" : "empty"} />,
|
||||
submenuOpenOnClick: true,
|
||||
submenu: [
|
||||
{
|
||||
label: 'Checkout',
|
||||
label: "Checkout",
|
||||
hidden: isCurrent,
|
||||
onSelect: () => tryCheckout(branch, false),
|
||||
},
|
||||
@@ -286,10 +286,10 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onSuccess() {
|
||||
showToast({
|
||||
id: 'git-merged-branch',
|
||||
id: "git-merged-branch",
|
||||
message: (
|
||||
<>
|
||||
Merged <InlineCode>{branch}</InlineCode> into{' '}
|
||||
Merged <InlineCode>{branch}</InlineCode> into{" "}
|
||||
<InlineCode>{currentBranch}</InlineCode>
|
||||
</>
|
||||
),
|
||||
@@ -298,8 +298,8 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
},
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-merged-branch-error',
|
||||
title: 'Error merging branch',
|
||||
id: "git-merged-branch-error",
|
||||
title: "Error merging branch",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -308,17 +308,17 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'New Branch...',
|
||||
label: "New Branch...",
|
||||
async onSelect() {
|
||||
const name = await showPrompt({
|
||||
id: 'git-new-branch-from',
|
||||
title: 'New Branch',
|
||||
id: "git-new-branch-from",
|
||||
title: "New Branch",
|
||||
description: (
|
||||
<>
|
||||
Create a new branch from <InlineCode>{branch}</InlineCode>
|
||||
</>
|
||||
),
|
||||
label: 'Branch Name',
|
||||
label: "Branch Name",
|
||||
});
|
||||
if (!name) return;
|
||||
|
||||
@@ -328,8 +328,8 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onError: (err) => {
|
||||
showErrorToast({
|
||||
id: 'git-branch-error',
|
||||
title: 'Error creating branch',
|
||||
id: "git-branch-error",
|
||||
title: "Error creating branch",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -339,12 +339,12 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Rename...',
|
||||
label: "Rename...",
|
||||
async onSelect() {
|
||||
const newName = await showPrompt({
|
||||
id: 'git-rename-branch',
|
||||
title: 'Rename Branch',
|
||||
label: 'New Branch Name',
|
||||
id: "git-rename-branch",
|
||||
title: "Rename Branch",
|
||||
label: "New Branch Name",
|
||||
defaultValue: branch,
|
||||
});
|
||||
if (!newName || newName === branch) return;
|
||||
@@ -355,20 +355,20 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onSuccess() {
|
||||
showToast({
|
||||
id: 'git-rename-branch-success',
|
||||
id: "git-rename-branch-success",
|
||||
message: (
|
||||
<>
|
||||
Renamed <InlineCode>{branch}</InlineCode> to{' '}
|
||||
Renamed <InlineCode>{branch}</InlineCode> to{" "}
|
||||
<InlineCode>{newName}</InlineCode>
|
||||
</>
|
||||
),
|
||||
color: 'success',
|
||||
color: "success",
|
||||
});
|
||||
},
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-rename-branch-error',
|
||||
title: 'Error renaming branch',
|
||||
id: "git-rename-branch-error",
|
||||
title: "Error renaming branch",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -376,15 +376,15 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
);
|
||||
},
|
||||
},
|
||||
{ type: 'separator', hidden: isCurrent },
|
||||
{ type: "separator", hidden: isCurrent },
|
||||
{
|
||||
label: 'Delete',
|
||||
color: 'danger',
|
||||
label: "Delete",
|
||||
color: "danger",
|
||||
hidden: isCurrent,
|
||||
onSelect: async () => {
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'git-delete-branch',
|
||||
title: 'Delete Branch',
|
||||
id: "git-delete-branch",
|
||||
title: "Delete Branch",
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{branch}</InlineCode>?
|
||||
@@ -401,18 +401,18 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-delete-branch-error',
|
||||
title: 'Error deleting branch',
|
||||
id: "git-delete-branch-error",
|
||||
title: "Error deleting branch",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (result.type === 'not_fully_merged') {
|
||||
if (result.type === "not_fully_merged") {
|
||||
const confirmed = await showConfirm({
|
||||
id: 'force-branch-delete',
|
||||
title: 'Branch not fully merged',
|
||||
id: "force-branch-delete",
|
||||
title: "Branch not fully merged",
|
||||
description: (
|
||||
<>
|
||||
<p>
|
||||
@@ -429,8 +429,8 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-force-delete-branch-error',
|
||||
title: 'Error force deleting branch',
|
||||
id: "git-force-delete-branch-error",
|
||||
title: "Error force deleting branch",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -447,21 +447,21 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
const isCurrent = currentBranch === branch;
|
||||
return {
|
||||
label: branch,
|
||||
leftSlot: <Icon icon={isCurrent ? 'check' : 'empty'} />,
|
||||
leftSlot: <Icon icon={isCurrent ? "check" : "empty"} />,
|
||||
submenuOpenOnClick: true,
|
||||
submenu: [
|
||||
{
|
||||
label: 'Checkout',
|
||||
label: "Checkout",
|
||||
hidden: isCurrent,
|
||||
onSelect: () => tryCheckout(branch, false),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
color: 'danger',
|
||||
label: "Delete",
|
||||
color: "danger",
|
||||
async onSelect() {
|
||||
const confirmed = await showConfirmDelete({
|
||||
id: 'git-delete-remote-branch',
|
||||
title: 'Delete Remote Branch',
|
||||
id: "git-delete-remote-branch",
|
||||
title: "Delete Remote Branch",
|
||||
description: (
|
||||
<>
|
||||
Permanently delete <InlineCode>{branch}</InlineCode> from the remote?
|
||||
@@ -476,19 +476,19 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
|
||||
disableToastError: true,
|
||||
onSuccess() {
|
||||
showToast({
|
||||
id: 'git-delete-remote-branch-success',
|
||||
id: "git-delete-remote-branch-success",
|
||||
message: (
|
||||
<>
|
||||
Deleted remote branch <InlineCode>{branch}</InlineCode>
|
||||
</>
|
||||
),
|
||||
color: 'success',
|
||||
color: "success",
|
||||
});
|
||||
},
|
||||
onError(err) {
|
||||
showErrorToast({
|
||||
id: 'git-delete-remote-branch-error',
|
||||
title: 'Error deleting remote branch',
|
||||
id: "git-delete-remote-branch-error",
|
||||
title: "Error deleting remote branch",
|
||||
message: String(err),
|
||||
});
|
||||
},
|
||||
@@ -534,7 +534,7 @@ const GitMenuButton = forwardRef<HTMLButtonElement, HTMLAttributes<HTMLButtonEle
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
className,
|
||||
'px-3 h-md border-t border-border flex items-center justify-between text-text-subtle outline-none focus-visible:bg-surface-highlight',
|
||||
"px-3 h-md border-t border-border flex items-center justify-between text-text-subtle outline-none focus-visible:bg-surface-highlight",
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -544,7 +544,7 @@ const GitMenuButton = forwardRef<HTMLButtonElement, HTMLAttributes<HTMLButtonEle
|
||||
|
||||
function SetupSyncDropdown({ workspaceMeta }: { workspaceMeta: WorkspaceMeta }) {
|
||||
const { value: hidden, set: setHidden } = useKeyValue<Record<string, boolean>>({
|
||||
key: 'setup_sync',
|
||||
key: "setup_sync",
|
||||
fallback: {},
|
||||
});
|
||||
|
||||
@@ -564,24 +564,24 @@ function SetupSyncDropdown({ workspaceMeta }: { workspaceMeta: WorkspaceMeta })
|
||||
fullWidth
|
||||
items={[
|
||||
{
|
||||
type: 'content',
|
||||
type: "content",
|
||||
label: banner,
|
||||
},
|
||||
{
|
||||
color: 'success',
|
||||
label: 'Open Workspace Settings',
|
||||
color: "success",
|
||||
label: "Open Workspace Settings",
|
||||
leftSlot: <Icon icon="settings" />,
|
||||
onSelect: () => openWorkspaceSettings('data'),
|
||||
onSelect: () => openWorkspaceSettings("data"),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: 'Hide This Message',
|
||||
label: "Hide This Message",
|
||||
leftSlot: <Icon icon="eye_closed" />,
|
||||
async onSelect() {
|
||||
const confirmed = await showConfirm({
|
||||
id: 'hide-sync-menu-prompt',
|
||||
title: 'Hide Setup Message',
|
||||
description: 'You can configure filesystem sync or Git it in the workspace settings',
|
||||
id: "hide-sync-menu-prompt",
|
||||
title: "Hide Setup Message",
|
||||
description: "You can configure filesystem sync or Git it in the workspace settings",
|
||||
});
|
||||
if (confirmed) {
|
||||
await setHidden((prev) => ({ ...prev, [workspaceMeta.workspaceId]: true }));
|
||||
@@ -608,7 +608,7 @@ function SetupGitDropdown({
|
||||
initRepo: () => void;
|
||||
}) {
|
||||
const { value: hidden, set: setHidden } = useKeyValue<Record<string, boolean>>({
|
||||
key: 'setup_git_repo',
|
||||
key: "setup_git_repo",
|
||||
fallback: {},
|
||||
});
|
||||
|
||||
@@ -622,21 +622,21 @@ function SetupGitDropdown({
|
||||
<Dropdown
|
||||
fullWidth
|
||||
items={[
|
||||
{ type: 'content', label: banner },
|
||||
{ type: "content", label: banner },
|
||||
{
|
||||
label: 'Initialize Git Repo',
|
||||
label: "Initialize Git Repo",
|
||||
leftSlot: <Icon icon="magic_wand" />,
|
||||
onSelect: initRepo,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: 'Hide This Message',
|
||||
label: "Hide This Message",
|
||||
leftSlot: <Icon icon="eye_closed" />,
|
||||
async onSelect() {
|
||||
const confirmed = await showConfirm({
|
||||
id: 'hide-git-init-prompt',
|
||||
title: 'Hide Git Setup',
|
||||
description: 'You can initialize a git repo outside of Yaak to bring this back',
|
||||
id: "hide-git-init-prompt",
|
||||
title: "Hide Git Setup",
|
||||
description: "You can initialize a git repo outside of Yaak to bring this back",
|
||||
});
|
||||
if (confirmed) {
|
||||
await setHidden((prev) => ({ ...prev, [workspaceId]: true }));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useGit } from '@yaakapp-internal/git';
|
||||
import { showDialog } from '../../lib/dialog';
|
||||
import { Button } from '../core/Button';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from '../core/Table';
|
||||
import { gitCallbacks } from './callbacks';
|
||||
import { addGitRemote } from './showAddRemoteDialog';
|
||||
import { useGit } from "@yaakapp-internal/git";
|
||||
import { showDialog } from "../../lib/dialog";
|
||||
import { Button } from "../core/Button";
|
||||
import { IconButton } from "../core/IconButton";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "../core/Table";
|
||||
import { gitCallbacks } from "./callbacks";
|
||||
import { addGitRemote } from "./showAddRemoteDialog";
|
||||
|
||||
interface Props {
|
||||
dir: string;
|
||||
@@ -57,9 +57,9 @@ export function GitRemotesDialog({ dir }: Props) {
|
||||
|
||||
GitRemotesDialog.show = (dir: string) => {
|
||||
showDialog({
|
||||
id: 'git-remotes',
|
||||
title: 'Manage Remotes',
|
||||
size: 'md',
|
||||
id: "git-remotes",
|
||||
title: "Manage Remotes",
|
||||
size: "md",
|
||||
render: ({ hide }) => <GitRemotesDialog onDone={hide} dir={dir} />,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GitCommit } from '@yaakapp-internal/git';
|
||||
import { formatDistanceToNowStrict } from 'date-fns';
|
||||
import type { GitCommit } from "@yaakapp-internal/git";
|
||||
import { formatDistanceToNowStrict } from "date-fns";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
TruncatedWideTableCell,
|
||||
} from '../core/Table';
|
||||
} from "../core/Table";
|
||||
|
||||
interface Props {
|
||||
log: GitCommit[];
|
||||
@@ -27,12 +27,14 @@ export function HistoryDialog({ log }: Props) {
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{log.map((l) => (
|
||||
<TableRow key={(l.author.name ?? '') + (l.author.email ?? '') + (l.message ?? 'n/a') + l.when}>
|
||||
<TableRow
|
||||
key={(l.author.name ?? "") + (l.author.email ?? "") + (l.message ?? "n/a") + l.when}
|
||||
>
|
||||
<TruncatedWideTableCell>
|
||||
{l.message || <em className="text-text-subtle">No message</em>}
|
||||
</TruncatedWideTableCell>
|
||||
<TableCell>
|
||||
<span title={`Email: ${l.author.email}`}>{l.author.name || 'Unknown'}</span>
|
||||
<span title={`Email: ${l.author.email}`}>{l.author.name || "Unknown"}</span>
|
||||
</TableCell>
|
||||
<TableCell className="text-text-subtle">
|
||||
<span title={l.when}>{formatDistanceToNowStrict(l.when)} ago</span>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { GitCallbacks } from '@yaakapp-internal/git';
|
||||
import { sync } from '../../init/sync';
|
||||
import { promptCredentials } from './credentials';
|
||||
import { promptDivergedStrategy } from './diverged';
|
||||
import { addGitRemote } from './showAddRemoteDialog';
|
||||
import { promptUncommittedChangesStrategy } from './uncommitted';
|
||||
import type { GitCallbacks } from "@yaakapp-internal/git";
|
||||
import { sync } from "../../init/sync";
|
||||
import { promptCredentials } from "./credentials";
|
||||
import { promptDivergedStrategy } from "./diverged";
|
||||
import { addGitRemote } from "./showAddRemoteDialog";
|
||||
import { promptUncommittedChangesStrategy } from "./uncommitted";
|
||||
|
||||
export function gitCallbacks(dir: string): GitCallbacks {
|
||||
return {
|
||||
addRemote: async () => {
|
||||
return addGitRemote(dir, 'origin');
|
||||
return addGitRemote(dir, "origin");
|
||||
},
|
||||
promptCredentials: async ({ url, error }) => {
|
||||
const creds = await promptCredentials({ url, error });
|
||||
if (creds == null) throw new Error('Cancelled credentials prompt');
|
||||
if (creds == null) throw new Error("Cancelled credentials prompt");
|
||||
return creds;
|
||||
},
|
||||
promptDiverged: async ({ remote, branch }) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { showPromptForm } from '../../lib/prompt-form';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { showPromptForm } from "../../lib/prompt-form";
|
||||
import { Banner } from "../core/Banner";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
|
||||
export interface GitCredentials {
|
||||
username: string;
|
||||
@@ -15,15 +15,15 @@ export async function promptCredentials({
|
||||
error: string | null;
|
||||
}): Promise<GitCredentials | null> {
|
||||
const isGitHub = /github\.com/i.test(remoteUrl);
|
||||
const userLabel = isGitHub ? 'GitHub Username' : 'Username';
|
||||
const passLabel = isGitHub ? 'GitHub Personal Access Token' : 'Password / Token';
|
||||
const userDescription = isGitHub ? 'Use your GitHub username (not your email).' : undefined;
|
||||
const userLabel = isGitHub ? "GitHub Username" : "Username";
|
||||
const passLabel = isGitHub ? "GitHub Personal Access Token" : "Password / Token";
|
||||
const userDescription = isGitHub ? "Use your GitHub username (not your email)." : undefined;
|
||||
const passDescription = isGitHub
|
||||
? 'GitHub requires a Personal Access Token (PAT) for write operations over HTTPS. Passwords are not supported.'
|
||||
: 'Enter your password or access token for this Git server.';
|
||||
? "GitHub requires a Personal Access Token (PAT) for write operations over HTTPS. Passwords are not supported."
|
||||
: "Enter your password or access token for this Git server.";
|
||||
const r = await showPromptForm({
|
||||
id: 'git-credentials',
|
||||
title: 'Credentials Required',
|
||||
id: "git-credentials",
|
||||
title: "Credentials Required",
|
||||
description: error ? (
|
||||
<Banner color="danger">{error}</Banner>
|
||||
) : (
|
||||
@@ -32,10 +32,10 @@ export async function promptCredentials({
|
||||
</>
|
||||
),
|
||||
inputs: [
|
||||
{ type: 'text', name: 'username', label: userLabel, description: userDescription },
|
||||
{ type: "text", name: "username", label: userLabel, description: userDescription },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'password',
|
||||
type: "text",
|
||||
name: "password",
|
||||
label: passLabel,
|
||||
description: passDescription,
|
||||
password: true,
|
||||
@@ -44,7 +44,7 @@ export async function promptCredentials({
|
||||
});
|
||||
if (r == null) return null;
|
||||
|
||||
const username = String(r.username || '');
|
||||
const password = String(r.password || '');
|
||||
const username = String(r.username || "");
|
||||
const password = String(r.password || "");
|
||||
return { username, password };
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { DivergedStrategy } from '@yaakapp-internal/git';
|
||||
import { useState } from 'react';
|
||||
import { showDialog } from '../../lib/dialog';
|
||||
import { Button } from '../core/Button';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { RadioCards } from '../core/RadioCards';
|
||||
import { HStack } from '../core/Stacks';
|
||||
import type { DivergedStrategy } from "@yaakapp-internal/git";
|
||||
import { useState } from "react";
|
||||
import { showDialog } from "../../lib/dialog";
|
||||
import { Button } from "../core/Button";
|
||||
import { InlineCode } from "../core/InlineCode";
|
||||
import { RadioCards } from "../core/RadioCards";
|
||||
import { HStack } from "../core/Stacks";
|
||||
|
||||
type Resolution = 'force_reset' | 'merge';
|
||||
type Resolution = "force_reset" | "merge";
|
||||
|
||||
const resolutionLabel: Record<Resolution, string> = {
|
||||
force_reset: 'Force Pull',
|
||||
merge: 'Merge',
|
||||
force_reset: "Force Pull",
|
||||
merge: "Merge",
|
||||
};
|
||||
|
||||
interface DivergedDialogProps {
|
||||
@@ -30,17 +30,18 @@ function DivergedDialog({ remote, branch, onResult, onHide }: DivergedDialogProp
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
onResult('cancel');
|
||||
onResult("cancel");
|
||||
onHide();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 mb-4">
|
||||
<p className="text-text-subtle">
|
||||
Your local branch has diverged from{' '}
|
||||
Your local branch has diverged from{" "}
|
||||
<InlineCode>
|
||||
{remote}/{branch}
|
||||
</InlineCode>. How would you like to resolve this?
|
||||
</InlineCode>
|
||||
. How would you like to resolve this?
|
||||
</p>
|
||||
<RadioCards
|
||||
name="diverged-strategy"
|
||||
@@ -48,24 +49,24 @@ function DivergedDialog({ remote, branch, onResult, onHide }: DivergedDialogProp
|
||||
onChange={setSelected}
|
||||
options={[
|
||||
{
|
||||
value: 'merge',
|
||||
label: 'Merge Commit',
|
||||
description: 'Combining local and remote changes into a single merge commit',
|
||||
value: "merge",
|
||||
label: "Merge Commit",
|
||||
description: "Combining local and remote changes into a single merge commit",
|
||||
},
|
||||
{
|
||||
value: 'force_reset',
|
||||
label: 'Force Pull',
|
||||
description: 'Discard local commits and reset to match the remote branch',
|
||||
value: "force_reset",
|
||||
label: "Force Pull",
|
||||
description: "Discard local commits and reset to match the remote branch",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<HStack space={2} justifyContent="start" className="flex-row-reverse">
|
||||
<Button
|
||||
color={selected === 'force_reset' ? 'danger' : 'primary'}
|
||||
color={selected === "force_reset" ? "danger" : "primary"}
|
||||
disabled={selected == null}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{selected != null ? resolutionLabel[selected] : 'Select an option'}
|
||||
{selected != null ? resolutionLabel[selected] : "Select an option"}
|
||||
</Button>
|
||||
<Button variant="border" onClick={handleCancel}>
|
||||
Cancel
|
||||
@@ -84,12 +85,12 @@ export async function promptDivergedStrategy({
|
||||
}): Promise<DivergedStrategy> {
|
||||
return new Promise((resolve) => {
|
||||
showDialog({
|
||||
id: 'git-diverged',
|
||||
title: 'Branches Diverged',
|
||||
id: "git-diverged",
|
||||
title: "Branches Diverged",
|
||||
hideX: true,
|
||||
size: 'sm',
|
||||
size: "sm",
|
||||
disableBackdropClose: true,
|
||||
onClose: () => resolve('cancel'),
|
||||
onClose: () => resolve("cancel"),
|
||||
render: ({ hide }) =>
|
||||
DivergedDialog({
|
||||
remote,
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import type { PullResult, PushResult } from '@yaakapp-internal/git';
|
||||
import { showToast } from '../../lib/toast';
|
||||
import type { PullResult, PushResult } from "@yaakapp-internal/git";
|
||||
import { showToast } from "../../lib/toast";
|
||||
|
||||
export function handlePushResult(r: PushResult) {
|
||||
switch (r.type) {
|
||||
case 'needs_credentials':
|
||||
showToast({ id: 'push-error', message: 'Credentials not found', color: 'danger' });
|
||||
case "needs_credentials":
|
||||
showToast({ id: "push-error", message: "Credentials not found", color: "danger" });
|
||||
break;
|
||||
case 'success':
|
||||
showToast({ id: 'push-success', message: r.message, color: 'success' });
|
||||
case "success":
|
||||
showToast({ id: "push-success", message: r.message, color: "success" });
|
||||
break;
|
||||
case 'up_to_date':
|
||||
showToast({ id: 'push-nothing', message: 'Already up-to-date', color: 'info' });
|
||||
case "up_to_date":
|
||||
showToast({ id: "push-nothing", message: "Already up-to-date", color: "info" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function handlePullResult(r: PullResult) {
|
||||
switch (r.type) {
|
||||
case 'needs_credentials':
|
||||
showToast({ id: 'pull-error', message: 'Credentials not found', color: 'danger' });
|
||||
case "needs_credentials":
|
||||
showToast({ id: "pull-error", message: "Credentials not found", color: "danger" });
|
||||
break;
|
||||
case 'success':
|
||||
showToast({ id: 'pull-success', message: r.message, color: 'success' });
|
||||
case "success":
|
||||
showToast({ id: "pull-success", message: r.message, color: "success" });
|
||||
break;
|
||||
case 'up_to_date':
|
||||
showToast({ id: 'pull-nothing', message: 'Already up-to-date', color: 'info' });
|
||||
case "up_to_date":
|
||||
showToast({ id: "pull-nothing", message: "Already up-to-date", color: "info" });
|
||||
break;
|
||||
case 'diverged':
|
||||
case "diverged":
|
||||
// Handled by mutation callback before reaching here
|
||||
break;
|
||||
case 'uncommitted_changes':
|
||||
case "uncommitted_changes":
|
||||
// Handled by mutation callback before reaching here
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import type { GitRemote } from '@yaakapp-internal/git';
|
||||
import { gitMutations } from '@yaakapp-internal/git';
|
||||
import { showPromptForm } from '../../lib/prompt-form';
|
||||
import { gitCallbacks } from './callbacks';
|
||||
import type { GitRemote } from "@yaakapp-internal/git";
|
||||
import { gitMutations } from "@yaakapp-internal/git";
|
||||
import { showPromptForm } from "../../lib/prompt-form";
|
||||
import { gitCallbacks } from "./callbacks";
|
||||
|
||||
export async function addGitRemote(dir: string, defaultName?: string): Promise<GitRemote> {
|
||||
const r = await showPromptForm({
|
||||
id: 'add-remote',
|
||||
title: 'Add Remote',
|
||||
id: "add-remote",
|
||||
title: "Add Remote",
|
||||
inputs: [
|
||||
{ type: 'text', label: 'Name', name: 'name', defaultValue: defaultName },
|
||||
{ type: 'text', label: 'URL', name: 'url' },
|
||||
{ type: "text", label: "Name", name: "name", defaultValue: defaultName },
|
||||
{ type: "text", label: "URL", name: "url" },
|
||||
],
|
||||
});
|
||||
if (r == null) throw new Error('Cancelled remote prompt');
|
||||
if (r == null) throw new Error("Cancelled remote prompt");
|
||||
|
||||
const name = String(r.name ?? '');
|
||||
const url = String(r.url ?? '');
|
||||
const name = String(r.name ?? "");
|
||||
const url = String(r.url ?? "");
|
||||
return gitMutations(dir, gitCallbacks(dir)).addRemote.mutateAsync({ name, url });
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { UncommittedChangesStrategy } from '@yaakapp-internal/git';
|
||||
import { showConfirm } from '../../lib/confirm';
|
||||
import type { UncommittedChangesStrategy } from "@yaakapp-internal/git";
|
||||
import { showConfirm } from "../../lib/confirm";
|
||||
|
||||
export async function promptUncommittedChangesStrategy(): Promise<UncommittedChangesStrategy> {
|
||||
const confirmed = await showConfirm({
|
||||
id: 'git-uncommitted-changes',
|
||||
title: 'Uncommitted Changes',
|
||||
description: 'You have uncommitted changes. Commit or reset your changes before pulling.',
|
||||
confirmText: 'Reset and Pull',
|
||||
color: 'danger',
|
||||
id: "git-uncommitted-changes",
|
||||
title: "Uncommitted Changes",
|
||||
description: "You have uncommitted changes. Commit or reset your changes before pulling.",
|
||||
confirmText: "Reset and Pull",
|
||||
color: "danger",
|
||||
});
|
||||
return confirmed ? 'reset' : 'cancel';
|
||||
return confirmed ? "reset" : "cancel";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user