diff --git a/apps/web/app/fonts.css b/apps/web/app/fonts.css new file mode 100644 index 0000000000..58df8f3b74 --- /dev/null +++ b/apps/web/app/fonts.css @@ -0,0 +1,71 @@ +@font-face { + font-family: 'Figtree'; + font-style: normal; + font-weight: 300 900; + font-display: swap; + src: url('/fonts/figtree/figtree-300-normal.woff2') format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: 'Figtree'; + font-style: normal; + font-weight: 300 900; + font-display: swap; + src: url('/fonts/figtree/figtree-300-normal.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Geist'; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('/fonts/geist/geist-100-normal.woff2') format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: 'Geist'; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('/fonts/geist/geist-100-normal.woff2') format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: 'Geist'; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('/fonts/geist/geist-100-normal.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Geist Mono'; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('/fonts/geist-mono/geist-mono-100-normal.woff2') format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: 'Geist Mono'; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('/fonts/geist-mono/geist-mono-100-normal.woff2') format('woff2'); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: 'Geist Mono'; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url('/fonts/geist-mono/geist-mono-100-normal.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 80e36586be..730bba6d9e 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -1,6 +1,7 @@ @import "tailwindcss"; @import "tw-animate-css"; @import "shadcn/tailwind.css"; +@import "./fonts.css"; @custom-variant dark (&:is(.dark *)); @@ -48,6 +49,11 @@ } :root { + --font-geist-sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + --font-sans: "Figtree", "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", + sans-serif; + --font-geist-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; --background: oklch(1 0 0); --foreground: oklch(0.145 0 0); --card: oklch(1 0 0); diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index b187b52d30..bcc23374e1 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,20 +1,7 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono, Figtree } from "next/font/google"; import "./globals.css"; import { ThemeProvider } from "@/components/theme-provider"; -const figtree = Figtree({subsets:['latin'],variable:'--font-sans'}); - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - export const metadata: Metadata = { title: "Kandev - AI Kanban", description: "AI-powered kanban board for developers", @@ -26,10 +13,8 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - + + {children} diff --git a/apps/web/app/settings/environment/[id]/page.tsx b/apps/web/app/settings/environment/[id]/page.tsx index cea4613a88..6f734581eb 100644 --- a/apps/web/app/settings/environment/[id]/page.tsx +++ b/apps/web/app/settings/environment/[id]/page.tsx @@ -19,7 +19,7 @@ import { DialogTitle, } from '@/components/ui/dialog'; import { SETTINGS_DATA } from '@/lib/settings/dummy-data'; -import type { Environment, EnvironmentType, BaseDocker } from '@/lib/settings/types'; +import type { Environment, EnvironmentType, BaseDocker, AgentType } from '@/lib/settings/types'; export default function EnvironmentEditPage({ params }: { params: Promise<{ id: string }> }) { const { id } = use(params); @@ -91,7 +91,7 @@ export default function EnvironmentEditPage({ params }: { params: Promise<{ id: }); }; - const handleToggleAgent = (agentId: string) => { + const handleToggleAgent = (agentId: AgentType) => { const installed = environment.installedAgents || []; if (installed.includes(agentId)) { setEnvironment({ @@ -287,8 +287,8 @@ export default function EnvironmentEditPage({ params }: { params: Promise<{ id: > handleToggleAgent(agent.id)} + checked={(environment.installedAgents || []).includes(agent.agent)} + onChange={() => handleToggleAgent(agent.agent)} className="h-4 w-4" />
diff --git a/apps/web/app/settings/integrations/page.tsx b/apps/web/app/settings/integrations/page.tsx index 2620cd253a..a5cb3e3f40 100644 --- a/apps/web/app/settings/integrations/page.tsx +++ b/apps/web/app/settings/integrations/page.tsx @@ -116,9 +116,12 @@ export default function IntegrationsPage() {
- {React.cloneElement(integration.icon as React.ReactElement, { - className: 'h-5 w-5', - })} + {React.isValidElement(integration.icon) + ? React.cloneElement( + integration.icon as React.ReactElement<{ className?: string }>, + { className: 'h-5 w-5' } + ) + : integration.icon}
diff --git a/apps/web/app/task/[id]/page.tsx b/apps/web/app/task/[id]/page.tsx index 1a3be7773f..5c3bcea5cd 100644 --- a/apps/web/app/task/[id]/page.tsx +++ b/apps/web/app/task/[id]/page.tsx @@ -1,6 +1,7 @@ 'use client'; -import { FormEvent, useMemo, useState } from 'react'; +import type { CSSProperties } from 'react'; +import { FormEvent, useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { DiffModeEnum, DiffView } from '@git-diff-view/react'; import '@git-diff-view/react/styles/diff-view.css'; @@ -324,15 +325,13 @@ function buildDiffData(filePath: string) { } export default function TaskPage() { - const defaultHorizontalLayout = getLocalStorage<[number, number]>( - 'task-layout-horizontal', - [75, 25] - ); - const defaultRightLayout = getLocalStorage<[number, number]>('task-layout-right', [55, 45]); - const defaultDiffMode = getLocalStorage<'unified' | 'split'>( - 'task-diff-view-mode', - 'unified' - ); + const defaultHorizontalLayout: [number, number] = [75, 25]; + const defaultRightLayout: [number, number] = [55, 45]; + const defaultDiffMode: 'unified' | 'split' = 'unified'; + const [horizontalLayout, setHorizontalLayout] = useState(defaultHorizontalLayout); + const [rightLayout, setRightLayout] = useState(defaultRightLayout); + const [horizontalSeed, setHorizontalSeed] = useState(0); + const [rightSeed, setRightSeed] = useState(0); const [selectedAgent, setSelectedAgent] = useState(AGENTS[0].id); const [chats, setChats] = useState(INITIAL_CHATS); const [leftTab, setLeftTab] = useState('chat'); @@ -362,6 +361,29 @@ export default function TaskPage() { ? DIFF_SAMPLES[selectedDiffPath]?.newContent ?? '' : ''; + useEffect(() => { + const storedHorizontal = getLocalStorage('task-layout-horizontal', defaultHorizontalLayout); + const storedRight = getLocalStorage('task-layout-right', defaultRightLayout); + const storedDiffMode = getLocalStorage('task-diff-view-mode', defaultDiffMode); + + if ( + storedHorizontal[0] !== defaultHorizontalLayout[0] || + storedHorizontal[1] !== defaultHorizontalLayout[1] + ) { + setHorizontalLayout(storedHorizontal); + setHorizontalSeed((value) => value + 1); + } + + if (storedRight[0] !== defaultRightLayout[0] || storedRight[1] !== defaultRightLayout[1]) { + setRightLayout(storedRight); + setRightSeed((value) => value + 1); + } + + if (storedDiffMode !== defaultDiffMode) { + setDiffViewMode(storedDiffMode); + } + }, []); + const handleSubmit = (event: FormEvent) => { event.preventDefault(); const trimmed = messageInput.trim(); @@ -480,7 +502,10 @@ export default function TaskPage() {
- + @@ -675,11 +700,15 @@ export default function TaskPage() {
setLocalStorage('task-layout-horizontal', sizes)} + onLayout={(sizes) => { + setHorizontalLayout(sizes as [number, number]); + setLocalStorage('task-layout-horizontal', sizes); + }} > - +
- + {isBottomCollapsed ? (
{topFilesPanel}
@@ -997,15 +1026,19 @@ export default function TaskPage() {
) : ( setLocalStorage('task-layout-right', sizes)} + onLayout={(sizes) => { + setRightLayout(sizes as [number, number]); + setLocalStorage('task-layout-right', sizes); + }} > - + {topFilesPanel} - +