feat: design system overhaul — sidebar, AI chats, settings, brainstorm, color cleanup
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 12s

- Sidebar: dynamic brand-accent colors, brainstorm section restyled
- AI chat general: popup panel with expand/collapse, hides when contextual AI open
- AI chat contextual: tabs reordered (Actions first), X close button, height fix
- Settings: all tabs restyled, 6 new color presets (sage, terracotta, iron, etc.)
- Global color cleanup: emerald/orange hardcoded → brand-accent dynamic
- Brainstorm page: orange → brand-accent throughout
- PageEntry animation component added to key pages
- Floating AI button: bg-brand-accent instead of hardcoded black
- i18n: all 15 locales updated with new AI/billing keys
- Billing: freemium quota tracking, BYOK, stripe subscription scaffolding
- Admin: integrated into new design
- AGENTS.md + CLAUDE.md project rules added
This commit is contained in:
Antigravity
2026-05-16 12:59:30 +00:00
parent 1fcea6ed7d
commit bd495be965
2284 changed files with 395285 additions and 2327 deletions

View File

@@ -4,8 +4,9 @@ import { CustomOpenAIProvider } from './providers/custom-openai';
import { AnthropicProvider } from './providers/anthropic';
import { GoogleProvider } from './providers/google';
import { AIProvider } from './types';
import { resolveAiRoute } from './router';
type ProviderType =
export type ProviderType =
| 'ollama'
| 'openai'
| 'google'
@@ -195,7 +196,8 @@ function createGLMProvider(config: Record<string, string>, modelName: string, em
return new CustomOpenAIProvider(apiKey, defaults.baseUrl, modelName || defaults.model, embeddingModelName || defaults.embeddingModel);
}
function getProviderInstance(providerType: ProviderType, config: Record<string, string>, modelName: string, embeddingModelName: string, ollamaBaseUrl?: string): AIProvider {
/** Exported for tests and Story 3.3+ orchestration; prefer `get*Provider` in production call sites. */
export function getProviderInstance(providerType: ProviderType, config: Record<string, string>, modelName: string, embeddingModelName: string, ollamaBaseUrl?: string): AIProvider {
switch (providerType) {
case 'ollama':
return createOllamaProvider(config, modelName, embeddingModelName, ollamaBaseUrl);
@@ -250,61 +252,15 @@ function getProviderConfigKeys(providerType: string): { apiKeyConfigKey: string;
}
export function getTagsProvider(config?: Record<string, string>): AIProvider {
const providerType = (
config?.AI_PROVIDER_TAGS ||
config?.AI_PROVIDER_EMBEDDING ||
config?.AI_PROVIDER ||
process.env.AI_PROVIDER_TAGS ||
process.env.AI_PROVIDER_EMBEDDING ||
process.env.AI_PROVIDER
);
if (!providerType) {
console.error('[getTagsProvider] FATAL: No provider configured. Config received:', config);
throw new Error(
'AI_PROVIDER_TAGS is not configured. Please set it in the admin settings or environment variables. ' +
'Options: ollama, openai, anthropic, anthropic_custom, deepseek, openrouter, mistral, zai, lmstudio, custom'
);
}
const provider = providerType.toLowerCase() as ProviderType;
const modelName = config?.AI_MODEL_TAGS || process.env.AI_MODEL_TAGS || 'granite4:latest';
const embeddingModelName = config?.AI_MODEL_EMBEDDING || process.env.AI_MODEL_EMBEDDING || 'embeddinggemma:latest';
const ollamaBaseUrl = config?.OLLAMA_BASE_URL_TAGS || config?.OLLAMA_BASE_URL;
return getProviderInstance(provider, config || {}, modelName, embeddingModelName, ollamaBaseUrl);
const cfg = config || {};
const route = resolveAiRoute('tags', cfg);
return getProviderInstance(route.providerType as ProviderType, cfg, route.modelName, route.embeddingModelName, route.ollamaBaseUrl);
}
export function getEmbeddingsProvider(config?: Record<string, string>): AIProvider {
const providerType = (
config?.AI_PROVIDER_EMBEDDING ||
config?.AI_PROVIDER_TAGS ||
config?.AI_PROVIDER ||
process.env.AI_PROVIDER_EMBEDDING ||
process.env.AI_PROVIDER_TAGS ||
process.env.AI_PROVIDER
);
if (!providerType) {
console.error('[getEmbeddingsProvider] FATAL: No provider configured. Config received:', config);
throw new Error(
'AI_PROVIDER_EMBEDDING is not configured. Please set it in the admin settings or environment variables. ' +
'Options: ollama, openai, deepseek, openrouter, mistral, zai, lmstudio, custom'
);
}
const provider = providerType.toLowerCase() as ProviderType;
if (provider === 'anthropic' || provider === 'anthropic_custom') {
throw new Error(
'AI_PROVIDER_EMBEDDING cannot use "anthropic" or "anthropic_custom": these gateways use the Anthropic Messages API only (no embeddings in Memento). Use ollama, openai, or "custom" with MiniMax OpenAI URL https://api.minimax.io/v1 for embeddings.'
);
}
const modelName = config?.AI_MODEL_TAGS || process.env.AI_MODEL_TAGS || 'granite4:latest';
const embeddingModelName = config?.AI_MODEL_EMBEDDING || process.env.AI_MODEL_EMBEDDING || 'embeddinggemma:latest';
const ollamaBaseUrl = config?.OLLAMA_BASE_URL_EMBEDDING || config?.OLLAMA_BASE_URL;
return getProviderInstance(provider, config || {}, modelName, embeddingModelName, ollamaBaseUrl);
const cfg = config || {};
const route = resolveAiRoute('embedding', cfg);
return getProviderInstance(route.providerType as ProviderType, cfg, route.modelName, route.embeddingModelName, route.ollamaBaseUrl);
}
export function getAIProvider(config?: Record<string, string>): AIProvider {
@@ -312,35 +268,9 @@ export function getAIProvider(config?: Record<string, string>): AIProvider {
}
export function getChatProvider(config?: Record<string, string>): AIProvider {
const providerType = (
config?.AI_PROVIDER_CHAT ||
config?.AI_PROVIDER_TAGS ||
config?.AI_PROVIDER_EMBEDDING ||
config?.AI_PROVIDER ||
process.env.AI_PROVIDER_CHAT ||
process.env.AI_PROVIDER_TAGS ||
process.env.AI_PROVIDER_EMBEDDING ||
process.env.AI_PROVIDER
);
if (!providerType) {
console.error('[getChatProvider] FATAL: No provider configured. Config received:', config);
throw new Error(
'AI_PROVIDER_CHAT is not configured. Please set it in the admin settings or environment variables. ' +
'Options: ollama, openai, anthropic, anthropic_custom, deepseek, openrouter, mistral, zai, lmstudio, custom'
);
}
const provider = providerType.toLowerCase() as ProviderType;
const modelName = (
config?.AI_MODEL_CHAT ||
process.env.AI_MODEL_CHAT ||
'granite4:latest'
);
const embeddingModelName = config?.AI_MODEL_EMBEDDING || process.env.AI_MODEL_EMBEDDING || 'embeddinggemma:latest';
const ollamaBaseUrl = config?.OLLAMA_BASE_URL_CHAT || config?.OLLAMA_BASE_URL_TAGS || config?.OLLAMA_BASE_URL_EMBEDDING || config?.OLLAMA_BASE_URL;
return getProviderInstance(provider, config || {}, modelName, embeddingModelName, ollamaBaseUrl);
const cfg = config || {};
const route = resolveAiRoute('chat', cfg);
return getProviderInstance(route.providerType as ProviderType, cfg, route.modelName, route.embeddingModelName, route.ollamaBaseUrl);
}
// Export for use by admin settings form and deploy scripts