Skip to content

Commit 78dc208

Browse files
Staging (#68)
* refactor: lol gpt 5 refactored ts for me * refactor: website card, mini chart route caching, database indexing / keys * fix: UI consistencies, icon colors, organization improvements * fix: clean up organization selector * fix: clean up organization dialog * feat: landing revamp (#56) * feat: navbar * feat: hero * feat: trusted by * feat: cards * feat: desc & faq * feat: footer svg * feat: footer & testimonials * feat: landing * fix: on click btns * fix: responsive * feat: light mode, dark mode, some icon fixes * feat: pricing page * feat: better pricing page * trusted by more people bro * feat: cleanup --------- Co-authored-by: Hyteq <[email protected]> * fix: layout t estimonials --------- Co-authored-by: dazai <[email protected]>
1 parent 1be88ba commit 78dc208

Some content is hidden

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

76 files changed

+9233
-4419
lines changed

.cursor/rules/01-MUST-DO.mdc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
---
22
description: Basic guidelines for the project so vibe coders don't fuck it up
3-
globs:
3+
globs:
44
alwaysApply: true
55
---
6+
67
Always ensure type-safety, don't use type: any unless needed, when creating APIs, responses, or components, create proper interfaces and make them in the shared types folders where it fits best, not in the same file
78

89
When working on anything, try to split off components, utils, anything reusable to ensure better loading speed and less complexity
@@ -39,10 +40,8 @@ use console properly, like console.error, console.time, console.json, console.ta
3940

4041
Use Dayjs NEVER date-fns, and Tanstack query for hooks, NEVER SWR
4142

42-
Use ONLY Zod V4 from zod/v4 never zod 3 from zod
43-
4443
use Icon at the end of phosphor react icons, like CaretIcon not Caret
4544

4645
use json.stringify() when adding debugging
4746

48-
Almost NEVER use useEffect unless it's critical
47+
Almost NEVER use useEffect unless it's critical

apps/api/src/query/screen-resolution-to-device-type.ts

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,69 @@ interface Resolution {
1313
aspect: number;
1414
}
1515

16+
// Orientation-agnostic key (longSide x shortSide)
17+
const makeKey = (w: number, h: number) => {
18+
const longSide = Math.max(w, h);
19+
const shortSide = Math.min(w, h);
20+
return `${longSide}x${shortSide}`;
21+
};
22+
23+
// Curated common resolutions mapped to device types (orientation-agnostic)
24+
// Keep this list intentionally small to avoid bloat while covering popular cases
25+
const COMMON_RESOLUTION_DEVICE_TYPE: Record<string, DeviceType> = {
26+
// Mobile (portrait long x short)
27+
'896x414': 'mobile', // iPhone XR/11
28+
'844x390': 'mobile', // iPhone 12/13/14
29+
'932x430': 'mobile', // iPhone 14/15 Pro Max
30+
'800x360': 'mobile', // Many Android
31+
'780x360': 'mobile',
32+
'736x414': 'mobile',
33+
'667x375': 'mobile',
34+
'640x360': 'mobile',
35+
'568x320': 'mobile',
36+
37+
// Tablets
38+
'1366x1024': 'tablet', // iPad Pro (CSS logical)
39+
'1280x800': 'tablet',
40+
'1180x820': 'tablet', // iPad Air
41+
'1024x768': 'tablet',
42+
'1280x720': 'tablet',
43+
44+
// Laptops (common)
45+
'1366x768': 'laptop',
46+
'1440x900': 'laptop',
47+
'1536x864': 'laptop',
48+
49+
// Desktop (16:9 standard)
50+
'1920x1080': 'desktop',
51+
'2560x1440': 'desktop',
52+
'3840x2160': 'desktop', // 4K UHD
53+
54+
// Ultrawide
55+
'3440x1440': 'ultrawide',
56+
'3840x1600': 'ultrawide',
57+
'5120x1440': 'ultrawide',
58+
};
59+
1660
function parseResolution(screenResolution: string): Resolution | null {
1761
if (!screenResolution || typeof screenResolution !== 'string') {
1862
return null;
1963
}
2064

21-
const parts = screenResolution.split('x');
22-
if (parts.length !== 2) {
23-
return null;
24-
}
25-
26-
const widthStr = parts[0];
27-
const heightStr = parts[1];
65+
// Normalize: trim, remove spaces, unify delimiter to 'x'
66+
const normalized = screenResolution
67+
.trim()
68+
.replace(/\s+/g, '')
69+
.replace(/[X×]/gi, 'x')
70+
.toLowerCase();
2871

29-
if (typeof widthStr !== 'string' || typeof heightStr !== 'string') {
72+
const parts = normalized.split('x');
73+
if (parts.length !== 2) {
3074
return null;
3175
}
3276

33-
const width = Number.parseInt(widthStr, 10);
34-
const height = Number.parseInt(heightStr, 10);
77+
const width = Number.parseInt(parts[0] || '', 10);
78+
const height = Number.parseInt(parts[1] || '', 10);
3579

3680
if (
3781
Number.isNaN(width) ||
@@ -46,28 +90,44 @@ function parseResolution(screenResolution: string): Resolution | null {
4690
}
4791

4892
function determineDeviceType(resolution: Resolution): DeviceType {
49-
const { width, aspect } = resolution;
93+
const w = Math.max(resolution.width, resolution.height);
94+
const h = Math.min(resolution.width, resolution.height);
95+
const aspect = w / h;
96+
97+
// Exact/common matches first
98+
const key = makeKey(w, h);
99+
if (COMMON_RESOLUTION_DEVICE_TYPE[key]) {
100+
return COMMON_RESOLUTION_DEVICE_TYPE[key];
101+
}
50102

51-
if (width <= 400 && aspect >= 0.9 && aspect <= 1.1) {
103+
// Watches: very small and near-square
104+
if (w <= 400 && aspect >= 0.85 && aspect <= 1.15) {
52105
return 'watch';
53106
}
54-
if (width <= 800 && (aspect < 0.9 || (aspect >= 1.8 && aspect <= 2.5))) {
107+
108+
// Ultrawide: very wide aspect and sufficiently large
109+
if (aspect >= 2.0 && w >= 2560) {
110+
return 'ultrawide';
111+
}
112+
113+
// Heuristics based primarily on the short side (portrait CSS width proxy)
114+
if (h <= 480) {
55115
return 'mobile';
56116
}
57-
if (width > 800 && width <= 1280 && aspect >= 1.1 && aspect <= 1.7) {
117+
if (h <= 900) {
58118
return 'tablet';
59119
}
60-
if (width > 1280 && width <= 1920 && aspect >= 1.3 && aspect <= 1.8) {
120+
121+
// Above tablet short-side: distinguish laptop vs desktop by long side
122+
if (w <= 1600) {
61123
return 'laptop';
62124
}
63-
if (aspect > 2.0 && width > 1920) {
64-
return 'ultrawide';
65-
}
66-
if (width > 1920 && width <= 2560 && aspect >= 1.3 && aspect <= 2.0) {
125+
if (w <= 3000) {
67126
return 'desktop';
68127
}
69128

70-
return 'unknown';
129+
// Very large (e.g., 4k+) default to desktop
130+
return 'desktop';
71131
}
72132

73133
/**

apps/api/src/query/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ export interface SimpleQueryConfig {
3838
plugins?: {
3939
parseReferrers?: boolean;
4040
normalizeUrls?: boolean;
41-
[key: string]: unknown;
41+
normalizeGeo?: boolean;
42+
deduplicateGeo?: boolean;
43+
mapDeviceTypes?: boolean;
4244
};
4345
customSql?: (
4446
websiteId: string,

0 commit comments

Comments
 (0)