Skip to content

Commit 27d4dd4

Browse files
authored
Merge pull request #3 from EfeDurmaz16/feat/clean-minimal-ui-redesign
Feat/clean minimal UI redesign
2 parents 3995fb3 + ca55465 commit 27d4dd4

138 files changed

Lines changed: 28782 additions & 679 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
continue-on-error: true
5454

5555
- name: Type check Web
56-
run: cd apps/web && bunx tsc --noEmit
56+
run: cd apps/web && bun run typecheck
5757
continue-on-error: false
5858

5959
- name: Biome check
@@ -160,12 +160,12 @@ jobs:
160160
BETTER_AUTH_SECRET: test-better-auth-secret-key
161161

162162
- name: Run Web tests
163-
run: cd apps/web && bun run test 2>/dev/null || true
163+
run: cd apps/web && bun run test
164164
env:
165165
NEXT_PUBLIC_API_URL: http://localhost:8080
166166

167167
- name: Run AI SDK Router tests
168-
run: cd apps/web && bun run test:router 2>/dev/null || true
168+
run: cd apps/web && bun run test:router
169169
env:
170170
OPENAI_API_KEY: test-key
171171
ANTHROPIC_API_KEY: test-key
@@ -203,7 +203,7 @@ jobs:
203203
run: cd packages/db && bunx prisma generate
204204

205205
- name: Build API
206-
run: cd services/api && bun run build 2>/dev/null || echo "No build script"
206+
run: cd services/api && bun run build
207207

208208
- name: Build Web
209209
run: cd apps/web && bun run build

apps/web/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"build": "next build",
99
"start": "next start",
1010
"lint": "eslint",
11-
"typecheck": "tsc --noEmit",
11+
"typecheck": "tsc --noEmit --incremental false",
1212
"test": "bunx vitest run",
1313
"test:watch": "bunx vitest",
1414
"test:ui": "bunx vitest --ui",
@@ -27,6 +27,7 @@
2727
"@ai-sdk/react": "^3.0.41",
2828
"@aspendos/db": "workspace:*",
2929
"@auth/core": "^0.34.3",
30+
"@anthropic-ai/sdk": "^0.57.0",
3031
"@better-auth/passkey": "^1.4.16",
3132
"@mlc-ai/web-llm": "^0.2.80",
3233
"@phosphor-icons/react": "^2.1.10",
@@ -48,6 +49,7 @@
4849
"@radix-ui/react-separator": "^1.1.8",
4950
"@radix-ui/react-slot": "^1.2.4",
5051
"@radix-ui/react-switch": "^1.2.6",
52+
"@radix-ui/react-tabs": "^1.1.13",
5153
"@radix-ui/react-tooltip": "^1.2.8",
5254
"@sentry/nextjs": "^10.35.0",
5355
"@serwist/next": "^9.5.0",
@@ -61,6 +63,7 @@
6163
"dexie-react-hooks": "^4.2.0",
6264
"embla-carousel-react": "^8.6.0",
6365
"katex": "^0.16.28",
66+
"groq-sdk": "^0.31.0",
6467
"lucide-react": "^0.562.0",
6568
"motion": "^12.27.2",
6669
"nanoid": "^5.1.6",

apps/web/src/app/pricing/page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const PRICING_TIERS = [
4848
chats: 100,
4949
voiceMinutes: 0,
5050
councilSessions: 0,
51+
models: 2,
5152
},
5253
cta: 'Get Started',
5354
slug: 'free',
@@ -68,6 +69,7 @@ const PRICING_TIERS = [
6869
chats: 300,
6970
voiceMinutes: 10,
7071
councilSessions: 10,
72+
models: 3,
7173
},
7274
cta: 'Get Started',
7375
slug: 'starter',
@@ -90,6 +92,7 @@ const PRICING_TIERS = [
9092
chats: 1500,
9193
voiceMinutes: 60,
9294
councilSessions: 50,
95+
models: 6,
9396
},
9497
cta: 'Upgrade to Pro',
9598
slug: 'pro',
@@ -112,6 +115,7 @@ const PRICING_TIERS = [
112115
chats: 5000,
113116
voiceMinutes: 180,
114117
councilSessions: 200,
118+
models: 8,
115119
},
116120
cta: 'Go Ultra',
117121
slug: 'ultra',

