Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
89c959b
GitLab support and encrypt credentials
Dec 16, 2024
d6da850
Merge branch 'main' into iAlsoWantGitLab
D-Byte Dec 16, 2024
277082a
chore: update commit hash to 0b473658465ac1f65ac4ee80b22101cd1f59183f
github-actions[bot] Dec 16, 2024
f69054a
chore: update commit hash to 0ee373629789f01fb9f54f6747735b51a94a5562
github-actions[bot] Dec 17, 2024
b9601b2
Merge branch 'main' of https://github.com/D-Byte/bolt.new-any-llm int…
Dec 17, 2024
3d37940
chore: update commit hash to b9601b26d739f935eda935c61dea4c7c6b9c7220
github-actions[bot] Dec 17, 2024
5ce0341
Merge with new Github setup
Dec 17, 2024
a01c2de
Updated to the new connect/disconnect methode
Dec 17, 2024
300d01b
Some lint cleanup
Dec 17, 2024
1c66d4e
fixed type errors in useGitProviders
Dec 17, 2024
761f142
Again some lint cleanup
Dec 17, 2024
f0f88ca
Merge pull request #2 from D-Byte/iAlsoWantGitLabTwo
D-Byte Dec 17, 2024
b8104b9
Merge branch 'main' into iAlsoWantGitLab
D-Byte Dec 17, 2024
adbce23
First setup for new structure
Dec 18, 2024
cdc49bd
Gitlab is working, github need testing
Dec 18, 2024
918d351
Fixed lint errors
Dec 18, 2024
3c17259
Clone and push work for github
Dec 19, 2024
a51ca2c
Merge branch 'main' into iAlsoWantGitLab
D-Byte Dec 19, 2024
7802bc5
Moved gitlab and github to own folders in lib/git
Dec 22, 2024
e028c01
Merge branch 'iAlsoWantGitLab' of https://github.com/D-Byte/bolt.new-…
Dec 22, 2024
f7baff8
And forgot to do the type check, and a cosmetic fix
Dec 22, 2024
c7a5335
Removed console logs and made icon not loading workaround
Dec 22, 2024
f740aa7
Merge branch 'main' of https://github.com/D-Byte/bolt.new-any-llm int…
Dec 22, 2024
6a72194
Also not anymore on main branch
Dec 22, 2024
5fe38c9
Merge pull request #3 from D-Byte/iAlsoWantGitLab
D-Byte Dec 22, 2024
07029fa
Was till a cosmetic error :-(
Dec 22, 2024
88682a8
Merge branch 'stackblitz-labs:main' into main
D-Byte Dec 28, 2024
a82814b
Fixed midding token in gitlab validation
Dec 28, 2024
fc8499f
from 0.5 base
Jan 3, 2025
2a53221
Was till a cosmetic error :-(
Dec 22, 2024
3aaac2e
Fixed midding token in gitlab validation
Dec 28, 2024
6513f93
Merge branch 'iAlsoWantGitLab' of https://github.com/D-Byte/bolt.new-…
Jan 3, 2025
48e5422
Added missing library Axios
Jan 3, 2025
f3350f6
Merge branch 'main' into iAlsoWantGitLab
D-Byte Jan 3, 2025
e1a4484
some strange thing with the lock file fixed
Jan 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/components/chat/GitCloneButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import ignore from 'ignore';
import { useGit } from '~/lib/hooks/useGit';
import type { Message } from 'ai';
import { detectProjectCommands, createCommandsMessage } from '~/utils/projectCommands';
import { generateId } from '~/utils/fileUtils';
import { useGit } from '~/lib/git';

const IGNORE_PATTERNS = [
'node_modules/**',
Expand Down
2 changes: 1 addition & 1 deletion app/components/git/GitUrlImport.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { useEffect, useState } from 'react';
import { ClientOnly } from 'remix-utils/client-only';
import { BaseChat } from '~/components/chat/BaseChat';
import { Chat } from '~/components/chat/Chat.client';
import { useGit } from '~/lib/hooks/useGit';
import { useChatHistory } from '~/lib/persistence';
import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands';
import { LoadingOverlay } from '~/components/ui/LoadingOverlay';
import { toast } from 'react-toastify';
import { useGit } from '~/lib/git';

const IGNORE_PATTERNS = [
'node_modules/**',
Expand Down
173 changes: 29 additions & 144 deletions app/components/settings/connections/ConnectionsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,151 +1,36 @@
import React, { useState, useEffect } from 'react';
import { toast } from 'react-toastify';
import Cookies from 'js-cookie';
import { logStore } from '~/lib/stores/logs';

interface GitHubUserResponse {
login: string;
id: number;
[key: string]: any; // for other properties we don't explicitly need
}
import React from 'react';
import { ProviderCard } from '~/lib/git/components/ProviderCard';
import { useGitProviders } from '~/lib/git/hooks/useGitProviders';

export default function ConnectionsTab() {
const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || '');
const [githubToken, setGithubToken] = useState(Cookies.get('githubToken') || '');
const [isConnected, setIsConnected] = useState(false);
const [isVerifying, setIsVerifying] = useState(false);

useEffect(() => {
// Check if credentials exist and verify them
if (githubUsername && githubToken) {
verifyGitHubCredentials();
}
}, []);

const verifyGitHubCredentials = async () => {
setIsVerifying(true);

try {
const response = await fetch('https://api.github.com/user', {
headers: {
Authorization: `Bearer ${githubToken}`,
},
});

if (response.ok) {
const data = (await response.json()) as GitHubUserResponse;

if (data.login === githubUsername) {
setIsConnected(true);
return true;
}
}

setIsConnected(false);

return false;
} catch (error) {
console.error('Error verifying GitHub credentials:', error);
setIsConnected(false);

return false;
} finally {
setIsVerifying(false);
}
};

const handleSaveConnection = async () => {
if (!githubUsername || !githubToken) {
toast.error('Please provide both GitHub username and token');
return;
}

setIsVerifying(true);

const isValid = await verifyGitHubCredentials();

if (isValid) {
Cookies.set('githubUsername', githubUsername);
Cookies.set('githubToken', githubToken);
logStore.logSystem('GitHub connection settings updated', {
username: githubUsername,
hasToken: !!githubToken,
});
toast.success('GitHub credentials verified and saved successfully!');
Cookies.set('git:github.com', JSON.stringify({ username: githubToken, password: 'x-oauth-basic' }));
setIsConnected(true);
} else {
toast.error('Invalid GitHub credentials. Please check your username and token.');
}
};

const handleDisconnect = () => {
Cookies.remove('githubUsername');
Cookies.remove('githubToken');
Cookies.remove('git:github.com');
setGithubUsername('');
setGithubToken('');
setIsConnected(false);
logStore.logSystem('GitHub connection removed');
toast.success('GitHub connection removed successfully!');
};
const {
providers,
credentials,
expandedProviders,
handleSaveConnection,
handleDisconnect,
updateProviderCredentials,
toggleProvider,
} = useGitProviders();

return (
<div className="p-4 mb-4 border border-bolt-elements-borderColor rounded-lg bg-bolt-elements-background-depth-3">
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">GitHub Connection</h3>
<div className="flex mb-4">
<div className="flex-1 mr-2">
<label className="block text-sm text-bolt-elements-textSecondary mb-1">GitHub Username:</label>
<input
type="text"
value={githubUsername}
onChange={(e) => setGithubUsername(e.target.value)}
disabled={isVerifying}
className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50"
/>
</div>
<div className="flex-1">
<label className="block text-sm text-bolt-elements-textSecondary mb-1">Personal Access Token:</label>
<input
type="password"
value={githubToken}
onChange={(e) => setGithubToken(e.target.value)}
disabled={isVerifying}
className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50"
/>
</div>
</div>
<div className="flex mb-4 items-center">
{!isConnected ? (
<button
onClick={handleSaveConnection}
disabled={isVerifying || !githubUsername || !githubToken}
className="bg-bolt-elements-button-primary-background rounded-lg px-4 py-2 mr-2 transition-colors duration-200 hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-button-primary-text disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
>
{isVerifying ? (
<>
<div className="i-ph:spinner animate-spin mr-2" />
Verifying...
</>
) : (
'Connect'
)}
</button>
) : (
<button
onClick={handleDisconnect}
className="bg-bolt-elements-button-danger-background rounded-lg px-4 py-2 mr-2 transition-colors duration-200 hover:bg-bolt-elements-button-danger-backgroundHover text-bolt-elements-button-danger-text"
>
Disconnect
</button>
)}
{isConnected && (
<span className="text-sm text-green-600 flex items-center">
<div className="i-ph:check-circle mr-1" />
Connected to GitHub
</span>
)}
</div>
<div className="space-y-4">
<div className="i-ph:github-logo-duotone hidden" />
{/* Preloading icons otherwise they will not be displayed. Need fixing. */}
<div className="i-ph:gitlab-logo-duotone hidden" />
{/* Preloading icons otherwise they will not be displayed. Need fixing. */}
{Object.entries(providers).map(([key, plugin]) => (
<ProviderCard
key={key}
provider={plugin.provider}
credentials={credentials[key]}
isExpanded={expandedProviders[key]}
onToggle={() => toggleProvider(key)}
onUpdateCredentials={(updates) => updateProviderCredentials(key, updates)}
onSave={() => handleSaveConnection(key)}
onDisconnect={() => handleDisconnect(key)}
/>
))}
</div>
);
}
83 changes: 47 additions & 36 deletions app/components/workbench/Workbench.client.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useStore } from '@nanostores/react';
import { motion, type HTMLMotionProps, type Variants } from 'framer-motion';
import { computed } from 'nanostores';
import { memo, useCallback, useEffect, useState } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
import {
type OnChangeCallback as OnEditorChange,
Expand All @@ -17,7 +17,7 @@ import { renderLogger } from '~/utils/logger';
import { EditorPanel } from './EditorPanel';
import { Preview } from './Preview';
import useViewport from '~/lib/hooks';
import Cookies from 'js-cookie';
import { getGitCredentials, createGitPushHandler, gitProviders } from '~/lib/git';

interface WorkspaceProps {
chatStarted?: boolean;
Expand Down Expand Up @@ -58,6 +58,10 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
renderLogger.trace('Workbench');

const [isSyncing, setIsSyncing] = useState(false);
const [hasCredentials, setHasCredentials] = useState<{ github: boolean; gitlab: boolean }>({
github: false,
gitlab: false,
});

const hasPreview = useStore(computed(workbenchStore.previews, (previews) => previews.length > 0));
const showWorkbench = useStore(workbenchStore.showWorkbench);
Expand All @@ -83,6 +87,17 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
workbenchStore.setDocuments(files);
}, [files]);

useEffect(() => {
const initCredentials = async () => {
const credentials = await getGitCredentials();
setHasCredentials({
github: credentials.github || false,
gitlab: credentials.gitlab || false,
});
};
initCredentials();
}, []);

const onEditorChange = useCallback<OnEditorChange>((update) => {
workbenchStore.setCurrentDocumentContent(update.content);
}, []);
Expand Down Expand Up @@ -120,6 +135,26 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
}
}, []);

