commit 37f769842b8f265c4cd90577807378c06da062a8 Author: AppCakes Date: Fri May 29 12:21:03 2026 +0000 build: 55712324-398e-4334-a254-fc30c84b2e47 diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz new file mode 100644 index 0000000..1c59e8f Binary files /dev/null and b/.yarn/install-state.gz differ diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..48354a3 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,101 @@ +# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof + +# Cordova plugins for Capacitor +capacitor-cordova-android-plugins + +# Copied web assets +app/src/main/assets/public + +# Generated Config files +app/src/main/assets/capacitor.config.json +app/src/main/assets/capacitor.plugins.json +app/src/main/res/xml/config.xml diff --git a/android/app/.gitignore b/android/app/.gitignore new file mode 100644 index 0000000..043df80 --- /dev/null +++ b/android/app/.gitignore @@ -0,0 +1,2 @@ +/build/* +!/build/.npmkeep diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..11930a8 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.android.application' + +android { + namespace = "io.ionic.starter" + compileSdk = rootProject.ext.compileSdkVersion + defaultConfig { + applicationId "com.habitmode.llemba.com" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + manifestPlaceholders = [googleMapsApiKey: System.getenv("VITE_GOOGLE_MAPS_KEY") ?: ""] + aaptOptions { + // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. + // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 + ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +repositories { + flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" + implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" + implementation project(':capacitor-android') + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" + implementation project(':capacitor-cordova-android-plugins') +} + +apply from: 'capacitor.build.gradle' + +try { + def servicesJSON = file('google-services.json') + if (servicesJSON.text) { + apply plugin: 'com.google.gms.google-services' + } +} catch(Exception e) { + logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") +} diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java new file mode 100644 index 0000000..f2c2217 --- /dev/null +++ b/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.app", appContext.getPackageName()); + } +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ea0b713 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/io/ionic/starter/MainActivity.java b/android/app/src/main/java/io/ionic/starter/MainActivity.java new file mode 100644 index 0000000..73e3a98 --- /dev/null +++ b/android/app/src/main/java/io/ionic/starter/MainActivity.java @@ -0,0 +1,5 @@ +package io.ionic.starter; + +import com.getcapacitor.BridgeActivity; + +public class MainActivity extends BridgeActivity {} diff --git a/android/app/src/main/res/drawable-land-hdpi/splash.png b/android/app/src/main/res/drawable-land-hdpi/splash.png new file mode 100644 index 0000000..e31573b Binary files /dev/null and b/android/app/src/main/res/drawable-land-hdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-land-mdpi/splash.png b/android/app/src/main/res/drawable-land-mdpi/splash.png new file mode 100644 index 0000000..f7a6492 Binary files /dev/null and b/android/app/src/main/res/drawable-land-mdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-land-xhdpi/splash.png b/android/app/src/main/res/drawable-land-xhdpi/splash.png new file mode 100644 index 0000000..8077255 Binary files /dev/null and b/android/app/src/main/res/drawable-land-xhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/android/app/src/main/res/drawable-land-xxhdpi/splash.png new file mode 100644 index 0000000..14c6c8f Binary files /dev/null and b/android/app/src/main/res/drawable-land-xxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/android/app/src/main/res/drawable-land-xxxhdpi/splash.png new file mode 100644 index 0000000..244ca25 Binary files /dev/null and b/android/app/src/main/res/drawable-land-xxxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-port-hdpi/splash.png b/android/app/src/main/res/drawable-port-hdpi/splash.png new file mode 100644 index 0000000..74faaa5 Binary files /dev/null and b/android/app/src/main/res/drawable-port-hdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-port-mdpi/splash.png b/android/app/src/main/res/drawable-port-mdpi/splash.png new file mode 100644 index 0000000..e944f4a Binary files /dev/null and b/android/app/src/main/res/drawable-port-mdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-port-xhdpi/splash.png b/android/app/src/main/res/drawable-port-xhdpi/splash.png new file mode 100644 index 0000000..564a82f Binary files /dev/null and b/android/app/src/main/res/drawable-port-xhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/android/app/src/main/res/drawable-port-xxhdpi/splash.png new file mode 100644 index 0000000..bfabe68 Binary files /dev/null and b/android/app/src/main/res/drawable-port-xxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/android/app/src/main/res/drawable-port-xxxhdpi/splash.png new file mode 100644 index 0000000..6929071 Binary files /dev/null and b/android/app/src/main/res/drawable-port-xxxhdpi/splash.png differ diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/splash.png b/android/app/src/main/res/drawable/splash.png new file mode 100644 index 0000000..f7a6492 Binary files /dev/null and b/android/app/src/main/res/drawable/splash.png differ diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b5ad138 --- /dev/null +++ b/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..c023e50 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..2127973 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..b441f37 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..72905b8 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..8ed0605 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..9502e47 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..4d1e077 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..df0f158 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..853db04 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..6cdf97c Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..2960cbb Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8e3093a Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..46de6e2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..d2ea9ab Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..a40d73e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..b2eaa17 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + app + app + io.ionic.starter + io.ionic.starter + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..be874e5 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/xml/file_paths.xml b/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..bd0c4d8 --- /dev/null +++ b/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java new file mode 100644 index 0000000..0297327 --- /dev/null +++ b/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..f8f0e43 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,29 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.13.0' + classpath 'com.google.gms:google-services:4.4.4' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +apply from: "variables.gradle" + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..2e87c52 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..1b33c55 Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7705927 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000..23d15a9 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..5eed7ee --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..3b4431d --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,5 @@ +include ':app' +include ':capacitor-cordova-android-plugins' +project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') + +apply from: 'capacitor.settings.gradle' \ No newline at end of file diff --git a/android/variables.gradle b/android/variables.gradle new file mode 100644 index 0000000..920c6cc --- /dev/null +++ b/android/variables.gradle @@ -0,0 +1,19 @@ +ext { + minSdkVersion = 24 + compileSdkVersion = 36 + targetSdkVersion = 36 + androidxActivityVersion = '1.11.0' + androidxAppCompatVersion = '1.7.1' + androidxCoordinatorLayoutVersion = '1.3.0' + androidxCoreVersion = '1.17.0' + androidxFragmentVersion = '1.8.9' + coreSplashScreenVersion = '1.2.0' + androidxWebkitVersion = '1.14.0' + junitVersion = '4.13.2' + androidxJunitVersion = '1.3.0' + androidxEspressoCoreVersion = '3.7.0' + cordovaAndroidVersion = '14.0.1' + rgcfaIncludeGoogle = true + androidxCredentialsVersion = '1.3.0' + javaVersion = JavaVersion.VERSION_17 +} \ No newline at end of file diff --git a/capacitor.config.ts b/capacitor.config.ts new file mode 100644 index 0000000..9252ce9 --- /dev/null +++ b/capacitor.config.ts @@ -0,0 +1,15 @@ +import type { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'com.habitmode.llemba.com', + appName: 'HabitMode', + webDir: 'dist', + plugins: { + FirebaseAuthentication: { + skipNativeAuth: false, + providers: ["google.com", "apple.com"], + }, + }, +}; + +export default config; diff --git a/codemagic.yaml b/codemagic.yaml new file mode 100644 index 0000000..96bfc2b --- /dev/null +++ b/codemagic.yaml @@ -0,0 +1,380 @@ +workflows: + android-debug: + name: Android – Debug APK + max_build_duration: 45 + instance_type: mac_mini_m2 + environment: + node: 22 + java: 21 + cache: + cache_paths: + - $HOME/.gradle/caches + - $HOME/.gradle/wrapper + scripts: + # Template ships with optional native plugins (maps, push, firebase auth) that most apps + # won't use. Keeping unused plugins in package.json adds SPM/Gradle deps and bloats the + # build. Strip any that have no import in src/ so only actually-used plugins are compiled. + - name: Strip unused scaffold packages + script: | + for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do + if ! grep -rq "$pkg" src/; then + node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));" + echo "Stripped $pkg (not used in src/)" + else + echo "Keeping $pkg (used in src/)" + fi + done + # Delete package-lock.json (generated if user ever ran npm locally) so it doesn't + # conflict with yarn.lock. --legacy-peer-deps silences peer dep warnings from older + # Ionic/React packages that haven't updated their peerDependencies for React 18/19. + - name: Install dependencies + script: rm -f package-lock.json && npm install --legacy-peer-deps + - name: Build web assets + script: npm run build + # Firebase config files are injected as base64 env vars by the Appcakes backend at + # build time. Decode them here so cap sync and Gradle can pick them up. Optional — + # builds without Firebase still succeed; native Firebase SDK just won't initialise. + - name: Write Firebase config + script: | + if [ -n "$GOOGLE_SERVICES_JSON" ]; then + echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json + echo "Wrote google-services.json ($(wc -c < android/app/google-services.json) bytes)" + else + echo "GOOGLE_SERVICES_JSON is empty — skipping" + fi + if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then + echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist + echo "Wrote GoogleService-Info.plist ($(wc -c < ios/App/App/GoogleService-Info.plist) bytes)" + fi + # cap sync copies the built web assets (dist/) into android/app/src/main/assets/public/ + # and updates Capacitor plugin registrations in MainActivity. Must run after npm build. + - name: Capacitor sync + script: npx cap sync android + - name: Set up debug keystore + script: | + if [ -n "$CM_DEBUG_KEYSTORE" ]; then + echo "$CM_DEBUG_KEYSTORE" | base64 --decode > /tmp/debug-keystore.p12 + fi + - name: Build debug APK + script: | + cd android + if [ -n "$CM_DEBUG_KEYSTORE" ]; then + ./gradlew assembleDebug \ + -Pandroid.injected.signing.store.file=/tmp/debug-keystore.p12 \ + -Pandroid.injected.signing.store.password=$CM_DEBUG_KEYSTORE_PASSWORD \ + -Pandroid.injected.signing.key.alias=debug \ + -Pandroid.injected.signing.key.password=$CM_DEBUG_KEYSTORE_PASSWORD \ + --no-daemon + else + ./gradlew assembleDebug --no-daemon + fi + artifacts: + - android/app/build/outputs/apk/debug/app-debug.apk + + android-release-apk: + name: Android – Release APK + max_build_duration: 45 + instance_type: mac_mini_m2 + environment: + node: 22 + java: 21 + cache: + cache_paths: + - $HOME/.gradle/caches + - $HOME/.gradle/wrapper + scripts: + - name: Strip unused scaffold packages + script: | + for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do + if ! grep -rq "$pkg" src/; then + node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));" + echo "Stripped $pkg (not used in src/)" + else + echo "Keeping $pkg (used in src/)" + fi + done + - name: Install dependencies + script: rm -f package-lock.json && npm install --legacy-peer-deps + - name: Build web assets + script: npm run build + - name: Write Firebase config + script: | + if [ -n "$GOOGLE_SERVICES_JSON" ]; then + echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json + fi + - name: Capacitor sync + script: npx cap sync android + - name: Set up keystore + script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks + - name: Build Release APK + script: | + cd android + ./gradlew assembleRelease \ + -Pandroid.injected.signing.store.file=/tmp/keystore.jks \ + -Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \ + -Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \ + -Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \ + --no-daemon + artifacts: + - android/app/build/outputs/apk/release/app-release.apk + + android-release-aab: + name: Android – Release AAB + max_build_duration: 45 + instance_type: mac_mini_m2 + environment: + node: 22 + java: 21 + cache: + cache_paths: + - $HOME/.gradle/caches + - $HOME/.gradle/wrapper + scripts: + - name: Strip unused scaffold packages + script: | + for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do + if ! grep -rq "$pkg" src/; then + node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));" + echo "Stripped $pkg (not used in src/)" + else + echo "Keeping $pkg (used in src/)" + fi + done + - name: Install dependencies + script: rm -f package-lock.json && npm install --legacy-peer-deps + - name: Build web assets + script: npm run build + - name: Write Firebase config + script: | + if [ -n "$GOOGLE_SERVICES_JSON" ]; then + echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json + fi + - name: Capacitor sync + script: npx cap sync android + - name: Set up keystore + script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks + - name: Build Release AAB + script: | + cd android + ./gradlew bundleRelease \ + -Pandroid.injected.signing.store.file=/tmp/keystore.jks \ + -Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \ + -Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \ + -Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \ + --no-daemon + artifacts: + - android/app/build/outputs/bundle/release/app-release.aab + + android-release: + name: Android – Publish to Google Play + max_build_duration: 60 + instance_type: mac_mini_m2 + environment: + node: 22 + java: 21 + cache: + cache_paths: + - $HOME/.gradle/caches + - $HOME/.gradle/wrapper + scripts: + - name: Strip unused scaffold packages + script: | + for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do + if ! grep -rq "$pkg" src/; then + node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));" + echo "Stripped $pkg (not used in src/)" + else + echo "Keeping $pkg (used in src/)" + fi + done + - name: Install dependencies + script: rm -f package-lock.json && npm install --legacy-peer-deps + - name: Build web assets + script: npm run build + - name: Write Firebase config + script: | + if [ -n "$GOOGLE_SERVICES_JSON" ]; then + echo "$GOOGLE_SERVICES_JSON" | base64 --decode > android/app/google-services.json + fi + - name: Capacitor sync + script: npx cap sync android + - name: Set up keystore + script: echo "$CM_KEYSTORE" | base64 --decode > /tmp/keystore.jks + - name: Build Release AAB + script: | + cd android + ./gradlew bundleRelease \ + -Pandroid.injected.signing.store.file=/tmp/keystore.jks \ + -Pandroid.injected.signing.store.password=$CM_KEYSTORE_PASSWORD \ + -Pandroid.injected.signing.key.alias=$CM_KEY_ALIAS \ + -Pandroid.injected.signing.key.password=$CM_KEY_PASSWORD \ + --no-daemon + publishing: + google_play: + credentials: $GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS + track: $GOOGLE_PLAY_TRACK + submit_as_draft: false + artifacts: + - android/app/build/outputs/bundle/release/app-release.aab + + ios-debug: + name: iOS – Debug IPA + max_build_duration: 60 + instance_type: mac_mini_m2 + environment: + node: 22 + xcode: latest + scripts: + - name: Strip unused scaffold packages + script: | + for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do + if ! grep -rq "$pkg" src/; then + node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));" + echo "Stripped $pkg (not used in src/)" + else + echo "Keeping $pkg (used in src/)" + fi + done + - name: Install dependencies + script: rm -f package-lock.json && npm install --legacy-peer-deps + - name: Build web assets + script: npm run build + # First sync: regenerates CapApp-SPM/Package.swift with the correct plugin list. + # Note: this project uses Capacitor SPM (no CocoaPods/Podfile), so cap sync replaces + # pod install — it regenerates CapApp-SPM/Package.swift and copies web assets. + - name: Capacitor sync + script: npx cap sync ios + # Decode Firebase plist to disk so it is present during the second sync. + - name: Write Firebase config + script: | + if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then + echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist + fi + # Second sync ensures CapApp-SPM/Package.swift is consistent after the plist write. + - name: Capacitor sync + script: npx cap sync ios + # Run configure_xcode.rb after both syncs so its xcodeproj changes (plist file + # reference + SPM product linking) are not overwritten by a subsequent cap sync. + # configure_xcode.rb is stamped fresh from template on every build push. + - name: Configure Xcode project + script: ruby configure_xcode.rb + - name: Set up signing + script: | + keychain initialize + if [ -n "$APP_STORE_CONNECT_PRIVATE_KEY" ]; then + app-store-connect fetch-signing-files \ + $(xcode-project detect-bundle-id) \ + --type IOS_APP_DEVELOPMENT \ + --certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \ + --issuer-id=$APP_STORE_CONNECT_ISSUER_ID \ + --key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \ + --create + xcode-project use-profiles + elif [ -n "$CM_CERTIFICATE" ]; then + echo "$CM_CERTIFICATE" | base64 --decode > /tmp/cert.p12 + keychain add-certificates \ + --certificate /tmp/cert.p12 \ + --certificate-password "$CM_CERTIFICATE_PASSWORD" + mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" + echo "$CM_PROVISIONING_PROFILE" | base64 --decode > "$HOME/Library/MobileDevice/Provisioning Profiles/profile.mobileprovision" + xcode-project use-profiles + else + echo "No signing credentials configured — build will fail at signing" + exit 1 + fi + # --workspace: Capacitor SPM projects require workspace context for Xcode to resolve + # the CapApp-SPM local package. Using --project causes xcodebuild to fail on + # Xcode 26+ because SPM resolution requires workspace scope. App.xcworkspace is a + # static file committed to the template (not generated by cap sync) that wraps + # App.xcodeproj for this purpose. + # --no-show-build-settings: xcode-project runs xcodebuild -showBuildSettings as a + # pre-build diagnostic. Xcode 26 fails this step for SPM projects even with + # --workspace, so we skip it. The actual archive step still surfaces real errors. + # --disable-xcpretty: xcpretty reformats and filters xcodebuild output, swallowing + # the actual error lines when a build fails. Raw output makes failures debuggable. + - name: Build IPA + script: | + xcode-project build-ipa \ + --workspace ios/App/App.xcworkspace \ + --scheme App \ + --no-show-build-settings \ + --disable-xcpretty + artifacts: + - build/ios/ipa/*.ipa + + ios-release: + name: iOS – Release IPA + max_build_duration: 90 + instance_type: mac_mini_m2 + environment: + node: 22 + xcode: latest + scripts: + - name: Strip unused scaffold packages + script: | + for pkg in "@capacitor/google-maps" "@capacitor/push-notifications" "@capacitor-firebase/authentication"; do + if ! grep -rq "$pkg" src/; then + node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));delete p.dependencies['${pkg}'];fs.writeFileSync('package.json',JSON.stringify(p,null,2));" + echo "Stripped $pkg (not used in src/)" + else + echo "Keeping $pkg (used in src/)" + fi + done + - name: Install dependencies + script: rm -f package-lock.json && npm install --legacy-peer-deps + - name: Build web assets + script: npm run build + - name: Capacitor sync + script: npx cap sync ios + - name: Configure Xcode project + script: | + if [ -n "$GOOGLE_SERVICE_INFO_PLIST" ]; then + echo "$GOOGLE_SERVICE_INFO_PLIST" | base64 --decode > ios/App/App/GoogleService-Info.plist + fi + - name: Capacitor sync + script: npx cap sync ios + - name: Configure Xcode project + script: ruby configure_xcode.rb + - name: Set up signing + script: | + keychain initialize + if [ -n "$APP_STORE_CONNECT_PRIVATE_KEY" ]; then + app-store-connect fetch-signing-files \ + $(xcode-project detect-bundle-id) \ + --type IOS_APP_STORE \ + --certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \ + --issuer-id=$APP_STORE_CONNECT_ISSUER_ID \ + --key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \ + --create + xcode-project use-profiles + else + echo "$CM_CERTIFICATE" | base64 --decode > /tmp/cert.p12 + keychain add-certificates \ + --certificate /tmp/cert.p12 \ + --certificate-password "$CM_CERTIFICATE_PASSWORD" + mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" + echo "$CM_PROVISIONING_PROFILE" | base64 --decode > "$HOME/Library/MobileDevice/Provisioning Profiles/profile.mobileprovision" + xcode-project use-profiles + fi + # See ios-debug Build IPA comments for --workspace, --no-show-build-settings, + # and --disable-xcpretty rationale — same reasons apply here. + - name: Build IPA + script: | + xcode-project build-ipa \ + --workspace ios/App/App.xcworkspace \ + --scheme App \ + --config Release \ + --no-show-build-settings \ + --disable-xcpretty + - name: Publish + script: | + if [ "$SUBMIT_TO_TESTFLIGHT" = "true" ] || [ "$SUBMIT_TO_APP_STORE" = "true" ]; then + app-store-connect publish \ + --certificate-key=@env:APP_STORE_CONNECT_PRIVATE_KEY \ + --issuer-id=$APP_STORE_CONNECT_ISSUER_ID \ + --key-id=$APP_STORE_CONNECT_KEY_IDENTIFIER \ + --submit-to-testflight=$SUBMIT_TO_TESTFLIGHT \ + --submit-to-app-store=$SUBMIT_TO_APP_STORE + fi + artifacts: + - build/ios/ipa/*.ipa diff --git a/configure_xcode.rb b/configure_xcode.rb new file mode 100644 index 0000000..65f9f3f --- /dev/null +++ b/configure_xcode.rb @@ -0,0 +1,81 @@ +#!/usr/bin/env ruby +# configure_xcode.rb — stamped fresh from template on every build (see gitea.service.ts) +# Handles all Xcode project modifications at build time: +# 1. Adds GoogleService-Info.plist to the App target if present +# 2. Scans Swift sources for Firebase imports and links matching SPM products + +require 'xcodeproj' + +PROJECT_PATH = 'ios/App/App.xcodeproj' +TARGET_NAME = 'App' +PLIST_SRC = 'ios/App/App/GoogleService-Info.plist' + +project = Xcodeproj::Project.open(PROJECT_PATH) +target = project.targets.find { |t| t.name == TARGET_NAME } +abort "Target '#{TARGET_NAME}' not found in #{PROJECT_PATH}" unless target + +# ── 1. GoogleService-Info.plist ─────────────────────────────────────────────── +if File.exist?(PLIST_SRC) + app_group = project.main_group['App'] + unless app_group&.files&.find { |f| f.path == 'GoogleService-Info.plist' } + file_ref = app_group.new_reference('GoogleService-Info.plist') + target.resources_build_phase.add_file_reference(file_ref) + puts "Added GoogleService-Info.plist to target" + end +end + +# ── 2. Scan Swift sources for Firebase imports ──────────────────────────────── +swift_files = Dir['ios/App/App/**/*.swift'] +firebase_imports = swift_files + .flat_map { |f| File.readlines(f) rescue [] } + .grep(/^\s*import\s+Firebase\w+/) + .map { |l| l.strip.split(/\s+/).last } + .uniq + .sort + +if firebase_imports.empty? + puts "No Firebase imports found — skipping SPM linking" + project.save + exit 0 +end + +puts "Firebase imports detected: #{firebase_imports.join(', ')}" + +# ── 3. Find firebase-ios-sdk package reference ──────────────────────────────── +firebase_pkg = project.root_object.package_references.find do |p| + p.respond_to?(:repositoryURL) && p.repositoryURL.to_s.include?('firebase-ios-sdk') +end + +unless firebase_pkg + warn "WARNING: firebase-ios-sdk not registered in project — cannot auto-link products" + project.save + exit 0 +end + +# ── 4. Link helper ──────────────────────────────────────────────────────────── +def link_spm_product(project, target, package_ref, product_name) + return if target.package_product_dependencies.any? { |d| d.product_name == product_name } + + dep = project.new(Xcodeproj::Project::Object::XCSwiftPackageProductDependency) + dep.product_name = product_name + dep.package = package_ref + target.package_product_dependencies << dep + + build_file = project.new(Xcodeproj::Project::Object::PBXBuildFile) + build_file.product_ref = dep + target.frameworks_build_phase.files << build_file + + puts "Linked #{product_name}" +end + +# FirebaseCore is a prerequisite for all other Firebase products +link_spm_product(project, target, firebase_pkg, 'FirebaseCore') + +firebase_imports.each do |product| + next if product == 'FirebaseCore' # already linked above + next if product == 'Firebase' # umbrella import, not a linkable product + link_spm_product(project, target, firebase_pkg, product) +end + +project.save +puts "Xcode project configured" diff --git a/index.html b/index.html new file mode 100644 index 0000000..e19807b --- /dev/null +++ b/index.html @@ -0,0 +1,28 @@ + + + + + Ionic App + + + + + + + + + + + + + + + + +
+ + + diff --git a/ionic.config.json b/ionic.config.json new file mode 100644 index 0000000..a1d1b22 --- /dev/null +++ b/ionic.config.json @@ -0,0 +1,5 @@ +{ + "name": "app", + "integrations": {}, + "type": "react-vite" +} diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..f470299 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,13 @@ +App/build +App/Pods +App/output +App/App/public +DerivedData +xcuserdata + +# Cordova plugins for Capacitor +capacitor-cordova-ios-plugins + +# Generated Config files +App/App/capacitor.config.json +App/App/config.xml diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj new file mode 100644 index 0000000..29de864 --- /dev/null +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -0,0 +1,380 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; + 4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */ = {isa = PBXBuildFile; productRef = 4D22ABE82AF431CB00220026 /* CapApp-SPM */; }; + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; + 504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; }; + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; + 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; + 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; + 504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1A4EB3C21FED79650016851F /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = ""; }; + 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; + 958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 504EC3011FED79650016851F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D22ABE92AF431CB00220026 /* CapApp-SPM in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 504EC2FB1FED79650016851F = { + isa = PBXGroup; + children = ( + 958DCC722DB07C7200EA8C5F /* debug.xcconfig */, + 504EC3061FED79650016851F /* App */, + 504EC3051FED79650016851F /* Products */, + ); + sourceTree = ""; + }; + 504EC3051FED79650016851F /* Products */ = { + isa = PBXGroup; + children = ( + 504EC3041FED79650016851F /* App.app */, + ); + name = Products; + sourceTree = ""; + }; + 504EC3061FED79650016851F /* App */ = { + isa = PBXGroup; + children = ( + 50379B222058CBB4000EE86E /* capacitor.config.json */, + 504EC3071FED79650016851F /* AppDelegate.swift */, + 504EC30B1FED79650016851F /* Main.storyboard */, + 504EC30E1FED79650016851F /* Assets.xcassets */, + 504EC3101FED79650016851F /* LaunchScreen.storyboard */, + 504EC3131FED79650016851F /* Info.plist */, + 1A4EB3C21FED79650016851F /* App.entitlements */, + 2FAD9762203C412B000D30F8 /* config.xml */, + 50B271D01FEDC1A000F3C39B /* public */, + ); + path = App; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 504EC3031FED79650016851F /* App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */; + buildPhases = ( + 504EC3001FED79650016851F /* Sources */, + 504EC3011FED79650016851F /* Frameworks */, + 504EC3021FED79650016851F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = App; + packageProductDependencies = ( + 4D22ABE82AF431CB00220026 /* CapApp-SPM */, + ); + productName = App; + productReference = 504EC3041FED79650016851F /* App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 504EC2FC1FED79650016851F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0920; + TargetAttributes = { + 504EC3031FED79650016851F = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 504EC2FB1FED79650016851F; + packageReferences = ( + D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */, + ); + productRefGroup = 504EC3051FED79650016851F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 504EC3031FED79650016851F /* App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 504EC3021FED79650016851F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */, + 50B271D11FEDC1A000F3C39B /* public in Resources */, + 504EC30F1FED79650016851F /* Assets.xcassets in Resources */, + 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, + 504EC30D1FED79650016851F /* Main.storyboard in Resources */, + 2FAD9763203C412B000D30F8 /* config.xml in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 504EC3001FED79650016851F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 504EC30B1FED79650016851F /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC30C1FED79650016851F /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 504EC3101FED79650016851F /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 504EC3111FED79650016851F /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 504EC3141FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 504EC3151FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 504EC3171FED79650016851F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = App/App.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; + PRODUCT_BUNDLE_IDENTIFIER = com.habitmode.llemba.com; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 504EC3181FED79650016851F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = App/App.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = App/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.habitmode.llemba.com; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3141FED79650016851F /* Debug */, + 504EC3151FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 504EC3171FED79650016851F /* Debug */, + 504EC3181FED79650016851F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "CapApp-SPM"; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4D22ABE82AF431CB00220026 /* CapApp-SPM */ = { + isa = XCSwiftPackageProductDependency; + package = D4C12C0A2AAA248700AAC8A2 /* XCLocalSwiftPackageReference "CapApp-SPM" */; + productName = "CapApp-SPM"; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 504EC2FC1FED79650016851F /* Project object */; +} diff --git a/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/App/App.xcworkspace/contents.xcworkspacedata b/ios/App/App.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b3795ee --- /dev/null +++ b/ios/App/App.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/App/App/App.entitlements b/ios/App/App/App.entitlements new file mode 100644 index 0000000..50d1f83 --- /dev/null +++ b/ios/App/App/App.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.developer.applesignin + + Default + + aps-environment + production + + \ No newline at end of file diff --git a/ios/App/App/AppDelegate.swift b/ios/App/App/AppDelegate.swift new file mode 100644 index 0000000..2d56b7e --- /dev/null +++ b/ios/App/App/AppDelegate.swift @@ -0,0 +1,48 @@ +import UIKit +import Capacitor +import FirebaseCore +import FirebaseMessaging + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FirebaseApp.configure() + return true + } + + func applicationWillResignActive(_ application: UIApplication) {} + + func applicationDidEnterBackground(_ application: UIApplication) {} + + func applicationWillEnterForeground(_ application: UIApplication) {} + + func applicationDidBecomeActive(_ application: UIApplication) {} + + func applicationWillTerminate(_ application: UIApplication) {} + + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { + return ApplicationDelegateProxy.shared.application(app, open: url, options: options) + } + + func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) + } + + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + Messaging.messaging().apnsToken = deviceToken + Messaging.messaging().token { token, error in + if let error = error { + NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error) + } else if let token = token { + NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: token) + } + } + } + + func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { + NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error) + } +} diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000..adf6ba0 Binary files /dev/null and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9b7d382 --- /dev/null +++ b/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "AppIcon-512@2x.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/App/App/Assets.xcassets/Contents.json b/ios/App/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/ios/App/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json b/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json new file mode 100644 index 0000000..d7d96a6 --- /dev/null +++ b/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "splash-2732x2732-2.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "splash-2732x2732-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "splash-2732x2732.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png new file mode 100644 index 0000000..33ea6c9 Binary files /dev/null and b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png differ diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png new file mode 100644 index 0000000..33ea6c9 Binary files /dev/null and b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png differ diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png new file mode 100644 index 0000000..33ea6c9 Binary files /dev/null and b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png differ diff --git a/ios/App/App/Base.lproj/LaunchScreen.storyboard b/ios/App/App/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..e7ae5d7 --- /dev/null +++ b/ios/App/App/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/App/App/Base.lproj/Main.storyboard b/ios/App/App/Base.lproj/Main.storyboard new file mode 100644 index 0000000..b44df7b --- /dev/null +++ b/ios/App/App/Base.lproj/Main.storyboard @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ios/App/App/Info.plist b/ios/App/App/Info.plist new file mode 100644 index 0000000..92aae05 --- /dev/null +++ b/ios/App/App/Info.plist @@ -0,0 +1,60 @@ + + + + + CAPACITOR_DEBUG + $(CAPACITOR_DEBUG) + CFBundleDevelopmentRegion + en + CFBundleDisplayName + HabitMode + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CFBundleURLTypes + + + CFBundleURLSchemes + + com.googleusercontent.apps.1097913965629-pf48952i0uv9shgvie3dq11s18sd0t2p + + + + + diff --git a/ios/App/CapApp-SPM/.gitignore b/ios/App/CapApp-SPM/.gitignore new file mode 100644 index 0000000..3b29812 --- /dev/null +++ b/ios/App/CapApp-SPM/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/ios/App/CapApp-SPM/Package.swift b/ios/App/CapApp-SPM/Package.swift new file mode 100644 index 0000000..5cde8b6 --- /dev/null +++ b/ios/App/CapApp-SPM/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 5.9 +import PackageDescription + +// DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands +let package = Package( + name: "CapApp-SPM", + platforms: [.iOS(.v15)], + products: [ + .library( + name: "CapApp-SPM", + targets: ["CapApp-SPM"]) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0") + ], + targets: [ + .target( + name: "CapApp-SPM", + dependencies: [ + .product(name: "Capacitor", package: "capacitor-swift-pm"), + .product(name: "Cordova", package: "capacitor-swift-pm") + ] + ) + ] +) diff --git a/ios/App/CapApp-SPM/README.md b/ios/App/CapApp-SPM/README.md new file mode 100644 index 0000000..03964db --- /dev/null +++ b/ios/App/CapApp-SPM/README.md @@ -0,0 +1,5 @@ +# CapApp-SPM + +This package is used to host SPM dependencies for your Capacitor project + +Do not modify the contents of it or there may be unintended consequences. diff --git a/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift b/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift new file mode 100644 index 0000000..945afec --- /dev/null +++ b/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift @@ -0,0 +1 @@ +public let isCapacitorApp = true diff --git a/ios/debug.xcconfig b/ios/debug.xcconfig new file mode 100644 index 0000000..53ce18d --- /dev/null +++ b/ios/debug.xcconfig @@ -0,0 +1 @@ +CAPACITOR_DEBUG = true diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b0740fb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3404 @@ +{ + "name": "app", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "app", + "version": "0.0.1", + "dependencies": { + "@capacitor-firebase/authentication": "^7.0.0", + "@capacitor/android": "8.3.4", + "@capacitor/app": "8.1.0", + "@capacitor/core": "8.3.4", + "@capacitor/google-maps": "^8.0.0", + "@capacitor/haptics": "8.0.2", + "@capacitor/ios": "8.3.4", + "@capacitor/keyboard": "8.0.3", + "@capacitor/push-notifications": "^8.1.1", + "@capacitor/status-bar": "8.0.2", + "@ionic/pwa-elements": "^3.4.0", + "@ionic/react": "^8.5.0", + "@ionic/react-router": "^8.5.0", + "@supabase/supabase-js": "^2.0.0", + "firebase": "^11.0.0", + "ionicons": "^7.4.0", + "katex": "^0.16.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-hook-form": "^7.54.0", + "react-katex": "^3.0.1", + "react-router": "^5.3.4", + "react-router-dom": "^5.3.4", + "zod": "^3.24.0", + "zustand": "^5.0.0" + }, + "devDependencies": { + "@capacitor/cli": "8.3.4", + "@types/katex": "^0.16.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@types/react-router": "^5.1.20", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react": "^4.0.1", + "terser": "^5.4.0", + "typescript": "~5.9.0", + "vite": "^5.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@capacitor-firebase/authentication": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@capacitor-firebase/authentication/-/authentication-7.5.0.tgz", + "integrity": "sha512-xPvKXWZhb0ZP/ysGYADWmFngQK82sPbRsZXE8b9W65MkFdoDc5df5O+gZTjw2wbVRFuUKqn07FHn9guxMg1iQw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/capawesome-team/" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/capawesome" + } + ], + "license": "Apache-2.0", + "peerDependencies": { + "@capacitor/core": ">=7.0.0", + "firebase": "^11.2.0" + }, + "peerDependenciesMeta": { + "firebase": { + "optional": true + } + } + }, + "node_modules/@capacitor/android": { + "version": "8.3.4", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^8.3.0" + } + }, + "node_modules/@capacitor/app": { + "version": "8.1.0", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/cli": { + "version": "8.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/cli-framework-output": "^2.2.8", + "@ionic/utils-subprocess": "^3.0.1", + "@ionic/utils-terminal": "^2.3.5", + "commander": "^12.1.0", + "debug": "^4.4.0", + "env-paths": "^2.2.0", + "fs-extra": "^11.2.0", + "kleur": "^4.1.5", + "native-run": "^2.0.3", + "open": "^8.4.0", + "plist": "^3.1.0", + "prompts": "^2.4.2", + "rimraf": "^6.0.1", + "semver": "^7.6.3", + "tar": "^7.5.3", + "tslib": "^2.8.1", + "xml2js": "^0.6.2" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/fs-extra": { + "version": "11.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@capacitor/cli/node_modules/semver": { + "version": "7.8.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/core": { + "version": "8.3.4", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/google-maps": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@capacitor/google-maps/-/google-maps-8.0.1.tgz", + "integrity": "sha512-XSOyanbtOeO5KrSfoJOpbcSW4EXvfdxp+SUy6umdIGFeR2WJKNnPC6isXn+zjJgJH8kblT2X4fF0dxhZV8wLBg==", + "license": "MIT", + "dependencies": { + "@googlemaps/js-api-loader": "^2.0.2", + "@googlemaps/markerclusterer": "^2.6.2", + "@types/google.maps": "^3.58.1" + }, + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/haptics": { + "version": "8.0.2", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/ios": { + "version": "8.3.4", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^8.3.0" + } + }, + "node_modules/@capacitor/keyboard": { + "version": "8.0.3", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/push-notifications": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-8.1.1.tgz", + "integrity": "sha512-WqzjPKIbYbARMN+GC0XMAJcxJpUUzqgzS/Ny8RODLrro38pQhm3GXYwX2Mwd+LZlLY39rGImkCkrKyQSNfuikA==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/status-bar": { + "version": "8.0.2", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@firebase/ai": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.1.tgz", + "integrity": "sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.17.tgz", + "integrity": "sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.23.tgz", + "integrity": "sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/analytics": "0.10.17", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", + "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.1.tgz", + "integrity": "sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.26.tgz", + "integrity": "sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check": "0.10.1", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-compat": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", + "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app": "0.13.2", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz", + "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.28", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.28.tgz", + "integrity": "sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth": "1.10.8", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.18.tgz", + "integrity": "sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.10.tgz", + "integrity": "sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.20.tgz", + "integrity": "sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.11.tgz", + "integrity": "sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/database": "1.0.20", + "@firebase/database-types": "1.0.15", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.15.tgz", + "integrity": "sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.12.1" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.8.0.tgz", + "integrity": "sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.53", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.53.tgz", + "integrity": "sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/firestore": "4.8.0", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.9.tgz", + "integrity": "sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.26.tgz", + "integrity": "sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/functions": "0.12.9", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/installations": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.18.tgz", + "integrity": "sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.18.tgz", + "integrity": "sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.22.tgz", + "integrity": "sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.22.tgz", + "integrity": "sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/messaging": "0.12.22", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/performance": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.7.tgz", + "integrity": "sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.20.tgz", + "integrity": "sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.7", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.5.tgz", + "integrity": "sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.18.tgz", + "integrity": "sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.6.5", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/storage": { + "version": "0.13.14", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.14.tgz", + "integrity": "sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.24.tgz", + "integrity": "sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/storage": "0.13.14", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.1.tgz", + "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==", + "license": "Apache-2.0" + }, + "node_modules/@googlemaps/js-api-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-2.0.2.tgz", + "integrity": "sha512-bKVuTqatS8Jven5aFqVB7rCHF1VFEzpzyi0ruzO0GUR+A7m9oMqMgtnmpANj7kMYEvvhty8Fk7TnJ1MKjWHu+Q==", + "license": "Apache-2.0", + "dependencies": { + "@types/google.maps": "^3.53.1" + } + }, + "node_modules/@googlemaps/markerclusterer": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.6.2.tgz", + "integrity": "sha512-U6uVhq8iWhiIckA89sgRu8OK35mjd6/3CuoZKWakKEf0QmRRWpatlsPb3kqXkoWSmbcZkopRiI4dnW6DQSd7bQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/supercluster": "^7.1.3", + "fast-equals": "^5.2.2", + "supercluster": "^8.0.1" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.16", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.16.tgz", + "integrity": "sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/core": { + "version": "8.8.7", + "license": "MIT", + "dependencies": { + "@stencil/core": "4.43.0", + "ionicons": "^8.0.13", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/@ionic/core/node_modules/@stencil/core": { + "version": "4.43.0", + "license": "MIT", + "bin": { + "stencil": "bin/stencil" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.10.0" + }, + "optionalDependencies": { + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9" + } + }, + "node_modules/@ionic/core/node_modules/ionicons": { + "version": "8.0.13", + "license": "MIT", + "dependencies": { + "@stencil/core": "^4.35.3" + } + }, + "node_modules/@ionic/core/node_modules/ionicons/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.44.0", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ionic/core/node_modules/ionicons/node_modules/@stencil/core": { + "version": "4.43.4", + "license": "MIT", + "bin": { + "stencil": "bin/stencil" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.10.0" + }, + "optionalDependencies": { + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0" + } + }, + "node_modules/@ionic/pwa-elements": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ionic/pwa-elements/-/pwa-elements-3.4.0.tgz", + "integrity": "sha512-WmL7An4NOsvJvct4V/A8hudr+yXgLBHYmxzqWGsetZmzN7C6suLRS/KIh2P1t8cSYCYL7HRrbv8NrIChPkh6Ow==", + "license": "MIT", + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "node_modules/@ionic/react": { + "version": "8.8.7", + "license": "MIT", + "dependencies": { + "@ionic/core": "8.8.7", + "ionicons": "^8.0.13", + "tslib": "*" + }, + "peerDependencies": { + "react": ">=16.8.6", + "react-dom": ">=16.8.6" + } + }, + "node_modules/@ionic/react-router": { + "version": "8.8.7", + "license": "MIT", + "dependencies": { + "@ionic/react": "8.8.7", + "tslib": "*" + }, + "peerDependencies": { + "react": ">=16.8.6", + "react-dom": ">=16.8.6", + "react-router": "^5.0.1", + "react-router-dom": "^5.0.1" + } + }, + "node_modules/@ionic/react/node_modules/ionicons": { + "version": "8.0.13", + "license": "MIT", + "dependencies": { + "@stencil/core": "^4.35.3" + } + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-terminal": "2.3.5", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.9", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@stencil/core": { + "version": "4.43.4", + "license": "MIT", + "bin": { + "stencil": "bin/stencil" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.10.0" + }, + "optionalDependencies": { + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0" + } + }, + "node_modules/@stencil/core/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.44.0", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@supabase/auth-js": { + "version": "2.106.1", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.106.1", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/phoenix": { + "version": "0.4.2", + "license": "MIT" + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.106.1", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.106.1", + "license": "MIT", + "dependencies": { + "@supabase/phoenix": "^0.4.2", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.106.1", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.106.1", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.106.1", + "@supabase/functions-js": "2.106.1", + "@supabase/postgrest-js": "2.106.1", + "@supabase/realtime-js": "2.106.1", + "@supabase/storage-js": "2.106.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/google.maps": { + "version": "3.64.1", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.64.1.tgz", + "integrity": "sha512-nEBoa6iDNipICtxJ5VlrOgPNZQ6ixIg5nuv8iryFj0Z/1NLgxyg3pQCVegPuCzGCyTQwQI/N3uZvLUysqAzaaw==", + "license": "MIT" + }, + "node_modules/@types/history": { + "version": "4.7.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.9.1", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/react": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", + "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.31", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.360", + "dev": true, + "license": "ISC" + }, + "node_modules/elementtree": { + "version": "0.1.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/firebase": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.10.0.tgz", + "integrity": "sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/ai": "1.4.1", + "@firebase/analytics": "0.10.17", + "@firebase/analytics-compat": "0.2.23", + "@firebase/app": "0.13.2", + "@firebase/app-check": "0.10.1", + "@firebase/app-check-compat": "0.3.26", + "@firebase/app-compat": "0.4.2", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.10.8", + "@firebase/auth-compat": "0.5.28", + "@firebase/data-connect": "0.3.10", + "@firebase/database": "1.0.20", + "@firebase/database-compat": "2.0.11", + "@firebase/firestore": "4.8.0", + "@firebase/firestore-compat": "0.3.53", + "@firebase/functions": "0.12.9", + "@firebase/functions-compat": "0.3.26", + "@firebase/installations": "0.6.18", + "@firebase/installations-compat": "0.2.18", + "@firebase/messaging": "0.12.22", + "@firebase/messaging-compat": "0.2.22", + "@firebase/performance": "0.7.7", + "@firebase/performance-compat": "0.2.20", + "@firebase/remote-config": "0.6.5", + "@firebase/remote-config-compat": "0.2.18", + "@firebase/storage": "0.13.14", + "@firebase/storage-compat": "0.3.24", + "@firebase/util": "1.12.1" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/history": { + "version": "4.10.1", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ionicons": { + "version": "7.4.0", + "license": "MIT", + "dependencies": { + "@stencil/core": "^4.0.3" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/katex": { + "version": "0.16.47", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/kdbush": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz", + "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==", + "license": "ISC" + }, + "node_modules/kleur": { + "version": "4.1.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "11.5.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/native-run": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-fs": "^3.1.7", + "@ionic/utils-terminal": "^2.3.4", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^4.1.1", + "plist": "^3.1.0", + "split2": "^4.2.0", + "through2": "^4.0.2", + "tslib": "^2.6.2", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.44", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/open": { + "version": "8.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "1.9.0", + "license": "MIT", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/plist": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/protobufjs": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.1.tgz", + "integrity": "sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-hook-form": { + "version": "7.76.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.76.1.tgz", + "integrity": "sha512-rYM7tPiWlu3nZchkR/ex7piyzui2vFPyaLnXnI/RnblB/L4qfMmyses8llJVtF1NpE9WBBsJlGtcSZzPCXW1qQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "license": "MIT" + }, + "node_modules/react-katex": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "katex": "^0.16.0" + }, + "peerDependencies": { + "prop-types": "^15.8.1", + "react": ">=15.3.2 <20" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "5.3.4", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-dom": { + "version": "5.3.4", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.1.3", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.1.4", + "dev": true, + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/tar": { + "version": "7.5.15", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/terser": { + "version": "5.47.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "license": "MIT" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/value-equal": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/sax": { + "version": "1.6.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz", + "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..431d5b4 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "app", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint" + }, + "dependencies": { + "@capacitor-firebase/authentication": "^8.0.0", + "@capacitor/android": "8.3.4", + "@capacitor/app": "8.1.0", + "@capacitor/core": "8.3.4", + "@capacitor/google-maps": "^8.0.0", + "@capacitor/haptics": "8.0.2", + "@capacitor/ios": "8.3.4", + "@capacitor/keyboard": "8.0.3", + "@capacitor/push-notifications": "^8.1.1", + "@capacitor/status-bar": "8.0.2", + "@ionic/pwa-elements": "^3.4.0", + "@ionic/react": "^8.5.0", + "@ionic/react-router": "^8.5.0", + "@supabase/supabase-js": "^2.0.0", + "firebase": "^11.0.0", + "ionicons": "^7.4.0", + "katex": "^0.16.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-hook-form": "^7.54.0", + "react-katex": "^3.0.1", + "react-router": "^5.3.4", + "react-router-dom": "^5.3.4", + "zod": "^3.24.0", + "zustand": "^5.0.0" + }, + "devDependencies": { + "@capacitor/cli": "8.3.4", + "@types/katex": "^0.16.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@types/react-router": "^5.1.20", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react": "^4.0.1", + "terser": "^5.4.0", + "typescript": "~5.9.0", + "vite": "^5.0.0" + }, + "description": "An Ionic project" +} diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..2ce8c03 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "Ionic App", + "name": "My Ionic App", + "icons": [ + { + "src": "assets/icon/favicon.png", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#ffffff", + "background_color": "#ffffff" +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..3786933 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { Redirect, Route } from 'react-router-dom'; +import { IonApp, IonRouterOutlet } from '@ionic/react'; +import { IonReactRouter } from '@ionic/react-router'; + +import '@ionic/react/css/core.css'; +import '@ionic/react/css/normalize.css'; +import '@ionic/react/css/structure.css'; +import '@ionic/react/css/typography.css'; +import '@ionic/react/css/padding.css'; +import '@ionic/react/css/float-elements.css'; +import '@ionic/react/css/text-alignment.css'; +import '@ionic/react/css/text-transformation.css'; +import '@ionic/react/css/flex-utils.css'; +import '@ionic/react/css/display.css'; + +import './theme/variables.css'; + +import Home from './pages/Home'; +import Account from './pages/Account'; +import Auth from './pages/Auth'; +import Stats from './pages/Stats'; +import HabitDetail from './pages/HabitDetail'; + +// Cast components to avoid React 18 / React Router v5 JSX element type conflicts +const RouteAny = Route as any; +const RedirectAny = Redirect as any; +const IonReactRouterAny = IonReactRouter as any; +const IonRouterOutletAny = IonRouterOutlet as any; +const IonAppAny = IonApp as any; + +const App = (): any => ( + + + + + + + + + } /> + + + +); + +export default App; diff --git a/src/assets/d43ad1fb95964cffaa808bf7a0364307-2.webp b/src/assets/d43ad1fb95964cffaa808bf7a0364307-2.webp new file mode 100644 index 0000000..68b9cc0 Binary files /dev/null and b/src/assets/d43ad1fb95964cffaa808bf7a0364307-2.webp differ diff --git a/src/assets/fonts/.gitkeep b/src/assets/fonts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/original-1a4cc934532cc6dee2bb3488b9081bda.webp b/src/assets/original-1a4cc934532cc6dee2bb3488b9081bda.webp new file mode 100644 index 0000000..05b702c Binary files /dev/null and b/src/assets/original-1a4cc934532cc6dee2bb3488b9081bda.webp differ diff --git a/src/assets/runner-drinking-water-photorealistic.png b/src/assets/runner-drinking-water-photorealistic.png new file mode 100644 index 0000000..04aed23 Binary files /dev/null and b/src/assets/runner-drinking-water-photorealistic.png differ diff --git a/src/assets/runner-drinking-water.png b/src/assets/runner-drinking-water.png new file mode 100644 index 0000000..1c806f4 Binary files /dev/null and b/src/assets/runner-drinking-water.png differ diff --git a/src/components/CustomProgressBar.tsx b/src/components/CustomProgressBar.tsx new file mode 100644 index 0000000..231ad3c --- /dev/null +++ b/src/components/CustomProgressBar.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { IonProgressBar } from '@ionic/react'; + +interface CustomProgressBarProps { + value: number; // 0 to 1 + label?: string; + statusText?: string; + color?: string; // Hex, rgb, or css variable + showPercent?: boolean; + style?: React.CSSProperties; +} + +export const CustomProgressBar: React.FC = ({ + value, + label, + statusText, + color = 'var(--ion-color-primary)', + showPercent = true, + style, +}) => { + const percentage = Math.round(Math.min(Math.max(value, 0), 1) * 100); + + return ( +
+ {/* Label and Status */} + {(label || statusText || showPercent) && ( +
+ + {label}{' '} + {statusText && ( + + • {statusText} + + )} + + {showPercent && ( + + {percentage}% + + )} +
+ )} + + {/* Progress Track */} + +
+ ); +}; + +export default CustomProgressBar; diff --git a/src/components/EmptyState.tsx b/src/components/EmptyState.tsx new file mode 100644 index 0000000..da2f6f5 --- /dev/null +++ b/src/components/EmptyState.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { IonButton, IonIcon } from '@ionic/react'; +import { sparklesOutline } from 'ionicons/icons'; + +interface EmptyStateProps { + title: string; + message: string; + actionLabel: string; + onAction: () => void; +} + +const EmptyState: React.FC = ({ + title, + message, + actionLabel, + onAction, +}) => { + return ( +
+
+ +
+

{title}

+

{message}

+ + {actionLabel} + +
+ ); +}; + +export default EmptyState; diff --git a/src/components/HabitCard.tsx b/src/components/HabitCard.tsx new file mode 100644 index 0000000..7d46a6c --- /dev/null +++ b/src/components/HabitCard.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { IonButton, IonIcon, IonSpinner } from '@ionic/react'; +import { + checkmarkOutline, + ellipseOutline, + flameOutline, + trashOutline, +} from 'ionicons/icons'; +import { useHistory } from 'react-router-dom'; +import type { Tables } from '../database.types'; + +interface HabitCardProps { + habit: Tables<'habits'>; + completedToday: boolean; + currentStreak: number; + completionRate: number; + onToggle: () => void; + onDelete: () => void; + busy: boolean; +} + +const HabitCard: React.FC = ({ + habit, + completedToday, + currentStreak, + completionRate, + onToggle, + onDelete, + busy, +}) => { + const history = useHistory(); + + return ( +
history.push(`/habit/${habit.id}`)} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + history.push(`/habit/${habit.id}`); + } + }} + aria-label={`View details for ${habit.name}`} + > + + +
+
+
+

