Video support, comment section, more
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# TaskForge
|
||||
|
||||
Task management for game projects, written in Jai. The same client code runs as a native Windows app (OpenGL 3.3) and in the browser (WebGL2 via WASM). All UI and rendering is custom immediate-mode code; the only web code is a small JS shim that forwards GL calls, input and HTTP.
|
||||
Task management for game projects, written in Jai. The same client code runs as a native Windows app (OpenGL 3.3) and in the browser (WebGL2 via WASM). All UI and rendering is custom immediate-mode code; the only web code is a small JS shim that forwards GL calls, input, HTTP and media decoding.
|
||||
|
||||
It runs in two modes:
|
||||
|
||||
@@ -9,13 +9,24 @@ It runs in two modes:
|
||||
|
||||
## Features
|
||||
|
||||
- **Custom fields** (Fields page): Text, Number, Checkbox, Select (one option), Tags (several options) and User (assign people). Options have colours, and a field can apply to tasks, epics or both. Add, rename, reorder and delete fields as you like.
|
||||
- **Custom fields** (Fields page): Text, Number, Checkbox, Select (one option), Tags (several options) and User (assign people). Options have colours, and a field can apply to tasks, epics or both.
|
||||
- **Views** for each context:
|
||||
- A **Board** turns any Select or Checkbox field into kanban columns, with drag & drop.
|
||||
- A **Checklist** turns each task into a checkbox. "Done" means a field has a chosen value, and items can be grouped by epic.
|
||||
- Views can filter by epic, by an option (e.g. `Discipline: Art`) or by a user (e.g. `Assignee: Me`), and choose which fields appear on cards.
|
||||
- **Epic rules** (Epic rules page): set a field on an epic from the state of its tasks, e.g. *Set **Art** to **Done** when **all** of its **Art** tasks have **Status** is **Done***. For each epic and field, the first matching rule from the top wins. If none match, the field keeps its value.
|
||||
- **Users** (server mode): sign-in with sessions, admins and deactivation. Assign people with User fields. Admins add users and reset passwords on the Users page; everyone can change their own password.
|
||||
- **Attachments:** images (PNG, JPEG, GIF, BMP, WebP) and videos (MP4/MOV, WebM, AVI) on tasks and comments. You can drop files onto the window, use the **Add image or video** button, or paste with Ctrl+V. The first attachment becomes the card's cover. Click a thumbnail to open the viewer; use Left/Right to move between attachments and Esc to close.
|
||||
- **Screenshots:** pasting a screenshot attaches it, e.g. after Win+Shift+S. The **Snip screen** button (native) opens the Windows snipping overlay and attaches the result automatically. **Capture screen** (web) grabs a frame of a screen, window or tab you pick.
|
||||
- **Video playback** in the viewer has play/pause (click or Space), seeking, time and mute.
|
||||
- Natively, Windows Media Foundation decodes video and audio inside the app: H.264/HEVC/AAC and anything else Windows has codecs for. Audio plays through waveOut and drives the clock.
|
||||
- In the browser, the browser's own decoder is used.
|
||||
- Attaching a video stores its size, duration and a poster frame, so thumbnails need no video decoding.
|
||||
- **Comments** on every task:
|
||||
- threads with replies,
|
||||
- editing that keeps the full edit history ("edited (2)" shows the earlier versions),
|
||||
- deletion that leaves a placeholder, so replies still make sense,
|
||||
- images, videos and screenshots inside comments.
|
||||
- **Text editing:** selection with the mouse (drag, double-click selects a word, triple-click selects all) and the keyboard (Shift+arrows/Home/End, Ctrl+A). Copy, cut and paste work on the selection, and typing replaces it.
|
||||
|
||||
## Building
|
||||
@@ -34,20 +45,23 @@ Each client build bakes `data/Karla-Regular.ttf` (SIL Open Font License) into `f
|
||||
|
||||
## Running locally
|
||||
|
||||
- **Native, local file:** `bin/TaskForge.exe [path/to/project.taskforge]`. The plain-text project file can live in your game's repository and diffs cleanly.
|
||||
- **Web, local:** serve `web/` with any static server (`python -m http.server -d web 8000`). The project lives in the browser; Export/Import move it to or from a file.
|
||||
- **Native, local file:** `bin/TaskForge.exe [path/to/project.taskforge]`.
|
||||
- The project is a plain-text file that can live in your game's repository and diffs cleanly.
|
||||
- Attachments are stored next to it in `<project>.taskforge.files/`.
|
||||
- **Web, local:** serve `web/` with any static server (`python -m http.server -d web 8000`). The project lives in the browser, with attachments in IndexedDB. Export/Import move the project (without attachments) to or from a file.
|
||||
- **Server on your machine:**
|
||||
```
|
||||
cd server/bin
|
||||
taskforge-server add-user daniel "Daniel" --admin # asks for a password
|
||||
taskforge-server --web ../../web --port 8080 # http://localhost:8080
|
||||
```
|
||||
`--demo` on the first start seeds the sample game project; without it you get a clean starter project (Status, Assignee, Discipline, Priority; Board and My tasks views). On Windows the server needs `sqlite3.dll` next to it (the build copies the one in `server/dev/`).
|
||||
- `--demo` on the first start seeds the sample game project. Without it you get a clean starter project: Status, Assignee, Discipline and Priority fields, plus Board and My tasks views.
|
||||
- On Windows the server needs `sqlite3.dll` next to it; the build copies the one in `server/dev/`.
|
||||
- **Native app against a server:** `bin/TaskForge.exe --server https://tasks.example.com`. The session is remembered in `%APPDATA%\TaskForge\session.txt`.
|
||||
|
||||
## Deploying to a DigitalOcean droplet
|
||||
|
||||
1. Create an Ubuntu 24.04 droplet (the smallest size is plenty) and point a domain's A record at its IP.
|
||||
1. Create an Ubuntu 24.04 droplet and point a domain's A record at its IP. The smallest size is fine for tasks; pick more disk if you'll attach a lot of video.
|
||||
2. From Git Bash on your machine:
|
||||
```
|
||||
deploy/deploy.sh root@<droplet-ip> tasks.example.com
|
||||
@@ -57,7 +71,7 @@ Each client build bakes `data/Karla-Regular.ttf` (SIL Open Font License) into `f
|
||||
- creates a `taskforge` system user and a hardened systemd service,
|
||||
- configures HTTPS for your domain through Caddy (automatic Let's Encrypt),
|
||||
- enables the firewall,
|
||||
- adds a daily SQLite backup to `/var/backups/taskforge` (kept for 14 days).
|
||||
- adds a daily backup of the database and attachments to `/var/backups/taskforge` (database copies kept for 14 days).
|
||||
|
||||
Every run builds the web client and the Linux server, uploads them, links the server on the droplet and restarts it. For later updates, run `deploy/deploy.sh root@<droplet-ip>`.
|
||||
3. Create the first admin on the droplet:
|
||||
@@ -68,12 +82,22 @@ Each client build bakes `data/Karla-Regular.ttf` (SIL Open Font License) into `f
|
||||
|
||||
Why link on the droplet: the server is cross-compiled to a Linux object file on Windows, but linking a Linux executable needs Linux's system libraries, which the droplet has. Alternatively, copy your Jai distribution to the droplet and run `jai-linux build.jai - server` there.
|
||||
|
||||
The backups live on the same droplet. For real safety, also enable DigitalOcean's droplet backups, or copy `/var/backups/taskforge` off the machine.
|
||||
|
||||
Useful on the droplet: `journalctl -u taskforge -f` (logs), `taskforge-server --db ... list-users`, `taskforge-server --db ... set-password <user>`.
|
||||
|
||||
## How the server works
|
||||
|
||||
- **Database:** SQLite in WAL mode, loaded at runtime (`libsqlite3.so.0` / `sqlite3.dll`). Tables: `records` (the project), `users`, `sessions`, `login_failures`, `meta`. Each worker thread has its own connection; writes are short `BEGIN IMMEDIATE` transactions.
|
||||
- **Project sync:** a project is a set of records, one text line per entity (`task 42 ...`, `val 42 7 sel 5`, ...), the same format as the local file. Every write bumps a global revision. Clients send the records they changed (found by diffing against what the server last confirmed) and receive everything changed since their last revision. The last writer wins per record, so two people editing different fields of the same task don't overwrite each other. Clients poll every 2 seconds, and immediately after local edits. New ids come in blocks of 1000 per client, so they never collide.
|
||||
- **Database:** SQLite in WAL mode, loaded at runtime (`libsqlite3.so.0` / `sqlite3.dll`). Tables: `records` (the project), `users`, `sessions`, `login_failures`, `blobs`, `meta`. Each worker thread has its own connection; writes are short `BEGIN IMMEDIATE` transactions.
|
||||
- **Attachments** are content-addressed:
|
||||
- Files are named by their SHA-256 and stored in `blobs/` next to the database (`--blobs` moves it).
|
||||
- Uploads (`POST /api/blob`, up to 600 MB) stream to disk while being hashed, and only images and videos are accepted.
|
||||
- Downloads (`GET /api/blob/<hash>`) need a session.
|
||||
- **Project sync:**
|
||||
- A project is a set of records, one text line per entity (`task 42 ...`, `val 42 7 sel 5`, `comment ...`, `attach ...`), the same format as the local file.
|
||||
- Every write bumps a global revision. Clients send the records they changed (found by diffing against what the server last confirmed) and receive everything changed since their last revision.
|
||||
- The last writer wins per record, so two people editing different fields of the same task don't overwrite each other.
|
||||
- Clients poll every 2 seconds, and immediately after local edits. New ids come in blocks of 1000 per client, so they never collide.
|
||||
- **Security:**
|
||||
- Passwords are hashed with PBKDF2-HMAC-SHA256 (310,000 iterations, random salt).
|
||||
- Session tokens are 256-bit random values, stored only as SHA-256 hashes; they expire after 30 days of inactivity.
|
||||
@@ -81,9 +105,13 @@ Useful on the droplet: `journalctl -u taskforge -f` (logs), `taskforge-server --
|
||||
- Changing a password signs out that user's other sessions.
|
||||
- Static file serving only accepts plain file names from the web directory.
|
||||
- HTTPS is handled by Caddy; the server listens on 127.0.0.1 only.
|
||||
- **HTTP API** (plain text bodies): `GET /api/info`, `POST /api/login`, `/api/logout`, `/api/sync`, `/api/users`.
|
||||
- **HTTP API:** `GET /api/info`, `POST /api/login`, `/api/logout`, `/api/sync`, `/api/users`, `POST /api/blob`, `GET /api/blob/<hash>`.
|
||||
|
||||
Current limits: one project per server, and changes arrive by polling (up to ~2 s delay) rather than push.
|
||||
Current limits:
|
||||
- One project per server.
|
||||
- Changes arrive by polling (up to ~2 s delay) rather than push.
|
||||
- Deleting an attachment removes it from the project but leaves its file in the blob store.
|
||||
- Client size limits are 50 MB for images and 500 MB for videos.
|
||||
|
||||
## Code layout
|
||||
|
||||
@@ -91,23 +119,30 @@ Current limits: one project per server, and changes arrive by polling (up to ~2
|
||||
|---|---|
|
||||
| `build.jai` | Build metaprogram for all targets + font atlas baker |
|
||||
| `src/main.jai` | Client entry; picks the platform layer |
|
||||
| `src/platform_native.jai` | Win32 window, WGL, input, files, clipboard, libcurl HTTP, `--sim` test harness |
|
||||
| `src/platform_web.jai`, `web/taskforge.js` | WASM exports + the JS shim (WebGL2, input, fetch, localStorage) |
|
||||
| `src/platform_native.jai` | Win32 window, WGL, input, files, clipboard (incl. images), file picker, drag & drop, snipping, libcurl HTTP, `--sim` test harness |
|
||||
| `src/video_native.jai` | Media Foundation + waveOut video playback and probing (Windows) |
|
||||
| `src/platform_web.jai`, `web/taskforge.js` | WASM exports + the JS shim (WebGL2, input, fetch, localStorage/IndexedDB, image/video decoding) |
|
||||
| `src/web_gl.jai` | GL declarations for WASM (same names as the GL module) |
|
||||
| `src/render.jai`, `src/font.jai` | Batched renderer and text |
|
||||
| `src/render.jai`, `src/font.jai` | Batched renderer (shapes, text, images) and text |
|
||||
| `src/ui.jai` | Immediate-mode widgets: buttons, text editing with selection, dropdowns, scrolling |
|
||||
| `src/model.jai` | Fields, tasks, views, users, epic rule evaluation |
|
||||
| `src/model.jai` | Fields, tasks, views, users, attachments, comments, epic rule evaluation |
|
||||
| `src/storage.jai` | Record format, local file, starter projects |
|
||||
| `src/sync.jai` | Sign-in and project sync with the server |
|
||||
| `src/media.jai` | Blob store, image cache, attaching files, video player state |
|
||||
| `src/attachments_ui.jai`, `src/comments.jai` | Thumbnails, viewer, card covers; comment threads |
|
||||
| `src/app.jai`, `src/account.jai` | Screens |
|
||||
| `server/server.jai` | HTTP server, API, users and sessions |
|
||||
| `server/sqlite.jai`, `server/crypto.jai` | SQLite binding; SHA-256 / HMAC / PBKDF2 / OS randomness |
|
||||
| `src/sha256.jai` | SHA-256 / HMAC / PBKDF2 (shared with the server) |
|
||||
| `server/server.jai` | HTTP server, API, users, sessions, blob storage |
|
||||
| `server/sqlite.jai`, `server/crypto.jai` | SQLite binding; OS randomness |
|
||||
| `deploy/` | Droplet setup, deploy script, systemd unit, Caddyfile |
|
||||
|
||||
## Keyboard
|
||||
|
||||
- `Esc`: close a popup or the task panel
|
||||
- `Esc`: close the viewer, a popup or the task panel
|
||||
- `Ctrl+S`: save / sync now
|
||||
- `Ctrl+V` with an image on the clipboard (task open): attach it
|
||||
- In the viewer: Left/Right for previous/next, `Space` for play/pause
|
||||
- In the comment box: `Ctrl+Enter` posts
|
||||
- In text fields:
|
||||
- `Shift`+arrows, `Home`, `End`: select
|
||||
- `Ctrl`+arrows: move by word
|
||||
@@ -121,7 +156,7 @@ Current limits: one project per server, and changes arrive by polling (up to ~2
|
||||
The native client can be scripted, for screenshots and regression checks:
|
||||
|
||||
```
|
||||
bin/TaskForge.exe test.taskforge --sim "wait 2;click 710 230;wait 2;type Hello;shot out.bmp;quit"
|
||||
bin/TaskForge.exe test.taskforge --sim "wait 2;click 710 230;wait 2;drop C:/art/concept.png;type Hello;shot out.bmp;quit"
|
||||
```
|
||||
|
||||
Commands: `move X Y`, `down`, `up`, `click X Y`, `type TEXT`, `key CODE [MODS]`, `wheel LINES`, `wait N`, `shot FILE.bmp`, `quit`.
|
||||
Commands: `move X Y`, `down`, `up`, `click X Y`, `type TEXT`, `key CODE [MODS]`, `wheel LINES`, `drop PATH`, `wait N`, `shot FILE.bmp`, `quit`.
|
||||
|
||||
Reference in New Issue
Block a user