mirror of
https://github.com/avinal/nikki.git
synced 2026-07-03 21:40:09 +05:30
Add README, task format docs, fix archived memos API
- README.md: introduction, features, build/install, tech stack, disclaimer, acknowledgements, logo at top - TASK_FORMAT.md: full task syntax reference with examples, validation rules, typo detection, notification behavior - Fix archived memos: use state query param instead of CEL filter - LOGO.md: remove triangle variant, keep circle only - logo-circle.svg tracked for README rendering Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context)
This commit is contained in:
+1
-1
@@ -29,4 +29,4 @@ styles.css
|
||||
# Dev artifacts
|
||||
PLAN.md
|
||||
Screenshot_*.png
|
||||
logo-*.svg
|
||||
logo-triangle.svg
|
||||
|
||||
@@ -13,7 +13,7 @@ The logo is inspired by the Google Foobar challenge logo, adapted into a circula
|
||||
| Teal | `#35BEB8` | Wedge section outside the circle |
|
||||
| Background | `#1F1F1F` | Launcher icon background |
|
||||
|
||||
## Geometry (Circle Variant — Primary)
|
||||
## Geometry
|
||||
|
||||
All measurements relative to a 108x108dp Android adaptive icon viewport, center at (54, 54).
|
||||
|
||||
@@ -84,28 +84,6 @@ sin(23.5°) = 0.39875
|
||||
L79.68,65.16 A28,28 0 0,0 79.68,42.84 Z" />
|
||||
```
|
||||
|
||||
## Geometry (Triangle Variant — Alternate)
|
||||
|
||||
Three concentric equilateral triangles pointing upward, with a 47-degree wedge cutting through the right edge.
|
||||
|
||||
### Triangle Circumradii (200x200 SVG, center at 100,100)
|
||||
|
||||
| Ring | Outer R | Inner R | Fill |
|
||||
|----------------|---------|---------|--------------|
|
||||
| Outer ring | 80 | 65 | Pink |
|
||||
| Middle ring | 55 | 40 | Pink |
|
||||
| Inner triangle | 30 | — | Pink (solid) |
|
||||
| Gap | 65→55 | — | Background |
|
||||
| Gap | 40→30 | — | Background |
|
||||
|
||||
### Wedge Intersections
|
||||
|
||||
The 47-degree wedge (centered on horizontal-right) intersects each triangle's right edge. The intersection points are computed by solving the parametric line-line intersection of the wedge rays with each triangle edge.
|
||||
|
||||
- **Black section**: wedge intersection with the middle ring (R=40 to R=55)
|
||||
- **Teal section**: wedge intersection with the outer ring (R=65 to R=80)
|
||||
- **Inner triangle**: stays fully pink
|
||||
|
||||
## Adaptive Icon Layers
|
||||
|
||||
| File | Purpose |
|
||||
@@ -123,8 +101,7 @@ Android adaptive icons use a 108dp canvas. The recommended safe zone is a 66dp d
|
||||
|
||||
## Files
|
||||
|
||||
- `logo-circle.svg` — Circle variant preview
|
||||
- `logo-triangle.svg` — Triangle variant preview
|
||||
- `androidApp/src/main/res/drawable/ic_launcher_foreground.xml` — Production circle logo
|
||||
- `logo-circle.svg` — SVG preview
|
||||
- `androidApp/src/main/res/drawable/ic_launcher_foreground.xml` — Production Android vector
|
||||
- `androidApp/src/main/res/drawable/ic_launcher_monochrome.xml` — Themed icon silhouette
|
||||
- `androidApp/src/main/res/drawable/ic_launcher_background.xml` — Dark background
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
<p align="center">
|
||||
<img src="logo-circle.svg" alt="Nikki logo" width="128" />
|
||||
</p>
|
||||
|
||||
# Nikki
|
||||
|
||||
A native Android client for [Memos](https://usememos.com) with integrated Todoist-style task management, built with Compose Multiplatform and inspired by the Windows Phone Metro design language.
|
||||
|
||||
The name comes from the Japanese word 日記 (nikki), meaning diary.
|
||||
|
||||
## Why Nikki
|
||||
|
||||
Memos is a great self-hosted note-taking tool, but its mobile experience is limited to a PWA. Nikki exists to be a proper native client that feels at home on your phone — fast, offline-capable, and opinionated about how notes and tasks should work together.
|
||||
|
||||
### Why Windows Phone
|
||||
|
||||
The Metro design language was the most readable, information-dense, and distraction-free mobile UI ever shipped. Large typography, flat surfaces, no chrome — it respected both content and the person reading it. Nikki borrows that philosophy: panorama pivot navigation, all 20 WP8 accent colors, and the principle that UI should get out of the way.
|
||||
|
||||
### Why Tasks Inside Notes
|
||||
|
||||
Most people track tasks in their notes anyway — `- [ ] buy milk` scattered across memos. Nikki parses those checkboxes and gives them structure: due dates, priorities, reminders, grouping. Your notes stay as plain markdown. The task layer is derived, not duplicated. One source of truth, two ways to see it.
|
||||
|
||||
### The Logo
|
||||
|
||||
Inspired by the Google Foobar challenge logo, adapted into a circle. A pink circle carries a 47-degree annular wedge on the right — black where it overlaps, teal where it extends beyond. Construction details are in [LOGO.md](LOGO.md).
|
||||
|
||||
## Features
|
||||
|
||||
**Memos**
|
||||
- Full CRUD with the Memos API (create, edit, pin, archive, delete)
|
||||
- Rich markdown rendering: headings, bold/italic, strikethrough, code blocks, tables, links, task checkboxes
|
||||
- Media attachments with authenticated image loading
|
||||
- Emoji reactions and comments
|
||||
- Visibility control (private, protected, public)
|
||||
- Pull-to-refresh sync with configurable interval
|
||||
|
||||
**Tasks**
|
||||
- Todoist-inspired syntax parsed from markdown checkboxes (see [TASK_FORMAT.md](TASK_FORMAT.md))
|
||||
- Due dates (ISO and natural: today, tomorrow), times (12h/24h), priorities (p1-p3), reminders, tags
|
||||
- Group by due date, list, priority, source memo, or status
|
||||
- Sort by due date or priority
|
||||
- Parser doctor: inline error/warning detection with typo suggestions
|
||||
- Android notifications with 4 priority channels (p1 bypasses DND)
|
||||
|
||||
**Explorer**
|
||||
- Activity calendar with memo density heatmap
|
||||
- Tag browser with counts
|
||||
- Date and search filtering
|
||||
- Archived memos view
|
||||
|
||||
**Offline**
|
||||
- Room database cache for offline reading
|
||||
- Pending sync queue for offline edits (auto-drains on reconnect)
|
||||
- Sync status banner with last-synced time
|
||||
|
||||
**Personalization**
|
||||
- 3 themes: dark, light, AMOLED black
|
||||
- 20 WP8 accent colors (lime, green, emerald, teal, cyan, cobalt, indigo, violet, pink, magenta, crimson, red, orange, amber, yellow, brown, olive, steel, mauve, taupe)
|
||||
- Configurable week start day, default visibility, default reminder
|
||||
|
||||
**Backup**
|
||||
- Export/import memos as JSON
|
||||
- Backup rules exclude credentials from cloud backup
|
||||
|
||||
## Requirements
|
||||
|
||||
- Android 8.0+ (API 26)
|
||||
- A running [Memos](https://github.com/usememos/memos) instance (v0.22+)
|
||||
- JDK 11+
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
# Clone
|
||||
git clone https://github.com/avinal/nikki.git
|
||||
cd nikki
|
||||
|
||||
# Build debug APK
|
||||
./gradlew :androidApp:assembleDebug
|
||||
|
||||
# Install on connected device
|
||||
./gradlew :androidApp:installDebug
|
||||
|
||||
# Run tests
|
||||
./gradlew :composeApp:testDebugUnitTest
|
||||
```
|
||||
|
||||
The project uses Gradle 9.4 with the Kotlin Multiplatform plugin. Android Studio or IntelliJ IDEA with the Compose Multiplatform plugin is recommended for development.
|
||||
|
||||
## Installation
|
||||
|
||||
Download the latest APK from [Releases](https://github.com/avinal/nikki/releases), or build from source using the instructions above.
|
||||
|
||||
On first launch, enter your Memos server URL and an access token (generate one in your Memos account settings).
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|-----------|
|
||||
| UI | Compose Multiplatform 1.11, Material 3 |
|
||||
| Language | Kotlin 2.3 |
|
||||
| Networking | Ktor 3.5 |
|
||||
| Database | Room 2.8 (multiplatform) |
|
||||
| Image loading | Coil 3.4 |
|
||||
| Background work | WorkManager + AlarmManager |
|
||||
| Serialization | kotlinx.serialization |
|
||||
| Date/time | kotlinx-datetime 0.8 |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
nikki/
|
||||
├── androidApp/ # Android app shell (MainActivity, receivers)
|
||||
├── composeApp/ # Kotlin Multiplatform shared code
|
||||
│ └── src/
|
||||
│ ├── commonMain/ # Shared UI, domain, API, parser, database
|
||||
│ ├── androidMain/ # Android notifications, file picker, alarms
|
||||
│ └── iosMain/ # iOS stubs (scaffolded, not active)
|
||||
├── gradle/ # Version catalog and wrapper
|
||||
├── LOGO.md # Logo construction spec
|
||||
└── TASK_FORMAT.md # Task syntax reference
|
||||
```
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project was built almost entirely with the help of AI (Claude). I am not an Android developer by trade, and this is my first serious mobile app. Considerable effort has been made to avoid security vulnerabilities, bloat, and deprecated patterns - including a dedicated security review; but gaps may exist.
|
||||
|
||||
If you find a security issue, bug, or anything concerning, please [open an issue](https://github.com/avinal/nikki/issues) or email me at ripple@avinal.space.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- [Memos](https://github.com/usememos/memos) by the usememos team — the self-hosted note-taking tool that Nikki connects to
|
||||
- [JetBrains](https://www.jetbrains.com/compose-multiplatform/) — Compose Multiplatform framework
|
||||
- The Windows Phone design team — for proving that flat, typography-first UI is timeless
|
||||
- [Google Foobar](https://foobar.withgoogle.com/) — inspiration for the logo
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
# Task Format
|
||||
|
||||
Nikki extracts tasks from standard markdown checkboxes in your memos. Any line matching `- [ ]` or `- [x]` is parsed as a task. Metadata is extracted inline — no special syntax beyond what you'd naturally write.
|
||||
|
||||
## Basic Format
|
||||
|
||||
```
|
||||
- [ ] task description [due-date] [due-time] [!reminder] [p#] [#list]
|
||||
```
|
||||
|
||||
All metadata fields are optional. Order doesn't matter. The task text is everything that remains after metadata is stripped.
|
||||
|
||||
## Due Dates
|
||||
|
||||
ISO format or natural language.
|
||||
|
||||
| Input | Meaning |
|
||||
|-------|---------|
|
||||
| `2026-06-15` | June 15, 2026 |
|
||||
| `today` | Today's date |
|
||||
| `tomorrow` | Tomorrow |
|
||||
| `yesterday` | Yesterday |
|
||||
|
||||
```
|
||||
- [ ] submit report 2026-06-15
|
||||
- [ ] buy milk today
|
||||
```
|
||||
|
||||
## Due Times
|
||||
|
||||
12-hour or 24-hour format.
|
||||
|
||||
| Input | Meaning |
|
||||
|-------|---------|
|
||||
| `5pm` | 17:00 |
|
||||
| `10:30am` | 10:30 |
|
||||
| `14:00` | 14:00 |
|
||||
| `9am` | 09:00 |
|
||||
|
||||
```
|
||||
- [ ] standup today 9am
|
||||
- [ ] deploy 2026-06-15 14:00
|
||||
```
|
||||
|
||||
If a time is set without a date, today is assumed (with a warning).
|
||||
|
||||
## Reminders
|
||||
|
||||
How far before the due date/time to send a notification. Prefixed with `!`.
|
||||
|
||||
| Input | Meaning |
|
||||
|-------|---------|
|
||||
| `!30min` | 30 minutes before |
|
||||
| `!1hr` | 1 hour before |
|
||||
| `!2day` | 2 days before |
|
||||
| `!1week` | 1 week before |
|
||||
|
||||
```
|
||||
- [ ] dentist tomorrow 3pm !1hr
|
||||
- [ ] taxes 2026-04-15 !1week
|
||||
```
|
||||
|
||||
A reminder without a due date or time is flagged as an error — there's nothing to count back from.
|
||||
|
||||
## Priority
|
||||
|
||||
Three levels: p1 (high), p2 (medium), p3 (low).
|
||||
|
||||
| Level | Notification behavior |
|
||||
|-------|----------------------|
|
||||
| `p1` | Alarm sound, vibration, bypasses DND |
|
||||
| `p2` | Notification sound, heads-up |
|
||||
| `p3` | Notification sound, subtle |
|
||||
|
||||
```
|
||||
- [ ] server is down p1
|
||||
- [ ] update docs p3
|
||||
```
|
||||
|
||||
Tasks without a priority are treated as unclassified (silent notification).
|
||||
|
||||
## Lists / Tags
|
||||
|
||||
Hashtags assign tasks to lists. A task can belong to multiple lists.
|
||||
|
||||
```
|
||||
- [ ] buy groceries #shopping
|
||||
- [ ] fix login bug #backend #urgent
|
||||
```
|
||||
|
||||
If a task has no `#tag`, it inherits the memo-level tags (tags set at the top of the memo). Task-level tags always take priority over memo-level tags.
|
||||
|
||||
## Complete Examples
|
||||
|
||||
```markdown
|
||||
# Sprint 14
|
||||
|
||||
- [ ] fix auth timeout 2026-06-10 p1 #backend
|
||||
- [ ] update onboarding copy tomorrow 5pm !30min p2 #frontend
|
||||
- [ ] write migration tests 2026-06-12 #backend #testing
|
||||
- [x] deploy staging
|
||||
- [ ] review PR today 3pm !15min p2
|
||||
- [ ] buy coffee #personal
|
||||
```
|
||||
|
||||
This produces 6 tasks. The completed one (`deploy staging`) is tracked but excluded from validation. Tasks can be grouped by due date, list, priority, source memo, or completion status.
|
||||
|
||||
## Validation
|
||||
|
||||
The parser doctor checks incomplete tasks for issues and shows inline warnings/errors in the task view.
|
||||
|
||||
### Errors
|
||||
|
||||
| Issue | Example | Message |
|
||||
|-------|---------|---------|
|
||||
| Invalid date | `2026-13-45` | invalid date, use YYYY-MM-DD format |
|
||||
| Invalid priority | `p5` | invalid priority, only p1, p2, p3 are supported |
|
||||
| Reminder without due | `!30min` (no date/time) | reminder has no due date or time to count back from |
|
||||
| Invalid reminder unit | `!5blah` | invalid reminder unit, use min, hr, day, or week |
|
||||
|
||||
### Warnings
|
||||
|
||||
| Issue | Example | Message |
|
||||
|-------|---------|---------|
|
||||
| Time without date | `5pm` (no date) | time without date, using today |
|
||||
| Past date | `2020-01-01` | date is in the past |
|
||||
| Multiple priorities | `p1 p2` | multiple priorities, using first (p1) |
|
||||
| Multiple dates | `2026-06-01 2026-07-01` | multiple dates found, using first |
|
||||
| Multiple reminders | `!30min !1hr` | multiple reminders, using first |
|
||||
| Typo | `tomorow` | did you mean "tomorrow"? |
|
||||
|
||||
### Typo Detection
|
||||
|
||||
Common misspellings are caught and suggestions offered:
|
||||
|
||||
- `tday`, `todya`, `toaday`, `toady` → today
|
||||
- `tmrw`, `tomorow`, `tommorow` → tomorrow
|
||||
- `yestrday`, `ysterday` → yesterday
|
||||
- Day name typos: `munday`, `tusday`, `wendsday`, `thurday`, `firday`, `saterday`, `sundie`, etc.
|
||||
|
||||
## Notification Behavior
|
||||
|
||||
When a task has both a due date/time and a reminder, two notifications may fire:
|
||||
|
||||
1. **Reminder notification** — fires `!duration` before the due time
|
||||
2. **Due notification** — fires at the due time itself
|
||||
|
||||
If a task has a date but no time, the notification fires at the default notify time (configurable in settings, default 8pm).
|
||||
|
||||
If a task has neither date nor time, no notification is scheduled.
|
||||
@@ -141,7 +141,7 @@ class MemosApiClient(
|
||||
suspend fun listArchivedMemos(): ApiResult<ListMemosResponse> = apiCall {
|
||||
httpClient.get(url("/memos")) {
|
||||
parameter("pageSize", 50)
|
||||
parameter("filter", "state == \"ARCHIVED\"")
|
||||
parameter("state", "ARCHIVED")
|
||||
}.body()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<rect width="200" height="200" fill="#1F1F1F"/>
|
||||
|
||||
<!-- Pink circle r=52 -->
|
||||
<circle cx="100" cy="100" r="52" fill="#EE67A4"/>
|
||||
|
||||
<!-- Black 47° annular sector: imaginary inner r=37 to pink edge r=52 -->
|
||||
<path fill="#231F20" d="
|
||||
M133.93,85.25
|
||||
L147.69,79.27
|
||||
A52,52 0 0,1 147.69,120.73
|
||||
L133.93,114.75
|
||||
A37,37 0 0,0 133.93,85.25
|
||||
Z"/>
|
||||
|
||||
<!-- Teal 47° annular sector: pink edge r=52 to imaginary outer r=65 -->
|
||||
<path fill="#35BEB8" d="
|
||||
M147.69,79.27
|
||||
L159.61,74.08
|
||||
A65,65 0 0,1 159.61,125.92
|
||||
L147.69,120.73
|
||||
A52,52 0 0,0 147.69,79.27
|
||||
Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 685 B |
Reference in New Issue
Block a user