Compare commits

..

6 Commits

Author SHA1 Message Date
Matthew Raymer
d9ce884513 fix: configure Vite for proper Node.js module handling in Electron
- Add vite-plugin-node-polyfills to provide Node.js built-in module polyfills
- Configure build target as 'node18' for Electron environment
- Switch to CommonJS format for Electron builds
- Add specific polyfills for sqlite3 dependencies (util, stream, buffer)
- Mark Node.js built-in modules as external in Electron builds

This fixes the "Module util has been externalized" error by properly handling
Node.js modules in the Electron environment, particularly for sqlite3 which
depends on Node.js built-in modules.
2025-05-26 14:06:21 +00:00
Matthew Raymer
a1a1543ae1 fix: update component imports in HomeView.vue
- Replace non-existent index.ts import with direct component imports
- Fix ChoiceButtonDialog import to use default import syntax
- Import ImageViewer directly from its component file

This fixes the component loading issues while maintaining the existing functionality.
The remaining linter errors are unrelated to these import changes and should be
addressed separately.
2025-05-26 13:22:59 +00:00
Matt Raymer
93591a5815 docs: storage documentation and feature checklist 2025-05-26 08:43:33 -04:00
Matt Raymer
b30c4c8b30 refactor: migrate database operations to PlatformService
- Add account management methods to PlatformService interface
- Implement account operations in all platform services
- Fix PlatformCapabilities interface by adding sqlite property
- Update util.ts to use PlatformService for account operations
- Standardize account and settings management across platforms

This change improves code organization by:
- Centralizing database operations through PlatformService
- Ensuring consistent account management across platforms
- Making platform-specific implementations more maintainable
- Reducing direct database access in utility functions

Note: Some linter errors remain regarding db.accounts access and sqlite
capabilities that need to be addressed in a follow-up commit.
2025-05-26 06:54:10 -04:00
Matt Raymer
1f9db0ba94 chore:update 2025-05-26 00:33:24 -04:00
Matt Raymer
bdc2d71d3c docs: migrate web storage implementation from wa-sql to absurd-sql
- Update web platform storage solution to use absurd-sql with IndexedDB backend

- Replace wa-sqlite dependencies with absurd-sql and @jlongster/sql.js

- Update WebSQLiteService implementation with SQLiteFS and IndexedDBBackend

- Add performance optimizations (WAL mode, mmap, temp store)

- Add type-safe query method and improved error handling

- Update platform capabilities matrix with new features

- Add absurd-sql compatibility checks in migration service

This change improves transaction support, performance, and reliability of the web platform's SQLite implementation.
2025-05-26 00:32:26 -04:00
541 changed files with 24111 additions and 83799 deletions

View File

