mirror of
https://github.com/ollama/ollama.git
synced 2026-09-21 13:38:14 -05:00
app: make integrations list scrollable (#17977)
This commit is contained in:
@@ -9,7 +9,6 @@ import {
|
||||
default as Onboarding,
|
||||
RunOllamaScreen,
|
||||
shouldShowClaudeConnectedIntro,
|
||||
terminalRowsForWindowHeight,
|
||||
WelcomeScreen,
|
||||
} from "./Onboarding";
|
||||
import {
|
||||
@@ -50,12 +49,6 @@ describe("Onboarding", () => {
|
||||
expect(html).not.toContain("Skip");
|
||||
});
|
||||
|
||||
it("shows more terminal integrations as the window gets taller", () => {
|
||||
expect(terminalRowsForWindowHeight(400)).toBe(1);
|
||||
expect(terminalRowsForWindowHeight(660)).toBe(4);
|
||||
expect(terminalRowsForWindowHeight(960)).toBe(8);
|
||||
});
|
||||
|
||||
it("renders the apps screen without browser platform globals", () => {
|
||||
vi.stubGlobal("navigator", undefined);
|
||||
try {
|
||||
@@ -365,7 +358,7 @@ describe("Onboarding", () => {
|
||||
expect(html).not.toContain("Sign up");
|
||||
});
|
||||
|
||||
it("groups disconnected Claude with applications and terminal separately", () => {
|
||||
it("groups disconnected Claude with a scrollable terminal list", () => {
|
||||
const integrations: IntegrationStatuses = [
|
||||
{
|
||||
id: "claude-desktop",
|
||||
@@ -463,10 +456,11 @@ describe("Onboarding", () => {
|
||||
expect(html).not.toContain("ChatGPT");
|
||||
expect(html).toContain("OpenCode");
|
||||
expect(html).toContain("Terminal");
|
||||
expect(html).toContain('aria-label="Show more apps"');
|
||||
expect(html).toContain('aria-expanded="false"');
|
||||
expect(html).toContain("grid-rows-[0fr]");
|
||||
expect(html).not.toContain("Collapse");
|
||||
expect(html).toContain("overflow-y-auto");
|
||||
expect(html).not.toContain('aria-label="Show more apps"');
|
||||
expect(html).not.toContain("aria-expanded");
|
||||
expect(html).not.toContain("grid-rows-[0fr]");
|
||||
expect(html).not.toContain("inert");
|
||||
expect(html).toContain("/launch-icons/claude.svg");
|
||||
expect(html).toContain("/launch-icons/claude-code.svg");
|
||||
expect(html).not.toContain("<table");
|
||||
|
||||
@@ -31,11 +31,7 @@ import {
|
||||
ShieldCheckIcon,
|
||||
Square2StackIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { CheckIcon } from "@heroicons/react/20/solid";
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
@@ -54,10 +50,6 @@ type ClaudeConnectPhase =
|
||||
| "launching"
|
||||
| "disconnecting";
|
||||
|
||||
const MINIMUM_APP_WINDOW_HEIGHT = 660;
|
||||
const TERMINAL_ROW_HEIGHT_WITH_GAP = 80;
|
||||
const TERMINAL_LIST_RESERVED_HEIGHT = 296;
|
||||
|
||||
export function shouldShowClaudeConnectedIntro(status: ClaudeDesktopStatus) {
|
||||
return status.connected && !status.startFailed && !status.used;
|
||||
}
|
||||
@@ -82,15 +74,6 @@ function getClaudeConnectionSummary() {
|
||||
return getStatus?.() ?? Promise.resolve(null);
|
||||
}
|
||||
|
||||
export function terminalRowsForWindowHeight(height: number): number {
|
||||
return Math.max(
|
||||
1,
|
||||
Math.floor(
|
||||
(height - TERMINAL_LIST_RESERVED_HEIGHT) / TERMINAL_ROW_HEIGHT_WITH_GAP,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
interface ScreenProps {
|
||||
isSigningIn: boolean;
|
||||
signInError: string | null;
|
||||
@@ -455,15 +438,6 @@ export function ConnectAppsScreen({
|
||||
const screenMounted = useRef(true);
|
||||
const [integrationStatuses, setIntegrationStatuses] =
|
||||
useState<IntegrationStatuses | null>(initialIntegrations ?? null);
|
||||
const [showAllIntegrations, setShowAllIntegrations] = useState(false);
|
||||
const [collapsedIntegrationCount, setCollapsedIntegrationCount] = useState(
|
||||
() =>
|
||||
terminalRowsForWindowHeight(
|
||||
typeof window === "undefined"
|
||||
? MINIMUM_APP_WINDOW_HEIGHT
|
||||
: window.innerHeight,
|
||||
),
|
||||
);
|
||||
const [statusError, setStatusError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -473,19 +447,6 @@ export function ConnectAppsScreen({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const updateCollapsedIntegrationCount = () => {
|
||||
setCollapsedIntegrationCount(
|
||||
terminalRowsForWindowHeight(window.innerHeight),
|
||||
);
|
||||
};
|
||||
|
||||
window.addEventListener("resize", updateCollapsedIntegrationCount);
|
||||
return () => {
|
||||
window.removeEventListener("resize", updateCollapsedIntegrationCount);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!copiedCommand) return;
|
||||
|
||||
@@ -939,15 +900,6 @@ export function ConnectAppsScreen({
|
||||
const claudeInstalled =
|
||||
claudeStatus?.installed ?? claudeIntegration?.installed ?? false;
|
||||
const isConnectingClaude = claudePhase !== "idle";
|
||||
const initialLaunchIntegrations = launchIntegrations.slice(
|
||||
0,
|
||||
collapsedIntegrationCount,
|
||||
);
|
||||
const additionalLaunchIntegrations = launchIntegrations.slice(
|
||||
collapsedIntegrationCount,
|
||||
);
|
||||
const canToggleIntegrations =
|
||||
launchIntegrations.length > collapsedIntegrationCount;
|
||||
const claudeStatusLabel =
|
||||
claudePhase === "installing"
|
||||
? "Downloading…"
|
||||
@@ -1102,60 +1054,10 @@ export function ConnectAppsScreen({
|
||||
>
|
||||
Terminal
|
||||
</h2>
|
||||
<div className="mt-2 overflow-hidden bg-white">
|
||||
<div className="mt-2 bg-white">
|
||||
<div className="space-y-2">
|
||||
{initialLaunchIntegrations.map(launchIntegrationRow)}
|
||||
{launchIntegrations.map(launchIntegrationRow)}
|
||||
</div>
|
||||
{canToggleIntegrations && (
|
||||
<div
|
||||
aria-hidden={!showAllIntegrations}
|
||||
inert={!showAllIntegrations}
|
||||
className={`grid transition-[grid-template-rows] ease-in-out motion-reduce:duration-0 ${
|
||||
showAllIntegrations
|
||||
? "duration-[750ms]"
|
||||
: "duration-[825ms]"
|
||||
} ${
|
||||
showAllIntegrations
|
||||
? "grid-rows-[1fr]"
|
||||
: "grid-rows-[0fr]"
|
||||
}`}
|
||||
>
|
||||
<div className="min-h-0 overflow-hidden">
|
||||
<div className="space-y-2 pt-2">
|
||||
{additionalLaunchIntegrations.map(
|
||||
launchIntegrationRow,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{canToggleIntegrations && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={
|
||||
showAllIntegrations
|
||||
? "Collapse apps"
|
||||
: "Show more apps"
|
||||
}
|
||||
aria-expanded={showAllIntegrations}
|
||||
className="mx-auto mt-3 flex h-10 w-10 items-center justify-center rounded-full text-neutral-600 transition-colors hover:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-500"
|
||||
onClick={() =>
|
||||
setShowAllIntegrations((current) => !current)
|
||||
}
|
||||
>
|
||||
{showAllIntegrations ? (
|
||||
<ChevronUpIcon
|
||||
aria-hidden="true"
|
||||
className="h-5 w-5"
|
||||
/>
|
||||
) : (
|
||||
<ChevronDownIcon
|
||||
aria-hidden="true"
|
||||
className="h-5 w-5"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user