{habit.name}

+ +
+

+ {habit.target_description || + 'Show up today and keep the chain alive.'} +

+
+ + {currentStreak} streak + + {completionRate}% this week +
+
+
+ + { + event.stopPropagation(); + onDelete(); + }} + disabled={busy} + > + + +
+ ); +}; + +export default HabitCard; diff --git a/src/components/HabitFormModal.tsx b/src/components/HabitFormModal.tsx new file mode 100644 index 0000000..e5d4cc6 --- /dev/null +++ b/src/components/HabitFormModal.tsx @@ -0,0 +1,153 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonInput, + IonItem, + IonLabel, + IonModal, + IonTextarea, + IonTitle, + IonToolbar, +} from '@ionic/react'; + +interface HabitFormModalProps { + isOpen: boolean; + onDismiss: () => void; + onSubmit: (values: { + name: string; + targetDescription: string; + color: string; + }) => Promise; + initialValues?: { + name: string; + targetDescription: string; + color: string; + }; +} + +const COLORS = [ + '#7c3aed', + '#0f766e', + '#ea580c', + '#dc2626', + '#2563eb', + '#059669', +]; + +const HabitFormModal: React.FC = ({ + isOpen, + onDismiss, + onSubmit, + initialValues, +}) => { + const isEditing = !!initialValues; + const [name, setName] = useState(initialValues?.name ?? ''); + const [targetDescription, setTargetDescription] = useState( + initialValues?.targetDescription ?? '' + ); + const [color, setColor] = useState(initialValues?.color ?? COLORS[0]); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(''); + + const canSubmit = useMemo( + () => name.trim().length > 0 && name.trim().length <= 80, + [name] + ); + + useEffect(() => { + if (isOpen) { + setName(initialValues?.name ?? ''); + setTargetDescription(initialValues?.targetDescription ?? ''); + setColor(initialValues?.color ?? COLORS[0]); + setError(''); + } + }, [isOpen, initialValues]); + + const handleSubmit = async () => { + setError(''); + setSaving(true); + try { + await onSubmit({ + name: name.trim(), + targetDescription: targetDescription.trim(), + color, + }); + setName(''); + setTargetDescription(''); + setColor(COLORS[0]); + onDismiss(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Unable to save habit.'); + } finally { + setSaving(false); + } + }; + + return ( + + + + {isEditing ? 'Edit Habit' : 'New Habit'} + + Close + + + + +
+ + setName(e.detail.value ?? '')} + /> + + + + setTargetDescription(e.detail.value ?? '')} + /> + + +
+ Color +
+ {COLORS.map((swatch) => ( +
+
+ + {error ?

{error}

: null} + + + {saving ? 'Saving…' : isEditing ? 'Save Changes' : 'Create Habit'} + +
+
+
+ ); +}; + +export default HabitFormModal; diff --git a/src/components/PageHeader.tsx b/src/components/PageHeader.tsx new file mode 100644 index 0000000..cd19f6e --- /dev/null +++ b/src/components/PageHeader.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { + IonHeader, + IonToolbar, + IonTitle, + IonButtons, + IonBackButton, + useIonViewWillEnter, +} from '@ionic/react'; +import { setStatusBarStyle, Style } from '../utils/statusBar'; + +interface PageHeaderProps { + title: string; + showBackButton?: boolean; + defaultBackHref?: string; + endActions?: React.ReactNode; + style?: React.CSSProperties; + toolbarStyle?: React.CSSProperties; +} + +export const PageHeader: React.FC = ({ + title, + showBackButton = false, + defaultBackHref = '/home', + endActions, + style, + toolbarStyle, +}) => { + // Use Ionic lifecycle hook to set status bar style automatically for pages using PageHeader + useIonViewWillEnter(() => { + setStatusBarStyle(Style.Dark); // Dark text/icons for light-colored headers + }); + + return ( + + + {showBackButton && ( + + + + )} + + + {title} + + + {endActions && {endActions}} + + + ); +}; + +export default PageHeader; diff --git a/src/components/RevisionInstrumentCard.tsx b/src/components/RevisionInstrumentCard.tsx new file mode 100644 index 0000000..6369e10 --- /dev/null +++ b/src/components/RevisionInstrumentCard.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { IonIcon } from '@ionic/react'; + +interface RevisionInstrumentCardProps { + title: string; + subtitle?: string; + icon: string; + iconColor: string; + iconBgColor: string; + onClick: () => void; + style?: React.CSSProperties; +} + +export const RevisionInstrumentCard: React.FC = ({ + title, + subtitle, + icon, + iconColor, + iconBgColor, + onClick, + style, +}) => { + return ( +
+
+ +
+ + {title} + + {subtitle && ( + + {subtitle} + + )} +
+ ); +}; + +export default RevisionInstrumentCard; diff --git a/src/components/StatsHabitCard.tsx b/src/components/StatsHabitCard.tsx new file mode 100644 index 0000000..62988c9 --- /dev/null +++ b/src/components/StatsHabitCard.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { IonProgressBar } from '@ionic/react'; + +interface StatsHabitCardProps { + name: string; + color: string; + completedCount: number; + totalDays: number; + streak: number; +} + +const StatsHabitCard: React.FC = ({ + name, + color, + completedCount, + totalDays, + streak, +}) => { + const ratio = totalDays > 0 ? completedCount / totalDays : 0; + const percentage = Math.round(ratio * 100); + + return ( +
+
+
+

{name}

+

+ {completedCount} of {totalDays} days completed +

+
+
+ {streak}🔥 +
+
+ +
+ {percentage}% consistency + Current streak {streak} +
+
+ ); +}; + +export default StatsHabitCard; diff --git a/src/database.types.ts b/src/database.types.ts new file mode 100644 index 0000000..7f6d11c --- /dev/null +++ b/src/database.types.ts @@ -0,0 +1,273 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export type Database = { + // Allows to automatically instantiate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "14.5" + } + public: { + Tables: { + habit_completions: { + Row: { + completed_on: string + created_at: string + habit_id: string + id: string + user_id: string + } + Insert: { + completed_on: string + created_at?: string + habit_id: string + id?: string + user_id: string + } + Update: { + completed_on?: string + created_at?: string + habit_id?: string + id?: string + user_id?: string + } + Relationships: [ + { + foreignKeyName: "habit_completions_habit_id_fkey" + columns: ["habit_id"] + isOneToOne: false + referencedRelation: "habits" + referencedColumns: ["id"] + }, + ] + } + habits: { + Row: { + color: string + created_at: string + fcm_token: string | null + id: string + name: string + target_description: string | null + user_id: string + } + Insert: { + color?: string + created_at?: string + fcm_token?: string | null + id?: string + name: string + target_description?: string | null + user_id: string + } + Update: { + color?: string + created_at?: string + fcm_token?: string | null + id?: string + name?: string + target_description?: string | null + user_id?: string + } + Relationships: [] + } + notes: { + Row: { + content: string + created_at: string + id: string + is_favorite: boolean + title: string + user_id: string + } + Insert: { + content: string + created_at?: string + id?: string + is_favorite?: boolean + title: string + user_id?: string + } + Update: { + content?: string + created_at?: string + id?: string + is_favorite?: boolean + title?: string + user_id?: string + } + Relationships: [] + } + todos: { + Row: { + created_at: string + description: string | null + due_date: string | null + id: string + is_completed: boolean + title: string + user_id: string + } + Insert: { + created_at?: string + description?: string | null + due_date?: string | null + id?: string + is_completed?: boolean + title: string + user_id?: string + } + Update: { + created_at?: string + description?: string | null + due_date?: string | null + id?: string + is_completed?: boolean + title?: string + user_id?: string + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + [_ in never]: never + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + public: { + Enums: {}, + }, +} as const diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..7cac7e7 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { setupIonicReact } from '@ionic/react'; +import { Capacitor } from '@capacitor/core'; +import App from './App'; + +// ── Ionic initialisation ────────────────────────────────────────────────────── +// ?mode=md|ios overrides (used by AppSuite preview); otherwise follow the platform. +const _urlMode = new URLSearchParams(window.location.search).get('mode'); +const _platform = Capacitor.getPlatform(); // 'ios' | 'android' | 'web' +setupIonicReact({ + mode: (_urlMode === 'md' ? 'md' : _urlMode === 'ios' ? 'ios' : _platform === 'android' ? 'md' : 'ios'), +}); + +// Derive the studio parent origin dynamically so this works in both dev +// (parent at localhost:3000) and production (parent at appcakes.qqura.com). +const studioOrigin = (() => { + try { + if (document.referrer) return new URL(document.referrer).origin; + } catch {} + return 'http://localhost:3000'; +})(); + +// ── Safe area bridge ────────────────────────────────────────────────────────── +// Priority: URL params (first load) → sessionStorage (HMR full-reloads) → postMessage. +// Capacitor sets env() natively in compiled apps; these paths cover the browser preview. +function _applyInsets(sat: string | null, sab: string | null) { + if (sat) { document.documentElement.style.setProperty('--ion-safe-area-top', `${sat}px`); sessionStorage.setItem('__apsuite_sat', sat); } + if (sab) { document.documentElement.style.setProperty('--ion-safe-area-bottom', `${sab}px`); sessionStorage.setItem('__apsuite_sab', sab); } +} + +const _sp = new URLSearchParams(window.location.search); +_applyInsets( + _sp.get('sat') ?? sessionStorage.getItem('__apsuite_sat'), + _sp.get('sab') ?? sessionStorage.getItem('__apsuite_sab'), +); + +window.addEventListener('message', (e) => { + if (e.data?.type !== '__apsuite_insets') return; + const { sat, sab } = e.data as { sat?: number; sab?: number }; + _applyInsets(sat != null ? String(sat) : null, sab != null ? String(sab) : null); +}); + +// Re-apply after React Fast Refresh so insets survive soft HMR cycles. +if (import.meta.hot) { + import.meta.hot.on('vite:afterUpdate', () => { + _applyInsets(sessionStorage.getItem('__apsuite_sat'), sessionStorage.getItem('__apsuite_sab')); + }); +} + +// ── Session bridge ──────────────────────────────────────────────────────────── +// Post auth session to the AppSuite parent frame so the AI can test auth-protected +// Edge Functions. Uses import.meta.glob so Vite never errors if supabase.ts is absent. +(async () => { + const mods = import.meta.glob('./supabase.ts', { eager: false }); + if ('./supabase.ts' in mods) { + try { + const { supabase } = await mods['./supabase.ts']() as { supabase: any }; + supabase.auth.onAuthStateChange((_event: unknown, session: any) => { + window.parent.postMessage( + { + type: '__apsuite_session', + access_token: session?.access_token ?? null, + email: session?.user?.email ?? null, + }, + studioOrigin, + ); + }); + } catch { + // supabase client failed to initialise — session bridge unavailable + } + } +})(); + +// ── Runtime error reporting ─────────────────────────────────────────────────── +// Forward uncaught errors to the AppSuite dev server so the AI can read them. +function reportError(message: string, stack?: string) { + fetch(`${studioOrigin}/api/agent/runtime-error`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ message, stack }), + }).catch(() => {}); +} + +window.onerror = (_msg, _src, _line, _col, err) => { + reportError(String(_msg), err?.stack); + return false; +}; + +window.addEventListener('unhandledrejection', (e) => { + const err = e.reason as Error | undefined; + reportError(err?.message ?? String(e.reason), err?.stack); +}); + +// ── PWA Elements (Action Sheet, Camera, Toast etc. in browser preview) ──────── +import { defineCustomElements } from '@ionic/pwa-elements/loader'; +defineCustomElements(window); + +// ── App bootstrap ───────────────────────────────────────────────────────────── +const container = document.getElementById('root'); +const root = createRoot(container!); +root.render( + + + , +); diff --git a/src/pages/Account.tsx b/src/pages/Account.tsx new file mode 100644 index 0000000..2200c7b --- /dev/null +++ b/src/pages/Account.tsx @@ -0,0 +1,196 @@ +import React, { useEffect, useState } from 'react'; +import { + IonButton, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonPage, + IonTitle, + IonToolbar, + IonText, +} from '@ionic/react'; +import { + waterOutline, + fitnessOutline, + shieldCheckmarkOutline, +} from 'ionicons/icons'; +import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; +import { Capacitor } from '@capacitor/core'; +import { Redirect as RedirectRR, useHistory } from 'react-router-dom'; +import { supabase } from '../supabase'; +import { + Style, + setStatusBarBackground, + setStatusBarStyle, +} from '../utils/statusBar'; +import { useIonViewWillEnter } from '@ionic/react'; +import runnerDrinkingWaterImage from '../assets/runner-drinking-water-photorealistic.png'; + +const RedirectAny = RedirectRR as any; + +const Account: React.FC = () => { + const history = useHistory(); + const [sessionChecked, setSessionChecked] = useState(false); + const [email, setEmail] = useState(null); + const [signingOut, setSigningOut] = useState(false); + const [signOutError, setSignOutError] = useState(''); + + useEffect(() => { + let mounted = true; + + supabase.auth.getSession().then(({ data }) => { + if (!mounted) return; + setEmail(data.session?.user.email ?? null); + setSessionChecked(true); + }); + + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_event, session) => { + if (!mounted) return; + setEmail(session?.user.email ?? null); + setSessionChecked(true); + + if (!session) { + history.replace('/auth'); + } + }); + + return () => { + mounted = false; + subscription.unsubscribe(); + }; + }, [history]); + + const handleSignOut = async () => { + setSignOutError(''); + setSigningOut(true); + + try { + const { error } = await supabase.auth.signOut(); + + if (error) { + setSignOutError(error.message); + return; + } + + if (Capacitor.isNativePlatform()) { + try { + await FirebaseAuthentication.signOut(); + } catch { + // Ignore if no native Google session exists. + } + } + + setEmail(null); + history.replace('/auth'); + } catch (error) { + setSignOutError( + error instanceof Error + ? error.message + : 'Unable to sign out right now.', + ); + } finally { + setSigningOut(false); + } + }; + + if (sessionChecked && !email) { + return ; + } + + return ( + + + + Account + + + +
+
+
+ Runner drinking water after a run +
+
+ +
+ Recovery & account +

{email ?? 'Loading...'}

+

+ Stay hydrated, stay consistent, and keep your habit history + safely synced across sessions. +

+ +
+
+ + Recovery mindset +
+
+ + Built for routines +
+
+
+
+ +
+ + +

Email

+

{email ?? '—'}

+
+
+ +
+
+ +
+
+

Your data is synced

+

+ Sign in to keep your streaks and progress available whenever + you come back. +

+
+
+ + + Back to Habits + + {signOutError ? ( + +

{signOutError}

+
+ ) : null} +
+ + + {signingOut ? 'Signing Out…' : 'Sign Out'} + +
+
+
+
+ ); +}; + +export default Account; diff --git a/src/pages/Auth.tsx b/src/pages/Auth.tsx new file mode 100644 index 0000000..c422bce --- /dev/null +++ b/src/pages/Auth.tsx @@ -0,0 +1,398 @@ +import React, { useMemo, useState } from 'react'; +import { + IonButton, + IonCard, + IonCardContent, + IonContent, + IonIcon, + IonInput, + IonItem, + IonLabel, + IonPage, + IonSegment, + IonSegmentButton, + IonText, + useIonViewWillEnter, +} from '@ionic/react'; +import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; +import { Capacitor } from '@capacitor/core'; +import { Redirect, useHistory } from 'react-router-dom'; +import { + Style, + setStatusBarBackground, + setStatusBarStyle, +} from '../utils/statusBar'; +import { supabase } from '../supabase'; + +const Auth: React.FC = () => { + const history = useHistory(); + const [mode, setMode] = useState<'login' | 'signup'>('login'); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [loading, setLoading] = useState(false); + const [googleLoading, setGoogleLoading] = useState(false); + const [appleLoading, setAppleLoading] = useState(false); + const [error, setError] = useState(''); + const [info, setInfo] = useState(''); + const [verificationMessage, setVerificationMessage] = useState(''); + const [sessionChecked, setSessionChecked] = useState(false); + const [hasSession, setHasSession] = useState(false); + + const showError = (msg: string) => { + setError(msg); + setTimeout(() => { + setError((current) => (current === msg ? '' : current)); + }, 4000); + }; + + useIonViewWillEnter(() => { + setStatusBarStyle(Style.Dark); + setStatusBarBackground('#f6f7fb'); + }); + + React.useEffect(() => { + let mounted = true; + + supabase.auth.getSession().then(({ data }) => { + if (!mounted) return; + setHasSession(Boolean(data.session)); + setSessionChecked(true); + }); + + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((_event, session) => { + if (!mounted) return; + setHasSession(Boolean(session)); + setSessionChecked(true); + }); + + return () => { + mounted = false; + subscription.unsubscribe(); + }; + }, []); + + const formValid = useMemo(() => { + return email.trim().length > 3 && password.length >= 6; + }, [email, password]); + + const handleSubmit = async () => { + setError(''); + setInfo(''); + setVerificationMessage(''); + setLoading(true); + + try { + if (mode === 'login') { + const { error: signInError } = await supabase.auth.signInWithPassword({ + email: email.trim(), + password, + }); + + if (signInError) { + showError(signInError.message); + return; + } + + history.replace('/home'); + return; + } + + const { data, error: signUpError } = await supabase.auth.signUp({ + email: email.trim(), + password, + }); + + if (signUpError) { + showError(signUpError.message); + return; + } + + if (data.session) { + history.replace('/home'); + return; + } + + setVerificationMessage( + 'Account created! Check your email for a verification link.' + ); + setInfo('Once verified, come back and sign in.'); + } finally { + setLoading(false); + } + }; + + const handleGoogleLogin = async () => { + setError(''); + setInfo(''); + setVerificationMessage(''); + setGoogleLoading(true); + + if (!Capacitor.isNativePlatform()) { + showError( + 'Google Sign-In is only available in the native app. Use email to sign in here.' + ); + setGoogleLoading(false); + return; + } + + try { + const result = await FirebaseAuthentication.signInWithGoogle(); + const idToken = result.credential?.idToken; + + if (!idToken) { + showError('Google sign-in did not return an ID token.'); + return; + } + + const { error: googleAuthError } = await supabase.auth.signInWithIdToken({ + provider: 'google', + token: idToken, + }); + + if (googleAuthError) { + showError(googleAuthError.message); + return; + } + + history.replace('/home'); + } catch (err) { + const message = + err instanceof Error ? err.message : 'Google sign-in failed.'; + showError(message); + } finally { + setGoogleLoading(false); + } + }; + + const handleAppleLogin = async () => { + setError(''); + setInfo(''); + setVerificationMessage(''); + setAppleLoading(true); + + if (!Capacitor.isNativePlatform()) { + showError( + 'Apple Sign-In is only available in the native app. Use email to sign in here.' + ); + setAppleLoading(false); + return; + } + + try { + const result = await FirebaseAuthentication.signInWithApple({ + skipNativeAuth: true, + }); + const idToken = result.credential?.idToken; + const rawNonce = result.credential?.nonce; + + if (!idToken) { + showError('Apple sign-in did not return an ID token.'); + return; + } + + const { error: appleAuthError } = await supabase.auth.signInWithIdToken({ + provider: 'apple', + token: idToken, + nonce: rawNonce || undefined, + }); + + if (appleAuthError) { + showError(appleAuthError.message); + return; + } + + history.replace('/home'); + } catch (err) { + const message = + err instanceof Error ? err.message : 'Apple sign-in failed.'; + showError(message); + } finally { + setAppleLoading(false); + } + }; + + if (sessionChecked && hasSession) { + return ; + } + + return ( + + +
+
+ Protect your streaks +

Build habits that actually stick.

+

+ Track daily wins, recover faster from misses, and see which habits + are getting real momentum. +

+
+ + + + + setMode(e.detail.value as 'login' | 'signup') + } + > + + Login + + + Sign Up + + + + {verificationMessage ? ( +
+

Check your inbox

+

{verificationMessage}

+ +

{info}

+
+ setMode('login')}> + Back to Login + +
+ ) : ( +
+ + setEmail(e.detail.value ?? '')} + /> + + + + setPassword(e.detail.value ?? '')} + /> + + + {error ? ( + +

{error}

+
+ ) : null} + + {info ? ( + +

{info}

+
+ ) : null} + + + {loading + ? 'Please wait…' + : mode === 'login' + ? 'Log In' + : 'Create Account'} + + + {mode === 'login' ? ( + <> +
+ or +
+ + + + + + + + + + + {googleLoading ? 'Connecting…' : 'Continue with Google'} + + + + + + + + + {appleLoading ? 'Connecting…' : 'Continue with Apple'} + + + ) : null} +
+ )} +
+
+
+
+
+ ); +}; + +export default Auth; diff --git a/src/pages/HabitDetail.tsx b/src/pages/HabitDetail.tsx new file mode 100644 index 0000000..1cabaa2 --- /dev/null +++ b/src/pages/HabitDetail.tsx @@ -0,0 +1,623 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { + IonAlert, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonSkeletonText, + IonTitle, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import { IonBackButton } from '@ionic/react'; +import { + checkmarkOutline, + createOutline, + ellipseOutline, + flameOutline, + sparklesOutline, + trashOutline, + trendingUpOutline, +} from 'ionicons/icons'; +import { Redirect, useHistory, useParams } from 'react-router-dom'; +import type { Tables } from '../database.types'; +import { supabase } from '../supabase'; +import HabitFormModal from '../components/HabitFormModal'; +import { + Style, + setStatusBarBackground, + setStatusBarStyle, +} from '../utils/statusBar'; + +interface HabitWithCompletions extends Tables<'habits'> { + completions: Tables<'habit_completions'>[]; +} + +const formatDate = (date: Date) => date.toISOString().slice(0, 10); + +const calculateStreak = (dates: string[]) => { + const unique = Array.from(new Set(dates)).sort((a, b) => b.localeCompare(a)); + let streak = 0; + let cursor = new Date(); + + if (!unique.includes(formatDate(cursor))) { + cursor.setDate(cursor.getDate() - 1); + } + + while (unique.includes(formatDate(cursor))) { + streak += 1; + cursor.setDate(cursor.getDate() - 1); + } + + return streak; +}; + +const HabitDetail: React.FC = () => { + const { id } = useParams<{ id: string }>(); + const history = useHistory(); + + const [sessionChecked, setSessionChecked] = useState(false); + const [userId, setUserId] = useState(null); + const [habit, setHabit] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [busy, setBusy] = useState(false); + const [showEditModal, setShowEditModal] = useState(false); + const [showDeleteAlert, setShowDeleteAlert] = useState(false); + + const loadHabit = useCallback(async () => { + setError(''); + + const { + data: { session }, + error: sessionError, + } = await supabase.auth.getSession(); + + if (sessionError) { + setError(sessionError.message); + setLoading(false); + return; + } + + if (!session?.user) { + setUserId(null); + setSessionChecked(true); + setLoading(false); + return; + } + + setUserId(session.user.id); + setSessionChecked(true); + + const { data, error: habitError } = await supabase + .from('habits') + .select('*, completions:habit_completions(*)') + .eq('id', id) + .single(); + + if (habitError) { + if (habitError.code === 'PGRST116') { + setHabit(null); + } else { + setError(habitError.message); + } + setLoading(false); + return; + } + + setHabit(data as HabitWithCompletions); + setLoading(false); + }, [id]); + + useEffect(() => { + loadHabit(); + }, [loadHabit]); + + const completionDates = useMemo( + () => habit?.completions.map((c) => c.completed_on) ?? [], + [habit], + ); + + const today = formatDate(new Date()); + const doneToday = completionDates.includes(today); + + const currentStreak = useMemo( + () => calculateStreak(completionDates), + [completionDates], + ); + + const totalCompletions = completionDates.length; + + // Calculate longest streak + const longestStreak = useMemo(() => { + const unique = Array.from(new Set(completionDates)).sort((a, b) => + a.localeCompare(b), + ); + if (unique.length === 0) return 0; + let maxStreak = 1; + let current = 1; + for (let i = 1; i < unique.length; i++) { + const prev = new Date(unique[i - 1]); + const curr = new Date(unique[i]); + const diffDays = + (curr.getTime() - prev.getTime()) / (1000 * 60 * 60 * 24); + if (diffDays === 1) { + current += 1; + maxStreak = Math.max(maxStreak, current); + } else { + current = 1; + } + } + return maxStreak; + }, [completionDates]); + + // Days since habit creation (or first completion, whichever is earlier) + const daysSinceStart = useMemo(() => { + const startDate = habit + ? new Date( + Math.min( + new Date(habit.created_at).getTime(), + ...completionDates.map((d) => new Date(d).getTime()), + Date.now(), + ), + ) + : new Date(); + const diff = Math.ceil( + (Date.now() - startDate.getTime()) / (1000 * 60 * 60 * 24), + ); + return Math.max(diff, 1); + }, [habit, completionDates]); + + const consistencyRate = Math.round((totalCompletions / daysSinceStart) * 100); + + // 30-day heatmap data + const heatmapDays = useMemo(() => { + return Array.from({ length: 30 }, (_, i) => { + const date = new Date(); + date.setDate(date.getDate() - (29 - i)); + const key = formatDate(date); + return { + key, + completed: completionDates.includes(key), + isToday: key === today, + }; + }); + }, [completionDates, today]); + + const toggleToday = async () => { + if (!userId || !habit) return; + setBusy(true); + + if (doneToday) { + const existing = habit.completions.find((c) => c.completed_on === today); + if (existing) { + const { error: deleteError } = await supabase + .from('habit_completions') + .delete() + .eq('id', existing.id); + if (deleteError) setError(deleteError.message); + } + } else { + const { error: insertError } = await supabase + .from('habit_completions') + .insert({ + habit_id: habit.id, + user_id: userId, + completed_on: today, + }); + if (insertError) setError(insertError.message); + } + + await loadHabit(); + setBusy(false); + }; + + const updateHabit = async (values: { + name: string; + targetDescription: string; + color: string; + }) => { + if (!userId || !habit) throw new Error('Habit not found.'); + + const { error: updateError } = await supabase + .from('habits') + .update({ + name: values.name, + target_description: values.targetDescription || null, + color: values.color, + }) + .eq('id', habit.id); + + if (updateError) throw updateError; + await loadHabit(); + }; + + const deleteHabit = async () => { + if (!habit) return; + setBusy(true); + const { error: deleteError } = await supabase + .from('habits') + .delete() + .eq('id', habit.id); + if (deleteError) { + setError(deleteError.message); + setBusy(false); + return; + } + history.push('/home'); + }; + + if (sessionChecked && !userId) { + return ; + } + + return ( + + + + + + + {habit?.name ?? 'Habit'} + + {habit && ( + <> + setShowEditModal(true)}> + + + setShowDeleteAlert(true)} + > + + + + )} + + + + + +
+ {/* Loading state */} + {loading ? ( + <> + + + + + + ) : error ? ( + /* Error state */ +
+

Something went wrong

+

{error}

+ Try Again +
+ ) : !habit ? ( + /* Not found state */ +
+

Habit not found

+

+ This habit may have been deleted or you don't have access + to it. +

+ Back to habits +
+ ) : ( + <> + {/* Habit info card */} +
+
+ + Your routine +
+

{habit.name}

+

+ {habit.target_description || + 'Show up today and keep the chain alive.'} +

+ + {/* Today toggle */} +
+ + + {doneToday ? 'Completed today' : "Mark today's habit done"} + +
+
+ + {/* All-time stats */} +
+

All-time stats

+
+ {/* Stats row */} +
+
+
+ {totalCompletions} +
+
+ total done +
+
+
+
+ {longestStreak} +
+
+ best streak +
+
+
+
+ {consistencyRate}% +
+
+ consistency +
+
+
+ + {/* Current streak highlight */} +
+
+ +
+
+

+ {currentStreak} day{currentStreak === 1 ? '' : 's'} and + counting +

+

+ {currentStreak > 0 + ? `You've shown up every day since ${(() => { + const d = new Date(); + d.setDate(d.getDate() - currentStreak + 1); + return d.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + }); + })()}.` + : "Start today and build a streak you're proud of."} +

+
+
+
+
+ + {/* 30-day heatmap */} +
+

Last 30 days

+
+
+ {heatmapDays.map((day) => ( +
+ ))} +
+
+ 30 days ago + + + {heatmapDays.filter((d) => d.completed).length} of 30 days + + Today +
+
+
+ + {/* Edit modal */} + {showEditModal && ( + setShowEditModal(false)} + onSubmit={updateHabit} + initialValues={{ + name: habit.name, + targetDescription: habit.target_description ?? '', + color: habit.color, + }} + /> + )} + + )} +
+ + {/* Delete confirmation */} + setShowDeleteAlert(false)} + header="Delete habit?" + message={`Are you sure you want to delete "${habit?.name ?? ''}"? This will also remove all its completion history. This action cannot be undone.`} + buttons={[ + { + text: 'Cancel', + role: 'cancel', + }, + { + text: 'Delete', + role: 'destructive', + handler: deleteHabit, + }, + ]} + /> +
+
+ ); +}; + +export default HabitDetail; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..b7455d3 --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,479 @@ +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRefresher, + IonRefresherContent, + IonSkeletonText, + IonToolbar, +} from '@ionic/react'; +import { + addOutline, + analyticsOutline, + checkmarkCircle, + personCircleOutline, + sparklesOutline, +} from 'ionicons/icons'; +import { PushNotifications } from '@capacitor/push-notifications'; +import { Capacitor } from '@capacitor/core'; +import { Redirect, useHistory } from 'react-router-dom'; +import type { Tables } from '../database.types'; +import { supabase } from '../supabase'; +import EmptyState from '../components/EmptyState'; +import HabitCard from '../components/HabitCard'; +import HabitFormModal from '../components/HabitFormModal'; + +interface HabitWithMeta extends Tables<'habits'> { + completions: Tables<'habit_completions'>[]; +} + +const formatDate = (date: Date) => date.toISOString().slice(0, 10); + +const calculateCurrentStreak = (dates: string[]) => { + const unique = Array.from(new Set(dates)).sort((a, b) => b.localeCompare(a)); + let streak = 0; + let cursor = new Date(); + + if (!unique.includes(formatDate(cursor))) { + cursor.setDate(cursor.getDate() - 1); + } + + while (unique.includes(formatDate(cursor))) { + streak += 1; + cursor.setDate(cursor.getDate() - 1); + } + + return streak; +}; + +const Home: React.FC = () => { + const history = useHistory(); + const [sessionChecked, setSessionChecked] = useState(false); + const [userId, setUserId] = useState(null); + const [habits, setHabits] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [showCreateModal, setShowCreateModal] = useState(false); + const [busyHabitId, setBusyHabitId] = useState(null); + const pushTokenRef = useRef(null); + const pushListenersReadyRef = useRef(false); + + const showError = (message: string) => { + setError(message); + window.setTimeout(() => setError(''), 4000); + }; + + const registerPushNotifications = useCallback( + async (currentUserId: string) => { + if (!Capacitor.isNativePlatform()) { + return; + } + + if (!pushListenersReadyRef.current) { + pushListenersReadyRef.current = true; + + await PushNotifications.removeAllListeners(); + + PushNotifications.addListener('registration', async (token) => { + if (pushTokenRef.current === token.value) { + return; + } + + pushTokenRef.current = token.value; + const { error: updateError } = await supabase + .from('habits') + .update({ fcm_token: token.value }) + .eq('user_id', currentUserId); + + if (updateError) { + showError(updateError.message); + } + }); + + PushNotifications.addListener( + 'registrationError', + (registrationError) => { + showError(registrationError.error ?? 'Push registration failed.'); + } + ); + } + + const permissionStatus = await PushNotifications.checkPermissions(); + if (permissionStatus.receive === 'prompt') { + const requested = await PushNotifications.requestPermissions(); + if (requested.receive !== 'granted') { + return; + } + } else if (permissionStatus.receive !== 'granted') { + return; + } + + await PushNotifications.register(); + }, + [showError] + ); + + const loadDashboard = useCallback(async () => { + setError(''); + + const { + data: { session }, + error: sessionError, + } = await supabase.auth.getSession(); + + if (sessionError) { + setError(sessionError.message); + setSessionChecked(true); + setLoading(false); + return; + } + + if (!session?.user) { + setUserId(null); + setSessionChecked(true); + setLoading(false); + return; + } + + setUserId(session.user.id); + setSessionChecked(true); + await registerPushNotifications(session.user.id); + + const { data, error: habitsError } = await supabase + .from('habits') + .select('*, completions:habit_completions(*)') + .order('created_at', { ascending: true }); + + if (habitsError) { + setError(habitsError.message); + setLoading(false); + return; + } + + setHabits((data ?? []) as HabitWithMeta[]); + setLoading(false); + }, [registerPushNotifications]); + + useEffect(() => { + loadDashboard(); + }, [loadDashboard]); + + const today = formatDate(new Date()); + + const habitStats = useMemo(() => { + const last7Days = Array.from({ length: 7 }, (_, index) => { + const date = new Date(); + date.setDate(date.getDate() - index); + return formatDate(date); + }); + + return habits.reduce< + Record + >((acc, habit) => { + const completionDates = habit.completions.map( + (entry) => entry.completed_on + ); + const completedInWeek = completionDates.filter((date) => + last7Days.includes(date) + ).length; + + acc[habit.id] = { + streak: calculateCurrentStreak(completionDates), + weekRate: Math.round((completedInWeek / last7Days.length) * 100), + doneToday: completionDates.includes(today), + }; + + return acc; + }, {}); + }, [habits, today]); + + const totalCompletedToday = habits.filter( + (habit) => habitStats[habit.id]?.doneToday + ).length; + const longestStreak = habits.reduce( + (max, habit) => Math.max(max, habitStats[habit.id]?.streak ?? 0), + 0 + ); + const completionRatio = habits.length + ? totalCompletedToday / habits.length + : 0; + const pendingCount = Math.max(habits.length - totalCompletedToday, 0); + + const createHabit = async (values: { + name: string; + targetDescription: string; + color: string; + }) => { + if (!userId) throw new Error('You need to be logged in.'); + + const { error: insertError } = await supabase.from('habits').insert({ + user_id: userId, + name: values.name, + target_description: values.targetDescription || null, + color: values.color, + }); + + if (insertError) throw insertError; + await loadDashboard(); + }; + + const toggleHabit = async (habit: HabitWithMeta) => { + if (!userId) return; + setBusyHabitId(habit.id); + + const existing = habit.completions.find( + (entry) => entry.completed_on === today + ); + + if (existing) { + const { error: deleteError } = await supabase + .from('habit_completions') + .delete() + .eq('id', existing.id); + if (deleteError) showError(deleteError.message); + } else { + const { error: insertError } = await supabase + .from('habit_completions') + .insert({ + habit_id: habit.id, + user_id: userId, + completed_on: today, + }); + if (insertError) { + showError(insertError.message); + } else { + const { error: pushError } = await supabase.functions.invoke( + 'send-habit-completion-push', + { + body: { habitId: habit.id }, + } + ); + + if (pushError) { + showError(pushError.message); + } + } + } + + await loadDashboard(); + setBusyHabitId(null); + }; + + const deleteHabit = async (habitId: string) => { + setBusyHabitId(habitId); + const { error: deleteError } = await supabase + .from('habits') + .delete() + .eq('id', habitId); + if (deleteError) { + showError(deleteError.message); + } + await loadDashboard(); + setBusyHabitId(null); + }; + + useEffect(() => { + return () => { + void PushNotifications.removeAllListeners(); + }; + }, []); + + if (sessionChecked && !userId) { + return ; + } + + return ( + + + + + history.push('/stats')} + > + + + history.push('/account')} + > + + + + + + + + { + await loadDashboard(); + e.detail.complete(); + }} + > + + + +
+
+
+
+

