diff --git a/apps/editor/app/privacy/page.tsx b/apps/editor/app/privacy/page.tsx new file mode 100644 index 00000000..bbc7c024 --- /dev/null +++ b/apps/editor/app/privacy/page.tsx @@ -0,0 +1,207 @@ +import type { Metadata } from 'next' +import Link from 'next/link' + +export const metadata: Metadata = { + title: 'Privacy Policy', + description: 'Privacy Policy for Pascal Editor and the Pascal platform.', +} + +export default function PrivacyPage() { + return ( +
+
+
+ +
+
+ +
+
+

Privacy Policy

+

+ Effective Date: February 20, 2026 +

+ +
+

1. Introduction

+

+ Pascal Group Inc. ("we," "us," or "our") operates the Pascal Editor and + Platform at pascal.app. This Privacy Policy explains how we collect, use, and + protect your information when you use our services. +

+
+ +
+

2. Information We Collect

+ +

Account Information

+

+ When you create an account, we collect: +

+
    +
  • Email address
  • +
  • Name
  • +
  • Profile picture/avatar
  • +
  • OAuth provider data (from Google when you sign in with Google)
  • +
+ +

Project Data

+

+ When you use the Platform, we store your projects, including 3D building designs, + floor plans, and associated metadata. +

+ +

Usage Analytics

+

+ We use Vercel Analytics and Speed Insights to collect anonymized usage data, + including page views, performance metrics, and general usage patterns. This helps + us improve the Platform. +

+
+ +
+

3. How We Use Your Information

+

We use your information to:

+
    +
  • Provide and maintain your account
  • +
  • Store and sync your projects across devices
  • +
  • Improve our services based on usage patterns
  • +
  • Send optional email notifications about new features and updates (you can opt out in settings)
  • +
  • Respond to support requests
  • +
  • Ensure platform security and prevent abuse
  • +
+
+ +
+

4. Data Storage

+

+ Your data is stored using Supabase (PostgreSQL database) on secure cloud + infrastructure. We implement appropriate technical and organizational measures + to protect your data. +

+
+ +
+

5. Third-Party Services

+

+ We use the following third-party services to operate the Platform: +

+
    +
  • + Google - OAuth authentication for sign-in +
  • +
  • + Vercel - Application hosting, analytics, and performance monitoring +
  • +
  • + Supabase - Database hosting and authentication infrastructure +
  • +
+

+ Each of these services has their own privacy policies governing their handling + of your data. +

+
+ +
+

6. Cookies

+

+ We use minimal cookies necessary for the Platform to function: +

+
    +
  • + Session cookies - Essential for authentication and keeping you + signed in +
  • +
  • + Analytics cookies - Used by Vercel Analytics to collect + anonymized usage data +
  • +
+
+ +
+

7. Your Rights

+

You have the right to:

+
    +
  • Access the personal data we hold about you
  • +
  • Request correction of inaccurate data
  • +
  • Request deletion of your data
  • +
  • Export your project data
  • +
  • Opt out of marketing communications
  • +
+

+ To exercise any of these rights, please contact us at{' '} + + support@pascal.app + + . +

+
+ +
+

8. Data Retention

+

+ We retain your data for as long as your account is active. If you delete your + account, we will delete your personal data and project data within 30 days, + except where we are required by law to retain certain information. +

+
+ +
+

9. Children's Privacy

+

+ The Platform is not intended for children under 13. We do not knowingly collect + personal information from children under 13. If you believe we have collected + such information, please contact us immediately. +

+
+ +
+

10. Changes to This Policy

+

+ We may update this Privacy Policy from time to time. We will notify you of + material changes by posting the updated policy on the Platform. Your continued + use of the Platform after changes are posted constitutes your acceptance of the + revised policy. +

+
+ +
+

11. Contact Us

+

+ If you have questions about this Privacy Policy or how we handle your data, + please contact us at{' '} + + support@pascal.app + + . +

+
+
+
+
+ ) +} diff --git a/apps/editor/app/settings/page.tsx b/apps/editor/app/settings/page.tsx index fefd6f20..f65d7eaa 100644 --- a/apps/editor/app/settings/page.tsx +++ b/apps/editor/app/settings/page.tsx @@ -32,6 +32,7 @@ export default async function Settings() { currentUsername={profile?.username ?? null} currentGithubUrl={profile?.githubUrl ?? null} currentXUrl={profile?.xUrl ?? null} + currentEmailNotifications={profile?.emailNotifications ?? true} connectedAccounts={connectedAccounts} /> ) diff --git a/apps/editor/app/terms/page.tsx b/apps/editor/app/terms/page.tsx new file mode 100644 index 00000000..5f48281c --- /dev/null +++ b/apps/editor/app/terms/page.tsx @@ -0,0 +1,177 @@ +import type { Metadata } from 'next' +import Link from 'next/link' + +export const metadata: Metadata = { + title: 'Terms of Service', + description: 'Terms of Service for Pascal Editor and the Pascal platform.', +} + +export default function TermsPage() { + return ( +
+
+
+ +
+
+ +
+
+

Terms of Service

+

+ Effective Date: February 20, 2026 +

+ +
+

1. Introduction

+

+ Welcome to Pascal Editor ("Editor") and the Pascal platform at pascal.app + ("Platform"), operated by Pascal Group Inc. ("we," "us," or "our"). + By accessing or using our services, you agree to these Terms of Service. +

+
+ +
+

2. The Editor and Platform

+

+ The Pascal Editor is open-source software released under the MIT License. + You may use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Editor software in accordance with the MIT License terms. +

+

+ The Pascal platform (pascal.app) and its associated services, including user accounts, + cloud storage, and project hosting, are proprietary services owned and operated by + Pascal Group Inc. These Terms govern your use of the Platform. +

+
+ +
+

3. Accounts and Authentication

+

+ To use certain features of the Platform, you must create an account. We use + Google OAuth and magic link email authentication through Supabase. You are + responsible for maintaining the security of your account credentials and for + all activities that occur under your account. +

+
+ +
+

4. Acceptable Use

+

You agree not to:

+
    +
  • Use the Platform for any unlawful purpose or in violation of any applicable laws
  • +
  • Upload, share, or distribute content that infringes intellectual property rights
  • +
  • Attempt to gain unauthorized access to the Platform or its systems
  • +
  • Interfere with or disrupt the Platform's infrastructure
  • +
  • Upload malicious code, viruses, or harmful content
  • +
  • Harass, abuse, or harm other users
  • +
  • Use the Platform to send spam or unsolicited communications
  • +
+
+ +
+

5. Your Content and Intellectual Property

+

+ You retain full ownership of all content, projects, and data you create or upload + to the Platform ("Your Content"). By using the Platform, you grant us a limited + license to store, display, and transmit Your Content solely to provide our services + to you. +

+

+ We do not claim any ownership rights over Your Content. You may export or delete + Your Content at any time. +

+
+ +
+

6. Platform Ownership

+

+ The Platform, including its design, features, and proprietary code, is owned by + Pascal Group Inc. and protected by intellectual property laws. While the Editor + source code is open-source under the MIT License, the Platform services, branding, + and infrastructure remain our proprietary property. +

+
+ +
+

7. Account Termination

+

+ We reserve the right to suspend or terminate your account if you violate these + Terms or engage in conduct that we determine is harmful to the Platform or other + users. You may also delete your account at any time by contacting us at{' '} + + support@pascal.app + + . +

+
+ +
+

8. Disclaimer of Warranties

+

+ THE PLATFORM IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTIES OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +

+

+ We do not warrant that the Platform will be uninterrupted, error-free, or free + of harmful components. +

+
+ +
+

9. Limitation of Liability

+

+ TO THE MAXIMUM EXTENT PERMITTED BY LAW, PASCAL GROUP INC. SHALL NOT BE LIABLE + FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, + INCLUDING LOSS OF DATA, PROFITS, OR GOODWILL, ARISING FROM YOUR USE OF THE + PLATFORM. +

+
+ +
+

10. Changes to Terms

+

+ We may update these Terms from time to time. We will notify you of material + changes by posting the updated Terms on the Platform. Your continued use of the + Platform after changes are posted constitutes your acceptance of the revised Terms. +

+
+ +
+

11. Contact Us

+

+ If you have questions about these Terms, please contact us at{' '} + + support@pascal.app + + . +

+
+
+
+
+ ) +} diff --git a/apps/editor/features/community/components/settings-page.tsx b/apps/editor/features/community/components/settings-page.tsx index c96c0478..ad6adaaa 100644 --- a/apps/editor/features/community/components/settings-page.tsx +++ b/apps/editor/features/community/components/settings-page.tsx @@ -5,7 +5,7 @@ import Link from 'next/link' import { ArrowLeft, Pencil } from 'lucide-react' import { useRef, useState } from 'react' import { authClient } from '../lib/auth/client' -import { updateUsername, updateProfile, uploadAvatar } from '../lib/auth/actions' +import { updateUsername, updateProfile, uploadAvatar, updateEmailNotifications } from '../lib/auth/actions' function GoogleIcon({ className }: { className?: string }) { return ( @@ -40,6 +40,7 @@ interface SettingsPageProps { currentUsername: string | null currentGithubUrl: string | null currentXUrl: string | null + currentEmailNotifications: boolean connectedAccounts: { providerId: string; accountId: string }[] } @@ -48,6 +49,7 @@ export function SettingsPage({ currentUsername, currentGithubUrl, currentXUrl, + currentEmailNotifications, connectedAccounts, }: SettingsPageProps) { const [username, setUsername] = useState(currentUsername ?? '') @@ -67,6 +69,8 @@ export function SettingsPage({ type: 'success' | 'error' text: string } | null>(null) + const [emailNotifications, setEmailNotifications] = useState(currentEmailNotifications) + const [isSavingNotifications, setIsSavingNotifications] = useState(false) const isGoogleConnected = connectedAccounts.some((a) => a.providerId === 'google') const initials = currentUsername @@ -149,6 +153,14 @@ export function SettingsPage({ (githubUrl.trim() || '') !== (currentGithubUrl ?? '') || (xUrl.trim() || '') !== (currentXUrl ?? '') + const handleToggleEmailNotifications = async () => { + const newValue = !emailNotifications + setEmailNotifications(newValue) + setIsSavingNotifications(true) + await updateEmailNotifications(newValue) + setIsSavingNotifications(false) + } + return (
@@ -305,6 +317,37 @@ export function SettingsPage({
+ {/* Notifications Section */} +
+

Notifications

+
+
+
+
Email notifications
+

+ Receive emails about new features and updates. +

+
+ +
+
+
+ {/* Social Links Section */}

Social Links

diff --git a/apps/editor/features/community/components/sign-in-dialog.tsx b/apps/editor/features/community/components/sign-in-dialog.tsx index d5c7067b..d5d0d2de 100644 --- a/apps/editor/features/community/components/sign-in-dialog.tsx +++ b/apps/editor/features/community/components/sign-in-dialog.tsx @@ -1,5 +1,6 @@ 'use client' +import Link from 'next/link' import { Mail, X } from 'lucide-react' import { useState } from 'react' import { authClient } from '../lib/auth/client' @@ -218,7 +219,15 @@ export function SignInDialog({ open, onOpenChange }: SignInDialogProps) {

- Sign in with Google or receive a magic link via email. + By signing in, you agree to our{' '} + + Terms of Service + {' '} + and{' '} + + Privacy Policy + + .

)} diff --git a/apps/editor/features/community/lib/auth/actions.ts b/apps/editor/features/community/lib/auth/actions.ts index b49b61b5..756b4b13 100644 --- a/apps/editor/features/community/lib/auth/actions.ts +++ b/apps/editor/features/community/lib/auth/actions.ts @@ -122,6 +122,7 @@ export async function getUserProfile(): Promise<{ username: string | null githubUrl: string | null xUrl: string | null + emailNotifications: boolean } | null> { const session = await getSession() if (!session?.user) return null @@ -131,6 +132,7 @@ export async function getUserProfile(): Promise<{ username: schema.users.username, githubUrl: schema.users.githubUrl, xUrl: schema.users.xUrl, + emailNotifications: schema.users.emailNotifications, }) .from(schema.users) .where(eq(schema.users.id, session.user.id)) @@ -278,3 +280,23 @@ export async function uploadAvatar( revalidatePath('/settings') return { success: true, imageUrl } } + +/** + * Update the current user's email notification preference + */ +export async function updateEmailNotifications( + enabled: boolean, +): Promise<{ success: boolean; error?: string }> { + const session = await getSession() + if (!session?.user) { + return { success: false, error: 'Not authenticated' } + } + + await db + .update(schema.users) + .set({ emailNotifications: enabled }) + .where(eq(schema.users.id, session.user.id)) + + revalidatePath('/settings') + return { success: true } +} diff --git a/packages/db/src/schema/auth/users.ts b/packages/db/src/schema/auth/users.ts index ac51834b..820f7134 100644 --- a/packages/db/src/schema/auth/users.ts +++ b/packages/db/src/schema/auth/users.ts @@ -19,6 +19,8 @@ export const users = pgTable( githubUrl: t.text('github_url'), /** X/Twitter profile URL */ xUrl: t.text('x_url'), + /** Whether the user wants to receive email notifications about new features and updates */ + emailNotifications: t.boolean('email_notifications').notNull().default(true), role: userRoles('role').notNull().default('user'), banned: t.boolean('banned').notNull().default(false), banReason: t.text('ban_reason'), diff --git a/supabase/migrations/20260220000001_add-email-notifications.sql b/supabase/migrations/20260220000001_add-email-notifications.sql new file mode 100644 index 00000000..a91ce24e --- /dev/null +++ b/supabase/migrations/20260220000001_add-email-notifications.sql @@ -0,0 +1 @@ +ALTER TABLE "auth_users" ADD COLUMN "email_notifications" boolean NOT NULL DEFAULT true;