Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"add": "^2.0.6",
"axios": "^1.10.0",
"bech32": "^2.0.0",
"bootstrap": "^4.5.0",
"clsx": "^2.0.0",
Expand Down
71 changes: 66 additions & 5 deletions src/people/hiveChat/ChatSplashScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { useState } from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';

interface User {
alias: string;
Expand All @@ -9,6 +9,10 @@ interface User {
interface SplashScreenProps {
user: User;
onSendMessage: (message: string) => void;
isWorkspaceIncomplete?: boolean;
isPATExpired?: boolean;
workspaceUuid?: string;
disableInput?: boolean;
}

const SplashScreenContainer = styled.div`
Expand Down Expand Up @@ -39,15 +43,72 @@ const WelcomeHeader = styled.h1`
const WelcomeTagline = styled.p`
font-size: 1.2rem;
color: #64748b;
margin-bottom: 2.5rem;
line-height: 1.6;
max-width: 600px;
`;

const SplashScreen: React.FC<SplashScreenProps> = ({ user, onSendMessage }) => {
const [visible, setVisible] = useState(true);
const RequirementsList = styled.ul`
text-align: left;
width: fit-content;
color: #64748b;
font-size: 1.1rem;
line-height: 1.8;
`;

const RequirementItem = styled.li`
margin-bottom: 0.1px;
`;

const SettingsLink = styled(Link)`
display: inline-block;
padding: 10px 24px;
margin-left: 20px;
background-color: #4285f4;
color: white;
text-decoration: none;
border-radius: 8px;
font-weight: 500;
transition: all 0.2s;

&:hover {
color: black;
background-color: #3367d6;
transform: translateY(-1px);
text-decoration: none;
}
`;

const SplashScreen: React.FC<SplashScreenProps> = ({
user,
isWorkspaceIncomplete,
isPATExpired,
workspaceUuid
}) => {
if (isWorkspaceIncomplete || isPATExpired) {
return (
<SplashScreenContainer>
<WelcomeHeader>Workspace Incomplete</WelcomeHeader>
<WelcomeTagline>
{isPATExpired
? 'Your GitHub Personal Access Token (PAT) has expired. Please update it in Settings.'
: 'To get started, please complete your workspace setup in the settings.'}
</WelcomeTagline>

{!isPATExpired && (
<>
<WelcomeTagline>You need to provide:</WelcomeTagline>
<RequirementsList>
<RequirementItem>Code Space URL</RequirementItem>
<RequirementItem>Code Graph URL</RequirementItem>
<RequirementItem>Secret Alias</RequirementItem>
</RequirementsList>
</>
)}

if (!visible) return null;
<SettingsLink to={`/workspace/${workspaceUuid}/mission`}>Go to Settings</SettingsLink>
</SplashScreenContainer>
);
}

return (
<SplashScreenContainer>
Expand Down
121 changes: 115 additions & 6 deletions src/people/hiveChat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { ModelOption } from './modelSelector.tsx';
import { ActionArtifactRenderer } from './ActionArtifactRenderer';
import ChatStatusDisplay from './ChatStatusDisplay.tsx';
import StaktrakRecorder from './StaktrakRecorder';

import axios from 'axios';
import SplashScreen from './ChatSplashScreen';

interface RouteParams {
Expand Down Expand Up @@ -640,6 +640,10 @@ export const HiveChatView: React.FC = observer(() => {
const [sseLogs, setSseLogs] = useState<SSEMessage[]>([]);
const [, setStaktrakReady] = useState(false);
const [iframeUrl, setIframeUrl] = useState('');
const [isWorkspaceComplete, setIsWorkspaceComplete] = useState<boolean>(false);
const [isPATExpired, setIsPATExpired] = useState<boolean>(false);
const [isValidating, setIsValidating] = useState<boolean>(true);
const [workspaceData, setWorkspaceData] = useState<any>(null);
useBrowserTabTitle('Hive Chat');

if (isVerboseLoggingEnabled) {
Expand Down Expand Up @@ -791,6 +795,19 @@ export const HiveChatView: React.FC = observer(() => {
const messageText = messageToSend || message;
if (!messageText.trim() || isSending) return;

if (!isWorkspaceComplete || isPATExpired) {
ui.setToasts([
{
title: 'Cannot Send Message',
color: 'danger',
text: isPATExpired
? 'Your GitHub PAT has expired. Please update it in settings.'
: 'Your workspace setup is incomplete. Please check settings.'
}
]);
return;
}

setIsSending(true);
try {
if (messageText.includes('@Testing')) {
Expand Down Expand Up @@ -1341,6 +1358,18 @@ export const HiveChatView: React.FC = observer(() => {
(textArtifact && textArtifact.length > 0);

const handleSplashMessage = async (msg: string) => {
if (!isWorkspaceComplete || isPATExpired) {
ui.setToasts([
{
title: 'Cannot Send Message',
color: 'danger',
text: isPATExpired
? 'Your GitHub PAT has expired. Please update it in settings.'
: 'Your workspace setup is incomplete. Please check settings.'
}
]);
return;
}
setMessage(msg);
setIsSending(true);

Expand Down Expand Up @@ -1835,7 +1864,76 @@ export const HiveChatView: React.FC = observer(() => {
}
}, [isTestingMode, artifactTab]);

if (loading) {
const validateGitHubPAT = async (pat: string): Promise<boolean> => {
if (!pat) return false;

try {
const response = await axios.get('https://api.github.com/user', {
headers: {
Authorization: `token ${pat}`
}
});

return response.status === 200;
} catch (error) {
console.error('Error validating GitHub PAT:', error);
return false;
}
};

const checkWorkspaceSetup = useCallback(async () => {
if (!uuid) return;

setIsValidating(true);
try {
const workspace = await main.getUserWorkspaceByUuid(uuid);
setWorkspaceData(workspace);

if (!workspace) {
setIsWorkspaceComplete(false);
setIsValidating(false);
return;
}

const codeGraph = await main.getWorkspaceCodeGraph(uuid);

const hasCodeGraphUrl = codeGraph && codeGraph.url;
const hasSecretAlias = codeGraph && codeGraph.secret_alias;

let patValid = false;
const codeSpaceConfig = await main.getCodeSpaceConfig(uuid);

const hasCodespaceUrl = codeSpaceConfig && codeSpaceConfig.url;
const hasUsername = codeSpaceConfig && codeSpaceConfig.username;
const hasPAT = codeSpaceConfig && codeSpaceConfig.pat;

if (hasPAT) {
patValid = await validateGitHubPAT(codeSpaceConfig.pat);
setIsPATExpired(!!(!patValid && hasPAT));
}

const isComplete = !!(
hasCodeGraphUrl &&
hasSecretAlias &&
hasCodespaceUrl &&
hasUsername &&
hasPAT &&
patValid
);
setIsWorkspaceComplete(isComplete);
} catch (error) {
console.error('Error checking workspace setup:', error);
setIsWorkspaceComplete(false);
} finally {
setIsValidating(false);
}
}, [uuid, main]);

useEffect(() => {
checkWorkspaceSetup();
}, [checkWorkspaceSetup]);

if (loading || isValidating) {
return (
<Container collapsed={collapsed} ref={containerRef}>
<LoadingContainer>
Expand Down Expand Up @@ -1902,6 +2000,10 @@ export const HiveChatView: React.FC = observer(() => {
<SplashScreen
user={{ alias: ui.meInfo?.owner_alias || 'User' }}
onSendMessage={handleSplashMessage}
isWorkspaceIncomplete={!isWorkspaceComplete}
isPATExpired={isPATExpired}
workspaceUuid={uuid}
disableInput={!isWorkspaceComplete || isPATExpired}
/>
</SplashContainer>
)}
Expand Down Expand Up @@ -1975,18 +2077,25 @@ export const HiveChatView: React.FC = observer(() => {
value={message}
onChange={handleMessageChange}
onKeyPress={handleKeyPress}
placeholder="Type your message..."
disabled={isSending}
placeholder={
!isWorkspaceComplete || isPATExpired
? 'Complete workspace setup to send messages'
: 'Type your message...'
}
disabled={isSending || !isWorkspaceComplete || isPATExpired}
/>
{isPdfUploadEnabled && (
<AttachButton onClick={() => setIsUploadModalOpen(true)} disabled={isSending}>
<AttachButton
onClick={() => setIsUploadModalOpen(true)}
disabled={isSending || !isWorkspaceComplete || isPATExpired}
>
Attach
<AttachIcon icon="attach_file" />
</AttachButton>
)}
<SendButton
onClick={() => handleSendMessage()}
disabled={!message.trim() || isSending}
disabled={!message.trim() || isSending || !isWorkspaceComplete || isPATExpired}
>
Send
</SendButton>
Expand Down
18 changes: 18 additions & 0 deletions src/store/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5310,6 +5310,24 @@ export class MainStore {
return null;
}
}

async getCodeSpaceConfig(
workspace_uuid: string
): Promise<{ url: string; username: string; pat: string } | null> {
try {
const response = await this.getCodeSpace(workspace_uuid);
if (!response) return null;

return {
url: response.codeSpaceURL || '',
username: response.username || '',
pat: response.githubPat || ''
};
} catch (e) {
console.log('Error getting code space config', e);
return null;
}
}
}

export const mainStore = new MainStore();
Loading