forked from jsnbuchanan/crowd-funder-for-time-pwa
docs: Update README-context.md with Vue development guidelines
- Add Pinia state management documentation - Add comprehensive Vue Facing Decorators guidelines - Add ES6 class development standards - Add documentation requirements - Remove outdated component templates and rules - Improve code organization and readability - Add TypeScript best practices - Add component structure guidelines - Add testing and accessibility requirements - Add style encapsulation recommendations This update provides clearer development guidelines for the project, particularly around Vue 3 component development and TypeScript usage.
This commit is contained in:
@@ -36,7 +36,6 @@ This application is built on a privacy-preserving claims architecture (via endor
|
||||
- **Merkle-Chained Claims**: Claims are cryptographically chained for verification and integrity
|
||||
- **Native and Web App**: Works on iOS, Android, and web browsers
|
||||
|
||||
|
||||
## User Journey
|
||||
|
||||
The typical progression of usage follows these stages:
|
||||
@@ -47,7 +46,6 @@ The typical progression of usage follows these stages:
|
||||
|
||||
3. **Action Triggers**: Offers of help serve as triggers and motivations to execute proposed projects, moving from ideas to action.
|
||||
|
||||
|
||||
## Context for LLM Development
|
||||
|
||||
When developing new functionality for Time Safari, consider these design principles:
|
||||
@@ -66,7 +64,6 @@ When developing new functionality for Time Safari, consider these design princip
|
||||
|
||||
7. **Low Resource Requirements**: The system should be lightweight enough to run on inexpensive devices users already own.
|
||||
|
||||
|
||||
## Use Cases to Support
|
||||
|
||||
LLM development should focus on enhancing these key use cases:
|
||||
@@ -79,7 +76,6 @@ LLM development should focus on enhancing these key use cases:
|
||||
|
||||
4. **Governance Experimentation**: Features that facilitate decision-making and collective governance.
|
||||
|
||||
|
||||
## Constraints
|
||||
|
||||
When developing new features, be mindful of these constraints:
|
||||
@@ -94,7 +90,38 @@ When developing new features, be mindful of these constraints:
|
||||
|
||||
5. **Offline-First When Possible**: Key functionality should work offline when feasible.
|
||||
|
||||
# Core Development Principles
|
||||
## Project Technologies
|
||||
|
||||
- Typescript using ES6 classes
|
||||
- TailwindCSS
|
||||
- Vite Build Tool
|
||||
- Playwright E2E testing
|
||||
- IndexDB
|
||||
- Camera, Image uploads, QR Code reader, ...
|
||||
|
||||
## Mobile Features
|
||||
|
||||
- Deep Linking
|
||||
- Local Notifications via a custom Capacitor plugin
|
||||
|
||||
## Project Architecture
|
||||
|
||||
- The application must work on web browser, PWA (Progressive Web Application), desktop via Electron, and mobile via Capacitor
|
||||
- Building for each platform is managed via Vite
|
||||
|
||||
## Core Development Principles
|
||||
|
||||
- DRY development
|
||||
- SOLID principles
|
||||
- Law of Demeter
|
||||
- Composition over Inheritence
|
||||
- Interface Segregation
|
||||
- Fail Fast
|
||||
- Principle of Least Astonishment
|
||||
- Information Hiding
|
||||
- Single Source of Truth
|
||||
- Principle of Least Privilege
|
||||
- Continuous Integration/Continuous Deployment (CI/CD)
|
||||
- Ensure all components support accessibility first design
|
||||
- Implement privacy-preserving features by default
|
||||
- Support progressive enhancement across devices
|
||||
@@ -102,229 +129,62 @@ When developing new features, be mindful of these constraints:
|
||||
- Build trust-enabling interactions
|
||||
- Optimize for low resource requirements
|
||||
- Support offline-first functionality where possible
|
||||
- Use class-based syntax with decorators from vue-facing-decorators.
|
||||
- Should attempt to maximize Lighthouse Score as close to 100 as possible.
|
||||
- Use Lighthouse for performance scoring
|
||||
|
||||
# Vue Component Structure
|
||||
- Use `@Options`, `@Ref`, `@Prop`, `@Emit`, and `@Watch` decorators for clear component structure
|
||||
## Vue Component Structure
|
||||
|
||||
- Use `@Options`, `@Ref`, `@Prop`, `@Emit`, and `@Watch` Typescript decorators for clear component structure
|
||||
- Extend `Vue` class with proper type annotations for props, refs, and methods
|
||||
- Use Tailwind utility classes for accessible and responsive design
|
||||
- Avoid `setup()` or Composition API; use class syntax consistently
|
||||
- Keep methods pure when possible; extract logic into utilities
|
||||
- Ensure lifecycle methods are clearly defined inside class
|
||||
- Use semantic HTML + Tailwind classes for styling
|
||||
- Pinia for state management
|
||||
|
||||
[rules.component-class]
|
||||
description = "Create a privacy-aware Vue 3 component"
|
||||
trigger = "vfd-component"
|
||||
insert = """
|
||||
import { Options, Vue } from 'vue-facing-decorator'
|
||||
import { PlatformService } from '@/services/platform'
|
||||
## Vue Facing Decorators
|
||||
|
||||
@Options({
|
||||
name: 'MyComponent',
|
||||
props: {},
|
||||
emits: ['error', 'privacy-consent'],
|
||||
})
|
||||
export default class MyComponent extends Vue {
|
||||
private platformService = new PlatformService()
|
||||
- Ensure all Vue 3 components are written using TypeScript with strict type checking enabled.
|
||||
- Always include explicit types for props, emits, and reactive properties.
|
||||
- When using @Options, ensure it includes metadata like name, template, or styles.
|
||||
- Use @Prop for defining props with validation and default values.
|
||||
- Use @Emit for emitting events with proper payload typing.
|
||||
- Use @Watch for reactive property changes, and @Ref for DOM references."
|
||||
- Organize Vue 3 components with a clear structure: imports at the top, followed by @Options metadata, then class properties (props, refs, reactive state), lifecycle hooks, methods, and finally @Watch or @Emit handlers.
|
||||
- Ensure all props have explicit types and optional validation.
|
||||
- Use TypeScript interfaces or types for complex prop structures.
|
||||
- Validate default values for props where applicable.
|
||||
- Use lifecycle hooks (e.g., onMounted, onUnmounted) sparingly and document their purpose.
|
||||
- Avoid side effects in lifecycle hooks unless absolutely necessary.
|
||||
- Use @Emit for emitting events with strongly typed payloads.
|
||||
- Ensure event names are descriptive and match the action being performed.
|
||||
- Use ref or reactive for managing internal state.
|
||||
- Avoid overusing reactive state for simple values. Prefer computed properties for derived state.
|
||||
- Write unit tests for components using Vue Test Utils and Jest/Vitest.
|
||||
- Ensure tests cover props, events, and lifecycle behavior.
|
||||
- Avoid unnecessary re-renders by using v-once for static content and memoizing expensive computations with computed properties.
|
||||
- Ensure components are accessible by using semantic HTML and ARIA attributes.
|
||||
- Use scoped styles or CSS modules to encapsulate styles.
|
||||
|
||||
// Lifecycle
|
||||
mounted() {
|
||||
this.checkPrivacyConsent()
|
||||
}
|
||||
## es6 classes
|
||||
|
||||
// Privacy check
|
||||
private async checkPrivacyConsent() {
|
||||
const hasConsent = await this.platformService.getPrivacyConsent()
|
||||
if (!hasConsent) {
|
||||
this.$emit('privacy-consent-needed')
|
||||
}
|
||||
}
|
||||
- Use ES6 class syntax with decorators (@Options, @Prop, @Emit).
|
||||
- Use modular imports and default exports.
|
||||
- Use arrow functions for methods and callbacks.
|
||||
- Use destructuring for props and state.
|
||||
- Provide default parameters for optional props or arguments.
|
||||
- Use template literals for dynamic strings.
|
||||
- Use spread/rest operators for object manipulation and arguments.
|
||||
- Use const/let appropriately for variable declarations.
|
||||
- Use enhanced object literals for cleaner syntax.
|
||||
- Use async/await for asynchronous operations.
|
||||
- Add scoped styles for encapsulation.
|
||||
- Ensure accessibility with semantic HTML and ARIA attributes.
|
||||
|
||||
// Error handling
|
||||
protected handleError(error: Error) {
|
||||
console.error('Component error:', error)
|
||||
this.$emit('error', error)
|
||||
}
|
||||
}
|
||||
"""
|
||||
## Documentation
|
||||
|
||||
[rules.decorator-prop]
|
||||
description = "Add a validated prop with privacy considerations"
|
||||
trigger = "vfd-prop"
|
||||
insert = """
|
||||
@Prop({
|
||||
required: true,
|
||||
validator: (value: any) => {
|
||||
// Add validation logic
|
||||
return typeof value === 'string' && value.length > 0
|
||||
}
|
||||
})
|
||||
propName!: string
|
||||
"""
|
||||
|
||||
[rules.decorator-watch]
|
||||
description = "Watch a property with offline support"
|
||||
trigger = "vfd-watch"
|
||||
insert = """
|
||||
@Watch('myProp')
|
||||
async onMyPropChanged(newVal: any, oldVal: any) {
|
||||
try {
|
||||
// Handle offline state
|
||||
if (!navigator.onLine) {
|
||||
await this.queueOfflineChange(newVal)
|
||||
return
|
||||
}
|
||||
await this.processChange(newVal)
|
||||
} catch (error) {
|
||||
this.handleError(error as Error)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
[rules.decorator-emit]
|
||||
description = "Emit a privacy-aware event"
|
||||
trigger = "vfd-emit"
|
||||
insert = """
|
||||
@Emit('data-update')
|
||||
async emitPrivateData() {
|
||||
const privateData = await this.platformService.getEncryptedData()
|
||||
return privateData
|
||||
}
|
||||
"""
|
||||
|
||||
[rules.component-template]
|
||||
description = "Create an accessible component template"
|
||||
trigger = "vfd-component-full"
|
||||
insert = """
|
||||
<template>
|
||||
<div
|
||||
class="p-4 bg-white dark:bg-gray-800"
|
||||
:aria-label="accessibilityLabel"
|
||||
>
|
||||
<h2
|
||||
class="text-lg font-semibold text-gray-900 dark:text-white"
|
||||
:id="headingId"
|
||||
>
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div
|
||||
class="mt-4"
|
||||
:aria-labelledby="headingId"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<!-- Offline indicator -->
|
||||
<div
|
||||
v-if="!isOnline"
|
||||
class="text-sm text-amber-600 mt-2"
|
||||
role="alert"
|
||||
>
|
||||
Currently offline - changes will sync when connection is restored
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-facing-decorator'
|
||||
import { generateUniqueId } from '@/utils/accessibility'
|
||||
|
||||
@Options({
|
||||
name: 'MyComponent',
|
||||
props: {
|
||||
title: String,
|
||||
accessibilityLabel: String
|
||||
}
|
||||
})
|
||||
export default class MyComponent extends Vue {
|
||||
title!: string
|
||||
accessibilityLabel!: string
|
||||
headingId = generateUniqueId()
|
||||
isOnline = navigator.onLine
|
||||
|
||||
mounted() {
|
||||
window.addEventListener('online', this.updateOnlineStatus)
|
||||
window.addEventListener('offline', this.updateOnlineStatus)
|
||||
}
|
||||
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('online', this.updateOnlineStatus)
|
||||
window.removeEventListener('offline', this.updateOnlineStatus)
|
||||
}
|
||||
|
||||
updateOnlineStatus() {
|
||||
this.isOnline = navigator.onLine
|
||||
}
|
||||
}
|
||||
</script>
|
||||
"""
|
||||
|
||||
[rules.async-method]
|
||||
description = "Add an async method with offline support and error handling"
|
||||
trigger = "vfd-async"
|
||||
insert = """
|
||||
async fetchData(): Promise<void> {
|
||||
try {
|
||||
if (!navigator.onLine) {
|
||||
const cachedData = await this.getCachedData()
|
||||
if (cachedData) {
|
||||
this.data = cachedData
|
||||
return
|
||||
}
|
||||
throw new Error('No cached data available offline')
|
||||
}
|
||||
|
||||
const response = await this.apiService.getData()
|
||||
this.data = response
|
||||
await this.cacheData(response)
|
||||
} catch (error) {
|
||||
this.handleError(error as Error)
|
||||
this.$emit('error', error)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
[rules.privacy-aware-component]
|
||||
description = "Create a privacy-aware component with DID handling"
|
||||
trigger = "vfd-privacy"
|
||||
insert = """
|
||||
import { Options, Vue } from 'vue-facing-decorator'
|
||||
import { PlatformService } from '@/services/platform'
|
||||
import { DidService } from '@/services/did'
|
||||
|
||||
@Options({
|
||||
name: 'PrivacyAwareComponent',
|
||||
props: {
|
||||
requiredPermissions: Array
|
||||
}
|
||||
})
|
||||
export default class PrivacyAwareComponent extends Vue {
|
||||
private platformService = new PlatformService()
|
||||
private didService = new DidService()
|
||||
|
||||
requiredPermissions!: string[]
|
||||
userDid = ''
|
||||
|
||||
async mounted() {
|
||||
await this.checkPermissions()
|
||||
await this.initializeDid()
|
||||
}
|
||||
|
||||
private async initializeDid() {
|
||||
try {
|
||||
this.userDid = await this.didService.getCurrentDid()
|
||||
} catch (error) {
|
||||
this.handleError(error as Error)
|
||||
}
|
||||
}
|
||||
|
||||
private async checkPermissions() {
|
||||
for (const permission of this.requiredPermissions) {
|
||||
const hasPermission = await this.platformService.checkPermission(permission)
|
||||
if (!hasPermission) {
|
||||
this.$emit('permission-required', permission)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
- Include JSDoc comments for all public methods and props.
|
||||
- Files must have comments explaing contents and workflow of file
|
||||
- Methods and props should explain role and workflow of each
|
||||
|
||||
Reference in New Issue
Block a user