Habits ({habits.length})

+
+

Daily

+ Weekly +
+
+ setShowCreateModal(true)} + > + + +
+ + + +
+ {totalCompletedToday} completed today + {Math.round(completionRatio * 100)}% completed +
+
+ + {loading ? ( +
+
+ + + + +
+
+ ) : error ? ( +
+

Couldn't load your habits

+

{error}

+ Try Again +
+ ) : habits.length === 0 ? ( +
+ setShowCreateModal(true)} + /> +
+ ) : ( + <> +
+
+ {habits.map((habit) => ( + toggleHabit(habit)} + onDelete={() => deleteHabit(habit.id)} + busy={busyHabitId === habit.id} + /> + ))} +
+
+ +
+

Momentum

+
+
+
+ {Math.round(completionRatio * 100)}% +
+
+
+

Keep your rhythm

+

+ {pendingCount === 0 + ? 'Everything is complete today. Enjoy the streak.' + : `${pendingCount} habit${pendingCount === 1 ? '' : 's'} left to protect today's routine.`} +

+
+ + {totalCompletedToday}{' '} + done + + + {longestStreak} day + best streak + +
+
+
+
+ + )} +
+ + setShowCreateModal(false)} + onSubmit={createHabit} + /> +
+
+ ); +}; + +export default Home; diff --git a/src/pages/Stats.tsx b/src/pages/Stats.tsx new file mode 100644 index 0000000..ce04cf3 --- /dev/null +++ b/src/pages/Stats.tsx @@ -0,0 +1,174 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonSegment, + IonSegmentButton, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { IonBackButton } from '@ionic/react'; +import { calendarOutline } from 'ionicons/icons'; +import { Redirect } from 'react-router-dom'; +import type { Tables } from '../database.types'; +import StatsHabitCard from '../components/StatsHabitCard'; +import { supabase } from '../supabase'; +import { + Style, + setStatusBarBackground, + setStatusBarStyle, +} from '../utils/statusBar'; +import { useIonViewWillEnter } from '@ionic/react'; + +interface HabitWithMeta extends Tables<'habits'> { + completions: Tables<'habit_completions'>[]; +} + +const formatDate = (date: Date) => date.toISOString().slice(0, 10); + +const calculateCurrentStreak = (dates: string[]) => { + const unique = Array.from(new Set(dates)).sort((a, b) => b.localeCompare(a)); + let streak = 0; + let cursor = new Date(); + + if (!unique.includes(formatDate(cursor))) { + cursor.setDate(cursor.getDate() - 1); + } + + while (unique.includes(formatDate(cursor))) { + streak += 1; + cursor.setDate(cursor.getDate() - 1); + } + + return streak; +}; + +const Stats: React.FC = () => { + const [period, setPeriod] = useState<'7' | '30'>('7'); + const [sessionChecked, setSessionChecked] = useState(false); + const [userId, setUserId] = useState(null); + const [habits, setHabits] = useState([]); + + useEffect(() => { + const load = async () => { + const { + data: { session }, + } = await supabase.auth.getSession(); + + if (!session?.user) { + setSessionChecked(true); + setUserId(null); + return; + } + + setUserId(session.user.id); + setSessionChecked(true); + + const { data } = await supabase + .from('habits') + .select('*, completions:habit_completions(*)') + .order('created_at', { ascending: true }); + + setHabits((data ?? []) as HabitWithMeta[]); + }; + + load(); + }, []); + + const days = Number(period); + const visibleDates = useMemo(() => { + return Array.from({ length: days }, (_, index) => { + const date = new Date(); + date.setDate(date.getDate() - index); + return formatDate(date); + }); + }, [days]); + + const summary = useMemo(() => { + return habits.map((habit) => { + const completionDates = habit.completions.map( + (entry) => entry.completed_on, + ); + const completedCount = completionDates.filter((date) => + visibleDates.includes(date), + ).length; + return { + id: habit.id, + name: habit.name, + color: habit.color, + completedCount, + streak: calculateCurrentStreak(completionDates), + }; + }); + }, [habits, visibleDates]); + + if (sessionChecked && !userId) { + return ; + } + + return ( + + + + + + + Stats + + + +
+
+ Consistency view +

See which habits you're actually keeping.

+

+ Flip between the last week and month to spot patterns before a + streak disappears. +

+
+ +
+ + setPeriod(e.detail.value as '7' | '30')} + > + Last 7 days + Last 30 days + +
+ + {summary.length === 0 ? ( +
+

No stats yet

+

+ Create a habit and start checking it off daily to unlock + consistency insights. +

+ Go to dashboard +
+ ) : ( +
+ {summary.map((habit) => ( + + ))} +
+ )} +
+
+
+ ); +}; + +export default Stats; diff --git a/src/supabase.ts b/src/supabase.ts new file mode 100644 index 0000000..ad21f22 --- /dev/null +++ b/src/supabase.ts @@ -0,0 +1,7 @@ +import { createClient } from '@supabase/supabase-js'; +import type { Database } from './database.types'; + +export const supabase = createClient( + 'https://zillycfjfbzofltgyjwy.supabase.co', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InppbGx5Y2ZqZmJ6b2ZsdGd5and5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzkxMjYzODQsImV4cCI6MjA5NDcwMjM4NH0.BB4ccBEQI2krc7SseC73234Nfya-92VGcTELNQdzt50', +); diff --git a/src/theme/variables.css b/src/theme/variables.css new file mode 100644 index 0000000..43cc99c --- /dev/null +++ b/src/theme/variables.css @@ -0,0 +1,869 @@ +:root { + --ion-color-primary: #6d28d9; + --ion-color-primary-rgb: 109, 40, 217; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #5b21b6; + --ion-color-primary-tint: #7c3aed; + + --ion-color-secondary: #0f766e; + --ion-color-secondary-rgb: 15, 118, 110; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #115e59; + --ion-color-secondary-tint: #14b8a6; + + --ion-color-tertiary: #ea580c; + --ion-color-tertiary-rgb: 234, 88, 12; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #c2410c; + --ion-color-tertiary-tint: #f97316; + + --ion-color-success: #16a34a; + --ion-color-success-rgb: 22, 163, 74; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #15803d; + --ion-color-success-tint: #22c55e; + + --ion-color-warning: #f59e0b; + --ion-color-warning-rgb: 245, 158, 11; + --ion-color-warning-contrast: #1f2937; + --ion-color-warning-contrast-rgb: 31, 41, 55; + --ion-color-warning-shade: #d97706; + --ion-color-warning-tint: #fbbf24; + + --ion-color-danger: #dc2626; + --ion-color-danger-rgb: 220, 38, 38; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #b91c1c; + --ion-color-danger-tint: #ef4444; + + --ion-color-dark: #111827; + --ion-color-dark-rgb: 17, 24, 39; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #030712; + --ion-color-dark-tint: #374151; + + --ion-color-medium: #6b7280; + --ion-color-medium-rgb: 107, 114, 128; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #4b5563; + --ion-color-medium-tint: #9ca3af; + + --ion-color-light: #f3f4f6; + --ion-color-light-rgb: 243, 244, 246; + --ion-color-light-contrast: #111827; + --ion-color-light-contrast-rgb: 17, 24, 39; + --ion-color-light-shade: #e5e7eb; + --ion-color-light-tint: #f9fafb; + + --ion-background-color: #f6f7fb; + --ion-text-color: #111827; + --ion-border-color: #e5e7eb; +} + +body { + background: + radial-gradient( + circle at top left, + rgba(168, 85, 247, 0.12), + transparent 34% + ), + linear-gradient(180deg, #fbf7ff 0%, #f2ecfb 100%); +} + +ion-toolbar { + --background: #ffffff; + --color: var(--ion-color-dark); +} + +.mono-nums { + font-variant-numeric: tabular-nums; +} + +.dashboard-shell { + display: flex; + flex-direction: column; + gap: 18px; +} + +.hero-card, +.habit-card, +.stats-card, +.empty-state-card, +.inline-message, +.auth-card { + background: #ffffff; + border: 1px solid #ebeef4; + border-radius: 24px; + box-shadow: 0 8px 24px rgba(17, 24, 39, 0.04); +} + +.hero-card { + padding: 24px; +} + +.hero-card.compact { + padding: 20px; +} + +.eyebrow { + display: inline-block; + font-size: 0.74rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--ion-color-primary); + margin-bottom: 8px; +} + +.hero-card h1, +.auth-hero h1, +.empty-state-card h2 { + margin: 0; + font-size: 1.9rem; + font-weight: 800; + letter-spacing: -0.04em; + color: var(--ion-color-dark); +} + +.hero-card p, +.auth-hero p, +.inline-message p, +.empty-state-card p, +.stats-card p, +.habit-copy p { + color: var(--ion-color-medium); + line-height: 1.5; +} + +.stack-list { + display: flex; + flex-direction: column; + gap: 14px; +} + +.habit-card { + padding: 18px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.habit-card-main { + display: flex; + gap: 14px; +} + +.habit-color-pill { + width: 10px; + border-radius: 999px; + min-height: 100%; +} + +.habit-copy { + flex: 1; +} + +.habit-copy h3, +.stats-card h3 { + margin: 0 0 6px; + font-size: 1.08rem; + font-weight: 700; + color: var(--ion-color-dark); +} + +.habit-metrics-row, +.stats-card-footer, +.stats-card-header { + display: flex; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} + +.habit-metrics-row span, +.stats-card-footer span, +.stats-card-header p { + font-size: 0.85rem; + color: var(--ion-color-medium); +} + +.habit-metrics-row ion-icon { + vertical-align: middle; +} + +.habit-actions { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; +} + +.empty-state-card, +.inline-message { + padding: 28px 22px; + text-align: center; +} + +.empty-state-icon { + width: 64px; + height: 64px; + margin: 0 auto 14px; + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(109, 40, 217, 0.1); + color: var(--ion-color-primary); + font-size: 30px; +} + +.auth-shell { + min-height: 100%; + padding: 32px 20px; + display: flex; + flex-direction: column; + justify-content: center; + gap: 24px; +} + +.auth-hero { + padding: 0 4px; +} + +.auth-card { + margin: 0; +} + +.auth-card ion-card-content { + padding: 20px; +} + +.auth-form, +.auth-message-block, +.habit-form-grid { + display: flex; + flex-direction: column; + gap: 16px; + margin-top: 18px; +} + +.auth-input-item { + --background: #f8fafc; + --border-radius: 18px; +} + +.auth-feedback { + margin: 0; + font-size: 0.88rem; +} + +.auth-divider { + position: relative; + text-align: center; + margin: 4px 0; +} + +.auth-divider::before { + content: ''; + position: absolute; + top: 50%; + left: 0; + right: 0; + height: 1px; + background: rgba(107, 114, 128, 0.22); +} + +.auth-divider span { + position: relative; + display: inline-block; + padding: 0 12px; + background: #ffffff; + font-size: 0.82rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--ion-color-medium); +} + +.google-auth-button, +.apple-auth-button { + --border-radius: 18px; + --border-color: rgba(17, 24, 39, 0.08); + --background: #ffffff; + --background-hover: #ffffff; + --background-activated: #f8fafc; + --color: var(--ion-color-dark); + --box-shadow: none; + font-weight: 700; + margin-top: 0; + margin-bottom: 0; +} + +.apple-auth-button { + margin-top: 10px; +} + +.error-text { + color: var(--ion-color-danger); +} + +.color-label { + display: block; + font-size: 0.9rem; + font-weight: 600; + margin-bottom: 10px; +} + +.habit-color-row { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.habit-color-dot { + width: 36px; + height: 36px; + border-radius: 50%; + border: 3px solid transparent; +} + +.habit-color-dot.selected { + border-color: #111827; +} + +.stats-filter-row { + display: flex; + align-items: center; + gap: 12px; +} + +.stats-card { + padding: 18px; + display: flex; + flex-direction: column; + gap: 14px; +} + +.stats-streak-badge { + font-size: 1.1rem; + font-weight: 700; +} + +.home-header ion-toolbar { + --background: transparent; + --border-color: transparent; + --padding-start: 12px; + --padding-end: 12px; +} + +.home-content { + --background: transparent; +} + +.home-shell { + padding-top: calc(var(--ion-safe-area-top) + 10px); + padding-bottom: 32px; + gap: 22px; +} + +.soft-icon-button { + --background: rgba(255, 255, 255, 0.86); + --background-hover: rgba(255, 255, 255, 0.96); + --background-activated: rgba(255, 255, 255, 0.96); + --border-radius: 999px; + --box-shadow: 0 10px 24px rgba(109, 40, 217, 0.08); + --color: #17121f; + --padding-start: 0; + --padding-end: 0; + --padding-top: 0; + --padding-bottom: 0; + width: 46px; + min-width: 46px; + height: 46px; + min-height: 46px; + border-radius: 999px; + margin-inline-start: 6px; +} + +.soft-icon-button::part(native) { + width: 46px; + min-width: 46px; + height: 46px; + min-height: 46px; + border-radius: 999px; +} + +.habits-hero { + padding: 6px 4px 0; +} + +.habits-hero-top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; +} + +.hero-kicker { + margin: 0 0 6px; + font-size: 0.98rem; + font-weight: 700; + color: rgba(34, 23, 54, 0.92); +} + +.hero-title-wrap { + display: flex; + align-items: baseline; + gap: 8px; + flex-wrap: wrap; +} + +.hero-title-wrap h1 { + margin: 0; + font-size: clamp(3rem, 8vw, 4.2rem); + line-height: 0.94; + letter-spacing: -0.08em; + font-weight: 800; + color: #110d18; +} + +.hero-title-wrap span { + font-size: clamp(2.2rem, 7vw, 3.5rem); + line-height: 1; + letter-spacing: -0.08em; + font-weight: 700; + color: rgba(26, 16, 38, 0.32); +} + +.hero-add-button { + --background: rgba(255, 255, 255, 0.86); + --background-hover: rgba(255, 255, 255, 0.96); + --border-radius: 999px; + --box-shadow: 0 14px 30px rgba(109, 40, 217, 0.1); + --color: var(--ion-color-primary); + --padding-start: 0; + --padding-end: 0; + --padding-top: 0; + --padding-bottom: 0; + width: 52px; + min-width: 52px; + height: 52px; + min-height: 52px; + border-radius: 999px; + margin: 4px 0 0; +} + +.hero-add-button::part(native) { + width: 52px; + min-width: 52px; + height: 52px; + min-height: 52px; + border-radius: 999px; +} + +.hero-progress-track { + margin-top: 20px; + height: 12px; + background: rgba(109, 40, 217, 0.12); + border-radius: 999px; + overflow: hidden; +} + +.hero-progress-fill { + height: 100%; + min-width: 12px; + border-radius: 999px; + background: linear-gradient(90deg, #5b21b6 0%, #c084fc 100%); + position: relative; + transition: width 0.3s ease; +} + +.hero-progress-dot { + position: absolute; + right: 2px; + top: 50%; + transform: translateY(-50%); + width: 8px; + height: 8px; + border-radius: 50%; + background: #ffffff; +} + +.hero-progress-meta { + margin-top: 10px; + display: flex; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; + font-size: 0.84rem; + font-weight: 600; + color: rgba(43, 31, 58, 0.74); +} + +.home-routine-panel, +.home-summary-card, +.home-inline-card, +.home-empty-state-card, +.habit-card--editorial { + background: rgba(255, 255, 255, 0.82); + border: 1px solid rgba(255, 255, 255, 0.72); + box-shadow: 0 18px 40px rgba(106, 62, 149, 0.08); + backdrop-filter: blur(12px); +} + +.home-routine-panel { + padding: 18px; + border-radius: 30px; +} + +.home-habit-stack { + gap: 10px; +} + +.habit-card--editorial { + border-radius: 24px; + padding: 14px 12px; + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 12px; + border: none; +} + +.habit-card--editorial.is-complete { + background: rgba(255, 255, 255, 0.95); +} + +.habit-check-button { + width: 40px; + height: 40px; + border-radius: 50%; + border: none; + background: rgba(109, 40, 217, 0.08); + color: rgba(109, 40, 217, 0.72); + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; +} + +.habit-check-button.is-complete { + background: rgba(109, 40, 217, 0.16); + color: var(--ion-color-primary); +} + +.habit-copy-topline { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 4px; +} + +.habit-copy-topline .habit-color-pill { + width: 10px; + min-height: 10px; + height: 10px; + flex-shrink: 0; +} + +.habit-card-tap-area { + background: none; + border: none; + padding: 0; + margin: 0; + text-align: left; + cursor: pointer; + min-width: 0; + display: block; +} + +.habit-card--editorial .habit-card-main { + min-width: 0; +} + +.habit-card--editorial .habit-copy h3 { + margin: 0; + font-size: 1.03rem; +} + +.habit-card--editorial .habit-copy p { + margin: 0; + font-size: 0.88rem; + color: rgba(52, 41, 66, 0.7); +} + +.habit-card--editorial .habit-metrics-row { + margin-top: 8px; +} + +.habit-card--editorial .habit-metrics-row span { + font-size: 0.8rem; +} + +.habit-delete-button { + --color: rgba(86, 68, 106, 0.72); + --padding-start: 6px; + --padding-end: 6px; + margin: 0; +} + +.home-complete-section { + display: flex; + flex-direction: column; + gap: 10px; +} + +.home-section-label { + margin: 0 4px; + font-size: 0.88rem; + font-weight: 700; + color: rgba(66, 50, 84, 0.74); +} + +.home-summary-card { + border-radius: 30px; + padding: 18px; + display: flex; + align-items: center; + gap: 16px; +} + +.summary-ring { + width: 88px; + height: 88px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.summary-ring-inner { + width: 66px; + height: 66px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.96); + display: flex; + align-items: center; + justify-content: center; + font-weight: 800; + color: #1d1430; +} + +.summary-copy h3 { + margin: 0 0 6px; + font-size: 1.05rem; +} + +.summary-copy p { + margin: 0; + color: rgba(52, 41, 66, 0.7); + line-height: 1.45; +} + +.summary-metrics { + display: flex; + flex-wrap: wrap; + gap: 10px 14px; + margin-top: 10px; +} + +.summary-metrics span { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 0.82rem; + font-weight: 600; + color: rgba(42, 30, 56, 0.78); +} + +.home-empty-state-card { + border-radius: 26px; + border: none; + box-shadow: none; +} + +.home-empty-state-button { + --border-radius: 999px; + --box-shadow: none; +} + +.account-shell { + gap: 20px; + padding-bottom: calc(24px + var(--ion-safe-area-bottom)); +} + +.account-hero-card { + overflow: hidden; + border-radius: 30px; + background: #ffffff; + box-shadow: 0 16px 38px rgba(71, 45, 108, 0.08); +} + +.account-hero-image-wrap { + position: relative; + aspect-ratio: 16 / 10; + background: #e9e1f6; +} + +.account-hero-image { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.account-hero-overlay { + position: absolute; + inset: 0; + background: linear-gradient( + 180deg, + rgba(17, 24, 39, 0) 0%, + rgba(17, 24, 39, 0.3) 100% + ); +} + +.account-hero-content { + padding: 20px; + display: flex; + flex-direction: column; + gap: 14px; +} + +.account-hero-content h1 { + margin: 0; + font-size: 1.55rem; + line-height: 1.15; + letter-spacing: -0.04em; + color: var(--ion-color-dark); + word-break: break-word; +} + +.account-hero-content p { + margin: 0; + color: rgba(55, 65, 81, 0.76); +} + +.account-highlight-row { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.account-highlight-chip { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 14px; + border-radius: 999px; + background: rgba(109, 40, 217, 0.1); + color: var(--ion-color-primary); + font-size: 0.84rem; + font-weight: 700; +} + +.account-highlight-chip.subtle { + background: rgba(15, 118, 110, 0.1); + color: var(--ion-color-secondary); +} + +.account-highlight-chip ion-icon, +.account-highlight-chip svg { + font-size: 1rem; +} + +.account-details-card { + gap: 18px; + border: none; +} + +.account-info-item { + --background: #f7f5fb; + --border-radius: 20px; + --padding-start: 16px; + --padding-end: 16px; + --inner-padding-end: 0px; +} + +.account-info-item h2 { + margin: 0 0 6px; + font-size: 0.82rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ion-color-primary); +} + +.account-info-item p { + margin: 0; + color: var(--ion-color-dark); + font-size: 1rem; + font-weight: 600; + word-break: break-word; +} + +.account-trust-row { + display: grid; + grid-template-columns: auto 1fr; + gap: 14px; + align-items: flex-start; + padding: 4px 2px; +} + +.account-trust-icon { + width: 42px; + height: 42px; + border-radius: 14px; + background: rgba(22, 163, 74, 0.12); + color: var(--ion-color-success); + display: flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; +} + +.account-trust-row h3 { + margin: 0 0 6px; + font-size: 1rem; + font-weight: 700; + color: var(--ion-color-dark); +} + +.account-trust-row p { + margin: 0; + color: rgba(55, 65, 81, 0.74); + line-height: 1.5; +} + +.account-primary-button, +.account-secondary-button { + --border-radius: 18px; + min-height: 50px; +} + +.account-primary-button { + --background: var(--ion-color-primary); + --box-shadow: none; +} + +.account-secondary-button { + --border-width: 1px; +} + +@media (max-width: 420px) { + .home-summary-card { + flex-direction: column; + align-items: flex-start; + } + + .account-hero-image-wrap { + aspect-ratio: 4 / 3; + } +} diff --git a/src/utils/statusBar.ts b/src/utils/statusBar.ts new file mode 100644 index 0000000..efcc03f --- /dev/null +++ b/src/utils/statusBar.ts @@ -0,0 +1,23 @@ +import { StatusBar, Style } from '@capacitor/status-bar'; +export { Style }; + +const _origin = (() => { + try { if (document.referrer) return new URL(document.referrer).origin; } catch {} + return window.location.origin; +})(); + +/** Set status bar icon/text colour. Use instead of StatusBar.setStyle directly. */ +export async function setStatusBarStyle(style: Style): Promise { + try { await StatusBar.setStyle({ style }); } catch {} + if (window.parent !== window) { + window.parent.postMessage({ type: '__apsuite_statusbar', style }, _origin); + } +} + +/** Set status bar background colour (Android). Use instead of StatusBar.setBackgroundColor directly. */ +export async function setStatusBarBackground(color: string): Promise { + try { await (StatusBar as any).setBackgroundColor({ color }); } catch {} + if (window.parent !== window) { + window.parent.postMessage({ type: '__apsuite_statusbar_bg', color }, _origin); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..59b2e95 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "types": ["vite/client"] + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..9d31e2a --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..25a3792 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,27 @@ +import { defineConfig, type Plugin } from 'vite'; +import react from '@vitejs/plugin-react'; + +const injectRouterBasename: Plugin = { + name: 'inject-router-basename', + enforce: 'pre', + transform(code, id) { + if (!/\.[jt]sx$/.test(id)) return; + return code.replace( + /]*basename)([^>]*)>/g, + '', + ); + }, +}; + +export default defineConfig({ + plugins: [injectRouterBasename, react()], + server: { + host: '0.0.0.0', + port: 8100, + allowedHosts: true, + hmr: { + clientPort: process.env.HMR_CLIENT_PORT ? parseInt(process.env.HMR_CLIENT_PORT) : undefined, + }, + }, + css: { devSourcemap: false }, +}); diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..24caffa --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1779 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/compat-data@^7.28.6": + version "7.29.3" + +"@babel/core@^7.28.0": + version "7.29.0" + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/remapping" "^2.3.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.29.0": + version "7.29.1" + dependencies: + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + dependencies: + "@babel/compat-data" "^7.28.6" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-globals@^7.28.0": + version "7.28.0" + +"@babel/helper-module-imports@^7.28.6": + version "7.28.6" + dependencies: + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + dependencies: + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" + +"@babel/helper-plugin-utils@^7.27.1": + version "7.28.6" + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + +"@babel/helpers@^7.28.6": + version "7.29.2" + dependencies: + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.3" + dependencies: + "@babel/types" "^7.29.0" + +"@babel/plugin-transform-react-jsx-self@^7.27.1": + version "7.27.1" + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-react-jsx-source@^7.27.1": + version "7.27.1" + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.13": + version "7.29.2" + +"@babel/template@^7.28.6": + version "7.28.6" + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + debug "^4.3.1" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + +"@capacitor-firebase/authentication@^7.0.0": + version "7.5.0" + resolved "https://registry.npmjs.org/@capacitor-firebase/authentication/-/authentication-7.5.0.tgz" + integrity sha512-xPvKXWZhb0ZP/ysGYADWmFngQK82sPbRsZXE8b9W65MkFdoDc5df5O+gZTjw2wbVRFuUKqn07FHn9guxMg1iQw== + +"@capacitor/android@8.3.4": + version "8.3.4" + +"@capacitor/app@8.1.0": + version "8.1.0" + +"@capacitor/cli@8.3.4": + version "8.3.4" + dependencies: + "@ionic/cli-framework-output" "^2.2.8" + "@ionic/utils-subprocess" "^3.0.1" + "@ionic/utils-terminal" "^2.3.5" + commander "^12.1.0" + debug "^4.4.0" + env-paths "^2.2.0" + fs-extra "^11.2.0" + kleur "^4.1.5" + native-run "^2.0.3" + open "^8.4.0" + plist "^3.1.0" + prompts "^2.4.2" + rimraf "^6.0.1" + semver "^7.6.3" + tar "^7.5.3" + tslib "^2.8.1" + xml2js "^0.6.2" + +"@capacitor/core@8.3.4": + version "8.3.4" + dependencies: + tslib "^2.1.0" + +"@capacitor/google-maps@^8.0.0": + version "8.0.1" + resolved "https://registry.npmjs.org/@capacitor/google-maps/-/google-maps-8.0.1.tgz" + integrity sha512-XSOyanbtOeO5KrSfoJOpbcSW4EXvfdxp+SUy6umdIGFeR2WJKNnPC6isXn+zjJgJH8kblT2X4fF0dxhZV8wLBg== + dependencies: + "@googlemaps/js-api-loader" "^2.0.2" + "@googlemaps/markerclusterer" "^2.6.2" + "@types/google.maps" "^3.58.1" + +"@capacitor/haptics@8.0.2": + version "8.0.2" + +"@capacitor/ios@8.3.4": + version "8.3.4" + +"@capacitor/keyboard@8.0.3": + version "8.0.3" + +"@capacitor/push-notifications@^8.1.1": + version "8.1.1" + resolved "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-8.1.1.tgz" + integrity sha512-WqzjPKIbYbARMN+GC0XMAJcxJpUUzqgzS/Ny8RODLrro38pQhm3GXYwX2Mwd+LZlLY39rGImkCkrKyQSNfuikA== + +"@capacitor/status-bar@8.0.2": + version "8.0.2" + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + +"@firebase/ai@1.4.1": + version "1.4.1" + resolved "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.1.tgz" + integrity sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/analytics-compat@0.2.23": + version "0.2.23" + resolved "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.23.tgz" + integrity sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw== + dependencies: + "@firebase/analytics" "0.10.17" + "@firebase/analytics-types" "0.8.3" + "@firebase/component" "0.6.18" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/analytics-types@0.8.3": + version "0.8.3" + resolved "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz" + integrity sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg== + +"@firebase/analytics@0.10.17": + version "0.10.17" + resolved "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.17.tgz" + integrity sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/installations" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/app-check-compat@0.3.26": + version "0.3.26" + resolved "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.26.tgz" + integrity sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ== + dependencies: + "@firebase/app-check" "0.10.1" + "@firebase/app-check-types" "0.5.3" + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/app-check-interop-types@0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz" + integrity sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A== + +"@firebase/app-check-types@0.5.3": + version "0.5.3" + resolved "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz" + integrity sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng== + +"@firebase/app-check@0.10.1": + version "0.10.1" + resolved "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.1.tgz" + integrity sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/app-compat@0.4.2": + version "0.4.2" + resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz" + integrity sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q== + dependencies: + "@firebase/app" "0.13.2" + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/app-types@0.9.3": + version "0.9.3" + resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz" + integrity sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw== + +"@firebase/app@0.13.2": + version "0.13.2" + resolved "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz" + integrity sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/auth-compat@0.5.28": + version "0.5.28" + resolved "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.28.tgz" + integrity sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q== + dependencies: + "@firebase/auth" "1.10.8" + "@firebase/auth-types" "0.13.0" + "@firebase/component" "0.6.18" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/auth-interop-types@0.2.4": + version "0.2.4" + resolved "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz" + integrity sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA== + +"@firebase/auth-types@0.13.0": + version "0.13.0" + resolved "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz" + integrity sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg== + +"@firebase/auth@1.10.8": + version "1.10.8" + resolved "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz" + integrity sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/component@0.6.18": + version "0.6.18" + resolved "https://registry.npmjs.org/@firebase/component/-/component-0.6.18.tgz" + integrity sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ== + dependencies: + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/data-connect@0.3.10": + version "0.3.10" + resolved "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.10.tgz" + integrity sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ== + dependencies: + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/database-compat@2.0.11": + version "2.0.11" + resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.11.tgz" + integrity sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/database" "1.0.20" + "@firebase/database-types" "1.0.15" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/database-types@1.0.15": + version "1.0.15" + resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.15.tgz" + integrity sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw== + dependencies: + "@firebase/app-types" "0.9.3" + "@firebase/util" "1.12.1" + +"@firebase/database@1.0.20": + version "1.0.20" + resolved "https://registry.npmjs.org/@firebase/database/-/database-1.0.20.tgz" + integrity sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + faye-websocket "0.11.4" + tslib "^2.1.0" + +"@firebase/firestore-compat@0.3.53": + version "0.3.53" + resolved "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.53.tgz" + integrity sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/firestore" "4.8.0" + "@firebase/firestore-types" "3.0.3" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/firestore-types@3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz" + integrity sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q== + +"@firebase/firestore@4.8.0": + version "4.8.0" + resolved "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.8.0.tgz" + integrity sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + "@firebase/webchannel-wrapper" "1.0.3" + "@grpc/grpc-js" "~1.9.0" + "@grpc/proto-loader" "^0.7.8" + tslib "^2.1.0" + +"@firebase/functions-compat@0.3.26": + version "0.3.26" + resolved "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.26.tgz" + integrity sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/functions" "0.12.9" + "@firebase/functions-types" "0.6.3" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/functions-types@0.6.3": + version "0.6.3" + resolved "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz" + integrity sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg== + +"@firebase/functions@0.12.9": + version "0.12.9" + resolved "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.9.tgz" + integrity sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg== + dependencies: + "@firebase/app-check-interop-types" "0.3.3" + "@firebase/auth-interop-types" "0.2.4" + "@firebase/component" "0.6.18" + "@firebase/messaging-interop-types" "0.2.3" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/installations-compat@0.2.18": + version "0.2.18" + resolved "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.18.tgz" + integrity sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/installations" "0.6.18" + "@firebase/installations-types" "0.5.3" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/installations-types@0.5.3": + version "0.5.3" + resolved "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz" + integrity sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA== + +"@firebase/installations@0.6.18": + version "0.6.18" + resolved "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.18.tgz" + integrity sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/util" "1.12.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/logger@0.4.4": + version "0.4.4" + resolved "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz" + integrity sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g== + dependencies: + tslib "^2.1.0" + +"@firebase/messaging-compat@0.2.22": + version "0.2.22" + resolved "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.22.tgz" + integrity sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/messaging" "0.12.22" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/messaging-interop-types@0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz" + integrity sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q== + +"@firebase/messaging@0.12.22": + version "0.12.22" + resolved "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.22.tgz" + integrity sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/installations" "0.6.18" + "@firebase/messaging-interop-types" "0.2.3" + "@firebase/util" "1.12.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/performance-compat@0.2.20": + version "0.2.20" + resolved "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.20.tgz" + integrity sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/performance" "0.7.7" + "@firebase/performance-types" "0.2.3" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/performance-types@0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz" + integrity sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ== + +"@firebase/performance@0.7.7": + version "0.7.7" + resolved "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.7.tgz" + integrity sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/installations" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + web-vitals "^4.2.4" + +"@firebase/remote-config-compat@0.2.18": + version "0.2.18" + resolved "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.18.tgz" + integrity sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/remote-config" "0.6.5" + "@firebase/remote-config-types" "0.4.0" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/remote-config-types@0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz" + integrity sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg== + +"@firebase/remote-config@0.6.5": + version "0.6.5" + resolved "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.5.tgz" + integrity sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/installations" "0.6.18" + "@firebase/logger" "0.4.4" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/storage-compat@0.3.24": + version "0.3.24" + resolved "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.24.tgz" + integrity sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/storage" "0.13.14" + "@firebase/storage-types" "0.8.3" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/storage-types@0.8.3": + version "0.8.3" + resolved "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz" + integrity sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg== + +"@firebase/storage@0.13.14": + version "0.13.14" + resolved "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.14.tgz" + integrity sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g== + dependencies: + "@firebase/component" "0.6.18" + "@firebase/util" "1.12.1" + tslib "^2.1.0" + +"@firebase/util@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@firebase/util/-/util-1.12.1.tgz" + integrity sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w== + dependencies: + tslib "^2.1.0" + +"@firebase/webchannel-wrapper@1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz" + integrity sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ== + +"@googlemaps/js-api-loader@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-2.0.2.tgz" + integrity sha512-bKVuTqatS8Jven5aFqVB7rCHF1VFEzpzyi0ruzO0GUR+A7m9oMqMgtnmpANj7kMYEvvhty8Fk7TnJ1MKjWHu+Q== + dependencies: + "@types/google.maps" "^3.53.1" + +"@googlemaps/markerclusterer@^2.6.2": + version "2.6.2" + resolved "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.6.2.tgz" + integrity sha512-U6uVhq8iWhiIckA89sgRu8OK35mjd6/3CuoZKWakKEf0QmRRWpatlsPb3kqXkoWSmbcZkopRiI4dnW6DQSd7bQ== + dependencies: + "@types/supercluster" "^7.1.3" + fast-equals "^5.2.2" + supercluster "^8.0.1" + +"@grpc/grpc-js@~1.9.0": + version "1.9.16" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.16.tgz" + integrity sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.8": + version "0.7.15" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz" + integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.5" + yargs "^17.7.2" + +"@ionic/cli-framework-output@^2.2.8": + version "2.2.8" + dependencies: + "@ionic/utils-terminal" "2.3.5" + debug "^4.0.0" + tslib "^2.0.1" + +"@ionic/core@8.8.7": + version "8.8.7" + dependencies: + "@stencil/core" "4.43.0" + ionicons "^8.0.13" + tslib "^2.1.0" + +"@ionic/pwa-elements@^3.4.0": + version "3.4.0" + resolved "https://registry.npmjs.org/@ionic/pwa-elements/-/pwa-elements-3.4.0.tgz" + integrity sha512-WmL7An4NOsvJvct4V/A8hudr+yXgLBHYmxzqWGsetZmzN7C6suLRS/KIh2P1t8cSYCYL7HRrbv8NrIChPkh6Ow== + +"@ionic/react-router@^8.5.0": + version "8.8.7" + dependencies: + "@ionic/react" "8.8.7" + tslib "*" + +"@ionic/react@^8.5.0", "@ionic/react@8.8.7": + version "8.8.7" + dependencies: + "@ionic/core" "8.8.7" + ionicons "^8.0.13" + tslib "*" + +"@ionic/utils-array@2.1.6": + version "2.1.6" + dependencies: + debug "^4.0.0" + tslib "^2.0.1" + +"@ionic/utils-fs@^3.1.7", "@ionic/utils-fs@3.1.7": + version "3.1.7" + dependencies: + "@types/fs-extra" "^8.0.0" + debug "^4.0.0" + fs-extra "^9.0.0" + tslib "^2.0.1" + +"@ionic/utils-object@2.1.6": + version "2.1.6" + dependencies: + debug "^4.0.0" + tslib "^2.0.1" + +"@ionic/utils-process@2.1.12": + version "2.1.12" + dependencies: + "@ionic/utils-object" "2.1.6" + "@ionic/utils-terminal" "2.3.5" + debug "^4.0.0" + signal-exit "^3.0.3" + tree-kill "^1.2.2" + tslib "^2.0.1" + +"@ionic/utils-stream@3.1.7": + version "3.1.7" + dependencies: + debug "^4.0.0" + tslib "^2.0.1" + +"@ionic/utils-subprocess@^3.0.1": + version "3.0.1" + dependencies: + "@ionic/utils-array" "2.1.6" + "@ionic/utils-fs" "3.1.7" + "@ionic/utils-process" "2.1.12" + "@ionic/utils-stream" "3.1.7" + "@ionic/utils-terminal" "2.3.5" + cross-spawn "^7.0.3" + debug "^4.0.0" + tslib "^2.0.1" + +"@ionic/utils-terminal@^2.3.4", "@ionic/utils-terminal@^2.3.5", "@ionic/utils-terminal@2.3.5": + version "2.3.5" + dependencies: + "@types/slice-ansi" "^4.0.0" + debug "^4.0.0" + signal-exit "^3.0.3" + slice-ansi "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + tslib "^2.0.1" + untildify "^4.0.0" + wrap-ansi "^7.0.0" + +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + dependencies: + minipass "^7.0.4" + +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + +"@jridgewell/source-map@^0.3.3": + version "0.3.11" + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.31" + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz" + integrity sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g== + +"@protobufjs/eventemitter@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz" + integrity sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg== + +"@protobufjs/fetch@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz" + integrity sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz" + integrity sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz" + integrity sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg== + +"@rolldown/pluginutils@1.0.0-beta.27": + version "1.0.0-beta.27" + +"@rollup/rollup-linux-x64-gnu@4.34.9": + version "4.34.9" + +"@rollup/rollup-linux-x64-gnu@4.44.0": + version "4.44.0" + +"@rollup/rollup-linux-x64-gnu@4.60.4": + version "4.60.4" + +"@stencil/core@^4.0.3", "@stencil/core@^4.35.3": + version "4.43.4" + optionalDependencies: + "@rollup/rollup-darwin-arm64" "4.44.0" + "@rollup/rollup-darwin-x64" "4.44.0" + "@rollup/rollup-linux-arm64-gnu" "4.44.0" + "@rollup/rollup-linux-arm64-musl" "4.44.0" + "@rollup/rollup-linux-x64-gnu" "4.44.0" + "@rollup/rollup-linux-x64-musl" "4.44.0" + "@rollup/rollup-win32-arm64-msvc" "4.44.0" + "@rollup/rollup-win32-x64-msvc" "4.44.0" + +"@stencil/core@4.43.0": + version "4.43.0" + optionalDependencies: + "@rollup/rollup-darwin-arm64" "4.34.9" + "@rollup/rollup-darwin-x64" "4.34.9" + "@rollup/rollup-linux-arm64-gnu" "4.34.9" + "@rollup/rollup-linux-arm64-musl" "4.34.9" + "@rollup/rollup-linux-x64-gnu" "4.34.9" + "@rollup/rollup-linux-x64-musl" "4.34.9" + "@rollup/rollup-win32-arm64-msvc" "4.34.9" + "@rollup/rollup-win32-x64-msvc" "4.34.9" + +"@supabase/auth-js@2.106.1": + version "2.106.1" + dependencies: + tslib "2.8.1" + +"@supabase/functions-js@2.106.1": + version "2.106.1" + dependencies: + tslib "2.8.1" + +"@supabase/phoenix@^0.4.2": + version "0.4.2" + +"@supabase/postgrest-js@2.106.1": + version "2.106.1" + dependencies: + tslib "2.8.1" + +"@supabase/realtime-js@2.106.1": + version "2.106.1" + dependencies: + "@supabase/phoenix" "^0.4.2" + tslib "2.8.1" + +"@supabase/storage-js@2.106.1": + version "2.106.1" + dependencies: + iceberg-js "^0.8.1" + tslib "2.8.1" + +"@supabase/supabase-js@^2.0.0": + version "2.106.1" + dependencies: + "@supabase/auth-js" "2.106.1" + "@supabase/functions-js" "2.106.1" + "@supabase/postgrest-js" "2.106.1" + "@supabase/realtime-js" "2.106.1" + "@supabase/storage-js" "2.106.1" + +"@types/babel__core@^7.20.5": + version "7.20.5" + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.27.0" + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.28.0" + dependencies: + "@babel/types" "^7.28.2" + +"@types/estree@1.0.8": + version "1.0.8" + +"@types/fs-extra@^8.0.0": + version "8.1.5" + dependencies: + "@types/node" "*" + +"@types/geojson@*": + version "7946.0.16" + resolved "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz" + integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== + +"@types/google.maps@^3.53.1", "@types/google.maps@^3.58.1": + version "3.64.1" + resolved "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.64.1.tgz" + integrity sha512-nEBoa6iDNipICtxJ5VlrOgPNZQ6ixIg5nuv8iryFj0Z/1NLgxyg3pQCVegPuCzGCyTQwQI/N3uZvLUysqAzaaw== + +"@types/history@^4.7.11": + version "4.7.11" + +"@types/katex@^0.16.0": + version "0.16.8" + +"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": + version "25.9.1" + dependencies: + undici-types ">=7.24.0 <7.24.7" + +"@types/react-dom@^19.0.0": + version "19.2.3" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz" + integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== + +"@types/react-router-dom@^5.3.3": + version "5.3.3" + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*", "@types/react-router@^5.1.20": + version "5.1.20" + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + +"@types/react@*", "@types/react@^19.0.0": + version "19.2.15" + resolved "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz" + integrity sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q== + dependencies: + csstype "^3.2.2" + +"@types/slice-ansi@^4.0.0": + version "4.0.0" + +"@types/supercluster@^7.1.3": + version "7.1.3" + resolved "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz" + integrity sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA== + dependencies: + "@types/geojson" "*" + +"@vitejs/plugin-react@^4.0.1": + version "4.7.0" + dependencies: + "@babel/core" "^7.28.0" + "@babel/plugin-transform-react-jsx-self" "^7.27.1" + "@babel/plugin-transform-react-jsx-source" "^7.27.1" + "@rolldown/pluginutils" "1.0.0-beta.27" + "@types/babel__core" "^7.20.5" + react-refresh "^0.17.0" + +"@xmldom/xmldom@^0.9.10": + version "0.9.10" + +acorn@^8.15.0: + version "8.16.0" + +ansi-regex@^5.0.1: + version "5.0.1" + +ansi-styles@^4.0.0: + version "4.3.0" + dependencies: + color-convert "^2.0.1" + +astral-regex@^2.0.0: + version "2.0.0" + +at-least-node@^1.0.0: + version "1.0.0" + +balanced-match@^4.0.2: + version "4.0.4" + +base64-js@^1.5.1: + version "1.5.1" + +baseline-browser-mapping@^2.10.12: + version "2.10.31" + +big-integer@1.6.x: + version "1.6.52" + +bplist-parser@^0.3.2: + version "0.3.2" + dependencies: + big-integer "1.6.x" + +brace-expansion@^5.0.5: + version "5.0.6" + dependencies: + balanced-match "^4.0.2" + +browserslist@^4.24.0: + version "4.28.2" + dependencies: + baseline-browser-mapping "^2.10.12" + caniuse-lite "^1.0.30001782" + electron-to-chromium "^1.5.328" + node-releases "^2.0.36" + update-browserslist-db "^1.2.3" + +buffer-crc32@~0.2.3: + version "0.2.13" + +buffer-from@^1.0.0: + version "1.1.2" + +caniuse-lite@^1.0.30001782: + version "1.0.30001793" + +chownr@^3.0.0: + version "3.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + +commander@^12.1.0: + version "12.1.0" + +commander@^2.20.0: + version "2.20.3" + +commander@^8.3.0: + version "8.3.0" + +convert-source-map@^2.0.0: + version "2.0.0" + +cross-spawn@^7.0.3: + version "7.0.6" + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +csstype@^3.2.2: + version "3.2.3" + +debug@^4.0.0, debug@^4.1.0, debug@^4.3.1, debug@^4.3.4, debug@^4.4.0: + version "4.4.3" + dependencies: + ms "^2.1.3" + +define-lazy-prop@^2.0.0: + version "2.0.0" + +electron-to-chromium@^1.5.328: + version "1.5.360" + +elementtree@^0.1.7: + version "0.1.7" + dependencies: + sax "1.1.4" + +emoji-regex@^8.0.0: + version "8.0.0" + +env-paths@^2.2.0: + version "2.2.1" + +esbuild@^0.21.3: + version "0.21.5" + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + +fast-equals@^5.2.2: + version "5.4.0" + resolved "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz" + integrity sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw== + +faye-websocket@0.11.4: + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fd-slicer@~1.1.0: + version "1.1.0" + dependencies: + pend "~1.2.0" + +firebase@^11.0.0: + version "11.10.0" + resolved "https://registry.npmjs.org/firebase/-/firebase-11.10.0.tgz" + integrity sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w== + dependencies: + "@firebase/ai" "1.4.1" + "@firebase/analytics" "0.10.17" + "@firebase/analytics-compat" "0.2.23" + "@firebase/app" "0.13.2" + "@firebase/app-check" "0.10.1" + "@firebase/app-check-compat" "0.3.26" + "@firebase/app-compat" "0.4.2" + "@firebase/app-types" "0.9.3" + "@firebase/auth" "1.10.8" + "@firebase/auth-compat" "0.5.28" + "@firebase/data-connect" "0.3.10" + "@firebase/database" "1.0.20" + "@firebase/database-compat" "2.0.11" + "@firebase/firestore" "4.8.0" + "@firebase/firestore-compat" "0.3.53" + "@firebase/functions" "0.12.9" + "@firebase/functions-compat" "0.3.26" + "@firebase/installations" "0.6.18" + "@firebase/installations-compat" "0.2.18" + "@firebase/messaging" "0.12.22" + "@firebase/messaging-compat" "0.2.22" + "@firebase/performance" "0.7.7" + "@firebase/performance-compat" "0.2.20" + "@firebase/remote-config" "0.6.5" + "@firebase/remote-config-compat" "0.2.18" + "@firebase/storage" "0.13.14" + "@firebase/storage-compat" "0.3.24" + "@firebase/util" "1.12.1" + +fs-extra@^11.2.0: + version "11.3.5" + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.0: + version "9.1.0" + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +glob@^13.0.3: + version "13.0.6" + dependencies: + minimatch "^10.2.2" + minipass "^7.1.3" + path-scurry "^2.0.2" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + +history@^4.9.0: + version "4.10.1" + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + +hoist-non-react-statics@^3.1.0: + version "3.3.2" + dependencies: + react-is "^16.7.0" + +http-parser-js@>=0.5.1: + version "0.5.10" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz" + integrity sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA== + +iceberg-js@^0.8.1: + version "0.8.1" + +idb@7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + +inherits@^2.0.3: + version "2.0.4" + +ini@^4.1.1: + version "4.1.3" + +ionicons@^7.4.0: + version "7.4.0" + dependencies: + "@stencil/core" "^4.0.3" + +ionicons@^8.0.13: + version "8.0.13" + dependencies: + "@stencil/core" "^4.35.3" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + +is-wsl@^2.2.0: + version "2.2.0" + dependencies: + is-docker "^2.0.0" + +isarray@0.0.1: + version "0.0.1" + +isexe@^2.0.0: + version "2.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + +jsesc@^3.0.2: + version "3.1.0" + +json5@^2.2.3: + version "2.2.3" + +jsonfile@^6.0.1: + version "6.2.1" + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +katex@^0.16.0: + version "0.16.47" + dependencies: + commander "^8.3.0" + +kdbush@^4.0.2: + version "4.1.0" + resolved "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz" + integrity sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ== + +kleur@^3.0.3: + version "3.0.3" + +kleur@^4.1.5: + version "4.1.5" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +long@^5.0.0, long@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/long/-/long-5.3.2.tgz" + integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + +loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^11.0.0: + version "11.5.0" + +lru-cache@^5.1.1: + version "5.1.1" + dependencies: + yallist "^3.0.2" + +minimatch@^10.2.2: + version "10.2.5" + dependencies: + brace-expansion "^5.0.5" + +minipass@^7.0.4, minipass@^7.1.2, minipass@^7.1.3: + version "7.1.3" + +minizlib@^3.1.0: + version "3.1.0" + dependencies: + minipass "^7.1.2" + +ms@^2.1.3: + version "2.1.3" + +nanoid@^3.3.12: + version "3.3.12" + +native-run@^2.0.3: + version "2.0.3" + dependencies: + "@ionic/utils-fs" "^3.1.7" + "@ionic/utils-terminal" "^2.3.4" + bplist-parser "^0.3.2" + debug "^4.3.4" + elementtree "^0.1.7" + ini "^4.1.1" + plist "^3.1.0" + split2 "^4.2.0" + through2 "^4.0.2" + tslib "^2.6.2" + yauzl "^2.10.0" + +node-releases@^2.0.36: + version "2.0.44" + +object-assign@^4.1.1: + version "4.1.1" + +open@^8.4.0: + version "8.4.2" + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +package-json-from-dist@^1.0.1: + version "1.0.1" + +path-key@^3.1.0: + version "3.1.1" + +path-scurry@^2.0.2: + version "2.0.2" + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + +path-to-regexp@^1.7.0: + version "1.9.0" + dependencies: + isarray "0.0.1" + +pend@~1.2.0: + version "1.2.0" + +picocolors@^1.1.1: + version "1.1.1" + +plist@^3.1.0: + version "3.1.1" + dependencies: + "@xmldom/xmldom" "^0.9.10" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + +postcss@^8.4.43: + version "8.5.15" + dependencies: + nanoid "^3.3.12" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +prompts@^2.4.2: + version "2.4.2" + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.6.2: + version "15.8.1" + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +protobufjs@^7.2.5: + version "7.6.1" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.1.tgz" + integrity sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.5" + "@protobufjs/eventemitter" "^1.1.1" + "@protobufjs/fetch" "^1.1.1" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.2" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.1" + "@types/node" ">=13.7.0" + long "^5.3.2" + +react-dom@18.2.0: + version "18.2.0" + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-hook-form@^7.54.0: + version "7.76.1" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.76.1.tgz" + integrity sha512-rYM7tPiWlu3nZchkR/ex7piyzui2vFPyaLnXnI/RnblB/L4qfMmyses8llJVtF1NpE9WBBsJlGtcSZzPCXW1qQ== + +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: + version "16.13.1" + +react-katex@^3.0.1: + version "3.1.0" + dependencies: + katex "^0.16.0" + +react-refresh@^0.17.0: + version "0.17.0" + +react-router-dom@^5.3.4: + version "5.3.4" + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.3.4" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@^5.3.4, react-router@5.3.4: + version "5.3.4" + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react@18.2.0: + version "18.2.0" + dependencies: + loose-envify "^1.1.0" + +readable-stream@3: + version "3.6.2" + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve-pathname@^3.0.0: + version "3.0.0" + +rimraf@^6.0.1: + version "6.1.3" + dependencies: + glob "^13.0.3" + package-json-from-dist "^1.0.1" + +rollup@^4.20.0: + version "4.60.4" + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.60.4" + "@rollup/rollup-android-arm64" "4.60.4" + "@rollup/rollup-darwin-arm64" "4.60.4" + "@rollup/rollup-darwin-x64" "4.60.4" + "@rollup/rollup-freebsd-arm64" "4.60.4" + "@rollup/rollup-freebsd-x64" "4.60.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.60.4" + "@rollup/rollup-linux-arm-musleabihf" "4.60.4" + "@rollup/rollup-linux-arm64-gnu" "4.60.4" + "@rollup/rollup-linux-arm64-musl" "4.60.4" + "@rollup/rollup-linux-loong64-gnu" "4.60.4" + "@rollup/rollup-linux-loong64-musl" "4.60.4" + "@rollup/rollup-linux-ppc64-gnu" "4.60.4" + "@rollup/rollup-linux-ppc64-musl" "4.60.4" + "@rollup/rollup-linux-riscv64-gnu" "4.60.4" + "@rollup/rollup-linux-riscv64-musl" "4.60.4" + "@rollup/rollup-linux-s390x-gnu" "4.60.4" + "@rollup/rollup-linux-x64-gnu" "4.60.4" + "@rollup/rollup-linux-x64-musl" "4.60.4" + "@rollup/rollup-openbsd-x64" "4.60.4" + "@rollup/rollup-openharmony-arm64" "4.60.4" + "@rollup/rollup-win32-arm64-msvc" "4.60.4" + "@rollup/rollup-win32-ia32-msvc" "4.60.4" + "@rollup/rollup-win32-x64-gnu" "4.60.4" + "@rollup/rollup-win32-x64-msvc" "4.60.4" + fsevents "~2.3.2" + +safe-buffer@>=5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + +sax@>=0.6.0: + version "1.6.0" + +sax@1.1.4: + version "1.1.4" + +scheduler@^0.23.0: + version "0.23.2" + dependencies: + loose-envify "^1.1.0" + +semver@^6.3.1: + version "6.3.1" + +semver@^7.6.3: + version "7.8.0" + +shebang-command@^2.0.0: + version "2.0.0" + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + +signal-exit@^3.0.3: + version "3.0.7" + +sisteransi@^1.0.5: + version "1.0.5" + +slice-ansi@^4.0.0: + version "4.0.0" + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +source-map-js@^1.2.1: + version "1.2.1" + +source-map-support@~0.5.20: + version "0.5.21" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + +split2@^4.2.0: + version "4.2.0" + +string_decoder@^1.1.1: + version "1.3.0" + dependencies: + safe-buffer "~5.2.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + dependencies: + ansi-regex "^5.0.1" + +supercluster@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz" + integrity sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ== + dependencies: + kdbush "^4.0.2" + +tar@^7.5.3: + version "7.5.15" + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" + +terser@^5.4.0: + version "5.47.1" + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.15.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +through2@^4.0.2: + version "4.0.2" + dependencies: + readable-stream "3" + +tiny-invariant@^1.0.2: + version "1.3.3" + +tiny-warning@^1.0.0: + version "1.0.3" + +tree-kill@^1.2.2: + version "1.2.2" + +tslib@*, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.6.2, tslib@^2.8.1, tslib@2.8.1: + version "2.8.1" + +typescript@~5.9.0: + version "5.9.3" + +"undici-types@>=7.24.0 <7.24.7": + version "7.24.6" + +universalify@^2.0.0: + version "2.0.1" + +untildify@^4.0.0: + version "4.0.0" + +update-browserslist-db@^1.2.3: + version "1.2.3" + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +util-deprecate@^1.0.1: + version "1.0.2" + +value-equal@^1.0.1: + version "1.0.1" + +vite@^5.0.0: + version "5.4.21" + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +web-vitals@^4.2.4: + version "4.2.4" + resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz" + integrity sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw== + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +which@^2.0.1: + version "2.0.2" + dependencies: + isexe "^2.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +xml2js@^0.6.2: + version "0.6.2" + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@^15.1.1: + version "15.1.1" + +xmlbuilder@~11.0.0: + version "11.0.1" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + +yallist@^5.0.0: + version "5.0.0" + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yauzl@^2.10.0: + version "2.10.0" + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +zod@^3.24.0: + version "3.25.76" + resolved "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== + +zustand@^5.0.0: + version "5.0.14" + resolved "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz" + integrity sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==