lytx 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +37 -0
- package/README.md +486 -0
- package/alchemy.run.ts +155 -0
- package/cli/bootstrap-admin.ts +284 -0
- package/cli/deploy-staging.ts +692 -0
- package/cli/import-events.ts +628 -0
- package/cli/import-sites.ts +518 -0
- package/cli/index.ts +609 -0
- package/cli/init-db.ts +269 -0
- package/cli/migrate-to-durable-objects.ts +564 -0
- package/cli/migration-worker.ts +300 -0
- package/cli/performance-test.ts +588 -0
- package/cli/pg/client.ts +4 -0
- package/cli/pg/new-site.ts +153 -0
- package/cli/rollback-durable-objects.ts +622 -0
- package/cli/seed-data.ts +459 -0
- package/cli/setup.js +18 -0
- package/cli/setup.ts +463 -0
- package/cli/validate-migration.ts +200 -0
- package/cli/wrangler-migration.jsonc +28 -0
- package/db/adapter.ts +166 -0
- package/db/analytics_engine/client.ts +0 -0
- package/db/analytics_engine/sites.ts +0 -0
- package/db/client.ts +16 -0
- package/db/d1/client.ts +8 -0
- package/db/d1/drizzle.config.ts +35 -0
- package/db/d1/migrations/0000_true_maelstrom.sql +165 -0
- package/db/d1/migrations/0001_wonderful_bloodaxe.sql +12 -0
- package/db/d1/migrations/0002_late_frightful_four.sql +1 -0
- package/db/d1/migrations/0003_cuddly_obadiah_stane.sql +16 -0
- package/db/d1/migrations/0004_mute_stardust.sql +1 -0
- package/db/d1/migrations/0005_awesome_silvermane.sql +3 -0
- package/db/d1/migrations/0006_volatile_shriek.sql +2 -0
- package/db/d1/migrations/0007_superb_lila_cheney.sql +1 -0
- package/db/d1/migrations/0008_bitter_longshot.sql +17 -0
- package/db/d1/migrations/0009_wonderful_madame_masque.sql +28 -0
- package/db/d1/migrations/meta/0000_snapshot.json +1112 -0
- package/db/d1/migrations/meta/0001_snapshot.json +1187 -0
- package/db/d1/migrations/meta/0002_snapshot.json +1194 -0
- package/db/d1/migrations/meta/0003_snapshot.json +1296 -0
- package/db/d1/migrations/meta/0004_snapshot.json +1303 -0
- package/db/d1/migrations/meta/0005_snapshot.json +1325 -0
- package/db/d1/migrations/meta/0006_snapshot.json +1339 -0
- package/db/d1/migrations/meta/0007_snapshot.json +1347 -0
- package/db/d1/migrations/meta/0008_snapshot.json +1464 -0
- package/db/d1/migrations/meta/0009_snapshot.json +1648 -0
- package/db/d1/migrations/meta/_journal.json +76 -0
- package/db/d1/schema.ts +407 -0
- package/db/d1/sites.ts +374 -0
- package/db/d1/teamAiUsage.ts +101 -0
- package/db/d1/teams.ts +127 -0
- package/db/durable/drizzle.config.ts +8 -0
- package/db/durable/durableObjectClient.ts +480 -0
- package/db/durable/events.ts +100 -0
- package/db/durable/migrations/0000_fair_bucky.sql +38 -0
- package/db/durable/migrations/meta/0000_snapshot.json +278 -0
- package/db/durable/migrations/meta/_journal.json +13 -0
- package/db/durable/migrations/migrations.js +10 -0
- package/db/durable/schema.ts +5 -0
- package/db/durable/siteDurableObject.ts +1352 -0
- package/db/durable/types.ts +53 -0
- package/db/postgres/client.ts +13 -0
- package/db/postgres/drizzle.config.ts +12 -0
- package/db/postgres/migrations/0000_brainy_sprite.sql +116 -0
- package/db/postgres/migrations/meta/0000_snapshot.json +681 -0
- package/db/postgres/migrations/meta/_journal.json +13 -0
- package/db/postgres/schema.ts +145 -0
- package/db/postgres/sites.ts +118 -0
- package/db/tranformReports.ts +595 -0
- package/db/types.ts +55 -0
- package/endpoints/api_worker.tsx +1854 -0
- package/endpoints/site_do_worker.ts +11 -0
- package/index.d.ts +63 -0
- package/index.ts +83 -0
- package/lib/auth.ts +279 -0
- package/lib/geojson/world_countries.json +45307 -0
- package/lib/random_name.ts +41 -0
- package/lib/sendMail.ts +252 -0
- package/package.json +142 -0
- package/public/favicon.ico +0 -0
- package/public/images/android-chrome-192x192.png +0 -0
- package/public/images/android-chrome-512x512.png +0 -0
- package/public/images/apple-touch-icon.png +0 -0
- package/public/images/favicon-16x16.png +0 -0
- package/public/images/favicon-32x32.png +0 -0
- package/public/images/lytx_dark_dashboard.png +0 -0
- package/public/images/lytx_light_dashboard.png +0 -0
- package/public/images/safari-pinned-tab.svg +4 -0
- package/public/logo.png +0 -0
- package/public/site.webmanifest +26 -0
- package/public/sw.js +107 -0
- package/src/Document.tsx +86 -0
- package/src/api/ai_api.ts +1156 -0
- package/src/api/authMiddleware.ts +45 -0
- package/src/api/auth_api.ts +465 -0
- package/src/api/event_labels_api.ts +193 -0
- package/src/api/events_api.ts +210 -0
- package/src/api/queueWorker.ts +303 -0
- package/src/api/reports_api.ts +278 -0
- package/src/api/seed_api.ts +288 -0
- package/src/api/sites_api.ts +904 -0
- package/src/api/tag_api.ts +458 -0
- package/src/api/tag_api_v2.ts +289 -0
- package/src/api/team_api.ts +456 -0
- package/src/app/Dashboard.tsx +1339 -0
- package/src/app/Events.tsx +974 -0
- package/src/app/Explore.tsx +312 -0
- package/src/app/Layout.tsx +58 -0
- package/src/app/Settings.tsx +1302 -0
- package/src/app/components/DashboardCard.tsx +118 -0
- package/src/app/components/EditableCell.tsx +123 -0
- package/src/app/components/EventForm.tsx +93 -0
- package/src/app/components/MarketingFooter.tsx +49 -0
- package/src/app/components/MarketingNav.tsx +150 -0
- package/src/app/components/Nav.tsx +755 -0
- package/src/app/components/NewSiteSetup.tsx +298 -0
- package/src/app/components/SQLEditor.tsx +740 -0
- package/src/app/components/SiteSelector.tsx +126 -0
- package/src/app/components/SiteTag.tsx +42 -0
- package/src/app/components/SiteTagInstallCard.tsx +241 -0
- package/src/app/components/WorldMapCard.tsx +337 -0
- package/src/app/components/charts/ChartComponents.tsx +1481 -0
- package/src/app/components/charts/EventFunnel.tsx +45 -0
- package/src/app/components/charts/EventSummary.tsx +194 -0
- package/src/app/components/charts/SankeyFlows.tsx +72 -0
- package/src/app/components/marketing/CheckIcon.tsx +16 -0
- package/src/app/components/marketing/MarketingLayout.tsx +23 -0
- package/src/app/components/marketing/SectionHeading.tsx +35 -0
- package/src/app/components/reports/AskAiWorkspace.tsx +371 -0
- package/src/app/components/reports/CreateReportStarter.tsx +74 -0
- package/src/app/components/reports/DashboardRouteFiltersContext.tsx +14 -0
- package/src/app/components/reports/DashboardToolbar.tsx +154 -0
- package/src/app/components/reports/DashboardWorkspaceLayout.tsx +63 -0
- package/src/app/components/reports/DashboardWorkspaceShell.tsx +118 -0
- package/src/app/components/reports/ReportBuilderWorkspace.tsx +76 -0
- package/src/app/components/reports/custom/CustomReportBuilderPage.tsx +1667 -0
- package/src/app/components/reports/custom/ReportWidgetChart.tsx +297 -0
- package/src/app/components/reports/custom/buildWidgetSql.ts +151 -0
- package/src/app/components/reports/custom/chartPalettes.ts +18 -0
- package/src/app/components/reports/custom/types.ts +50 -0
- package/src/app/components/reports/reportBuilderMenuItems.ts +17 -0
- package/src/app/components/reports/useDashboardToolbarControls.tsx +235 -0
- package/src/app/components/ui/AlertBanner.tsx +101 -0
- package/src/app/components/ui/Button.tsx +55 -0
- package/src/app/components/ui/Card.tsx +80 -0
- package/src/app/components/ui/Input.tsx +72 -0
- package/src/app/components/ui/Link.tsx +23 -0
- package/src/app/components/ui/ReportBuilderMenu.tsx +246 -0
- package/src/app/components/ui/ThemeToggle.tsx +54 -0
- package/src/app/constants.ts +6 -0
- package/src/app/headers.ts +33 -0
- package/src/app/providers/AuthProvider.tsx +189 -0
- package/src/app/providers/ClientProviders.tsx +18 -0
- package/src/app/providers/QueryProvider.tsx +23 -0
- package/src/app/providers/ThemeProvider.tsx +88 -0
- package/src/app/utils/chartThemes.ts +146 -0
- package/src/app/utils/keybinds.ts +96 -0
- package/src/app/utils/media.tsx +24 -0
- package/src/client.tsx +114 -0
- package/src/config/createLytxAppConfig.ts +252 -0
- package/src/config/resourceNames.ts +88 -0
- package/src/db/index.ts +67 -0
- package/src/index.css +285 -0
- package/src/lib/featureFlags.ts +69 -0
- package/src/pages/GetStarted.tsx +290 -0
- package/src/pages/Home.tsx +268 -0
- package/src/pages/Login.tsx +283 -0
- package/src/pages/PrivacyPolicy.tsx +120 -0
- package/src/pages/Signup.tsx +267 -0
- package/src/pages/TermsOfService.tsx +126 -0
- package/src/pages/VerifyEmail.tsx +56 -0
- package/src/session/durableObject.ts +7 -0
- package/src/session/siteSchema.ts +86 -0
- package/src/session/types.ts +36 -0
- package/src/templates/README.md +80 -0
- package/src/templates/cleanFunctions.js +44 -0
- package/src/templates/embedFunctions.js +52 -0
- package/src/templates/lytx-shared.ts +662 -0
- package/src/templates/lytxpixel-core.ts +144 -0
- package/src/templates/lytxpixel.ts +267 -0
- package/src/templates/lytxpixelBrowser.js +634 -0
- package/src/templates/lytxpixelBrowser.mjs +634 -0
- package/src/templates/parseData.js +12 -0
- package/src/templates/script.ts +31 -0
- package/src/templates/template.tsx +50 -0
- package/src/templates/test.js +3 -0
- package/src/templates/trackWebEvents.ts +177 -0
- package/src/templates/vendors/clickcease.ts +8 -0
- package/src/templates/vendors/google.ts +174 -0
- package/src/templates/vendors/linkedin.ts +23 -0
- package/src/templates/vendors/meta.ts +56 -0
- package/src/templates/vendors/quantcast.ts +22 -0
- package/src/templates/vendors/simplfi.ts +7 -0
- package/src/types/app-context.ts +16 -0
- package/src/utilities/dashboardParams.ts +188 -0
- package/src/utilities/dashboardQueries.ts +537 -0
- package/src/utilities/dashboardTransforms.ts +167 -0
- package/src/utilities/dataValidation.ts +414 -0
- package/src/utilities/detector.ts +73 -0
- package/src/utilities/encrypt.ts +103 -0
- package/src/utilities/index.ts +13 -0
- package/src/utilities/parser.ts +117 -0
- package/src/utilities/performanceMonitoring.ts +570 -0
- package/src/utilities/route_interuptors.ts +24 -0
- package/src/worker.tsx +675 -0
- package/tsconfig.json +78 -0
- package/types/env.d.ts +16 -0
- package/types/rw.d.ts +7 -0
- package/types/shims.d.ts +53 -0
- package/types/vite.d.ts +19 -0
- package/vite/vite-plugin-pixel-bundle.ts +126 -0
- package/vite.config.ts +53 -0
- package/worker-configuration.d.ts +8401 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export type LytxResourceStagePosition = "prefix" | "suffix" | "none";
|
|
2
|
+
|
|
3
|
+
export type LytxResourceNames = {
|
|
4
|
+
appName: string;
|
|
5
|
+
workerName: string;
|
|
6
|
+
durableHostWorkerName: string;
|
|
7
|
+
durableObjectNamespaceName: string;
|
|
8
|
+
d1DatabaseName: string;
|
|
9
|
+
eventsKvNamespaceName: string;
|
|
10
|
+
configKvNamespaceName: string;
|
|
11
|
+
sessionsKvNamespaceName: string;
|
|
12
|
+
eventsQueueName: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type LytxResourceNamingOptions = {
|
|
16
|
+
prefix?: string;
|
|
17
|
+
suffix?: string;
|
|
18
|
+
stage?: string;
|
|
19
|
+
stagePosition?: LytxResourceStagePosition;
|
|
20
|
+
separator?: "-" | "_";
|
|
21
|
+
overrides?: Partial<LytxResourceNames>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_LYTX_RESOURCE_NAMES: LytxResourceNames = {
|
|
25
|
+
appName: "lytx",
|
|
26
|
+
workerName: "lytx-app",
|
|
27
|
+
durableHostWorkerName: "lytx-app-do-host",
|
|
28
|
+
durableObjectNamespaceName: "site-durable-object",
|
|
29
|
+
d1DatabaseName: "lytx-core-db",
|
|
30
|
+
eventsKvNamespaceName: "LYTX_EVENTS",
|
|
31
|
+
configKvNamespaceName: "lytx_config",
|
|
32
|
+
sessionsKvNamespaceName: "lytx_sessions",
|
|
33
|
+
eventsQueueName: "site-events-queue",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function normalizeSegment(value: string): string {
|
|
37
|
+
return value
|
|
38
|
+
.trim()
|
|
39
|
+
.replace(/\s+/g, "-")
|
|
40
|
+
.replace(/[^A-Za-z0-9_-]/g, "-")
|
|
41
|
+
.replace(/[-_]{2,}/g, "-")
|
|
42
|
+
.replace(/^[-_]+|[-_]+$/g, "");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function joinSegments(segments: Array<string | undefined>, separator: "-" | "_"): string {
|
|
46
|
+
const normalized = segments
|
|
47
|
+
.map((segment) => (segment ? normalizeSegment(segment) : ""))
|
|
48
|
+
.filter((segment) => segment.length > 0);
|
|
49
|
+
return normalized.join(separator);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolveLytxResourceNames(
|
|
53
|
+
options: LytxResourceNamingOptions = {},
|
|
54
|
+
): LytxResourceNames {
|
|
55
|
+
const separator = options.separator ?? "-";
|
|
56
|
+
const stagePosition = options.stagePosition ?? "none";
|
|
57
|
+
const stage_segment = options.stage ? normalizeSegment(options.stage) : "";
|
|
58
|
+
const prefix_segment = options.prefix ? normalizeSegment(options.prefix) : "";
|
|
59
|
+
const suffix_segment = options.suffix ? normalizeSegment(options.suffix) : "";
|
|
60
|
+
|
|
61
|
+
const withStrategy = (base: string): string => {
|
|
62
|
+
if (stagePosition === "prefix") {
|
|
63
|
+
return joinSegments([prefix_segment, stage_segment, base, suffix_segment], separator);
|
|
64
|
+
}
|
|
65
|
+
if (stagePosition === "suffix") {
|
|
66
|
+
return joinSegments([prefix_segment, base, stage_segment, suffix_segment], separator);
|
|
67
|
+
}
|
|
68
|
+
return joinSegments([prefix_segment, base, suffix_segment], separator);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const resolved: LytxResourceNames = {
|
|
72
|
+
...DEFAULT_LYTX_RESOURCE_NAMES,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
for (const [key, default_name] of Object.entries(DEFAULT_LYTX_RESOURCE_NAMES) as Array<
|
|
76
|
+
[keyof LytxResourceNames, string]
|
|
77
|
+
>) {
|
|
78
|
+
const override = options.overrides?.[key];
|
|
79
|
+
if (override) {
|
|
80
|
+
resolved[key] = normalizeSegment(override);
|
|
81
|
+
} else {
|
|
82
|
+
const strategy_name = withStrategy(default_name);
|
|
83
|
+
resolved[key] = strategy_name.length > 0 ? strategy_name : default_name;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return resolved;
|
|
88
|
+
}
|
package/src/db/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { env } from "cloudflare:workers";
|
|
2
|
+
import { IS_DEV } from "rwsdk/constants";
|
|
3
|
+
|
|
4
|
+
type Create_user = { data: { username: string } };
|
|
5
|
+
type User_Profile = {
|
|
6
|
+
id: string;
|
|
7
|
+
username: string;
|
|
8
|
+
createdAt: Date;
|
|
9
|
+
} | null
|
|
10
|
+
;
|
|
11
|
+
type Create_credential = { data: { userId: string, credentialId: string, publicKey: Uint8Array<ArrayBufferLike>, counter: number } };
|
|
12
|
+
|
|
13
|
+
type DB_Credential = {
|
|
14
|
+
id: string;
|
|
15
|
+
createdAt: Date;
|
|
16
|
+
userId: string;
|
|
17
|
+
credentialId: string;
|
|
18
|
+
publicKey: Uint8Array;
|
|
19
|
+
counter: number;
|
|
20
|
+
} | null
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
//WARNING: This is a placeholder till we swap in db
|
|
24
|
+
export const db = {
|
|
25
|
+
user: {
|
|
26
|
+
create: async ({ data }: Create_user) => {
|
|
27
|
+
return { id: "test" }
|
|
28
|
+
},
|
|
29
|
+
findUnique: async ({ where }: { where: { id: string } }): Promise<User_Profile> => {
|
|
30
|
+
return {
|
|
31
|
+
id: "test",
|
|
32
|
+
createdAt: new Date(),
|
|
33
|
+
username: "test",
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
credential: {
|
|
38
|
+
create: async ({ data }: Create_credential) => {
|
|
39
|
+
},
|
|
40
|
+
//TODO:REPLACE With drizzle call
|
|
41
|
+
findUnique: async ({ where }: { where: { credentialId: string } }): Promise<DB_Credential> => {
|
|
42
|
+
return {
|
|
43
|
+
id: "test",
|
|
44
|
+
createdAt: new Date(),
|
|
45
|
+
userId: "test",
|
|
46
|
+
credentialId: "test",
|
|
47
|
+
publicKey: new Uint8Array(),
|
|
48
|
+
counter: 0,
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
//TODO:REPLACE With drizzle call
|
|
53
|
+
update: async ({ where, data }: { where: { credentialId: string }, data: { counter: number } }) => {
|
|
54
|
+
//TODO: Update the counter
|
|
55
|
+
if (IS_DEV) console.log(where, data);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
export type User = User_Profile;
|
|
63
|
+
|
|
64
|
+
export const setupDb = async (cf_env: typeof env) => {
|
|
65
|
+
//TODO: Setup the db
|
|
66
|
+
if (IS_DEV) console.log(cf_env);
|
|
67
|
+
}
|
package/src/index.css
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "@fontsource/montserrat/400.css";
|
|
3
|
+
@import "@fontsource/montserrat/500.css";
|
|
4
|
+
@import "@fontsource/montserrat/600.css";
|
|
5
|
+
@import "@fontsource/montserrat/700.css";
|
|
6
|
+
|
|
7
|
+
@theme {
|
|
8
|
+
/* Color Palette - Light Mode */
|
|
9
|
+
--color-primary: #f97316;
|
|
10
|
+
--color-primary-hover: #ea580c;
|
|
11
|
+
--color-primary-light: #fdba74;
|
|
12
|
+
|
|
13
|
+
--color-brand-cta: #D95C00;
|
|
14
|
+
--color-brand-cta-hover: #c45300;
|
|
15
|
+
|
|
16
|
+
--color-secondary: #6b7280;
|
|
17
|
+
--color-secondary-hover: #4b5563;
|
|
18
|
+
--color-secondary-light: #9ca3af;
|
|
19
|
+
|
|
20
|
+
--color-accent: #f59e0b;
|
|
21
|
+
--color-accent-hover: #d97706;
|
|
22
|
+
--color-accent-light: #fcd34d;
|
|
23
|
+
|
|
24
|
+
--color-danger: #ef4444;
|
|
25
|
+
--color-danger-hover: #dc2626;
|
|
26
|
+
--color-danger-light: #fca5a5;
|
|
27
|
+
|
|
28
|
+
--color-warning: #f59e0b;
|
|
29
|
+
--color-warning-hover: #d97706;
|
|
30
|
+
--color-warning-light: #fcd34d;
|
|
31
|
+
|
|
32
|
+
/* Neutral Colors - Light Mode */
|
|
33
|
+
--color-white: #ffffff;
|
|
34
|
+
--color-gray-50: #f9fafb;
|
|
35
|
+
--color-gray-100: #f3f4f6;
|
|
36
|
+
--color-gray-200: #e5e7eb;
|
|
37
|
+
--color-gray-300: #d1d5db;
|
|
38
|
+
--color-gray-400: #9ca3af;
|
|
39
|
+
--color-gray-500: #6b7280;
|
|
40
|
+
--color-gray-600: #4b5563;
|
|
41
|
+
--color-gray-700: #374151;
|
|
42
|
+
--color-gray-800: #1f2937;
|
|
43
|
+
--color-gray-900: #111827;
|
|
44
|
+
--color-black: #000000;
|
|
45
|
+
|
|
46
|
+
/* Typography Scale */
|
|
47
|
+
--font-size-xs: 0.75rem;
|
|
48
|
+
--font-size-sm: 0.875rem;
|
|
49
|
+
--font-size-base: 1rem;
|
|
50
|
+
--font-size-lg: 1.125rem;
|
|
51
|
+
--font-size-xl: 1.25rem;
|
|
52
|
+
--font-size-2xl: 1.5rem;
|
|
53
|
+
--font-size-3xl: 1.875rem;
|
|
54
|
+
--font-size-4xl: 2.25rem;
|
|
55
|
+
--font-montserrat: "Montserrat", "Inter", system-ui, sans-serif;
|
|
56
|
+
|
|
57
|
+
/* Line Heights */
|
|
58
|
+
--line-height-tight: 1.25;
|
|
59
|
+
--line-height-normal: 1.5;
|
|
60
|
+
--line-height-relaxed: 1.75;
|
|
61
|
+
|
|
62
|
+
/* Spacing Scale */
|
|
63
|
+
--spacing-xs: 0.25rem;
|
|
64
|
+
--spacing-sm: 0.5rem;
|
|
65
|
+
--spacing-md: 1rem;
|
|
66
|
+
--spacing-lg: 1.5rem;
|
|
67
|
+
--spacing-xl: 2rem;
|
|
68
|
+
--spacing-2xl: 3rem;
|
|
69
|
+
--spacing-3xl: 4rem;
|
|
70
|
+
|
|
71
|
+
/* Border Radius */
|
|
72
|
+
--radius-sm: 0.25rem;
|
|
73
|
+
--radius-md: 0.375rem;
|
|
74
|
+
--radius-lg: 0.5rem;
|
|
75
|
+
--radius-xl: 0.75rem;
|
|
76
|
+
--radius-2xl: 1rem;
|
|
77
|
+
|
|
78
|
+
/* Shadows */
|
|
79
|
+
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
80
|
+
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
81
|
+
--shadow-lg:
|
|
82
|
+
0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
83
|
+
--shadow-xl:
|
|
84
|
+
0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
|
|
88
|
+
|
|
89
|
+
:root {
|
|
90
|
+
/* Dark Mode Theme Variables */
|
|
91
|
+
--theme-bg-primary: #121212; /* Main background */
|
|
92
|
+
--theme-bg-secondary: #171717; /* Secondary background */
|
|
93
|
+
--theme-bg-tertiary: #222222; /* Tertiary background */
|
|
94
|
+
--theme-text-primary: #f8fafc; /* Primary text */
|
|
95
|
+
--theme-text-secondary: #cbd5e1; /* Secondary text */
|
|
96
|
+
--theme-text-tertiary: #94a3b8; /* Tertiary text */
|
|
97
|
+
--theme-border-primary: #1f2937; /* Borders */
|
|
98
|
+
--theme-border-secondary: #f59e0b; /* Accent borders / focus */
|
|
99
|
+
|
|
100
|
+
/* Component Colors */
|
|
101
|
+
--theme-card-bg: #171717; /* Card background */
|
|
102
|
+
--theme-card-border: #1f2937; /* Card borders */
|
|
103
|
+
--theme-input-bg: #141414; /* Input background */
|
|
104
|
+
--theme-input-border: #334155; /* Input borders */
|
|
105
|
+
--theme-input-border-focus: #f59e0b; /* Input focus borders */
|
|
106
|
+
--theme-button-bg: #f97316; /* Button background */
|
|
107
|
+
--theme-button-hover: #ea580c; /* Button hover */
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[data-theme="light"] {
|
|
111
|
+
/* Light Mode Theme Variables */
|
|
112
|
+
--theme-bg-primary: var(--color-white);
|
|
113
|
+
--theme-bg-secondary: var(--color-gray-50);
|
|
114
|
+
--theme-bg-tertiary: var(--color-gray-100);
|
|
115
|
+
--theme-text-primary: var(--color-gray-900);
|
|
116
|
+
--theme-text-secondary: var(--color-gray-600);
|
|
117
|
+
--theme-text-tertiary: var(--color-gray-500);
|
|
118
|
+
--theme-border-primary: var(--color-gray-200);
|
|
119
|
+
--theme-border-secondary: var(--color-gray-300);
|
|
120
|
+
|
|
121
|
+
/* Component Colors */
|
|
122
|
+
--theme-card-bg: var(--color-white);
|
|
123
|
+
--theme-card-border: var(--color-gray-200);
|
|
124
|
+
--theme-input-bg: var(--color-white);
|
|
125
|
+
--theme-input-border: var(--color-gray-300);
|
|
126
|
+
--theme-input-border-focus: var(--color-gray-400);
|
|
127
|
+
--theme-button-bg: var(--color-primary);
|
|
128
|
+
--theme-button-hover: var(--color-primary-hover);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* View Transitions API - Navigation Animations */
|
|
132
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
133
|
+
::view-transition-old(root) {
|
|
134
|
+
animation: 150ms ease-out both fade-out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
::view-transition-new(root) {
|
|
138
|
+
animation: 150ms ease-in 50ms both fade-in;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@keyframes fade-out {
|
|
142
|
+
from {
|
|
143
|
+
opacity: 1;
|
|
144
|
+
}
|
|
145
|
+
to {
|
|
146
|
+
opacity: 0;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@keyframes fade-in {
|
|
151
|
+
from {
|
|
152
|
+
opacity: 0;
|
|
153
|
+
}
|
|
154
|
+
to {
|
|
155
|
+
opacity: 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Disable view transitions for users who prefer reduced motion */
|
|
161
|
+
@media (prefers-reduced-motion: reduce) {
|
|
162
|
+
::view-transition-group(*),
|
|
163
|
+
::view-transition-old(*),
|
|
164
|
+
::view-transition-new(*) {
|
|
165
|
+
animation: none !important;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@layer base {
|
|
170
|
+
/* Enable smooth transitions only during theme toggles. */
|
|
171
|
+
:root.theme-transition * {
|
|
172
|
+
transition:
|
|
173
|
+
background-color 0.2s ease-in-out,
|
|
174
|
+
color 0.2s ease-in-out,
|
|
175
|
+
border-color 0.2s ease-in-out;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@media (prefers-reduced-motion: reduce) {
|
|
179
|
+
:root.theme-transition * {
|
|
180
|
+
transition: none !important;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Only remove focus styles for non-keyboard interactions */
|
|
185
|
+
*:focus:not(:focus-visible) {
|
|
186
|
+
outline: none;
|
|
187
|
+
box-shadow: none;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Ensure keyboard focus is always visible */
|
|
191
|
+
*:focus-visible {
|
|
192
|
+
outline: 2px solid var(--color-primary);
|
|
193
|
+
outline-offset: 2px;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
body {
|
|
197
|
+
background-color: var(--theme-bg-primary);
|
|
198
|
+
color: var(--theme-text-primary);
|
|
199
|
+
font-family: Inter, system-ui, sans-serif;
|
|
200
|
+
font-size: var(--font-size-base);
|
|
201
|
+
line-height: var(--line-height-normal);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
button:not(:disabled),
|
|
205
|
+
[role="button"]:not([aria-disabled="true"]),
|
|
206
|
+
a[href],
|
|
207
|
+
summary,
|
|
208
|
+
select,
|
|
209
|
+
label[for],
|
|
210
|
+
input[type="checkbox"],
|
|
211
|
+
input[type="radio"],
|
|
212
|
+
input[type="range"],
|
|
213
|
+
input[type="date"],
|
|
214
|
+
input[type="time"],
|
|
215
|
+
input[type="datetime-local"],
|
|
216
|
+
input[type="month"],
|
|
217
|
+
input[type="week"],
|
|
218
|
+
input[type="color"],
|
|
219
|
+
input[type="file"],
|
|
220
|
+
input[type="submit"],
|
|
221
|
+
input[type="button"],
|
|
222
|
+
input[type="reset"] {
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
input:not([type]),
|
|
227
|
+
input[type="text"],
|
|
228
|
+
input[type="email"],
|
|
229
|
+
input[type="password"],
|
|
230
|
+
input[type="search"],
|
|
231
|
+
input[type="tel"],
|
|
232
|
+
input[type="url"],
|
|
233
|
+
input[type="number"],
|
|
234
|
+
textarea {
|
|
235
|
+
cursor: text;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
button:disabled,
|
|
239
|
+
input:disabled,
|
|
240
|
+
select:disabled,
|
|
241
|
+
textarea:disabled,
|
|
242
|
+
[aria-disabled="true"] {
|
|
243
|
+
cursor: not-allowed;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Nivo tooltip theming */
|
|
248
|
+
div[style*="pointer-events: none;"] > div[style*="background: white;"],
|
|
249
|
+
div[style*="pointer-events: none;"]
|
|
250
|
+
> div[style*="background: rgb(255, 255, 255)"] {
|
|
251
|
+
background: var(--theme-card-bg) !important;
|
|
252
|
+
color: var(--theme-text-primary) !important;
|
|
253
|
+
border: 1px solid var(--theme-border-primary) !important;
|
|
254
|
+
border-radius: 8px !important;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
div[style*="pointer-events: none;"]
|
|
258
|
+
> div[style*="background: white;"]
|
|
259
|
+
table
|
|
260
|
+
tbody
|
|
261
|
+
tr
|
|
262
|
+
td,
|
|
263
|
+
div[style*="pointer-events: none;"]
|
|
264
|
+
> div[style*="background: rgb(255, 255, 255)"]
|
|
265
|
+
table
|
|
266
|
+
tbody
|
|
267
|
+
tr
|
|
268
|
+
td {
|
|
269
|
+
color: var(--theme-text-primary) !important;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
div[style*="pointer-events: none;"]
|
|
273
|
+
> div[style*="background: white;"]
|
|
274
|
+
table
|
|
275
|
+
tbody
|
|
276
|
+
tr
|
|
277
|
+
td[style*="color: rgb(0, 0, 0);"],
|
|
278
|
+
div[style*="pointer-events: none;"]
|
|
279
|
+
> div[style*="background: rgb(255, 255, 255)"]
|
|
280
|
+
table
|
|
281
|
+
tbody
|
|
282
|
+
tr
|
|
283
|
+
td[style*="color: rgb(0, 0, 0);"] {
|
|
284
|
+
color: var(--theme-text-primary) !important;
|
|
285
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { env } from "cloudflare:workers";
|
|
2
|
+
|
|
3
|
+
const TRUE_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
4
|
+
|
|
5
|
+
const isEnabled = (value: unknown) => {
|
|
6
|
+
if (typeof value === "boolean") return value;
|
|
7
|
+
if (typeof value !== "string") return false;
|
|
8
|
+
return TRUE_VALUES.has(value.trim().toLowerCase());
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const readRawFlag = (keys: string[]) => {
|
|
12
|
+
const bindingValues = env as unknown as Record<string, string | boolean | undefined>;
|
|
13
|
+
for (const key of keys) {
|
|
14
|
+
if (bindingValues[key] !== undefined) return bindingValues[key];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const processEnv =
|
|
18
|
+
typeof process !== "undefined"
|
|
19
|
+
? (process as { env?: Record<string, string | undefined> }).env
|
|
20
|
+
: undefined;
|
|
21
|
+
|
|
22
|
+
for (const key of keys) {
|
|
23
|
+
if (processEnv?.[key] !== undefined) return processEnv[key];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return undefined;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const resolveFlag = (keys: string[], defaultValue: boolean) => {
|
|
30
|
+
const raw = readRawFlag(keys);
|
|
31
|
+
|
|
32
|
+
if (raw === undefined || raw === null) {
|
|
33
|
+
return defaultValue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof raw === "string" && raw.trim().length === 0) {
|
|
37
|
+
return defaultValue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return isEnabled(raw);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const isReportBuilderEnabled = () => {
|
|
44
|
+
return resolveFlag(["REPORT_BUILDER"], false);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const isAskAiEnabled = () => {
|
|
48
|
+
return resolveFlag(["ASK_AI", "ASK_AI_ENABLED"], true);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const isDashboardEnabled = () => {
|
|
52
|
+
return resolveFlag(["LYTX_FEATURE_DASHBOARD", "LYTX_DASHBOARD"], true);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const isEventsEnabled = () => {
|
|
56
|
+
return resolveFlag(["LYTX_FEATURE_EVENTS", "LYTX_EVENTS_ENABLED"], true);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const isAuthEnabled = () => {
|
|
60
|
+
return resolveFlag(["LYTX_FEATURE_AUTH", "LYTX_AUTH_ENABLED"], true);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const isAiFeatureEnabled = () => {
|
|
64
|
+
return resolveFlag(["LYTX_FEATURE_AI", "LYTX_AI_ENABLED"], true);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const isTagScriptEnabled = () => {
|
|
68
|
+
return resolveFlag(["LYTX_FEATURE_TAG_SCRIPT", "LYTX_TAG_SCRIPT_ENABLED"], true);
|
|
69
|
+
};
|