- Moved all hookify configuration files from `.claude/` to `.claude/hooks/` subdirectory for better organization - Added four new blocking hooks to prevent common error handling anti-patterns: - `block-broad-exception-handler`: Prevents catching generic `Exception` with only logging - `block-datetime-now-fallback`: Blocks returning `datetime.now()` as fallback on parse failures to prevent data corruption - `block-default
5.5 KiB
5.5 KiB
NoteFlow Common Utilities & Patterns
Python Utilities
Domain Utilities (domain/utils/)
time.py—utc_now()for UTC-aware datetimevalidation.py— Validation helpers
Config Constants (config/constants/)
core.py— DAYS_PER_WEEK, HOURS_PER_DAY, DEFAULT_LLM_TEMPERATUREdomain.py— DEFAULT_ANTHROPIC_MODEL, WEBHOOK_EVENT_TYPESerrors.py— Error code constantshttp.py— HTTP-related constants
Domain Constants (domain/constants/)
fields.py— Field name constants (EMAIL, PROVIDER, PROJECT_ID)
Settings (config/settings/)
_main.py— Main settings (database, ASR, gRPC, storage)_triggers.py— Trigger settings_calendar.py— Calendar provider settings_features.py— Feature flags
Key Env Vars:
NOTEFLOW_DATABASE_URL— PostgreSQL connectionNOTEFLOW_ASR_MODEL_SIZE— Whisper sizeNOTEFLOW_GRPC_PORT— gRPC port (default: 50051)NOTEFLOW_MEETINGS_DIR— Audio storage directoryNOTEFLOW_RETENTION_DAYS— Retention policy (default: 90)
TypeScript Utilities (client/src/lib/)
Configuration (config/)
app-config.ts— App-wide constantsdefaults.ts— Default valuesprovider-endpoints.ts— AI provider endpointsserver.ts— Server connection defaults
Formatting & Time
format.ts— Time, duration, GB, percentagestime.ts— Time constants (SECONDS_PER_, MS_PER_)timing-constants.ts— Polling intervals, timeouts
Logging
// NEVER use console.log - always use clientlog system
import { addClientLog } from '@/lib/client-logs';
import { debug, errorLog } from '@/lib/debug';
addClientLog({
level: 'info',
source: 'app',
message: 'Event occurred',
details: 'Context',
});
const log = debug('ComponentName');
log('Debug message', { data });
const logError = errorLog('ComponentName');
logError('Error occurred', error);
Preferences (preferences/)
import { preferences } from '@/lib/preferences';
// Initialize on app start
await preferences.initialize();
// Read/write
const prefs = preferences.get();
await preferences.set('selected_input_device', deviceId);
await preferences.replace({ ...current, theme: 'dark' });
// Subscribe to changes
const unsubscribe = preferences.subscribe((prefs) => {
console.log('Changed:', prefs);
});
Storage
storage-keys.ts— localStorage key constantsstorage-utils.ts— localStorage helpers
Caching
cache/meeting-cache.ts— Meeting cache with TTL & events
Event System
tauri-events.ts— Tauri event subscriptionsevent-emitter.ts— Generic event emitter
Other Utilities
utils.ts— Generic TypeScript utilitiesobject-utils.ts— Object manipulationspeaker-utils.ts— Speaker ID formattingintegration-utils.ts— Integration helpersentity-store.ts— Entity caching for NERcrypto.ts— Client-side encryptionoauth-utils.ts— OAuth flow helpers
Rust Constants (constants.rs)
pub mod grpc {
pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(300);
pub const DEFAULT_PORT: u16 = 50051;
pub const MAX_RETRY_ATTEMPTS: u32 = 3;
}
pub mod audio {
pub const DEFAULT_SAMPLE_RATE: u32 = 16000;
pub const DEFAULT_CHANNELS: u32 = 1;
pub const DEFAULT_BUFFER_SIZE: usize = 1600; // 100ms
pub const MIN_DB_LEVEL: f32 = -60.0;
pub const MAX_DB_LEVEL: f32 = 0.0;
}
pub mod storage {
pub const MAX_AUDIO_SIZE_BYTES: u64 = 5 * 1024 * 1024 * 1024; // 5GB
}
pub mod triggers {
pub const POLL_INTERVAL: Duration = Duration::from_secs(5);
pub const DEFAULT_SNOOZE_DURATION: Duration = Duration::from_secs(600);
pub const AUTO_START_THRESHOLD: f32 = 0.7;
}
pub mod crypto {
pub const KEY_SIZE: usize = 32; // 256-bit AES
pub const KEYCHAIN_SERVICE: &str = "NoteFlow";
}
pub mod identity {
pub const DEFAULT_USER_ID: &str = "local-user";
pub const DEFAULT_WORKSPACE_ID: &str = "local-workspace";
}
Key Patterns
Python: Protocol-Based Dependency Injection
class MeetingService:
def __init__(self, uow: UnitOfWork):
self._uow = uow
async def create_meeting(self, params: MeetingCreateParams) -> Meeting:
async with self._uow:
meeting = Meeting.create(params)
await self._uow.meetings.add(meeting)
await self._uow.commit()
return meeting
TypeScript: Connection-Aware Components
function MyComponent() {
const { isConnected, isReadOnly } = useConnection();
if (isReadOnly) return <OfflineBanner />;
return <ConnectedUI />;
}
Rust: Lazy Initialization
pub struct CryptoManager {
crypto: OnceLock<Result<CryptoBox>>,
}
impl CryptoManager {
pub fn get_or_init(&self) -> Result<&CryptoBox> {
self.crypto.get_or_try_init(CryptoBox::new)
}
}
Rust: State Access via RwLock
let mut rec = state.recording.write();
*rec = Some(session);
Feature Flags
| Flag | Default | Controls |
|---|---|---|
NOTEFLOW_FEATURE_TEMPLATES_ENABLED |
true |
AI templates |
NOTEFLOW_FEATURE_PDF_EXPORT_ENABLED |
true |
PDF export |
NOTEFLOW_FEATURE_NER_ENABLED |
false |
Entity extraction |
NOTEFLOW_FEATURE_CALENDAR_ENABLED |
false |
Calendar sync |
NOTEFLOW_FEATURE_WEBHOOKS_ENABLED |
true |
Webhooks |
Access: get_feature_flags().<flag_name> or get_settings().feature_flags.<flag_name>