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:
+78
-218
@@ -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
|
- **Merkle-Chained Claims**: Claims are cryptographically chained for verification and integrity
|
||||||
- **Native and Web App**: Works on iOS, Android, and web browsers
|
- **Native and Web App**: Works on iOS, Android, and web browsers
|
||||||
|
|
||||||
|
|
||||||
## User Journey
|
## User Journey
|
||||||
|
|
||||||
The typical progression of usage follows these stages:
|
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.
|
3. **Action Triggers**: Offers of help serve as triggers and motivations to execute proposed projects, moving from ideas to action.
|
||||||
|
|
||||||
|
|
||||||
## Context for LLM Development
|
## Context for LLM Development
|
||||||
|
|
||||||
When developing new functionality for Time Safari, consider these design principles:
|
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.
|
7. **Low Resource Requirements**: The system should be lightweight enough to run on inexpensive devices users already own.
|
||||||
|
|
||||||
|
|
||||||
## Use Cases to Support
|
## Use Cases to Support
|
||||||
|
|
||||||
LLM development should focus on enhancing these key use cases:
|
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.
|
4. **Governance Experimentation**: Features that facilitate decision-making and collective governance.
|
||||||
|
|
||||||
|
|
||||||
## Constraints
|
## Constraints
|
||||||
|
|
||||||
When developing new features, be mindful of these 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.
|
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
|
- Ensure all components support accessibility first design
|
||||||
- Implement privacy-preserving features by default
|
- Implement privacy-preserving features by default
|
||||||
- Support progressive enhancement across devices
|
- Support progressive enhancement across devices
|
||||||
@@ -102,229 +129,62 @@ When developing new features, be mindful of these constraints:
|
|||||||
- Build trust-enabling interactions
|
- Build trust-enabling interactions
|
||||||
- Optimize for low resource requirements
|
- Optimize for low resource requirements
|
||||||
- Support offline-first functionality where possible
|
- 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
|
## Vue Component Structure
|
||||||
- Use `@Options`, `@Ref`, `@Prop`, `@Emit`, and `@Watch` decorators for clear 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
|
- Extend `Vue` class with proper type annotations for props, refs, and methods
|
||||||
- Use Tailwind utility classes for accessible and responsive design
|
- Use Tailwind utility classes for accessible and responsive design
|
||||||
- Avoid `setup()` or Composition API; use class syntax consistently
|
- Avoid `setup()` or Composition API; use class syntax consistently
|
||||||
- Keep methods pure when possible; extract logic into utilities
|
- Keep methods pure when possible; extract logic into utilities
|
||||||
- Ensure lifecycle methods are clearly defined inside class
|
- Ensure lifecycle methods are clearly defined inside class
|
||||||
- Use semantic HTML + Tailwind classes for styling
|
- Use semantic HTML + Tailwind classes for styling
|
||||||
|
- Pinia for state management
|
||||||
|
|
||||||
[rules.component-class]
|
## Vue Facing Decorators
|
||||||
description = "Create a privacy-aware Vue 3 component"
|
|
||||||
trigger = "vfd-component"
|
|
||||||
insert = """
|
|
||||||
import { Options, Vue } from 'vue-facing-decorator'
|
|
||||||
import { PlatformService } from '@/services/platform'
|
|
||||||
|
|
||||||
@Options({
|
- Ensure all Vue 3 components are written using TypeScript with strict type checking enabled.
|
||||||
name: 'MyComponent',
|
- Always include explicit types for props, emits, and reactive properties.
|
||||||
props: {},
|
- When using @Options, ensure it includes metadata like name, template, or styles.
|
||||||
emits: ['error', 'privacy-consent'],
|
- Use @Prop for defining props with validation and default values.
|
||||||
})
|
- Use @Emit for emitting events with proper payload typing.
|
||||||
export default class MyComponent extends Vue {
|
- Use @Watch for reactive property changes, and @Ref for DOM references."
|
||||||
private platformService = new PlatformService()
|
- 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
|
## es6 classes
|
||||||
mounted() {
|
|
||||||
this.checkPrivacyConsent()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Privacy check
|
- Use ES6 class syntax with decorators (@Options, @Prop, @Emit).
|
||||||
private async checkPrivacyConsent() {
|
- Use modular imports and default exports.
|
||||||
const hasConsent = await this.platformService.getPrivacyConsent()
|
- Use arrow functions for methods and callbacks.
|
||||||
if (!hasConsent) {
|
- Use destructuring for props and state.
|
||||||
this.$emit('privacy-consent-needed')
|
- 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
|
## Documentation
|
||||||
protected handleError(error: Error) {
|
|
||||||
console.error('Component error:', error)
|
|
||||||
this.$emit('error', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
[rules.decorator-prop]
|
- Include JSDoc comments for all public methods and props.
|
||||||
description = "Add a validated prop with privacy considerations"
|
- Files must have comments explaing contents and workflow of file
|
||||||
trigger = "vfd-prop"
|
- Methods and props should explain role and workflow of each
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|||||||
Reference in New Issue
Block a user