Skip to content
Open
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
10 changes: 8 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@
<body>
<script>
// Apply theme before first paint to prevent flash of white on dark mode.
// Must stay in sync with the storageKey and logic in src/lib/theme-provider.tsx.
// Must stay in sync with the storageKey and logic in src/stores/local-settings-store.ts.
;(function () {
var theme = localStorage.getItem('ui_theme')
var theme = null
try {
var raw = localStorage.getItem('thunderbolt-local-settings')
if (raw) {
theme = JSON.parse(raw).state.theme
}
} catch (e) {}
var isDark =
theme === 'dark' || (theme !== 'light' && matchMedia('(prefers-color-scheme: dark)').matches)
document.documentElement.classList.add(isDark ? 'dark' : 'light')
Expand Down
6 changes: 2 additions & 4 deletions src/ai/eval/debug-single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
* Usage: bun run src/ai/eval/debug-single.ts
*/
import { aiFetchStreamingResponse } from '@/ai/fetch'
import { getSettings } from '@/dal'
import { setupTestDatabase, teardownTestDatabase } from '@/dal/test-utils'
import { getDb } from '@/db/database'
import { getLocalSetting } from '@/stores/local-settings-store'
import { defaultModelGptOss120b } from '@/defaults/models'
import { defaultModeChat } from '@/defaults/modes'
import { isSsoMode } from '@/lib/auth-mode'
Expand All @@ -21,11 +20,11 @@
import { extractCitations, extractLinkPreviewUrls, extractWidgets } from './scoring'

const run = async () => {
console.log('=== EVAL DEBUG: Single Scenario ===\n')

Check warning on line 23 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error

console.log('[1/5] Setting up database...')

Check warning on line 25 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error
await setupTestDatabase()
console.log('[1/5] Database ready.\n')

Check warning on line 27 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error

const modelId = defaultModelGptOss120b.id
const prompt = "What's the current price of Bitcoin?"
Expand All @@ -36,17 +35,16 @@
id: uuidv7(),
})

console.log(`[2/5] Model: ${defaultModelGptOss120b.name} (${modelId})`)

Check warning on line 38 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error
console.log(`[2/5] Mode: ${defaultModeChat.name}`)

Check warning on line 39 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error
console.log(`[2/5] Prompt: "${prompt}"\n`)

Check warning on line 40 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error

const db = getDb()
const { cloudUrl } = await getSettings(db, { cloud_url: 'http://localhost:8000/v1' })
const cloudUrl = getLocalSetting('cloudUrl')
const httpClient = createAuthenticatedClient(cloudUrl, getAuthToken, {
credentials: isSsoMode() ? 'include' : undefined,
})

console.log('[3/5] Calling aiFetchStreamingResponse...')

Check warning on line 47 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error
const start = performance.now()

const response = await aiFetchStreamingResponse({
Expand All @@ -59,13 +57,13 @@
})

const callDuration = ((performance.now() - start) / 1000).toFixed(1)
console.log(`[3/5] Response status: ${response.status} (${callDuration}s)\n`)

Check warning on line 60 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error

console.log('[4/5] Parsing stream with parseStream()...')

Check warning on line 62 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error
const parsed = await parseStream(response)
const parseDuration = ((performance.now() - start) / 1000).toFixed(1)

console.log(`[4/5] Parse complete (${parseDuration}s total)`)

Check warning on line 66 in src/ai/eval/debug-single.ts

View workflow job for this annotation

GitHub Actions / typescript

Unexpected console statement. Only these console methods are allowed: warn, error
console.log(` Steps: ${parsed.stepCount}`)
console.log(
` Tool calls: ${parsed.toolCalls.length} (${parsed.toolCalls.map((t) => t.toolName).join(', ') || 'none'})`,
Expand Down
4 changes: 2 additions & 2 deletions src/ai/eval/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getSettings } from '@/dal'
import { getModel } from '@/dal/models'
import { getModelProfile } from '@/dal/model-profiles'
import { getDb } from '@/db/database'
import { getLocalSetting } from '@/stores/local-settings-store'
import { isSsoMode } from '@/lib/auth-mode'
import { getAuthToken } from '@/lib/auth-token'
import { createAuthenticatedClient } from '@/lib/http'
Expand All @@ -24,8 +25,7 @@ let _evalHttpClientPromise: Promise<import('@/lib/http').HttpClient> | null = nu
const getEvalHttpClient = () => {
if (!_evalHttpClientPromise) {
_evalHttpClientPromise = (async () => {
const db = getDb()
const { cloudUrl } = await getSettings(db, { cloud_url: 'http://localhost:8000/v1' })
const cloudUrl = getLocalSetting('cloudUrl')
return createAuthenticatedClient(cloudUrl, getAuthToken, {
credentials: isSsoMode() ? 'include' : undefined,
})
Expand Down
4 changes: 2 additions & 2 deletions src/ai/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@/ai/step-logic'
import { getModel, getModelProfile, getSettings } from '@/dal'
import { getDb } from '@/db/database'
import { getLocalSetting } from '@/stores/local-settings-store'
import { isSsoMode } from '@/lib/auth-mode'
import { getAuthToken } from '@/lib/auth-token'
import { fetch as baseFetch } from '@/lib/fetch'
Expand Down Expand Up @@ -72,8 +73,7 @@ type AiFetchStreamingResponseOptions = {
export const createModel = async (modelConfig: Model) => {
switch (modelConfig.provider) {
case 'thunderbolt': {
const db = getDb()
const { cloudUrl } = await getSettings(db, { cloud_url: 'http://localhost:8000/v1' })
const cloudUrl = getLocalSetting('cloudUrl')
const token = getAuthToken() || 'thunderbolt'
// SSO web flow authenticates via session cookies — the SSO callback is a
// browser redirect, not an XHR, so `set-auth-token` never reaches the
Expand Down
2 changes: 1 addition & 1 deletion src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export const App = () => {
}

return (
<ThemeProvider defaultTheme="system">
<ThemeProvider>
{renderAppContent()}
<RevokedDeviceModal open={revokedDeviceOpen} />
</ThemeProvider>
Expand Down
8 changes: 4 additions & 4 deletions src/components/chat/citation-badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'
import { useIsMobile } from '@/hooks/use-mobile'
import { useSettings } from '@/hooks/use-settings'
import { useLocalSettingsStore } from '@/stores/local-settings-store'
import type { CitationSource } from '@/types/citation'
import { memo, useState } from 'react'
import { useCitationPopover } from './citation-popover'
Expand Down Expand Up @@ -91,7 +91,7 @@ ManagedBadge.displayName = 'ManagedBadge'
const StandaloneBadge = memo(({ sources }: { sources: CitationSource[] }) => {
const [isOpen, setIsOpen] = useState(false)
const { isMobile } = useIsMobile()
const { cloudUrl } = useSettings({ cloud_url: 'http://localhost:8000/v1' })
const cloudUrl = useLocalSettingsStore((s) => s.cloudUrl)
const { displayName, additionalCount, ariaLabel } = getBadgeLabel(sources)

const badge = (
Expand All @@ -118,7 +118,7 @@ const StandaloneBadge = memo(({ sources }: { sources: CitationSource[] }) => {
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger asChild>{badge}</PopoverTrigger>
<PopoverContent align="start" side="bottom" className="w-[420px] overflow-hidden rounded-2xl p-0">
<SourceList sources={sources} proxyBase={cloudUrl.value} />
<SourceList sources={sources} proxyBase={cloudUrl} />
</PopoverContent>
</Popover>
)
Expand All @@ -137,7 +137,7 @@ const StandaloneBadge = memo(({ sources }: { sources: CitationSource[] }) => {
<SheetHeader className="sr-only">
<SheetTitle>{sources.length === 1 ? 'Source' : 'Sources'}</SheetTitle>
</SheetHeader>
<SourceList sources={sources} proxyBase={cloudUrl.value} />
<SourceList sources={sources} proxyBase={cloudUrl} />
</SheetContent>
</Sheet>
</>
Expand Down
8 changes: 4 additions & 4 deletions src/components/chat/citation-popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { Popover, PopoverAnchor, PopoverContent } from '@/components/ui/popover'
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'
import { useIsMobile } from '@/hooks/use-mobile'
import { useSettings } from '@/hooks/use-settings'
import { useLocalSettingsStore } from '@/stores/local-settings-store'
import type { CitationSource } from '@/types/citation'
import {
createContext,
Expand Down Expand Up @@ -64,7 +64,7 @@ export const CitationPopoverProvider = ({ children }: { children: ReactNode }) =

const CitationOverlay = memo(({ popover, close }: { popover: PopoverData | null; close: () => void }) => {
const { isMobile } = useIsMobile()
const { cloudUrl } = useSettings({ cloud_url: 'http://localhost:8000/v1' })
const cloudUrl = useLocalSettingsStore((s) => s.cloudUrl)
const anchorRef = useRef<HTMLSpanElement>(null)

useEffect(() => {
Expand Down Expand Up @@ -105,7 +105,7 @@ const CitationOverlay = memo(({ popover, close }: { popover: PopoverData | null;
<SheetHeader className="sr-only">
<SheetTitle>{sources.length === 1 ? 'Source' : 'Sources'}</SheetTitle>
</SheetHeader>
<SourceList sources={sources} proxyBase={cloudUrl.value} />
<SourceList sources={sources} proxyBase={cloudUrl} />
</SheetContent>
</Sheet>
)
Expand All @@ -124,7 +124,7 @@ const CitationOverlay = memo(({ popover, close }: { popover: PopoverData | null;
/>
</PopoverAnchor>
<PopoverContent align="start" side="bottom" className="w-[420px] overflow-hidden rounded-2xl p-0">
<SourceList sources={sources} proxyBase={cloudUrl.value} />
<SourceList sources={sources} proxyBase={cloudUrl} />
</PopoverContent>
</Popover>
)
Expand Down
8 changes: 4 additions & 4 deletions src/components/chat/tool-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { useSettings } from '@/hooks/use-settings'
import { useLocalSettingsStore } from '@/stores/local-settings-store'
import { getProxiedFaviconUrl } from '@/lib/url-utils'
import { cn } from '@/lib/utils'
import { motion } from 'framer-motion'
Expand Down Expand Up @@ -44,13 +44,13 @@ export const extractFaviconUrl = (toolName: string, output: unknown): string | n
*/
const useToolFavicon = (toolName: string, toolOutput: unknown, isLoading: boolean, isError: boolean) => {
const [failedFavicons, setFailedFavicons] = useState<Set<string>>(new Set())
const { cloudUrl } = useSettings({ cloud_url: 'http://localhost:8000/v1' })
const cloudUrl = useLocalSettingsStore((s) => s.cloudUrl)

const handleFaviconError = (url: string) => {
setFailedFavicons((prev) => new Set(prev).add(url))
}

if (!toolOutput || isLoading || isError || !cloudUrl.value) {
if (!toolOutput || isLoading || isError) {
Comment thread
raivieiraadriano92 marked this conversation as resolved.
return { favicon: null, originalFaviconUrl: null, handleFaviconError }
}

Expand All @@ -60,7 +60,7 @@ const useToolFavicon = (toolName: string, toolOutput: unknown, isLoading: boolea
return { favicon: null, originalFaviconUrl, handleFaviconError }
}

const favicon = getProxiedFaviconUrl(originalFaviconUrl, cloudUrl.value)
const favicon = getProxiedFaviconUrl(originalFaviconUrl, cloudUrl)
return { favicon, originalFaviconUrl, handleFaviconError }
} catch {
return { favicon: null, originalFaviconUrl: null, handleFaviconError }
Expand Down
6 changes: 4 additions & 2 deletions src/components/sign-in/sign-in-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { useAuth, useHttpClient } from '@/contexts'
import { useSettings } from '@/hooks/use-settings'
import { useLocalSettingsStore } from '@/stores/local-settings-store'
import { isLocalhostUrl } from '@/lib/utils'
import { type ReactNode, type RefObject, useCallback, useEffect } from 'react'
import { SignInEmailStep } from './sign-in-email-step'
Expand Down Expand Up @@ -78,8 +79,9 @@ export const SignInForm = ({
}: SignInFormProps) => {
const authClient = useAuth()
const httpClient = useHttpClient()
const { cloudUrl, preferredName } = useSettings({ cloud_url: 'http://localhost:8000/v1', preferred_name: '' })
const isLocalhost = isLocalhostUrl(cloudUrl.value)
const cloudUrl = useLocalSettingsStore((s) => s.cloudUrl)
const { preferredName } = useSettings({ preferred_name: '' })
const isLocalhost = isLocalhostUrl(cloudUrl)
const displayName = preferredName.value as string

const { state, isValidEmail, actions } = useSignInFormState({
Expand Down
12 changes: 4 additions & 8 deletions src/components/sso-redirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { setAuthToken } from '@/lib/auth-token'
import { isSafeUrl } from '@/lib/url-utils'
import { isTauri } from '@/lib/platform'
import { startSsoFlowLoopback } from '@/lib/sso-loopback'
import { useSettings } from '@/hooks/use-settings'
import { useLocalSettingsStore } from '@/stores/local-settings-store'
import Loading from '@/loading'

/**
Expand All @@ -19,18 +19,14 @@ import Loading from '@/loading'
* of navigating the webview (WKWebView drops cookies during cross-origin redirects).
*/
const SsoRedirect = () => {
const { cloudUrl } = useSettings({ cloud_url: String })
const cloudUrl = useLocalSettingsStore((s) => s.cloudUrl)
const [error, setError] = useState(false)
const [retryKey, setRetryKey] = useState(0)

useEffect(() => {
if (cloudUrl.isLoading || !cloudUrl.value) {
return
}

setError(false)
const abortController = new AbortController()
const baseUrl = cloudUrl.value.replace(/\/v1$/, '')
const baseUrl = cloudUrl.replace(/\/v1$/, '')

const redirectToSso = async () => {
try {
Expand Down Expand Up @@ -74,7 +70,7 @@ const SsoRedirect = () => {
redirectToSso()

return () => abortController.abort()
}, [cloudUrl.isLoading, cloudUrl.value, retryKey])
}, [cloudUrl, retryKey])

if (error) {
return (
Expand Down
23 changes: 5 additions & 18 deletions src/db/powersync/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { fetchConfig } from '@/api/config'
import { getSettings } from '@/dal'
import { defaultSettingCloudUrl } from '@/defaults/settings'
import { getLocalSetting, useLocalSettingsStore } from '@/stores/local-settings-store'
import { withTimeout } from '@/lib/timeout'
import type { AbstractPowerSyncDatabase } from '@powersync/common'
import { SyncStreamConnectionMethod, WASQLiteOpenFactory, WASQLiteVFS } from '@powersync/web'
Expand Down Expand Up @@ -45,9 +44,6 @@ export const getPowerSyncDatabaseConfig = (
return 'safari-tauri'
}

/** LocalStorage key for sync enabled flag */
const syncEnabledKey = 'powersync_sync_enabled'

/** Max time to wait for initial sync before continuing (e.g. when network is down) */
const initialSyncTimeoutMs = 10_000

Expand Down Expand Up @@ -90,23 +86,14 @@ export const reconnectSync = async (): Promise<void> => {
/**
* Check if sync is enabled by user preference
*/
export const isSyncEnabled = (): boolean => {
if (typeof localStorage === 'undefined') {
return false
}
return localStorage.getItem(syncEnabledKey) === 'true'
}
export const isSyncEnabled = (): boolean => getLocalSetting('syncEnabled')

/**
* Set sync enabled preference, connect/disconnect from PowerSync, and dispatch change event
*/
export const setSyncEnabled = async (enabled: boolean): Promise<void> => {
if (typeof localStorage === 'undefined') {
return
}

// Update localStorage and dispatch event
localStorage.setItem(syncEnabledKey, String(enabled))
// Update store and dispatch event
useLocalSettingsStore.getState().setLocalSetting('syncEnabled', enabled)
Comment thread
raivieiraadriano92 marked this conversation as resolved.
window.dispatchEvent(new CustomEvent(syncEnabledChangeEvent, { detail: enabled }))

// Connect or disconnect from PowerSync Cloud
Expand Down Expand Up @@ -245,7 +232,7 @@ export class PowerSyncDatabaseImpl implements DatabaseInterface {
}

try {
const { cloudUrl } = await getSettings(this._db, { cloud_url: defaultSettingCloudUrl.value })
const cloudUrl = getLocalSetting('cloudUrl')

// Re-fetch config before connecting so the upload encoder has the correct E2EE flag.
// If /config failed at app init (e.g. offline), this retries before sync starts.
Expand Down
45 changes: 0 additions & 45 deletions src/defaults/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,6 @@ export const defaultSettingExperimentalFeatureTasks: Setting = {
userId: null,
}

export const defaultSettingNativeFetchEnabled: Setting = {
key: 'is_native_fetch_enabled',
value: 'false',
updatedAt: null,
defaultHash: null,
userId: null,
}

export const defaultSettingDebugPosthog: Setting = {
key: 'debug_posthog',
value: 'false',
updatedAt: null,
defaultHash: null,
userId: null,
}

export const defaultSettingPreferredName: Setting = {
key: 'preferred_name',
value: null,
Expand Down Expand Up @@ -98,22 +82,6 @@ export const defaultSettingLocationLng: Setting = {
userId: null,
}

export const defaultSettingCloudUrl: Setting = {
key: 'cloud_url',
value: import.meta.env.VITE_THUNDERBOLT_CLOUD_URL || 'http://localhost:8000/v1',
updatedAt: null,
defaultHash: null,
userId: null,
}

export const defaultSettingTheme: Setting = {
key: 'ui-theme',
value: 'system',
updatedAt: null,
defaultHash: null,
userId: null,
}

export const defaultSettingDistanceUnit: Setting = {
key: 'distance_unit',
value: null,
Expand Down Expand Up @@ -186,29 +154,17 @@ export const defaultSettingIntegrationsDoNotAskAgain: Setting = {
userId: null,
}

export const defaultSettingHapticsEnabled: Setting = {
key: 'haptics_enabled',
value: 'true',
updatedAt: null,
defaultHash: null,
userId: null,
}

/**
* Array of all default settings for iteration
*/
export const defaultSettings: ReadonlyArray<Setting> = [
defaultSettingDataCollection,
defaultSettingTriggersEnabled,
defaultSettingExperimentalFeatureTasks,
defaultSettingNativeFetchEnabled,
defaultSettingDebugPosthog,
defaultSettingPreferredName,
defaultSettingLocationName,
defaultSettingLocationLat,
defaultSettingLocationLng,
defaultSettingCloudUrl,
defaultSettingTheme,
defaultSettingDistanceUnit,
defaultSettingTemperatureUnit,
defaultSettingDateFormat,
Expand All @@ -218,5 +174,4 @@ export const defaultSettings: ReadonlyArray<Setting> = [
defaultSettingUserHasCompletedOnboarding,
defaultSettingContentViewWidth,
defaultSettingIntegrationsDoNotAskAgain,
defaultSettingHapticsEnabled,
] as const
Loading
Loading