apps/web/src/app/yula/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { WelcomeGuideTrigger } from '@/components/onboarding';
99
// Components
1010
import { PACTimeline } from '@/components/pac-timeline';
1111
import { EngineIndicator, HybridEngineToggle } from '@/components/settings';
12+
import { useYulaStore } from '@/stores/yula-store';
1213
import { cn } from '@/lib/utils';
1314

1415
type ActiveView = 'chat' | 'council' | 'memory';

apps/web/src/components/council/use-council.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export function useCouncil() {
105105
const verdict: CouncilVerdict = {
106106
recommendation:
107107
data.synthesis || generateLocalConsensus(question, thoughts),
108-
confidence: undefined,
108+
confidence: 0.85,
109109
reasoning:
110110
'After careful deliberation, the Council has reached a balanced consensus that considers logical analysis, creative possibilities, and prudent risk assessment.',
111111
contributions: thoughts,
@@ -114,7 +114,7 @@ export function useCouncil() {
114114
} else {
115115
const verdict: CouncilVerdict = {
116116
recommendation: generateLocalConsensus(question, thoughts),
117-
confidence: undefined,
117+
confidence: 0.8,
118118
reasoning:
119119
'After careful deliberation, the Council has reached a balanced consensus.',
120120
contributions: thoughts,
@@ -124,7 +124,7 @@ export function useCouncil() {
124124
} catch {
125125
const verdict: CouncilVerdict = {
126126
recommendation: generateLocalConsensus(question, thoughts),
127-
confidence: undefined,
127+
confidence: 0.8,
128128
reasoning:
129129
'After careful deliberation, the Council has reached a balanced consensus.',
130130
contributions: thoughts,
@@ -187,7 +187,8 @@ export function useCouncil() {
187187
const councilThought: CouncilThought = {
188188
persona: frontendPersona,
189189
thought: thoughts[frontendPersona] || data.content,
190-
confidence: undefined,
190+
confidence:
191+
typeof data.confidence === 'number' ? data.confidence : 0.75,
191192
timestamp: new Date(),
192193
};
193194
addCouncilThought(councilThought);

apps/web/src/components/gamification/level-badge.tsx

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -79,40 +79,58 @@ export function LevelBadge({
7979
xl: 'text-3xl',
8080
};
8181

82-
const Badge = animated ? motion.div : 'div';
83-
const animationProps = animated
84-
? {
85-
initial: { scale: 0, rotate: -180 },
86-
animate: { scale: 1, rotate: 0 },
87-
transition: { type: 'spring', stiffness: 200, damping: 15 },
88-
}
89-
: {};
90-
9182
return (
9283
<div className={cn('flex items-center gap-2', className)}>
93-
<Badge
94-
className={cn(
95-
'relative flex items-center justify-center rounded-full',
96-
`bg-gradient-to-br ${config.gradient}`,
97-
'shadow-lg',
98-
sizeClasses[size]
99-
)}
100-
{...animationProps}
101-
>
102-
<span className={iconSizes[size]}>{config.icon}</span>
103-
{showLevel && (
104-
<span
105-
className={cn(
106-
'absolute -bottom-1 -right-1 flex items-center justify-center',
107-
'bg-background border-2 border-background rounded-full',
108-
'text-[10px] font-bold text-foreground',
109-
size === 'sm' ? 'w-4 h-4' : 'w-5 h-5'
110-
)}
111-
>
112-
{level}
113-
</span>
114-
)}
115-
</Badge>
84+
{animated ? (
85+
<motion.div
86+
className={cn(
87+
'relative flex items-center justify-center rounded-full',
88+
`bg-gradient-to-br ${config.gradient}`,
89+
'shadow-lg',
90+
sizeClasses[size]
91+
)}
92+
initial={{ scale: 0, rotate: -180 }}
93+
animate={{ scale: 1, rotate: 0 }}
94+
transition={{ type: 'spring', stiffness: 200, damping: 15 }}
95+
>
96+
<span className={iconSizes[size]}>{config.icon}</span>
97+
{showLevel && (
98+
<span
99+
className={cn(
100+
'absolute -bottom-1 -right-1 flex items-center justify-center',
101+
'bg-background border-2 border-background rounded-full',
102+
'text-[10px] font-bold text-foreground',
103+
size === 'sm' ? 'w-4 h-4' : 'w-5 h-5'
104+
)}
105+
>
106+
{level}
107+
</span>
108+
)}
109+
</motion.div>
110+
) : (
111+
<div
112+
className={cn(
113+
'relative flex items-center justify-center rounded-full',
114+
`bg-gradient-to-br ${config.gradient}`,
115+
'shadow-lg',
116+
sizeClasses[size]
117+
)}
118+
>
119+
<span className={iconSizes[size]}>{config.icon}</span>
120+
{showLevel && (
121+
<span
122+
className={cn(
123+
'absolute -bottom-1 -right-1 flex items-center justify-center',
124+
'bg-background border-2 border-background rounded-full',
125+
'text-[10px] font-bold text-foreground',
126+
size === 'sm' ? 'w-4 h-4' : 'w-5 h-5'
127+
)}
128+
>
129+
{level}
130+
</span>
131+
)}
132+
</div>
133+
)}
116134

117135
{showName && (
118136
<div className="flex flex-col">

apps/web/src/components/gamification/streak-indicator.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export function StreakIndicator({
110110
<SnowflakeIcon
111111
key={i}
112112
className="w-4 h-4 text-cyan-400"
113-
title="Streak Freeze Available"
113+
aria-label="Streak Freeze Available"
114114
/>
115115
))}
116116
</div>

apps/web/src/components/import/import-uploader.tsx

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,30 @@ export function ImportUploader({
6565
const [isDragging, setIsDragging] = React.useState(false);
6666
const fileInputRef = React.useRef<HTMLInputElement>(null);
6767

68+
const processFiles = React.useCallback(
69+
(newFiles: File[]) => {
70+
const remainingSlots = maxFiles - files.length;
71+
const filesToProcess = newFiles.slice(0, remainingSlots);
72+
73+
const processedFiles: UploadedFile[] = filesToProcess
74+
.filter((file) => {
75+
const ext = '.' + file.name.split('.').pop()?.toLowerCase();
76+
return ACCEPTED_TYPES.includes(ext) && file.size <= MAX_FILE_SIZE;
77+
})
78+
.map((file) => ({
79+
file,
80+
id: `${file.name}-${crypto.randomUUID()}`,
81+
source: detectSource(file.name),
82+
status: 'pending' as const,
83+
}));
84+
85+
if (processedFiles.length > 0) {
86+
onFilesSelected(processedFiles);
87+
}
88+
},
89+
[files, maxFiles, onFilesSelected]
90+
);
91+
6892
const handleDragOver = React.useCallback((e: React.DragEvent) => {
6993
e.preventDefault();
7094
e.stopPropagation();
@@ -101,30 +125,6 @@ export function ImportUploader({
101125
[processFiles]
102126
);
103127

104-
const processFiles = React.useCallback(
105-
(newFiles: File[]) => {
106-
const remainingSlots = maxFiles - files.length;
107-
const filesToProcess = newFiles.slice(0, remainingSlots);
108-
109-
const processedFiles: UploadedFile[] = filesToProcess
110-
.filter((file) => {
111-
const ext = '.' + file.name.split('.').pop()?.toLowerCase();
112-
return ACCEPTED_TYPES.includes(ext) && file.size <= MAX_FILE_SIZE;
113-
})
114-
.map((file) => ({
115-
file,
116-
id: `${file.name}-${crypto.randomUUID()}`,
117-
source: detectSource(file.name),
118-
status: 'pending' as const,
119-
}));
120-
121-
if (processedFiles.length > 0) {
122-
onFilesSelected(processedFiles);
123-
}
124-
},
125-
[files, maxFiles, onFilesSelected]
126-
);
127-
128128
const canAddMore = files.length < maxFiles;
129129

130130
return (

apps/web/src/components/memory-graph/memory-graph.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ type GraphNode = NodeObject & {
5454

5555
export function MemoryGraph({ className, height = 400, width }: MemoryGraphProps) {
5656
const containerRef = useRef<HTMLDivElement>(null);
57-
const graphRef = useRef<ForceGraphMethods | null>(null);
57+
const graphRef = useRef<ForceGraphMethods | undefined>(undefined);
5858
const [dimensions, setDimensions] = useState({ width: width || 400, height });
5959
const [searchQuery, setSearchQuery] = useState('');
6060
const [selectedCategory, setSelectedCategory] = useState<MemoryNodeCategory | null>(null);
@@ -118,20 +118,22 @@ export function MemoryGraph({ className, height = 400, width }: MemoryGraphProps
118118

119119
// Custom node rendering
120120
const nodeCanvasObject = useCallback(
121-
(node: GraphNode, ctx: CanvasRenderingContext2D, globalScale: number) => {
121+
(node: NodeObject, ctx: CanvasRenderingContext2D, globalScale: number) => {
122122
if (typeof node.x !== 'number' || typeof node.y !== 'number') return;
123-
const label = node.name;
123+
const label = typeof node.name === 'string' ? node.name : '';
124124
const fontSize = 12 / globalScale;
125125
const nodeRadius = 8;
126126

127127
// Draw node circle
128128
ctx.beginPath();
129129
ctx.arc(node.x, node.y, nodeRadius, 0, 2 * Math.PI);
130-
ctx.fillStyle = node.color;
130+
ctx.fillStyle = typeof node.color === 'string' ? node.color : '#8b5cf6';
131131
ctx.fill();
132132

133133
// Draw border
134-
ctx.strokeStyle = categoryColors[node.category as MemoryNodeCategory].border;
134+
const category =
135+
typeof node.category === 'string' ? (node.category as MemoryNodeCategory) : null;
136+
ctx.strokeStyle = category ? categoryColors[category].border : 'rgba(255,255,255,0.3)';
135137
ctx.lineWidth = 2 / globalScale;
136138
ctx.stroke();
137139

@@ -147,8 +149,11 @@ export function MemoryGraph({ className, height = 400, width }: MemoryGraphProps
147149

148150
// Handle node click
149151
const handleNodeClick = useCallback(
150-
(node: GraphNode) => {
151-
selectNode(node.id);
152+
(node: NodeObject) => {
153+
const nodeId =
154+
typeof node.id === 'number' ? node.id.toString() : typeof node.id === 'string' ? node.id : null;
155+
if (!nodeId) return;
156+
selectNode(nodeId);
152157
// Center on node
153158
if (graphRef.current && typeof node.x === 'number' && typeof node.y === 'number') {
154159
graphRef.current.centerAt(node.x, node.y, 500);
@@ -160,8 +165,11 @@ export function MemoryGraph({ className, height = 400, width }: MemoryGraphProps
160165

161166
// Handle node hover
162167
const handleNodeHover = useCallback(
163-
(node: GraphNode | null) => {
164-
hoverNode(node?.id || null);
168+
(node: NodeObject | null) => {
169+
const nodeId = node
170+
? typeof node.id === 'number' ? node.id.toString() : typeof node.id === 'string' ? node.id : null
171+
: null;
172+
hoverNode(nodeId);
165173
},
166174
[hoverNode]
167175
);

apps/web/src/components/onboarding/spotlight-overlay.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ const SpotlightMask = ({
152152
const transitionProps = prefersReducedMotion
153153
? {}
154154
: {
155-
transition: { duration: TRANSITION_DURATION, ease: 'easeInOut' },
155+
transition: { duration: TRANSITION_DURATION, ease: 'easeInOut' as const },
156156
};
157157

158158
return (
@@ -231,7 +231,7 @@ const SpotlightTooltip = ({
231231
initial: { opacity: 0, scale: 0.95, y: 10 },
232232
animate: { opacity: 1, scale: 1, y: 0 },
233233
exit: { opacity: 0, scale: 0.95, y: 10 },
234-
transition: { duration: TRANSITION_DURATION, ease: 'easeOut' },
234+
transition: { duration: TRANSITION_DURATION, ease: 'easeOut' as const },
235235
};
236236

237237
return (
@@ -464,7 +464,7 @@ export function SpotlightOverlay({ children, className }: SpotlightOverlayProps)
464464
transition={
465465
prefersReducedMotion
466466
? {}
467-
: { duration: TRANSITION_DURATION, ease: 'easeInOut' }
467+
: { duration: TRANSITION_DURATION, ease: 'easeInOut' as const }
468468
}
469469
/>
470470
)}

0 commit comments

Comments
 (0)