const cleanPath = (path: string): string => {
return path.replace('/home/project/', '');
};

const getWorkbenchFiles = useCallback(() => {
const docs = workbenchStore.files.get();
return Object.entries(docs).reduce(
(acc, [path, doc]) => {
if (doc && 'content' in doc) {
acc[cleanPath(path)] = (doc as { content: string }).content;
}

return acc;
},
{} as Record<string, string>,
);
}, []);

const pushFunctions = useMemo(() => createGitPushHandler(getWorkbenchFiles), [getWorkbenchFiles]);

return (
chatStarted && (
<motion.div
Expand Down Expand Up @@ -168,40 +203,15 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
<div className="i-ph:terminal" />
Toggle Terminal
</PanelHeaderButton>
<PanelHeaderButton
className="mr-1 text-sm"
onClick={() => {
const repoName = prompt(
'Please enter a name for your new GitHub repository:',
'bolt-generated-project',
);

if (!repoName) {
alert('Repository name is required. Push to GitHub cancelled.');
return;
}

const githubUsername = Cookies.get('githubUsername');
const githubToken = Cookies.get('githubToken');

if (!githubUsername || !githubToken) {
const usernameInput = prompt('Please enter your GitHub username:');
const tokenInput = prompt('Please enter your GitHub personal access token:');

if (!usernameInput || !tokenInput) {
alert('GitHub username and token are required. Push to GitHub cancelled.');
return;
}

workbenchStore.pushToGitHub(repoName, usernameInput, tokenInput);
} else {
workbenchStore.pushToGitHub(repoName, githubUsername, githubToken);
}
}}
>
<div className="i-ph:github-logo" />
Push to GitHub
</PanelHeaderButton>
{Object.entries(gitProviders).map(
([name, plugin]) =>
hasCredentials[name as keyof typeof hasCredentials] && (
<PanelHeaderButton key={name} className="mr-1 text-sm" onClick={pushFunctions[name]}>
<div className={plugin.provider.icon} />
Push to {plugin.provider.title}
</PanelHeaderButton>
),
)}
</div>
)}
<IconButton
Expand Down Expand Up @@ -245,6 +255,7 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
)
);
});

interface ViewProps extends HTMLMotionProps<'div'> {
children: JSX.Element;
}
Expand Down
Loading
Loading