321 lines
8.2 KiB
Markdown
321 lines
8.2 KiB
Markdown
# Daily Timer - AI Assistant Instructions
|
|
|
|
## Project Overview
|
|
|
|
- **Purpose**: Desktop meeting timer application with speaker management
|
|
- **Framework**: Wails v2.11.0 (Go backend + Svelte frontend)
|
|
- **Status**: Active development
|
|
|
|
## Technology Stack
|
|
|
|
| Component | Technology |
|
|
| ------------- | --------------------------- |
|
|
| **Backend** | Go 1.21+ |
|
|
| **Framework** | Wails v2.11.0 |
|
|
| **Frontend** | Svelte 4 + Vite |
|
|
| **Database** | SQLite via GORM |
|
|
| **Audio** | Web Audio API (oscillators) |
|
|
| **Linter** | golangci-lint |
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
daily-timer/
|
|
├── main.go # App entry point
|
|
├── wails.json # Wails configuration
|
|
├── internal/
|
|
│ ├── app/
|
|
│ │ └── app.go # Main App struct, Wails bindings
|
|
│ ├── models/
|
|
│ │ └── models.go # Data models (Participant, Meeting, etc.)
|
|
│ ├── storage/
|
|
│ │ └── storage.go # GORM database operations
|
|
│ └── timer/
|
|
│ └── timer.go # Timer logic, event emission
|
|
├── frontend/
|
|
│ ├── src/
|
|
│ │ ├── App.svelte # Main component, event handlers
|
|
│ │ ├── components/
|
|
│ │ │ ├── Timer.svelte
|
|
│ │ │ ├── ParticipantList.svelte
|
|
│ │ │ ├── Controls.svelte
|
|
│ │ │ ├── Settings.svelte
|
|
│ │ │ ├── History.svelte
|
|
│ │ │ └── Setup.svelte
|
|
│ │ └── lib/
|
|
│ │ └── i18n.js # Translations (RU/EN)
|
|
│ └── wailsjs/ # Auto-generated Go bindings
|
|
└── build/ # Build output
|
|
```
|
|
|
|
## Critical Rules - MUST FOLLOW
|
|
|
|
### 1. Wails Terminal Directory
|
|
|
|
**ALWAYS run `wails dev` from project root**, not from subdirectories:
|
|
|
|
```bash
|
|
# ✅ CORRECT
|
|
cd /Users/admin-msk/git/daily-timer && wails dev
|
|
|
|
# ❌ WRONG - causes "wails.json not found" error
|
|
cd /Users/admin-msk/git/daily-timer/internal/timer && wails dev
|
|
```
|
|
|
|
### 2. TypeScript Model Casing
|
|
|
|
Go structs use PascalCase, but Wails generates TypeScript with camelCase:
|
|
|
|
```go
|
|
// Go model
|
|
type Session struct {
|
|
ID uint
|
|
StartTime time.Time
|
|
}
|
|
```
|
|
|
|
```typescript
|
|
// Generated TypeScript - use lowercase!
|
|
session.id; // ✅ CORRECT
|
|
session.ID; // ❌ WRONG - undefined
|
|
session.startTime; // ✅ CORRECT
|
|
session.StartTime; // ❌ WRONG - undefined
|
|
```
|
|
|
|
### 3. GORM Partial Updates
|
|
|
|
**NEVER use `db.Save()` for partial updates** - it overwrites ALL fields with zero values:
|
|
|
|
```go
|
|
// ❌ WRONG - overwrites Active, Order, etc. with zero values
|
|
func (s *Storage) UpdateParticipant(p *models.Participant) error {
|
|
return s.db.Save(p).Error
|
|
}
|
|
|
|
// ✅ CORRECT - updates only specified fields
|
|
func (s *Storage) UpdateParticipant(p *models.Participant) error {
|
|
return s.db.Model(p).Updates(map[string]interface{}{
|
|
"name": p.Name,
|
|
"email": p.Email,
|
|
"time_limit": p.TimeLimit,
|
|
}).Error
|
|
}
|
|
```
|
|
|
|
### 4. Timer Event Flags
|
|
|
|
Use separate flags for different event types to prevent blocking:
|
|
|
|
```go
|
|
// ❌ WRONG - one flag blocks both events
|
|
speakerWarned bool // After warning, timeup never fires
|
|
|
|
// ✅ CORRECT - separate flags
|
|
speakerWarned bool // For warning event
|
|
speakerTimeUpEmitted bool // For timeup event
|
|
```
|
|
|
|
### 5. Sound in Wails (macOS)
|
|
|
|
- Use Web Audio API oscillators, not audio files
|
|
- macOS "Do Not Disturb" mode blocks sounds
|
|
- Always add test buttons in Settings for verification
|
|
|
|
```javascript
|
|
function playBeep(frequency, duration) {
|
|
const ctx = new AudioContext();
|
|
const osc = ctx.createOscillator();
|
|
const gain = ctx.createGain();
|
|
osc.connect(gain);
|
|
gain.connect(ctx.destination);
|
|
osc.frequency.value = frequency;
|
|
gain.gain.setValueAtTime(0.3, ctx.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + duration);
|
|
osc.start();
|
|
osc.stop(ctx.currentTime + duration);
|
|
}
|
|
```
|
|
|
|
### 6. Svelte Event Dispatching
|
|
|
|
When adding interactivity to list items:
|
|
|
|
```svelte
|
|
<!-- Parent component -->
|
|
<ChildList on:customEvent={handleEvent} />
|
|
|
|
<!-- Child component -->
|
|
<script>
|
|
import { createEventDispatcher } from 'svelte'
|
|
const dispatch = createEventDispatcher()
|
|
|
|
function handleClick(id) {
|
|
dispatch('customEvent', { id })
|
|
}
|
|
</script>
|
|
```
|
|
|
|
### 7. i18n Translations
|
|
|
|
Always update BOTH languages (RU and EN) in `frontend/src/lib/i18n.js`:
|
|
|
|
```javascript
|
|
translations: {
|
|
ru: {
|
|
history: {
|
|
deleteAll: 'Удалить историю',
|
|
}
|
|
},
|
|
en: {
|
|
history: {
|
|
deleteAll: 'Delete History',
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 8. Go Linter (golangci-lint)
|
|
|
|
Run before committing:
|
|
|
|
```bash
|
|
/opt/homebrew/bin/golangci-lint run ./...
|
|
```
|
|
|
|
Common fixes:
|
|
|
|
- Remove explicit types when inferring: `idx := -1` not `var idx int = -1`
|
|
- Remove unused functions
|
|
- Handle all error returns
|
|
|
|
## Common Bugs & Solutions
|
|
|
|
| Bug | Cause | Solution |
|
|
| ----------------------- | --------------------------- | ------------------------------------- |
|
|
| `wails.json not found` | Wrong directory | Run from project root |
|
|
| Data disappears on edit | `db.Save()` overwrites | Use `Updates(map[string]interface{})` |
|
|
| Event never fires | Shared flag blocks | Use separate flags per event |
|
|
| Sounds don't play | DND mode or no AudioContext | Add test buttons, check DND |
|
|
| `undefined` in template | Wrong case (ID vs id) | Check generated TS types |
|
|
| Drag-drop doesn't save | Missing backend call | Call `ReorderParticipants()` |
|
|
| "App is damaged" error | macOS Gatekeeper | Run `xattr -cr "Daily Timer.app"` |
|
|
|
|
## Development Commands
|
|
|
|
```bash
|
|
# Run in development mode (ALWAYS run manually from clean shell!)
|
|
cd /Users/admin-msk/git/daily-timer && wails dev
|
|
|
|
# Build for production
|
|
wails build
|
|
|
|
# Run linter
|
|
/opt/homebrew/bin/golangci-lint run ./...
|
|
|
|
# Generate bindings (automatic in wails dev)
|
|
wails generate module
|
|
```
|
|
|
|
## Release Workflow
|
|
|
|
### Building Release
|
|
|
|
```bash
|
|
# Build and package for current architecture
|
|
make release
|
|
|
|
# Build for all macOS architectures (arm64 + amd64)
|
|
make release-all
|
|
```
|
|
|
|
Output: `dist/Daily-Timer-vX.X.X-macos-arm64.zip`
|
|
|
|
### Publishing Release to Gitea
|
|
|
|
```bash
|
|
# Create and push tag
|
|
git tag -a v0.2.0 -m "Release description"
|
|
git push origin v0.2.0
|
|
|
|
# Build and upload to Gitea (requires GITEA_TOKEN)
|
|
GITEA_TOKEN=your_token make release-publish
|
|
```
|
|
|
|
### Rebuilding Existing Release
|
|
|
|
If you need to rebuild a release (e.g., after fixing icon or bugs):
|
|
|
|
```bash
|
|
# 1. Delete old dist folder
|
|
rm -rf dist
|
|
|
|
# 2. Delete local and remote tag
|
|
git tag -d v0.1.0
|
|
git push origin :refs/tags/v0.1.0
|
|
|
|
# 3. Delete old release on Gitea (get release ID from web UI or API)
|
|
curl -s -X DELETE \
|
|
-H "Authorization: token $GITEA_TOKEN" \
|
|
"https://git.movida.biz/api/v1/repos/bell/daily-timer/releases/RELEASE_ID"
|
|
|
|
# 4. Create new tag
|
|
git tag -a v0.1.0 -m "Release description"
|
|
git push origin v0.1.0
|
|
|
|
# 5. Build and upload
|
|
GITEA_TOKEN=your_token make release-publish
|
|
```
|
|
|
|
### macOS Gatekeeper Fix
|
|
|
|
**IMPORTANT**: Built app is not signed. macOS shows "app is damaged" error.
|
|
|
|
Fix for users after downloading:
|
|
|
|
```bash
|
|
xattr -cr "Daily Timer.app"
|
|
```
|
|
|
|
Or: Right-click → Open → Open (first launch only)
|
|
|
|
For apps in /Applications:
|
|
|
|
```bash
|
|
xattr -cr "/Applications/Daily Timer.app"
|
|
```
|
|
|
|
## Event System
|
|
|
|
Timer emits events via Wails runtime:
|
|
|
|
- `timer:tick` - Every 100ms during active timer
|
|
- `timer:speaker_warning` - When speaker time is running low
|
|
- `timer:speaker_timeup` - When speaker time expired
|
|
- `timer:meeting_warning` - When meeting time running low
|
|
- `timer:meeting_ended` - When meeting ends
|
|
- `timer:speaker_changed` - When switching speakers
|
|
|
|
Frontend listens in App.svelte:
|
|
|
|
```javascript
|
|
EventsOn('timer:speaker_warning', handleWarning);
|
|
```
|
|
|
|
## Debugging
|
|
|
|
Open DevTools in Wails app: `Cmd+Option+I`
|
|
|
|
Add console.log for event debugging:
|
|
|
|
```javascript
|
|
function handleWarning(state) {
|
|
console.log('=== handleWarning EVENT RECEIVED ===');
|
|
console.log('settings:', settings);
|
|
// ...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
**Last Updated**: February 2026
|