storkit: merge 446_story_oauth_login_button_in_web_ui

This commit is contained in:
dave
2026-03-31 10:04:52 +00:00
parent 23562dfa61
commit 321c88e05e
7 changed files with 149 additions and 2 deletions
+4 -1
View File
@@ -6,6 +6,7 @@ import type { AgentConfigInfo } from "../api/agents";
import { agentsApi } from "../api/agents";
import type {
AnthropicModelInfo,
OAuthStatus,
PipelineState,
WizardStateData,
} from "../api/client";
@@ -164,9 +165,10 @@ const getContextWindowSize = (
interface ChatProps {
projectPath: string;
onCloseProject: () => void;
oauthStatus?: OAuthStatus | null;
}
export function Chat({ projectPath, onCloseProject }: ChatProps) {
export function Chat({ projectPath, onCloseProject, oauthStatus = null }: ChatProps) {
const { messages, setMessages, clearMessages } = useChatHistory(projectPath);
const [loading, setLoading] = useState(false);
const [model, setModel] = useState("claude-code-pty");
@@ -940,6 +942,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
enableTools={enableTools}
onToggleTools={setEnableTools}
wsConnected={wsConnected}
oauthStatus={oauthStatus}
/>
{/* Two-column content area */}
+56
View File
@@ -1,4 +1,5 @@
import * as React from "react";
import type { OAuthStatus } from "../api/client";
import { api } from "../api/client";
const { useState, useEffect } = React;
@@ -32,6 +33,7 @@ interface ChatHeaderProps {
enableTools: boolean;
onToggleTools: (enabled: boolean) => void;
wsConnected: boolean;
oauthStatus?: OAuthStatus | null;
}
const getContextEmoji = (percentage: number): string => {
@@ -55,6 +57,7 @@ export function ChatHeader({
enableTools,
onToggleTools,
wsConnected,
oauthStatus = null,
}: ChatHeaderProps) {
const hasModelOptions = availableModels.length > 0 || claudeModels.length > 0;
const [showConfirm, setShowConfirm] = useState(false);
@@ -340,6 +343,59 @@ export function ChatHeader({
</div>
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
{oauthStatus !== null &&
(!oauthStatus.authenticated || oauthStatus.expired) && (
<button
type="button"
title="Authenticate with Claude via OAuth"
onClick={() => {
window.open("/oauth/authorize", "_blank", "noopener,noreferrer");
}}
style={{
padding: "6px 12px",
borderRadius: "99px",
border: "none",
fontSize: "0.85em",
backgroundColor: "#1a3a5c",
color: "#7eb8f7",
cursor: "pointer",
outline: "none",
transition: "all 0.2s",
whiteSpace: "nowrap",
}}
onMouseOver={(e) => {
e.currentTarget.style.backgroundColor = "#234d7a";
e.currentTarget.style.color = "#a8d4ff";
}}
onMouseOut={(e) => {
e.currentTarget.style.backgroundColor = "#1a3a5c";
e.currentTarget.style.color = "#7eb8f7";
}}
onFocus={(e) => {
e.currentTarget.style.backgroundColor = "#234d7a";
e.currentTarget.style.color = "#a8d4ff";
}}
onBlur={(e) => {
e.currentTarget.style.backgroundColor = "#1a3a5c";
e.currentTarget.style.color = "#7eb8f7";
}}
>
{oauthStatus.expired ? "Re-authenticate" : "Login with Claude"}
</button>
)}
{oauthStatus?.authenticated && !oauthStatus.expired && (
<span
title="Authenticated with Claude via OAuth"
style={{
fontSize: "0.8em",
color: "#4caf50",
whiteSpace: "nowrap",
}}
>
Claude
</span>
)}
<div
style={{
fontSize: "0.75em",
@@ -1,4 +1,5 @@
import type { KeyboardEvent } from "react";
import type { OAuthStatus } from "../../api/client";
import { ProjectPathInput } from "./ProjectPathInput.tsx";
import { RecentProjectsList } from "./RecentProjectsList.tsx";
@@ -24,6 +25,7 @@ export interface SelectionScreenProps {
onCloseSuggestions: () => void;
completionError: string | null;
currentPartial: string;
oauthStatus?: OAuthStatus | null;
}
export function SelectionScreen({
@@ -43,6 +45,7 @@ export function SelectionScreen({
onCloseSuggestions,
completionError,
currentPartial,
oauthStatus = null,
}: SelectionScreenProps) {
const resolvedHomeDir = homeDir
? homeDir.endsWith("/")
@@ -57,6 +60,37 @@ export function SelectionScreen({
<h1>Storkit</h1>
<p>Paste or complete a project path to start.</p>
{oauthStatus !== null && (
<div style={{ marginBottom: "1rem" }}>
{!oauthStatus.authenticated || oauthStatus.expired ? (
<button
type="button"
onClick={() => {
window.open("/oauth/authorize", "_blank", "noopener,noreferrer");
}}
style={{
padding: "8px 16px",
borderRadius: "6px",
border: "1px solid #1a3a5c",
backgroundColor: "#1a3a5c",
color: "#7eb8f7",
cursor: "pointer",
fontSize: "0.9em",
}}
>
{oauthStatus.expired ? "Re-authenticate with Claude" : "Login with Claude"}
</button>
) : (
<span
title="Authenticated with Claude via OAuth"
style={{ color: "#4caf50", fontSize: "0.9em" }}
>
Authenticated with Claude
</span>
)}
</div>
)}
{knownProjects.length > 0 && (
<RecentProjectsList
projects={knownProjects}