@@ -1,310 +0,0 @@
# Cursor Markdown Ruleset for TimeSafari Documentation
## Overview
This ruleset enforces consistent markdown formatting standards across all project
documentation, ensuring readability, maintainability, and compliance with
markdownlint best practices.
## General Formatting Standards
### Line Length
- **Maximum line length**: 80 characters
- **Exception**: Code blocks (JSON, shell, TypeScript, etc.) - no line length
enforcement
- **Rationale**: Ensures readability across different screen sizes and terminal
widths
### Blank Lines
- **Headings**: Must be surrounded by blank lines above and below
- **Lists**: Must be surrounded by blank lines above and below
- **Code blocks**: Must be surrounded by blank lines above and below
- **Maximum consecutive blank lines**: 1 (no multiple blank lines)
- **File start**: No blank lines at the beginning of the file
- **File end**: Single newline character at the end
### Whitespace
- **No trailing spaces**: Remove all trailing whitespace from lines
- **No tabs**: Use spaces for indentation
- **Consistent indentation**: 2 spaces for list items and nested content
## Heading Standards
### Format
- **Style**: ATX-style headings (`#`, `##`, `###`, etc.)
- **Case**: Title case for general headings
- **Code references**: Use backticks for file names and technical terms
-`### Current package.json Scripts`
-`### Current Package.json Scripts`
### Hierarchy
- **H1 (#)**: Document title only
- **H2 (##)**: Major sections
- **H3 (###)**: Subsections
- **H4 (####)**: Sub-subsections
- **H5+**: Avoid deeper nesting
## List Standards
### Unordered Lists
- **Marker**: Use `-` (hyphen) consistently
- **Indentation**: 2 spaces for nested items
- **Blank lines**: Surround lists with blank lines
### Ordered Lists
- **Format**: `1.`, `2.`, `3.` (sequential numbering)
- **Indentation**: 2 spaces for nested items
- **Blank lines**: Surround lists with blank lines
### Task Lists
- **Format**: `- [ ]` for incomplete, `- [x]` for complete
- **Use case**: Project planning, checklists, implementation tracking
## Code Block Standards
### Fenced Code Blocks
- **Syntax**: Triple backticks with language specification
- **Languages**: `json`, `bash`, `typescript`, `javascript`, `yaml`, `markdown`
- **Blank lines**: Must be surrounded by blank lines above and below
- **Line length**: No enforcement within code blocks
### Inline Code
- **Format**: Single backticks for inline code references
- **Use case**: File names, commands, variables, properties
## Special Content Standards
### JSON Examples
```json
{
"property": "value",
"nested": {
"property": "value"
}
}
```
### Shell Commands
```bash
# Command with comment
npm run build:web
# Multi-line command
VITE_GIT_HASH=`git log -1 --pretty=format:%h` \
vite build --config vite.config.web.mts
```
### TypeScript Examples
```typescript
// Function with JSDoc
/**
* Get environment configuration
* @param env - Environment name
* @returns Environment config object
*/
const getEnvironmentConfig = (env: string) => {
switch (env) {
case 'prod':
return { /* production settings */ };
default:
return { /* development settings */ };
}
};
```
## File Structure Standards
### Document Header
```markdown
# Document Title
**Author**: Matthew Raymer
**Date**: YYYY-MM-DD
**Status**: 🎯 **STATUS** - Brief description
## Overview
Brief description of the document's purpose and scope.
```
### Section Organization
1. **Overview/Introduction**
2. **Current State Analysis**
3. **Implementation Plan**
4. **Technical Details**
5. **Testing & Validation**
6. **Next Steps**
## Markdownlint Configuration
### Required Rules
```json
{
"MD013": { "code_blocks": false },
"MD012": true,
"MD022": true,
"MD031": true,
"MD032": true,
"MD047": true,
"MD009": true
}
```
### Rule Explanations
- **MD013**: Line length (disabled for code blocks)
- **MD012**: No multiple consecutive blank lines
- **MD022**: Headings should be surrounded by blank lines
- **MD031**: Fenced code blocks should be surrounded by blank lines
- **MD032**: Lists should be surrounded by blank lines
- **MD047**: Files should end with a single newline
- **MD009**: No trailing spaces
## Validation Commands
### Check Single File
```bash
npx markdownlint docs/filename.md
```
### Check All Documentation
```bash
npx markdownlint docs/
```
### Auto-fix Common Issues
```bash
# Remove trailing spaces
sed -i 's/[[:space:]]*$//' docs/filename.md
# Remove multiple blank lines
sed -i '/^$/N;/^\n$/D' docs/filename.md
# Add newline at end if missing
echo "" >> docs/filename.md
```
## Common Patterns
### Implementation Plans
```markdown
## Implementation Plan
### Phase 1: Foundation (Day 1)
#### 1.1 Component Setup
- [ ] Create new component file
- [ ] Add basic structure
- [ ] Implement core functionality
#### 1.2 Configuration
- [ ] Update configuration files
- [ ] Add environment variables
- [ ] Test configuration loading
```
### Status Tracking
```markdown
**Status**: ✅ **COMPLETE** - All phases finished
**Progress**: 75% (15/20 components)
**Next**: Ready for testing phase
```
### Performance Metrics
```markdown
#### 📊 Performance Metrics
- **Build Time**: 2.3 seconds (50% faster than baseline)
- **Bundle Size**: 1.2MB (30% reduction)
- **Success Rate**: 100% (no failures in 50 builds)
```
## Enforcement
### Pre-commit Hooks
- Run markdownlint on all changed markdown files
- Block commits with linting violations
- Auto-fix common issues when possible
### CI/CD Integration
- Include markdownlint in build pipeline
- Generate reports for documentation quality
- Fail builds with critical violations
### Team Guidelines
- All documentation PRs must pass markdownlint
- Use provided templates for new documents
- Follow established patterns for consistency
## Templates
### New Document Template
```markdown
# Document Title
**Author**: Matthew Raymer
**Date**: YYYY-MM-DD
**Status**: 🎯 **PLANNING** - Ready for Implementation
## Overview
Brief description of the document's purpose and scope.
## Current State
Description of current situation or problem.
## Implementation Plan
### Phase 1: Foundation
- [ ] Task 1
- [ ] Task 2
## Next Steps
1. **Review and approve plan**
2. **Begin implementation**
3. **Test and validate**
---
**Status**: Ready for implementation
**Priority**: Medium
**Estimated Effort**: X days
**Dependencies**: None
**Stakeholders**: Development team
```
---
**Last Updated**: 2025-07-09
**Version**: 1.0
**Maintainer**: Matthew Raymer

172
.cursor/rules/SQLITE.mdc Normal file
View File

@@ -0,0 +1,172 @@
---
description:
globs:
alwaysApply: true
---
# @capacitor-community/sqlite MDC Ruleset
## Project Overview
This ruleset is for the `@capacitor-community/sqlite` plugin, a Capacitor community plugin that provides native and Electron SQLite database functionality with encryption support.
## Key Features
- Native SQLite database support for iOS, Android, and Electron
- Database encryption support using SQLCipher (Native) and better-sqlite3-multiple-ciphers (Electron)
- Biometric authentication support
- Cross-platform database operations
- JSON import/export capabilities
- Database migration support
- Sync table functionality
## Platform Support Matrix
### Core Database Operations
| Operation | Android | iOS | Electron | Web |
|-----------|---------|-----|----------|-----|
| Create Connection (RW) | ✅ | ✅ | ✅ | ✅ |
| Create Connection (RO) | ✅ | ✅ | ✅ | ❌ |
| Open DB (non-encrypted) | ✅ | ✅ | ✅ | ✅ |
| Open DB (encrypted) | ✅ | ✅ | ✅ | ❌ |
| Execute/Query | ✅ | ✅ | ✅ | ✅ |
| Import/Export JSON | ✅ | ✅ | ✅ | ✅ |
### Security Features
| Feature | Android | iOS | Electron | Web |
|---------|---------|-----|----------|-----|
| Encryption | ✅ | ✅ | ✅ | ❌ |
| Biometric Auth | ✅ | ✅ | ✅ | ❌ |
| Secret Management | ✅ | ✅ | ✅ | ❌ |
## Configuration Requirements
### Base Configuration
```typescript
// capacitor.config.ts
{
plugins: {
CapacitorSQLite: {
iosDatabaseLocation: 'Library/CapacitorDatabase',
iosIsEncryption: true,
iosKeychainPrefix: 'your-app-prefix',
androidIsEncryption: true,
electronIsEncryption: true
}
}
}
```
### Platform-Specific Requirements
#### Android
- Minimum SDK: 23
- Target SDK: 35
- Required Gradle JDK: 21
- Required Android Gradle Plugin: 8.7.2
- Required manifest settings for backup prevention
- Required data extraction rules
#### iOS
- No additional configuration needed beyond base setup
- Supports biometric authentication
- Uses keychain for encryption
#### Electron
Required dependencies:
```json
{
"dependencies": {
"better-sqlite3-multiple-ciphers": "latest",
"electron-json-storage": "latest",
"jszip": "latest",
"node-fetch": "2.6.7",
"crypto": "latest",
"crypto-js": "latest"
}
}
```
#### Web
- Requires `sql.js` and `jeep-sqlite`
- Manual copy of `sql-wasm.wasm` to assets folder
- Framework-specific asset placement:
- Angular: `src/assets/`
- Vue/React: `public/assets/`
## Best Practices
### Database Operations
1. Always close connections after use
2. Use transactions for multiple operations
3. Implement proper error handling
4. Use prepared statements for queries
5. Implement proper database versioning
### Security
1. Always use encryption for sensitive data
2. Implement proper secret management
3. Use biometric authentication when available
4. Follow platform-specific security guidelines
### Performance
1. Use appropriate indexes
2. Implement connection pooling
3. Use transactions for bulk operations
4. Implement proper database cleanup
## Common Issues and Solutions
### Android
- Build data properties conflict: Add to `app/build.gradle`:
```gradle
packagingOptions {
exclude 'build-data.properties'
}
```
### Electron
- Node-fetch version must be ≤2.6.7
- For Capacitor Electron v5:
- Use Electron@25.8.4
- Add `"skipLibCheck": true` to tsconfig.json
### Web
- Ensure proper WASM file placement
- Handle browser compatibility
- Implement proper fallbacks
## Version Compatibility
- Requires Node.js ≥16.0.0
- Compatible with Capacitor ≥7.0.0
- Supports TypeScript 4.1.5+
## Testing Requirements
- Unit tests for database operations
- Platform-specific integration tests
- Encryption/decryption tests
- Biometric authentication tests
- Migration tests
- Sync functionality tests
## Documentation
- API Documentation: `/docs/API.md`
- Connection API: `/docs/APIConnection.md`
- DB Connection API: `/docs/APIDBConnection.md`
- Release Notes: `/docs/info_releases.md`
- Changelog: `CHANGELOG.md`
## Contributing Guidelines
- Follow Ionic coding standards
- Use provided linting and formatting tools
- Maintain platform compatibility
- Update documentation
- Add appropriate tests
- Follow semantic versioning
## Maintenance
- Regular security updates
- Platform compatibility checks
- Performance optimization
- Documentation updates
- Dependency updates
## License
MIT License - See LICENSE file for details

View File

@@ -7,13 +7,13 @@ alwaysApply: true
## 1. Platform Support Matrix
| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) |
|---------|-----------|-------------------|-------------------|
| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented |
| Deep Linking | URL Parameters | App URL Open Events | Not Implemented |
| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs |
| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented |
| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks |
| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | PyWebView (Desktop) |
|---------|-----------|-------------------|-------------------|-------------------|
| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented | Not Implemented |
| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | Not Implemented |
| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | PyWebView Python Bridge |
| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | Not Implemented |
| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks | process.env checks |
## 2. Project Structure
@@ -42,6 +42,7 @@ src/
├── main.common.ts # Shared initialization
├── main.capacitor.ts # Mobile entry
├── main.electron.ts # Electron entry
├── main.pywebview.ts # PyWebView entry
└── main.web.ts # Web/PWA entry
```
@@ -51,7 +52,9 @@ root/
├── vite.config.common.mts # Shared config
├── vite.config.capacitor.mts # Mobile build
├── vite.config.electron.mts # Electron build
── vite.config.web.mts # Web/PWA build
── vite.config.pywebview.mts # PyWebView build
├── vite.config.web.mts # Web/PWA build
└── vite.config.utils.mts # Build utilities
```
## 3. Service Architecture
@@ -65,7 +68,8 @@ services/
├── platforms/ # Platform-specific services
│ ├── WebPlatformService.ts
│ ├── CapacitorPlatformService.ts
── ElectronPlatformService.ts
── ElectronPlatformService.ts
│ └── PyWebViewPlatformService.ts
└── factory/ # Service factories
└── PlatformServiceFactory.ts
```
@@ -149,7 +153,7 @@ export function createBuildConfig(mode: string) {
return {
define: {
'process.env.VITE_PLATFORM': JSON.stringify(mode),
// PWA is automatically enabled for web platforms via build configuration
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isNative),
__IS_MOBILE__: JSON.stringify(isCapacitor),
__USE_QR_READER__: JSON.stringify(!isCapacitor)
}
@@ -163,7 +167,8 @@ export function createBuildConfig(mode: string) {
# Build commands from package.json
"build:web": "vite build --config vite.config.web.mts",
"build:capacitor": "vite build --config vite.config.capacitor.mts",
"build:electron": "vite build --config vite.config.electron.mts"
"build:electron": "vite build --config vite.config.electron.mts",
"build:pywebview": "vite build --config vite.config.pywebview.mts"
```
## 6. Testing Strategy

View File

@@ -1,35 +0,0 @@
---
description: rules used while developing
globs:
alwaysApply: true
---
✅ use system date command to timestamp all interactions with accurate date and time
✅ python script files must always have a blank line at their end
✅ remove whitespace at the end of lines
✅ use npm run lint-fix to check for warnings
✅ do not use npm run dev let me handle running and supplying feedback
✅ do not add or commit for the user; let him control that process
always preview changes and commit message to use and allow me to copy and paste
✅ Preferred Commit Message Format
Short summary in the first line (concise and high-level).
Avoid long commit bodies unless truly necessary.
✅ Valued Content in Commit Messages
Specific fixes or features.
Symptoms or problems that were fixed.
Notes about tests passing or TS/linting errors being resolved (briefly).
❌ Avoid in Commit Messages
Vague terms: “improved”, “enhanced”, “better” — especially from AI.
Minor changes: small doc tweaks, one-liners, cleanup, or lint fixes.
Redundant blurbs: repeated across files or too generic.
Multiple overlapping purposes in a single commit — prefer narrow, focused commits.
Long explanations of what can be deduced from good in-line code comments.
Guiding Principle
Let code and inline documentation speak for themselves. Use commits to highlight what isn't obvious from reading the code.

View File

@@ -1,6 +0,0 @@
---
description:
globs:
alwaysApply: true
---
All references in the codebase to Dexie apply only to migration from IndexedDb to Sqlite and will be deprecated in future versions.

View File

@@ -1,314 +0,0 @@
---
globs: *.md
alwaysApply: false
---
# Cursor Markdown Ruleset for TimeSafari Documentation
## Overview
This ruleset enforces consistent markdown formatting standards across all project
documentation, ensuring readability, maintainability, and compliance with
markdownlint best practices.
## General Formatting Standards
### Line Length
- **Maximum line length**: 80 characters
- **Exception**: Code blocks (JSON, shell, TypeScript, etc.) - no line length
enforcement
- **Rationale**: Ensures readability across different screen sizes and terminal
widths
### Blank Lines
- **Headings**: Must be surrounded by blank lines above and below
- **Lists**: Must be surrounded by blank lines above and below
- **Code blocks**: Must be surrounded by blank lines above and below
- **Maximum consecutive blank lines**: 1 (no multiple blank lines)
- **File start**: No blank lines at the beginning of the file
- **File end**: Single newline character at the end
### Whitespace
- **No trailing spaces**: Remove all trailing whitespace from lines
- **No tabs**: Use spaces for indentation
- **Consistent indentation**: 2 spaces for list items and nested content
## Heading Standards
### Format
- **Style**: ATX-style headings (`#`, `##`, `###`, etc.)
- **Case**: Title case for general headings
- **Code references**: Use backticks for file names and technical terms
- ✅ `### Current package.json Scripts`
- ❌ `### Current Package.json Scripts`
### Hierarchy
- **H1 (#)**: Document title only
- **H2 (##)**: Major sections
- **H3 (###)**: Subsections
- **H4 (####)**: Sub-subsections
- **H5+**: Avoid deeper nesting
## List Standards
### Unordered Lists
- **Marker**: Use `-` (hyphen) consistently
- **Indentation**: 2 spaces for nested items
- **Blank lines**: Surround lists with blank lines
### Ordered Lists
- **Format**: `1.`, `2.`, `3.` (sequential numbering)
- **Indentation**: 2 spaces for nested items
- **Blank lines**: Surround lists with blank lines
### Task Lists
- **Format**: `- [ ]` for incomplete, `- [x]` for complete
- **Use case**: Project planning, checklists, implementation tracking
## Code Block Standards
### Fenced Code Blocks
- **Syntax**: Triple backticks with language specification
- **Languages**: `json`, `bash`, `typescript`, `javascript`, `yaml`, `markdown`
- **Blank lines**: Must be surrounded by blank lines above and below
- **Line length**: No enforcement within code blocks
### Inline Code
- **Format**: Single backticks for inline code references
- **Use case**: File names, commands, variables, properties
## Special Content Standards
### JSON Examples
```json
{
"property": "value",
"nested": {
"property": "value"
}
}
```
### Shell Commands
```bash
# Command with comment
npm run build:web
# Multi-line command
VITE_GIT_HASH=`git log -1 --pretty=format:%h` \
vite build --config vite.config.web.mts
```
### TypeScript Examples
```typescript
// Function with JSDoc
/**
* Get environment configuration
* @param env - Environment name
* @returns Environment config object
*/
const getEnvironmentConfig = (env: string) => {
switch (env) {
case 'prod':
return { /* production settings */ };
default:
return { /* development settings */ };
}
};
```
## File Structure Standards
### Document Header
```markdown
# Document Title
**Author**: Matthew Raymer
**Date**: YYYY-MM-DD
**Status**: 🎯 **STATUS** - Brief description
## Overview
Brief description of the document's purpose and scope.
```
### Section Organization
1. **Overview/Introduction**
2. **Current State Analysis**
3. **Implementation Plan**
4. **Technical Details**
5. **Testing & Validation**
6. **Next Steps**
## Markdownlint Configuration
### Required Rules
```json
{
"MD013": { "code_blocks": false },
"MD012": true,
"MD022": true,
"MD031": true,
"MD032": true,
"MD047": true,
"MD009": true
}
```
### Rule Explanations
- **MD013**: Line length (disabled for code blocks)
- **MD012**: No multiple consecutive blank lines
- **MD022**: Headings should be surrounded by blank lines
- **MD031**: Fenced code blocks should be surrounded by blank lines
- **MD032**: Lists should be surrounded by blank lines
- **MD047**: Files should end with a single newline
- **MD009**: No trailing spaces
## Validation Commands
### Check Single File
```bash
npx markdownlint docs/filename.md
```
### Check All Documentation
```bash
npx markdownlint docs/
```
### Auto-fix Common Issues
```bash
# Remove trailing spaces
sed -i 's/[[:space:]]*$//' docs/filename.md
# Remove multiple blank lines
sed -i '/^$/N;/^\n$/D' docs/filename.md
# Add newline at end if missing
echo "" >> docs/filename.md
```
## Common Patterns
### Implementation Plans
```markdown
## Implementation Plan
### Phase 1: Foundation (Day 1)
#### 1.1 Component Setup
- [ ] Create new component file
- [ ] Add basic structure
- [ ] Implement core functionality
#### 1.2 Configuration
- [ ] Update configuration files
- [ ] Add environment variables
- [ ] Test configuration loading
```
### Status Tracking
```markdown
**Status**: ✅ **COMPLETE** - All phases finished
**Progress**: 75% (15/20 components)
**Next**: Ready for testing phase
```
### Performance Metrics
```markdown
#### 📊 Performance Metrics
- **Build Time**: 2.3 seconds (50% faster than baseline)
- **Bundle Size**: 1.2MB (30% reduction)
- **Success Rate**: 100% (no failures in 50 builds)
```
## Enforcement
### Pre-commit Hooks
- Run markdownlint on all changed markdown files
- Block commits with linting violations
- Auto-fix common issues when possible
### CI/CD Integration
- Include markdownlint in build pipeline
- Generate reports for documentation quality
- Fail builds with critical violations
### Team Guidelines
- All documentation PRs must pass markdownlint
- Use provided templates for new documents
- Follow established patterns for consistency
## Templates
### New Document Template
```markdown
# Document Title
**Author**: Matthew Raymer
**Date**: YYYY-MM-DD
**Status**: 🎯 **PLANNING** - Ready for Implementation
## Overview
Brief description of the document's purpose and scope.
## Current State
Description of current situation or problem.
## Implementation Plan
### Phase 1: Foundation
- [ ] Task 1
- [ ] Task 2
## Next Steps
1. **Review and approve plan**
2. **Begin implementation**
3. **Test and validate**
---
**Status**: Ready for implementation
**Priority**: Medium
**Estimated Effort**: X days
**Dependencies**: None
**Stakeholders**: Development team
```
---
**Last Updated**: 2025-07-09
**Version**: 1.0
**Maintainer**: Matthew Raymer

267
.cursor/rules/wa-sqlite.mdc Normal file
View File

@@ -0,0 +1,267 @@
---
description:
globs:
alwaysApply: true
---
# wa-sqlite Usage Guide
## Table of Contents
- [1. Overview](#1-overview)
- [2. Installation](#2-installation)
- [3. Basic Setup](#3-basic-setup)
- [3.1 Import and Initialize](#31-import-and-initialize)
- [3.2 Basic Database Operations](#32-basic-database-operations)
- [4. Virtual File Systems (VFS)](#4-virtual-file-systems-vfs)
- [4.1 Available VFS Options](#41-available-vfs-options)
- [4.2 Using a VFS](#42-using-a-vfs)
- [5. Best Practices](#5-best-practices)
- [5.1 Error Handling](#51-error-handling)
- [5.2 Transaction Management](#52-transaction-management)
- [5.3 Prepared Statements](#53-prepared-statements)
- [6. Performance Considerations](#6-performance-considerations)
- [7. Common Issues and Solutions](#7-common-issues-and-solutions)
- [8. TypeScript Support](#8-typescript-support)
## 1. Overview
wa-sqlite is a WebAssembly build of SQLite that enables SQLite database operations in web browsers and JavaScript environments. It provides both synchronous and asynchronous builds, with support for custom virtual file systems (VFS) for persistent storage.
## 2. Installation
```bash
npm install wa-sqlite
# or
yarn add wa-sqlite
```
## 3. Basic Setup
### 3.1 Import and Initialize
```javascript
// Choose one of these imports based on your needs:
// - wa-sqlite.mjs: Synchronous build
// - wa-sqlite-async.mjs: Asynchronous build (required for async VFS)
// - wa-sqlite-jspi.mjs: JSPI-based async build (experimental, Chromium only)
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite.mjs';
import * as SQLite from 'wa-sqlite';
async function initDatabase() {
// Initialize SQLite module
const module = await SQLiteESMFactory();
const sqlite3 = SQLite.Factory(module);
// Open database (returns a Promise)
const db = await sqlite3.open_v2('myDatabase');
return { sqlite3, db };
}
```
### 3.2 Basic Database Operations
```javascript
async function basicOperations() {
const { sqlite3, db } = await initDatabase();
try {
// Create a table
await sqlite3.exec(db, `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`);
// Insert data
await sqlite3.exec(db, `
INSERT INTO users (name, email)
VALUES ('John Doe', 'john@example.com')
`);
// Query data
const results = [];
await sqlite3.exec(db, 'SELECT * FROM users', (row, columns) => {
results.push({ row, columns });
});
return results;
} finally {
// Always close the database when done
await sqlite3.close(db);
}
}
```
## 4. Virtual File Systems (VFS)
### 4.1 Available VFS Options
wa-sqlite provides several VFS implementations for persistent storage:
1. **IDBBatchAtomicVFS** (Recommended for general use)
- Uses IndexedDB with batch atomic writes
- Works in all contexts (Window, Worker, Service Worker)
- Supports WAL mode
- Best performance with `PRAGMA synchronous=normal`
2. **IDBMirrorVFS**
- Keeps files in memory, persists to IndexedDB
- Works in all contexts
- Good for smaller databases
3. **OPFS-based VFS** (Origin Private File System)
- Various implementations available:
- AccessHandlePoolVFS
- OPFSAdaptiveVFS
- OPFSCoopSyncVFS
- OPFSPermutedVFS
- Better performance but limited to Worker contexts
### 4.2 Using a VFS
```javascript
import { IDBBatchAtomicVFS } from 'wa-sqlite/src/examples/IDBBatchAtomicVFS.js';
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite-async.mjs';
import * as SQLite from 'wa-sqlite';
async function initDatabaseWithVFS() {
const module = await SQLiteESMFactory();
const sqlite3 = SQLite.Factory(module);
// Register VFS
const vfs = await IDBBatchAtomicVFS.create('myApp', module);
sqlite3.vfs_register(vfs, true);
// Open database with VFS
const db = await sqlite3.open_v2('myDatabase');
// Configure for better performance
await sqlite3.exec(db, 'PRAGMA synchronous = normal');
await sqlite3.exec(db, 'PRAGMA journal_mode = WAL');
return { sqlite3, db };
}
```
## 5. Best Practices
### 5.1 Error Handling
```javascript
async function safeDatabaseOperation() {
const { sqlite3, db } = await initDatabase();
try {
await sqlite3.exec(db, 'SELECT * FROM non_existent_table');
} catch (error) {
if (error.code === SQLite.SQLITE_ERROR) {
console.error('SQL error:', error.message);
} else {
console.error('Database error:', error);
}
} finally {
await sqlite3.close(db);
}
}
```
### 5.2 Transaction Management
```javascript
async function transactionExample() {
const { sqlite3, db } = await initDatabase();
try {
await sqlite3.exec(db, 'BEGIN TRANSACTION');
// Perform multiple operations
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Alice']);
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Bob']);
await sqlite3.exec(db, 'COMMIT');
} catch (error) {
await sqlite3.exec(db, 'ROLLBACK');
throw error;
} finally {
await sqlite3.close(db);
}
}
```
### 5.3 Prepared Statements
```javascript
async function preparedStatementExample() {
const { sqlite3, db } = await initDatabase();
try {
// Prepare statement
const stmt = await sqlite3.prepare(db, 'SELECT * FROM users WHERE id = ?');
// Execute with different parameters
await sqlite3.bind(stmt, 1, 1);
while (await sqlite3.step(stmt) === SQLite.SQLITE_ROW) {
const row = sqlite3.row(stmt);
console.log(row);
}
// Reset and reuse
await sqlite3.reset(stmt);
await sqlite3.bind(stmt, 1, 2);
// ... execute again
await sqlite3.finalize(stmt);
} finally {
await sqlite3.close(db);
}
}
```
## 6. Performance Considerations
1. **VFS Selection**
- Use IDBBatchAtomicVFS for general-purpose applications
- Consider OPFS-based VFS for better performance in Worker contexts
- Use MemoryVFS for temporary databases
2. **Configuration**
- Set appropriate page size (default is usually fine)
- Use WAL mode for better concurrency
- Consider `PRAGMA synchronous=normal` for better performance
- Adjust cache size based on your needs
3. **Concurrency**
- Use transactions for multiple operations
- Be aware of VFS-specific concurrency limitations
- Consider using Web Workers for heavy database operations
## 7. Common Issues and Solutions
1. **Database Locking**
- Use appropriate transaction isolation levels
- Implement retry logic for busy errors
- Consider using WAL mode
2. **Storage Limitations**
- Be aware of browser storage quotas
- Implement cleanup strategies
- Monitor database size
3. **Cross-Context Access**
- Use appropriate VFS for your context
- Consider message passing for cross-context communication
- Be aware of storage access limitations
## 8. TypeScript Support
wa-sqlite includes TypeScript definitions. The main types are:
```typescript
type SQLiteCompatibleType = number | string | Uint8Array | Array<number> | bigint | null;
interface SQLiteAPI {
open_v2(filename: string, flags?: number, zVfs?: string): Promise<number>;
exec(db: number, sql: string, callback?: (row: any[], columns: string[]) => void): Promise<number>;
close(db: number): Promise<number>;
// ... other methods
}
```
## Additional Resources
- [Official GitHub Repository](https://github.com/rhashimoto/wa-sqlite)
- [Online Demo](https://rhashimoto.github.io/wa-sqlite/demo/)
- [API Reference](https://rhashimoto.github.io/wa-sqlite/docs/)
- [FAQ](https://github.com/rhashimoto/wa-sqlite/issues?q=is%3Aissue+label%3Afaq+)
- [Discussion Forums](https://github.com/rhashimoto/wa-sqlite/discussions)

View File

@@ -1,171 +0,0 @@
# TimeSafari Docker Ignore File
# Author: Matthew Raymer
# Description: Excludes unnecessary files from Docker build context
#
# Benefits:
# - Faster build times
# - Smaller build context
# - Reduced image size
# - Better security (excludes sensitive files)
# Dependencies
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Build outputs
# dist - Allow dist directory for Docker builds (contains pre-built assets)
dist-*
build
*.tsbuildinfo
# Development files
.git
.gitignore
README.md
CHANGELOG.md
CONTRIBUTING.md
BUILDING.md
LICENSE
# IDE and editor files
.vscode
.idea
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Dependency directories
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# Test files
test-playwright
test-playwright-results
test-results
test-scripts
# Documentation
doc
# Scripts (keep only what's needed for build)
scripts/test-*.sh
scripts/*.js
scripts/README.md
# Platform-specific files
android
ios
electron
# Docker files (avoid recursive copying)
Dockerfile*
docker-compose*
.dockerignore
# CI/CD files
.github
.gitlab-ci.yml
.travis.yml
.circleci
# Temporary files
tmp
temp
# Backup files
*.bak
*.backup
# Archive files
*.tar
*.tar.gz
*.zip
*.rar
# Certificate files
*.pem
*.key
*.crt
*.p12
# Configuration files that might contain secrets
*.secrets
secrets.json
config.local.json

View File

@@ -1,14 +1,12 @@
# Only the variables that start with VITE_ are seen in the application import.meta.env in Vue.
# iOS doesn't like spaces in the app title.
TIME_SAFARI_APP_TITLE="TimeSafari_Dev"
VITE_APP_SERVER=http://localhost:8080
VITE_APP_SERVER=http://localhost:3000
# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production).
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000
# Using shared server by default to ease setup, which works for shared test users.
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000
#VITE_DEFAULT_PUSH_SERVER... can't be set up with localhost domain
VITE_PASSKEYS_ENABLED=true

6
.env.example Normal file
View File

@@ -0,0 +1,6 @@
# Admin DID credentials
ADMIN_DID=did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F
ADMIN_PRIVATE_KEY=2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b
# API Configuration
ENDORSER_API_URL=https://test-api.endorser.ch/api/v2/claim

View File

@@ -9,4 +9,3 @@ VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app

View File

@@ -9,5 +9,4 @@ VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch
VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app
VITE_PASSKEYS_ENABLED=true

View File

@@ -4,12 +4,6 @@ module.exports = {
node: true,
es2022: true,
},
ignorePatterns: [
'node_modules/',
'dist/',
'dist-electron/',
'*.d.ts'
],
extends: [
"plugin:vue/vue3-recommended",
"eslint:recommended",

79
.gitignore vendored
View File

@@ -51,81 +51,6 @@ vendor/
# Build logs
build_logs/
# PWA icon files generated by capacitor-assets
icons
android/app/src/main/assets/public
android/app/src/main/res
*.log
# Generated Android assets and resources (should be generated during build)
android/app/src/main/assets/public/
# Generated Android resources (icons, splash screens, etc.)
android/app/src/main/res/drawable*/
android/app/src/main/res/mipmap*/
android/app/src/main/res/values/ic_launcher_background.xml
# Keep these Android configuration files in version control:
# - android/app/src/main/assets/capacitor.plugins.json
# - android/app/src/main/res/values/strings.xml
# - android/app/src/main/res/values/styles.xml
# - android/app/src/main/res/layout/activity_main.xml
# - android/app/src/main/res/xml/config.xml
# - android/app/src/main/res/xml/file_paths.xml
sql-wasm.wasm
# Temporary and generated files
temp.*
*.tmp
*.temp
*.bak
*.cache
git.diff.*
*.har
# Development artifacts
dev-dist/
*.map
# OS generated files
Thumbs.db
ehthumbs.db
Desktop.ini
# Capacitor build outputs and generated files
android/app/build/
android/capacitor-cordova-android-plugins/build/
ios/App/App/public/assets/
ios/App/App/build/
ios/App/build/
# Capacitor build artifacts (covered by android/app/build/ above)
# Keep these Capacitor files in version control:
# - capacitor.config.json (root, electron, ios)
# - src/main.capacitor.ts
# - vite.config.capacitor.mts
# - android/capacitor.settings.gradle
# - android/app/capacitor.build.gradle
# - android/app/src/main/assets/capacitor.plugins.json
# Electron build outputs and generated files
electron/build/
electron/app/
electron/dist/
electron/out/
# Keep these Electron files in version control:
# - electron/src/preload.ts (source)
# - electron/src/index.ts (source)
# - electron/src/setup.ts (source)
# - electron/package.json
# - electron/electron-builder.config.json
# - electron/build-packages.sh
# - electron/live-runner.js
# - electron/resources/electron-publisher-custom.js
# Gradle cache files
android/.gradle/file-system.probe
android/.gradle/caches/
coverage

View File

@@ -1 +0,0 @@
{"MD013": {"code_blocks": false}}

1
.npmrc
View File

@@ -1 +0,0 @@
@jsr:registry=https://npm.jsr.io

File diff suppressed because it is too large Load Diff

View File

@@ -5,46 +5,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.3] - 2025.07.12
### Changed
- Photo is pinned to profile mode
### Fixed
- Deep link URLs (and other prod settings)
- Error in BVC begin view
## [Unreleased]
### Changed
- Photo is pinned to profile mode
## [1.0.2] - 2025.06.20 - 276e0a741bc327de3380c4e508cccb7fee58c06d
### Added
- Version on feed title
## [1.0.1] - 2025.06.20
### Added
- Allow a user to block someone else's content from view
## [1.0.0] - 2025.06.20 - 5aa693de6337e5dbb278bfddc6bd39094bc14f73
### Added
- Web-oriented migration from IndexedDB to SQLite
## [0.5.8]
### Added
- /deep-link/ path for URLs that are shared with people
### Changed
- External links now go to /deep-link/...
- Feed visuals now have arrow imagery from giver to receiver
## [0.4.7]
### Fixed
- Cameras everywhere
### Changed
- IndexedDB -> SQLite
## [0.4.5] - 2025.02.23

View File

@@ -1,170 +1,36 @@
# TimeSafari Docker Build
# Author: Matthew Raymer
# Description: Multi-stage Docker build for TimeSafari web application
#
# Build Process:
# 1. Base stage: Node.js with build dependencies
# 2. Builder stage: Copy pre-built web assets from host
# 3. Production stage: Nginx server with optimized assets
#
# Note: Web assets are built on the host using npm scripts before Docker build
#
# Security Features:
# - Non-root user execution
# - Minimal attack surface with Alpine Linux
# - Multi-stage build to reduce image size
# - No build dependencies in final image
#
# Usage:
# IMPORTANT: Build web assets first, then build Docker image
#
# Using npm scripts (recommended):
# Production: npm run build:web:docker:prod
# Test: npm run build:web:docker:test
# Development: npm run build:web:docker
#
# Manual workflow:
# 1. Build web assets: npm run build:web:build -- --mode production
# 2. Build Docker: docker build -t timesafari:latest .
#
# Note: For development, use npm run build:web directly (no Docker needed)
#
# Build Arguments:
# BUILD_MODE: development, test, or production (default: production)
# NODE_ENV: node environment (default: production)
#
# Environment Variables:
# NODE_ENV: Build environment (development/production)
# BUILD_MODE: Build mode for asset selection (development/test/production)
#
# Build Context:
# This Dockerfile is designed to work when the build context is set to
# ./crowd-funder-for-time-pwa from the parent directory (where docker-compose.yml is located)
# Build stage
FROM node:22-alpine3.20 AS builder
# =============================================================================
# BASE STAGE - Common dependencies and setup
# =============================================================================
FROM node:22-alpine3.20 AS base
# Install build dependencies
# Install system dependencies for build process
RUN apk add --no-cache \
bash \
git \
python3 \
py3-pip \
py3-setuptools \
make \
g++ \
gcc \
&& rm -rf /var/cache/apk/*
# Create non-root user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
RUN apk add --no-cache bash git python3 py3-pip py3-setuptools make g++ gcc
# Set working directory
WORKDIR /app
# Copy package files for dependency installation
# Note: These files are in the project root (crowd-funder-for-time-pwa directory)
# Copy package files
COPY package*.json ./
# Install dependencies with security audit
RUN npm ci --only=production --audit --fund=false && \
npm audit fix --audit-level=moderate || true
# Install dependencies
RUN npm ci
# =============================================================================
# BUILDER STAGE - Copy pre-built assets
# =============================================================================
FROM base AS builder
# Copy source code
COPY . .
# Define build arguments with defaults
ARG BUILD_MODE=production
ARG NODE_ENV=production
# Build the application
RUN npm run build:web
# Set environment variables from build arguments
ENV BUILD_MODE=${BUILD_MODE}
ENV NODE_ENV=${NODE_ENV}
# Copy pre-built assets from host
# Note: dist/ directory is in the project root (crowd-funder-for-time-pwa directory)
COPY dist/ ./dist/
# Verify build output exists
RUN ls -la dist/ || (echo "Build output not found in dist/ directory" && exit 1)
# =============================================================================
# PRODUCTION STAGE - Nginx server
# =============================================================================
FROM nginx:alpine AS production
# Define build arguments for production stage
ARG BUILD_MODE=production
ARG NODE_ENV=production
# Set environment variables
ENV BUILD_MODE=${BUILD_MODE}
ENV NODE_ENV=${NODE_ENV}
# Install security updates and clean cache
RUN apk update && \
apk upgrade && \
apk add --no-cache \
curl \
&& rm -rf /var/cache/apk/*
# Use existing nginx user from base image (nginx user and group already exist)
# No need to create new user as nginx:alpine already has nginx user
# Copy main nginx configuration
COPY docker/nginx.conf /etc/nginx/nginx.conf
# Copy production nginx configuration
COPY docker/default.conf /etc/nginx/conf.d/default.conf
# Production stage
FROM nginx:alpine
# Copy built assets from builder stage
COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html
COPY --from=builder /app/dist /usr/share/nginx/html
# Create necessary directories with proper permissions
RUN mkdir -p /var/cache/nginx /var/log/nginx /tmp && \
chown -R nginx:nginx /var/cache/nginx /var/log/nginx /tmp && \
chown -R nginx:nginx /usr/share/nginx/html
# Switch to non-root user
USER nginx
# Copy nginx configuration if needed
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/ || exit 1
# Start nginx with proper signal handling
CMD ["nginx", "-g", "daemon off;"]
# =============================================================================
# TEST STAGE - For test environment testing
# =============================================================================
FROM production AS test
# Define build arguments for test stage
ARG BUILD_MODE=test
ARG NODE_ENV=test
# Set environment variables
ENV BUILD_MODE=${BUILD_MODE}
ENV NODE_ENV=${NODE_ENV}
# Copy test-specific nginx configuration
COPY docker/staging.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Health check for staging
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/health || exit 1
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -1,4 +1,5 @@
source "https://rubygems.org"
gem "fastlane"
gem "cocoapods"

View File

@@ -22,7 +22,26 @@ GEM
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.2)
aws-partitions (1.1066.0)
aws-sdk-core (3.220.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.99.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.182.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.11.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
benchmark (0.4.0)
bigdecimal (3.1.9)
@@ -64,13 +83,96 @@ GEM
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
concurrent-ruby (1.3.5)
connection_pool (2.5.0)
declarative (0.0.20)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
drb (2.2.1)
emoji_regex (3.2.3)
escape (0.0.4)
ethon (0.16.0)
ffi (>= 1.15.0)
excon (0.112.0)
faraday (1.10.4)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.1.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
fastlane (2.227.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored (~> 1.2)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-env (>= 1.6.0, < 2.0.0)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
http-cookie (~> 1.0.5)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
optparse (>= 0.1.1, < 1.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.5)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (~> 3)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.4.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
ffi (1.17.1)
ffi (1.17.1-aarch64-linux-gnu)
ffi (1.17.1-aarch64-linux-musl)
@@ -85,27 +187,107 @@ GEM
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.5.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.31.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.8.1)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.9.0)
mutex_m
i18n (1.14.7)
concurrent-ruby (~> 1.0)
jmespath (1.6.2)
json (2.10.2)
jwt (2.10.1)
base64
logger (1.6.6)
mini_magick (4.13.2)
mini_mime (1.1.5)
minitest (5.25.5)
molinillo (0.8.0)
multi_json (1.15.0)
multipart-post (2.4.1)
mutex_m (0.3.0)
nanaimo (0.4.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
nkf (0.2.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.2)
public_suffix (4.0.7)
rake (13.2.1)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.4.1)
rouge (3.28.0)
ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
securerandom (0.4.1)
security (0.1.5)
signet (0.19.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.10)
CFPropertyList
naturally
sysrandom (1.0.5)
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.2)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
typhoeus (1.4.1)
ethon (>= 0.9.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
@@ -113,6 +295,10 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.4.0)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
aarch64-linux-gnu
@@ -129,6 +315,7 @@ PLATFORMS
DEPENDENCIES
cocoapods
fastlane
BUNDLED WITH
2.6.5

127
README.md
View File

@@ -3,32 +3,6 @@
[Time Safari](https://timesafari.org/) allows people to ease into collaboration: start with expressions of gratitude
and expand to crowd-fund with time & money, then record and see the impact of contributions.
## Database Migration Status
**Current Status**: The application is undergoing a migration from Dexie (IndexedDB) to SQLite using absurd-sql. This migration is in **Phase 2** with a well-defined migration fence in place.
### Migration Progress
-**SQLite Database Service**: Fully implemented with absurd-sql
-**Platform Service Layer**: Unified database interface across platforms
-**Settings Migration**: Core user settings transferred
-**Account Migration**: Identity and key management
- 🔄 **Contact Migration**: User contact data (via import interface)
- 📋 **Code Cleanup**: Remove unused Dexie imports
### Migration Fence
The migration is controlled by a **migration fence** that separates legacy Dexie code from the new SQLite implementation. See [Migration Fence Definition](doc/migration-fence-definition.md) for complete details.
**Key Points**:
- Legacy Dexie database is disabled by default
- All database operations go through `PlatformServiceMixin`
- Migration tools provide controlled access to both databases
- Clear separation between legacy and new code
### Migration Documentation
- [Migration Guide](doc/migration-to-wa-sqlite.md) - Complete migration process
- [Migration Fence Definition](doc/migration-fence-definition.md) - Fence boundaries and rules
- [Database Migration Guide](doc/database-migration-guide.md) - User-facing migration tools
## Roadmap
See [project.task.yaml](project.task.yaml) for current priorities.
@@ -45,92 +19,18 @@ npm install
npm run dev
```
See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker).
See [BUILDING.md](BUILDING.md) for more details.
## Development Database Clearing
TimeSafari provides a simple script-based approach to clear the database for development purposes.
### Quick Usage
```bash
# Run the database clearing script
./scripts/clear-database.sh
# Then restart your development server
npm run build:electron:dev # For Electron
npm run build:web:dev # For Web
```
### What It Does
#### **Electron (Desktop App)**
- Automatically finds and clears the SQLite database files
- Works on Linux, macOS, and Windows
- Clears all data and forces fresh migrations on next startup
#### **Web Browser**
- Provides instructions for using custom browser data directories
- Shows manual clearing via browser DevTools
- Ensures reliable database clearing without browser complications
### Safety Features
-**Interactive Script**: Guides you through the process
-**Platform Detection**: Automatically detects your OS
-**Clear Instructions**: Step-by-step guidance for each platform
-**Safe Paths**: Only clears TimeSafari-specific data
### Manual Commands (if needed)
#### **Electron Database Location**
```bash
# Linux
rm -rf ~/.config/TimeSafari/*
# macOS
rm -rf ~/Library/Application\ Support/TimeSafari/*
# Windows
rmdir /s /q %APPDATA%\TimeSafari
```
#### **Web Browser (Custom Data Directory)**
```bash
# Create isolated browser profile
mkdir ~/timesafari-dev-data
```
## Domain Configuration
TimeSafari uses a centralized domain configuration system to ensure consistent
URL generation across all environments. This prevents localhost URLs from
appearing in shared links during development.
### Key Features
-**Production URLs for Sharing**: All copy link buttons use production domain
-**Environment-Specific Internal URLs**: Internal operations use appropriate
environment URLs
-**Single Point of Control**: Change domain in one place for entire app
-**Type-Safe Configuration**: Full TypeScript support
### Quick Reference
```typescript
// For sharing functionality (always production)
import { PROD_SHARE_DOMAIN } from "@/constants/app";
const shareLink = `${PROD_SHARE_DOMAIN}/deep-link/claim/123`;
// For internal operations (environment-specific)
import { APP_SERVER } from "@/constants/app";
const apiUrl = `${APP_SERVER}/api/claim/123`;
```
### Documentation
- [Domain Configuration System](docs/domain-configuration.md) - Complete guide
- [Constants and Configuration](src/constants/app.ts) - Core constants
## Tests
See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions.
## Icons
Application icons are in the `assets` directory, processed by the `capacitor-assets` command.
@@ -166,25 +66,6 @@ Key principles:
- Common interfaces are shared through `common.ts`
- Type definitions are generated from Zod schemas where possible
### Database Architecture
The application uses a platform-agnostic database layer with Vue mixins for service access:
* `src/services/PlatformService.ts` - Database interface definition
* `src/services/PlatformServiceFactory.ts` - Platform-specific service factory
* `src/services/AbsurdSqlDatabaseService.ts` - SQLite implementation
* `src/utils/PlatformServiceMixin.ts` - Vue mixin for database access with caching
* `src/db/` - Legacy Dexie database (migration in progress)
**Development Guidelines**:
- Always use `PlatformServiceMixin` for database operations in components
- Test with PlatformServiceMixin for new features
- Use migration tools for data transfer between systems
- Leverage mixin's ultra-concise methods: `$db()`, `$exec()`, `$one()`, `$contacts()`, `$settings()`
**Architecture Decision**: The project uses Vue mixins over Composition API composables for platform service access. See [Architecture Decisions](doc/architecture-decisions.md) for detailed rationale.
### Kudos
Gifts make the world go 'round!

7
android/.gitignore vendored
View File

@@ -84,6 +84,13 @@ 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

View File

@@ -31,8 +31,8 @@ android {
applicationId "app.timesafari.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 35
versionName "1.0.2"
versionCode 18
versionName "0.4.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -91,8 +91,6 @@ dependencies {
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
implementation project(':capacitor-community-sqlite')
implementation "androidx.biometric:biometric:1.2.0-alpha05"
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"

View File

@@ -9,7 +9,6 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-community-sqlite')
implementation project(':capacitor-mlkit-barcode-scanning')
implementation project(':capacitor-app')
implementation project(':capacitor-camera')

View File

@@ -13,7 +13,6 @@
android:exported="true"
android:label="@string/title_activity_main"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBarLaunch">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -2,6 +2,7 @@
"appId": "app.timesafari",
"appName": "TimeSafari",
"webDir": "dist",
"bundledWebRuntime": false,
"server": {
"cleartext": true
},
@@ -15,106 +16,6 @@
}
]
}
},
"SplashScreen": {
"launchShowDuration": 3000,
"launchAutoHide": true,
"backgroundColor": "#ffffff",
"androidSplashResourceName": "splash",
"androidScaleType": "CENTER_CROP",
"showSpinner": false,
"androidSpinnerStyle": "large",
"iosSpinnerStyle": "small",
"spinnerColor": "#999999",
"splashFullScreen": true,
"splashImmersive": true
},
"CapacitorSQLite": {
"iosDatabaseLocation": "Library/CapacitorDatabase",
"iosIsEncryption": false,
"iosBiometric": {
"biometricAuth": false,
"biometricTitle": "Biometric login for TimeSafari"
},
"androidIsEncryption": false,
"androidBiometric": {
"biometricAuth": false,
"biometricTitle": "Biometric login for TimeSafari"
},
"electronIsEncryption": false
}
},
"ios": {
"contentInset": "never",
"allowsLinkPreview": true,
"scrollEnabled": true,
"limitsNavigationsToAppBoundDomains": true,
"backgroundColor": "#ffffff",
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
},
"android": {
"allowMixedContent": false,
"captureInput": true,
"webContentsDebuggingEnabled": false,
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
},
"electron": {
"deepLinking": {
"schemes": [
"timesafari"
]
},
"buildOptions": {
"appId": "app.timesafari",
"productName": "TimeSafari",
"directories": {
"output": "dist-electron-packages"
},
"files": [
"dist/**/*",
"electron/**/*"
],
"mac": {
"category": "public.app-category.productivity",
"target": [
{
"target": "dmg",
"arch": [
"x64",
"arm64"
]
}
]
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
]
},
"linux": {
"target": [
{
"target": "AppImage",
"arch": [
"x64"
]
}
],
"category": "Utility"
}
}
}
}

View File

@@ -1,8 +1,4 @@
[
{
"pkg": "@capacitor-community/sqlite",
"classpath": "com.getcapacitor.community.database.sqlite.CapacitorSQLitePlugin"
},
{
"pkg": "@capacitor-mlkit/barcode-scanning",
"classpath": "io.capawesome.capacitorjs.plugins.mlkit.barcodescanning.BarcodeScannerPlugin"

View File

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2480 4005 c-25 -7 -58 -20 -75 -29 -16 -9 -40 -16 -52 -16 -17 0
-24 -7 -28 -27 -3 -16 -14 -45 -24 -65 -21 -41 -13 -55 18 -38 25 13 67 13 92
-1 15 -8 35 -4 87 17 99 39 130 41 197 10 64 -29 77 -31 107 -15 20 11 20 11
-3 35 -12 13 -30 24 -38 24 -24 1 -132 38 -148 51 -8 7 -11 20 -7 32 12 37
-40 47 -126 22z"/>
<path d="M1450 3775 c-7 -8 -18 -15 -24 -15 -7 0 -31 -14 -54 -32 -29 -22 -38
-34 -29 -40 17 -11 77 -10 77 1 0 5 16 16 35 25 60 29 220 19 290 -18 17 -9
33 -16 37 -16 4 0 31 -15 60 -34 108 -70 224 -215 282 -353 30 -71 53 -190 42
-218 -10 -27 -23 -8 -52 75 -30 90 -88 188 -120 202 -13 6 -26 9 -29 6 -3 -2
11 -51 30 -108 28 -83 35 -119 35 -179 0 -120 -22 -127 -54 -17 -11 37 -13 21
-18 -154 -5 -180 -8 -200 -32 -264 -51 -132 -129 -245 -199 -288 -21 -12 -79
-49 -129 -80 -161 -102 -294 -141 -473 -141 -228 0 -384 76 -535 259 -81 99
-118 174 -154 312 -31 121 -35 273 -11 437 19 127 19 125 -4 125 -23 0 -51
-34 -87 -104 -14 -28 -33 -64 -41 -81 -19 -34 -22 -253 -7 -445 9 -106 12
-119 44 -170 19 -30 42 -67 50 -81 64 -113 85 -140 130 -169 28 -18 53 -44 61
-62 8 -20 36 -45 83 -76 62 -39 80 -46 151 -54 44 -5 96 -13 115 -18 78 -20
238 -31 282 -19 24 6 66 8 95 5 76 -9 169 24 319 114 32 19 80 56 106 82 27
26 52 48 58 48 5 0 27 26 50 58 48 66 56 70 132 71 62 1 165 29 238 64 112 55
177 121 239 245 37 76 39 113 10 267 -12 61 -23 131 -26 156 -5 46 -5 47 46
87 92 73 182 70 263 -8 l51 -49 -6 -61 c-4 -34 -13 -85 -21 -113 -28 -103 -30
-161 -4 -228 16 -44 32 -67 55 -83 18 -11 39 -37 47 -58 10 -23 37 -53 73 -81
32 -25 69 -57 82 -71 14 -14 34 -26 47 -26 12 0 37 -7 56 -15 20 -8 66 -17
104 -20 107 -10 110 -11 150 -71 50 -75 157 -177 197 -187 18 -5 53 -24 78
-42 71 -51 176 -82 304 -89 61 -4 127 -12 147 -18 29 -9 45 -8 77 6 23 9 50
16 60 16 31 0 163 46 216 76 28 15 75 46 105 69 30 23 69 49 85 58 17 8 46 31
64 51 19 20 40 36 47 36 18 0 77 70 100 120 32 66 45 108 55 173 5 32 16 71
24 87 43 84 43 376 0 549 -27 105 -43 127 -135 188 -30 21 -65 46 -77 57 -13
11 -23 17 -23 14 0 -3 21 -46 47 -94 79 -151 85 -166 115 -263 25 -83 28 -110
28 -226 0 -144 -17 -221 -75 -335 -39 -77 -208 -244 -304 -299 -451 -263 -975
-67 -1138 426 -23 70 -26 95 -28 254 -1 108 -7 183 -14 196 -6 12 -11 31 -11
43 0 32 31 122 52 149 10 13 18 28 18 34 0 5 25 40 56 78 60 73 172 170 219
190 30 12 30 13 6 17 -15 2 -29 -2 -37 -12 -6 -9 -16 -16 -22 -16 -6 0 -23
-11 -39 -24 -15 -12 -33 -25 -40 -27 -17 -6 -82 -60 -117 -97 -65 -70 -75 -82
-107 -133 -23 -34 -35 -46 -37 -35 -3 16 20 87 44 134 6 12 9 34 6 48 -4 22
-8 25 -31 19 -14 -3 -38 -15 -53 -26 -34 -24 -34 -21 -6 28 65 112 184 206
291 227 15 3 39 9 55 12 l27 6 -24 9 c-90 35 -304 -66 -478 -225 -39 -36 -74
-66 -77 -66 -22 0 18 82 72 148 19 23 32 46 28 49 -4 4 -26 13 -49 19 -73 21
-161 54 -171 64 -6 6 -20 10 -32 10 -21 0 -21 -1 -8 -40 45 -130 8 -247 -93
-299 -25 -13 -31 0 -14 29 15 22 1 33 -22 17 -56 -36 -117 -22 -117 28 0 13
-16 47 -35 76 -22 34 -33 60 -29 73 4 16 -3 26 -26 39 -16 10 -30 21 -30 25 1
18 54 64 87 76 l38 13 -33 5 c-30 4 -115 -18 -154 -42 -13 -7 -20 -5 -27 8 -9
16 -12 16 -53 1 -160 -61 -258 -104 -258 -114 0 -7 10 -20 21 -31 103 -91 217
-297 249 -449 28 -135 41 -237 35 -276 -14 -91 -48 -170 -97 -220 -44 -47 -68
-60 -68 -40 0 6 4 12 8 15 5 3 24 35 42 72 l33 67 -6 141 c-4 103 -11 158 -26
205 -12 35 -21 70 -21 77 0 7 -20 56 -45 108 -82 173 -227 322 -392 401 -67
33 -90 39 -163 42 -108 5 -130 10 -130 28 0 20 -63 20 -80 0z"/>
<path d="M3710 3765 c0 -20 8 -28 39 -41 22 -8 42 -22 45 -30 5 -14 42 -19 70
-8 10 4 -7 21 -58 55 -41 27 -79 49 -85 49 -6 0 -11 -11 -11 -25z"/>
<path d="M3173 3734 c-9 -25 10 -36 35 -18 12 8 22 19 22 25 0 16 -50 10 -57
-7z"/>
<path d="M1982 3728 c6 -16 36 -34 44 -26 3 4 4 14 1 23 -7 17 -51 21 -45 3z"/>
<path d="M1540 3620 c0 -5 7 -10 16 -10 8 0 12 5 9 10 -3 6 -10 10 -16 10 -5
0 -9 -4 -9 -10z"/>
<path d="M4467 3624 c-4 -4 23 -27 60 -50 84 -56 99 -58 67 -9 -28 43 -107 79
-127 59z"/>
<path d="M655 3552 c-11 -2 -26 -9 -33 -14 -7 -6 -27 -18 -45 -27 -36 -18 -58
-64 -39 -83 9 -9 25 1 70 43 53 48 78 78 70 84 -2 1 -12 -1 -23 -3z"/>
<path d="M1015 3460 c-112 -24 -247 -98 -303 -165 -53 -65 -118 -214 -136
-311 -20 -113 -20 -145 -1 -231 20 -88 49 -153 102 -230 79 -113 186 -182 331
-214 108 -24 141 -24 247 1 130 30 202 72 316 181 102 100 153 227 152 384 0
142 -58 293 -150 395 -60 67 -180 145 -261 171 -75 23 -232 34 -297 19z m340
-214 c91 -43 174 -154 175 -234 0 -18 -9 -51 -21 -73 -19 -37 -19 -42 -5 -64
35 -54 12 -121 -48 -142 -22 -7 -47 -19 -55 -27 -9 -8 -41 -27 -71 -42 -50
-26 -64 -29 -155 -29 -111 0 -152 14 -206 68 -49 49 -63 85 -64 162 0 59 4 78
28 118 31 52 96 105 141 114 23 5 33 17 56 68 46 103 121 130 225 81z"/>
<path d="M3985 3464 c-44 -7 -154 -44 -200 -67 -55 -28 -138 -96 -162 -132
-10 -16 -39 -75 -64 -130 l-44 -100 0 -160 0 -160 45 -90 c53 -108 152 -214
245 -264 59 -31 215 -71 281 -71 53 0 206 40 255 67 98 53 203 161 247 253 53
113 74 193 74 280 -1 304 -253 564 -557 575 -49 2 -103 1 -120 -1z m311 -220
c129 -68 202 -209 160 -309 -15 -35 -15 -42 -1 -72 26 -55 -3 -118 -59 -129
-19 -3 -43 -15 -53 -26 -26 -29 -99 -64 -165 -78 -45 -10 -69 -10 -120 -1 -74
15 -113 37 -161 91 -110 120 -50 331 109 385 24 8 44 23 52 39 6 14 18 38 25
53 33 72 127 93 213 47z"/>
<path d="M487 3394 c-21 -12 -27 -21 -25 -40 2 -14 7 -26 12 -27 14 -3 48 48
44 66 -3 14 -6 14 -31 1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 KiB

View File

@@ -0,0 +1,11 @@
Model Information:
* title: Lupine Plant
* source: https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439
* author: rufusrockwell (https://sketchfab.com/rufusrockwell)
Model License:
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
* requirements: Author must be credited. Commercial use is allowed.
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
This work is based on "Lupine Plant" (https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) by rufusrockwell (https://sketchfab.com/rufusrockwell) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)

View File

@@ -0,0 +1,229 @@
{
"accessors": [
{
"bufferView": 2,
"componentType": 5126,
"count": 2759,
"max": [
41.3074951171875,
40.37548828125,
87.85917663574219
],
"min": [
-35.245540618896484,
-36.895416259765625,
-0.9094290137290955
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 33108,
"componentType": 5126,
"count": 2759,
"max": [
0.9999382495880127,
0.9986748695373535,
0.9985831379890442
],
"min": [
-0.9998949766159058,
-0.9975876212120056,
-0.411094069480896
],
"type": "VEC3"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 2759,
"max": [
0.9987699389457703,
0.9998998045921326,
0.9577858448028564,
1.0
],
"min": [
-0.9987726807594299,
-0.9990445971488953,
-0.999801516532898,
1.0
],
"type": "VEC4"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 2759,
"max": [
1.0061479806900024,
0.9993550181388855
],
"min": [
0.00279300007969141,
0.0011620000004768372
],
"type": "VEC2"
},
{
"bufferView": 0,
"componentType": 5125,
"count": 6378,
"type": "SCALAR"
}
],
"asset": {
"extras": {
"author": "rufusrockwell (https://sketchfab.com/rufusrockwell)",
"license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)",
"source": "https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439",
"title": "Lupine Plant"
},
"generator": "Sketchfab-12.68.0",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 25512,
"name": "floatBufferViews",
"target": 34963
},
{
"buffer": 0,
"byteLength": 22072,
"byteOffset": 25512,
"byteStride": 8,
"name": "floatBufferViews",
"target": 34962
},
{
"buffer": 0,
"byteLength": 66216,
"byteOffset": 47584,
"byteStride": 12,
"name": "floatBufferViews",
"target": 34962
},
{
"buffer": 0,
"byteLength": 44144,
"byteOffset": 113800,
"byteStride": 16,
"name": "floatBufferViews",
"target": 34962
}
],
"buffers": [
{
"byteLength": 157944,
"uri": "scene.bin"
}
],
"images": [
{
"uri": "textures/lambert2SG_baseColor.png"
},
{
"uri": "textures/lambert2SG_normal.png"
}
],
"materials": [
{
"alphaCutoff": 0.2,
"alphaMode": "MASK",
"doubleSided": true,
"name": "lambert2SG",
"normalTexture": {
"index": 1
},
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicFactor": 0.0
}
}
],
"meshes": [
{
"name": "Object_0",
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 0,
"TANGENT": 2,
"TEXCOORD_0": 3
},
"indices": 4,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"children": [
1
],
"matrix": [
1.0,
0.0,
0.0,
0.0,
0.0,
2.220446049250313e-16,
-1.0,
0.0,
0.0,
1.0,
2.220446049250313e-16,
0.0,
0.0,
0.0,
0.0,
1.0
],
"name": "Sketchfab_model"
},
{
"children": [
2
],
"name": "LupineSF.obj.cleaner.materialmerger.gles"
},
{
"mesh": 0,
"name": "Object_2"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9987,
"wrapS": 10497,
"wrapT": 10497
}
],
"scene": 0,
"scenes": [
{
"name": "Sketchfab_Scene",
"nodes": [
0
]
}
],
"textures": [
{
"sampler": 0,
"source": 0
},
{
"sampler": 0,
"source": 1
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

View File

@@ -1,15 +1,7 @@
package app.timesafari;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
//import com.getcapacitor.community.sqlite.SQLite;
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize SQLite
//registerPlugin(SQLite.class);
}
// ... existing code ...
}

View File

@@ -0,0 +1,5 @@
package timesafari.app;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

View File

@@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.11.1'
classpath 'com.android.tools.build:gradle:8.9.1'
classpath 'com.google.gms:google-services:4.4.0'
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -2,9 +2,6 @@
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
include ':capacitor-community-sqlite'
project(':capacitor-community-sqlite').projectDir = new File('../node_modules/@capacitor-community/sqlite/android')
include ':capacitor-mlkit-barcode-scanning'
project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../node_modules/@capacitor-mlkit/barcode-scanning/android')

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

BIN
assets/icon-only.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

View File

@@ -1,36 +0,0 @@
{
"icon": {
"ios": {
"source": "resources/ios/icon/icon.png",
"target": "ios/App/App/Assets.xcassets/AppIcon.appiconset"
},
"android": {
"source": "resources/android/icon/icon.png",
"target": "android/app/src/main/res"
},
"web": {
"source": "resources/web/icon/icon.png",
"target": "public/img/icons"
}
},
"splash": {
"ios": {
"source": "resources/ios/splash/splash.png",
"target": "ios/App/App/Assets.xcassets/Splash.imageset"
},
"android": {
"source": "resources/android/splash/splash.png",
"target": "android/app/src/main/res"
}
},
"splashDark": {
"ios": {
"source": "resources/ios/splash/splash_dark.png",
"target": "ios/App/App/Assets.xcassets/SplashDark.imageset"
},
"android": {
"source": "resources/android/splash/splash_dark.png",
"target": "android/app/src/main/res"
}
}
}

View File

@@ -2,6 +2,7 @@
"appId": "app.timesafari",
"appName": "TimeSafari",
"webDir": "dist",
"bundledWebRuntime": false,
"server": {
"cleartext": true
},
@@ -15,97 +16,6 @@
}
]
}
},
"SplashScreen": {
"launchShowDuration": 3000,
"launchAutoHide": true,
"backgroundColor": "#ffffff",
"androidSplashResourceName": "splash",
"androidScaleType": "CENTER_CROP",
"showSpinner": false,
"androidSpinnerStyle": "large",
"iosSpinnerStyle": "small",
"spinnerColor": "#999999",
"splashFullScreen": true,
"splashImmersive": true
},
"CapacitorSQLite": {
"iosDatabaseLocation": "Library/CapacitorDatabase",
"iosIsEncryption": false,
"iosBiometric": {
"biometricAuth": false,
"biometricTitle": "Biometric login for TimeSafari"
},
"androidIsEncryption": false,
"androidBiometric": {
"biometricAuth": false,
"biometricTitle": "Biometric login for TimeSafari"
},
"electronIsEncryption": false
}
},
"ios": {
"contentInset": "never",
"allowsLinkPreview": true,
"scrollEnabled": true,
"limitsNavigationsToAppBoundDomains": true,
"backgroundColor": "#ffffff",
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
},
"android": {
"allowMixedContent": false,
"captureInput": true,
"webContentsDebuggingEnabled": false,
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
},
"electron": {
"deepLinking": {
"schemes": ["timesafari"]
},
"buildOptions": {
"appId": "app.timesafari",
"productName": "TimeSafari",
"directories": {
"output": "dist-electron-packages"
},
"files": [
"dist/**/*",
"electron/**/*"
],
"mac": {
"category": "public.app-category.productivity",
"target": [
{
"target": "dmg",
"arch": ["x64", "arm64"]
}
]
},
"win": {
"target": [
{
"target": "nsis",
"arch": ["x64"]
}
]
},
"linux": {
"target": [
{
"target": "AppImage",
"arch": ["x64"]
}
],
"category": "Utility"
}
}
}
}

View File

@@ -100,7 +100,6 @@ try {
- `src/interfaces/deepLinks.ts`: Type definitions and validation schemas
- `src/services/deepLinks.ts`: Deep link processing service
- `src/main.capacitor.ts`: Capacitor integration
- `src/views/DeepLinkRedirectView.vue`: Page to handle links to both mobile and web
## Type Safety Examples

Some files were not shown because too many files have changed in this diff Show More