feat: add browser environment support with access token authentication

Enable the web client to run in plain browser environments alongside Telegram Mini App support:

- Server: Add CLI_API_TOKEN authentication path as alternative to Telegram initData
- Client: Add useAuthSource hook to detect and manage Telegram vs browser auth sources
- Client: Add usePlatform hook for platform abstraction with graceful haptic feedback degradation
- Client: Add LoginPrompt component for browser access token login
- Client: Extend useTheme to fall back to system prefers-color-scheme in browser
- Client: Migrate all direct HapticFeedback calls to use usePlatform hook
This commit is contained in:
weishu
2025-12-18 12:45:49 +08:00
parent 5fe1256e11
commit cf2b96b566
14 changed files with 552 additions and 96 deletions
@@ -2,7 +2,7 @@ import { useMemo, useState } from 'react'
import type { ApiClient } from '@/api/client'
import type { SessionMetadataSummary } from '@/types/api'
import type { ChatToolCall, ToolPermission } from '@/chat/types'
import { getTelegramWebApp } from '@/hooks/useTelegram'
import { usePlatform } from '@/hooks/usePlatform'
function isObject(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === 'object'
@@ -108,6 +108,7 @@ export function PermissionFooter(props: {
disabled: boolean
onDone: () => void
}) {
const { haptic } = usePlatform()
const permission = props.tool.permission
const [loading, setLoading] = useState<'allow' | 'deny' | 'abort' | null>(null)
const [loadingAllEdits, setLoadingAllEdits] = useState(false)
@@ -121,15 +122,15 @@ export function PermissionFooter(props: {
const summary = formatPermissionSummary(permission, props.tool.name, props.tool.input, codex)
const isPending = permission.status === 'pending'
const run = async (action: () => Promise<void>, haptic: 'success' | 'error') => {
const run = async (action: () => Promise<void>, hapticType: 'success' | 'error') => {
if (props.disabled) return
setError(null)
try {
await action()
getTelegramWebApp()?.HapticFeedback?.notificationOccurred(haptic)
haptic.notification(hapticType)
props.onDone()
} catch (e) {
getTelegramWebApp()?.HapticFeedback?.notificationOccurred('error')
haptic.notification('error')
setError(e instanceof Error ? e.message : 'Request failed')
}
}