From 2af8dbf814475d4afc3bfaa2c1be261adc1fc49d Mon Sep 17 00:00:00 2001 From: Chinmay Chaudhari Date: Mon, 17 Aug 2026 23:32:28 +0530 Subject: [PATCH 1/2] site: publish the project website on the branch GitHub Pages serves MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pages for this repository is configured as branch-based, serving `fdroid-repo` at the root, so that is the only branch whose contents actually appear at https://ccextractor.github.io/taskwarrior-flutter/. The site lived on a feature branch where nothing could serve it; this puts the built output where Pages will. Only additions — every existing path is untouched, so the F-Droid repo, its metadata, assets and index are exactly as they were. The site occupies the root and `repo/` continues to serve F-Droid clients. Two deliberate choices: - Built for https://ccextractor.github.io/taskwarrior-flutter/ rather than the custom domain, because no custom domain is configured on Pages right now. - The generated CNAME (taskwarrior.ccextractor.org) is NOT included. Committing it would make Pages claim a domain with no DNS pointing at it, which would take the site offline and, worse, break the F-Droid repo URL that existing users' clients already poll. Once the DNS record exists, add the CNAME and rebuild with that baseURL. - .nojekyll added so Pages serves the files as-is instead of running them through Jekyll on every push. The nightly F-Droid workflow force-pushes this branch, but it checks out fdroid-repo before building its orphan commit and then `git add .`, so the site files are re-added and survive the deploy. That does mean the site depends on that checkout step continuing to exist. Hugo source stays with the app code; only the built output belongs here. --- .nojekyll | 0 categories/index.html | 5 + categories/index.xml | 1 + css/style.css | 165 ++++++++++++++++++++++++++++++++ docs/architecture/index.html | 23 +++++ docs/faq/index.html | 15 +++ docs/getting-started/index.html | 7 ++ docs/index.html | 11 +++ docs/index.xml | 76 +++++++++++++++ docs/sync-setup/index.html | 14 +++ downloads/index.html | 9 ++ features/index.html | 12 +++ index.html | 11 +++ index.xml | 130 +++++++++++++++++++++++++ sitemap.xml | 1 + tags/index.html | 5 + tags/index.xml | 1 + 17 files changed, 486 insertions(+) create mode 100644 .nojekyll create mode 100644 categories/index.html create mode 100644 categories/index.xml create mode 100644 css/style.css create mode 100644 docs/architecture/index.html create mode 100644 docs/faq/index.html create mode 100644 docs/getting-started/index.html create mode 100644 docs/index.html create mode 100644 docs/index.xml create mode 100644 docs/sync-setup/index.html create mode 100644 downloads/index.html create mode 100644 features/index.html create mode 100644 index.html create mode 100644 index.xml create mode 100644 sitemap.xml create mode 100644 tags/index.html create mode 100644 tags/index.xml diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 00000000..e065e6ff --- /dev/null +++ b/categories/index.html @@ -0,0 +1,5 @@ +Categories · TaskWarrior Mobile

Categories

\ No newline at end of file diff --git a/categories/index.xml b/categories/index.xml new file mode 100644 index 00000000..832c165c --- /dev/null +++ b/categories/index.xml @@ -0,0 +1 @@ +Categories on TaskWarrior Mobilehttps://ccextractor.github.io/taskwarrior-flutter/categories/Recent content in Categories on TaskWarrior MobileHugoen-us \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 00000000..37f1d885 --- /dev/null +++ b/css/style.css @@ -0,0 +1,165 @@ +:root { + --bg: #0f1419; + --surface: #171d26; + --text: #e6e9ee; + --muted: #9aa4b2; + --accent: #7c5cff; + --accent-2: #4dd0e1; + --border: #263041; + --ok: #3fb950; + --fail: #f85149; + --radius: 12px; + --maxw: 900px; +} + +* { box-sizing: border-box; } + +html { -webkit-text-size-adjust: 100%; } + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.6; +} + +a { color: var(--accent-2); text-decoration: none; } +a:hover { text-decoration: underline; } + +code { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 6px; + padding: 0.1em 0.4em; + font-size: 0.9em; +} + +/* Header */ +.site-header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--border); + background: var(--surface); +} +.brand { font-weight: 700; font-size: 1.15rem; color: var(--text); } +.brand span { color: var(--accent); } +.site-header nav { display: flex; flex-wrap: wrap; gap: 1.1rem; } +.site-header nav a { color: var(--muted); font-weight: 500; } +.site-header nav a:hover { color: var(--text); text-decoration: none; } + +/* Layout */ +.content { max-width: var(--maxw); margin: 0 auto; padding: 2.5rem 1.5rem 4rem; } + +/* Hero */ +.hero { text-align: center; padding: 2rem 0 1rem; } +.hero h1 { font-size: clamp(2rem, 6vw, 3rem); margin: 0 0 0.5rem; } +.tagline { color: var(--muted); font-size: 1.15rem; max-width: 640px; margin: 0 auto 1.5rem; } +.cta { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; } + +.button { + display: inline-block; + background: var(--accent); + color: #fff; + padding: 0.7rem 1.4rem; + border-radius: var(--radius); + font-weight: 600; +} +.button:hover { filter: brightness(1.1); text-decoration: none; } +.button-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); } + +/* Features */ +.features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1rem; + margin: 2.5rem 0; +} +.feature { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1.25rem; +} +.feature h3 { margin: 0 0 0.4rem; color: var(--accent-2); } +.feature p { margin: 0; color: var(--muted); } + +/* Pages */ +.page h1 { border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; } +.page h2 { margin-top: 2rem; } +.page-list { padding-left: 1.2rem; } + +/* Content tables (markdown) */ +.page table { + width: 100%; + border-collapse: collapse; + margin: 1.25rem 0; + font-size: 0.95rem; +} +.page th, .page td { + text-align: left; + padding: 0.55rem 0.7rem; + border-bottom: 1px solid var(--border); + vertical-align: top; +} +.page th { color: var(--muted); font-weight: 600; } +.page thead tr { border-bottom: 2px solid var(--border); } + +/* Code blocks */ +.page pre { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1rem; + overflow-x: auto; +} +.page pre code { background: none; border: none; padding: 0; } +.page blockquote { + margin: 1.25rem 0; + padding: 0.25rem 1rem; + border-left: 3px solid var(--accent); + color: var(--muted); +} + +/* Nightly build table */ +.table-scroll { overflow-x: auto; } +.nightly-builds table { + width: 100%; + border-collapse: collapse; + margin: 1rem 0; + font-size: 0.92rem; +} +.nightly-builds th, .nightly-builds td { + text-align: left; + padding: 0.55rem 0.7rem; + border-bottom: 1px solid var(--border); + vertical-align: top; +} +.nightly-builds th { color: var(--muted); font-weight: 600; } +.nowrap { white-space: nowrap; } +.empty { color: var(--muted); font-style: italic; } + +.status { + display: inline-block; + padding: 0.1em 0.6em; + border-radius: 999px; + font-size: 0.8em; + font-weight: 600; + text-transform: capitalize; +} +.status-success { background: rgba(63,185,80,0.15); color: var(--ok); } +.status-failure { background: rgba(248,81,73,0.15); color: var(--fail); } + +/* Footer */ +.site-footer { + border-top: 1px solid var(--border); + padding: 1.5rem; + text-align: center; + color: var(--muted); + font-size: 0.9rem; +} diff --git a/docs/architecture/index.html b/docs/architecture/index.html new file mode 100644 index 00000000..1e4ed027 --- /dev/null +++ b/docs/architecture/index.html @@ -0,0 +1,23 @@ +Architecture · TaskWarrior Mobile

Architecture

TaskWarrior Mobile is a Flutter app sitting on top of a native Rust core, so the same +task engine that powers TaskWarrior on the desktop runs unchanged on your phone.

Layers

  • UI — Flutter (Dart). Screens, navigation, and state management use +GetX. This layer never talks to the network for task +data; it reads and writes through the core.
  • Core — Rust (tc_helper). A thin wrapper around +TaskChampion exposed to Dart +through flutter_rust_bridge. It +owns task storage, mutations, and sync.
  • Storage & sync — TaskChampion. Tasks live in a local replica on the device. +Syncing reconciles that replica with a TaskChampion sync server; there is no +bespoke HTTP API in between.

Offline-first data flow

tap "complete"  ─▶  Flutter  ─▶  Rust FFI  ─▶  local replica   (instant, no network)
+                                                     │
+                                            (later, when online)
+                                                     ▼
+                                          TaskChampion sync server
+

Every create, edit, and complete is committed to the local replica synchronously, so the +UI never waits on the network. Synchronisation is a separate, best-effort step that runs +when connectivity is available.

Why a Rust core?

Reusing TaskChampion means the mobile app shares TaskWarrior’s battle-tested task model — +recurrence, dependencies, annotations, tags, and the sync protocol — instead of +reimplementing them. The Rust bridge surfaces those capabilities to the Flutter UI with a +small, typed FFI surface.

\ No newline at end of file diff --git a/docs/faq/index.html b/docs/faq/index.html new file mode 100644 index 00000000..8dc3eba6 --- /dev/null +++ b/docs/faq/index.html @@ -0,0 +1,15 @@ +FAQ · TaskWarrior Mobile

FAQ

Do I need a sync server to use the app?

No. TaskWarrior Mobile is fully usable offline — tasks are stored in a local replica on +your device. A sync server is only needed if you want your tasks mirrored to another +device or backed up remotely.

Is my data encrypted?

Yes. When you sync, your tasks are encrypted locally with your encryption secret before +being uploaded. The server stores only ciphertext; only clients that hold the secret can +read your tasks.

Which platforms are supported?

The app targets Android, iOS, Windows, macOS, and Linux from a single Flutter codebase. +Android builds are published as APKs on the downloads page.

Does it work with my existing TaskWarrior setup?

If your TaskWarrior 3.x desktop syncs through a TaskChampion sync server, point the app at +the same server, client ID, and encryption secret and your tasks will sync between them. +See setting up sync.

What’s the difference between stable and nightly builds?

Stable builds are tagged releases meant for everyday use. Nightly builds are automatic +snapshots of the main branch — useful for trying the latest changes, but less tested.

Where do I report a bug or request a feature?

Open an issue on the +GitHub repository. Pull +requests are welcome too — see the contributing notes.

\ No newline at end of file diff --git a/docs/getting-started/index.html b/docs/getting-started/index.html new file mode 100644 index 00000000..e23ba5e1 --- /dev/null +++ b/docs/getting-started/index.html @@ -0,0 +1,7 @@ +Getting started · TaskWarrior Mobile

Getting started

Install the app

Grab a build from the downloads page:

  • Stable — the latest signed APK from the GitHub releases page.
  • Nightly — an automatically-built snapshot from the main branch, for testing the newest changes.

On Android you may need to allow installation from your browser or file manager the +first time you sideload an APK.

Create your first task

  1. Open the app — you land on the task list.
  2. Tap the + button.
  3. Give the task a description, and optionally a project, tags, priority, and due date.
  4. Save. The task is written to the local database immediately — no network required.

Everything you do works fully offline. Sync is optional and layered on top; set it up +whenever you want your tasks mirrored to a server or another device.

Next steps

\ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..b4e9c1d5 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,11 @@ +Documentation · TaskWarrior Mobile
\ No newline at end of file diff --git a/docs/index.xml b/docs/index.xml new file mode 100644 index 00000000..b9c03827 --- /dev/null +++ b/docs/index.xml @@ -0,0 +1,76 @@ +Documentation on TaskWarrior Mobilehttps://ccextractor.github.io/taskwarrior-flutter/docs/Recent content in Documentation on TaskWarrior MobileHugoen-usGetting startedhttps://ccextractor.github.io/taskwarrior-flutter/docs/getting-started/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/getting-started/<h2 id="install-the-app">Install the app</h2> +<p>Grab a build from the <a href="../../downloads/">downloads page</a>:</p> +<ul> +<li><strong>Stable</strong> — the latest signed APK from the GitHub <a href="https://github.com/CCExtractor/taskwarrior-flutter/releases">releases page</a>.</li> +<li><strong>Nightly</strong> — an automatically-built snapshot from the <code>main</code> branch, for testing the newest changes.</li> +</ul> +<p>On Android you may need to allow installation from your browser or file manager the +first time you sideload an APK.</p> +<h2 id="create-your-first-task">Create your first task</h2> +<ol> +<li>Open the app — you land on the task list.</li> +<li>Tap the <strong>+</strong> button.</li> +<li>Give the task a description, and optionally a project, tags, priority, and due date.</li> +<li>Save. The task is written to the local database immediately — no network required.</li> +</ol> +<p>Everything you do works fully offline. Sync is optional and layered on top; set it up +whenever you want your tasks mirrored to a server or another device.</p>Setting up synchttps://ccextractor.github.io/taskwarrior-flutter/docs/sync-setup/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/sync-setup/<p>TaskWarrior Mobile syncs through a <a href="https://github.com/GothenburgBitFactory/taskchampion">TaskChampion</a> +sync server — the same protocol used by TaskWarrior 3.x on the desktop. Sync is +<strong>optional and offline-first</strong>: your tasks always live in a local replica, and syncing +simply reconciles that replica with the server when you are online.</p> +<h2 id="what-you-need">What you need</h2> +<p>Three values from your TaskChampion sync server:</p> +<table> + <thead> + <tr> + <th>Field</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong>Sync server URL</strong></td> + <td>The base URL of the TaskChampion server, e.g. <code>https://example.org/taskchampion/</code>.</td> + </tr> + <tr> + <td><strong>Client ID</strong></td> + <td>A UUID identifying your replica on the server.</td> + </tr> + <tr> + <td><strong>Encryption secret</strong></td> + <td>The secret used to encrypt your data end-to-end. Only clients that share it can read your tasks.</td> + </tr> + </tbody> +</table> +<p>The server never sees your task data in the clear — everything is encrypted locally with +your encryption secret before it is uploaded.</p>Architecturehttps://ccextractor.github.io/taskwarrior-flutter/docs/architecture/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/architecture/<p>TaskWarrior Mobile is a Flutter app sitting on top of a native Rust core, so the same +task engine that powers TaskWarrior on the desktop runs unchanged on your phone.</p> +<h2 id="layers">Layers</h2> +<ul> +<li><strong>UI — Flutter (Dart).</strong> Screens, navigation, and state management use +<a href="https://pub.dev/packages/get">GetX</a>. This layer never talks to the network for task +data; it reads and writes through the core.</li> +<li><strong>Core — Rust (<code>tc_helper</code>).</strong> A thin wrapper around +<a href="https://github.com/GothenburgBitFactory/taskchampion">TaskChampion</a> exposed to Dart +through <a href="https://github.com/fzyzcjy/flutter_rust_bridge"><code>flutter_rust_bridge</code></a>. It +owns task storage, mutations, and sync.</li> +<li><strong>Storage &amp; sync — TaskChampion.</strong> Tasks live in a local replica on the device. +Syncing reconciles that replica with a TaskChampion sync server; there is no +bespoke HTTP API in between.</li> +</ul> +<h2 id="offline-first-data-flow">Offline-first data flow</h2> +<pre tabindex="0"><code>tap &#34;complete&#34; ─▶ Flutter ─▶ Rust FFI ─▶ local replica (instant, no network) + │ + (later, when online) + ▼ + TaskChampion sync server +</code></pre><p>Every create, edit, and complete is committed to the local replica synchronously, so the +UI never waits on the network. Synchronisation is a separate, best-effort step that runs +when connectivity is available.</p>FAQhttps://ccextractor.github.io/taskwarrior-flutter/docs/faq/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/faq/<h2 id="do-i-need-a-sync-server-to-use-the-app">Do I need a sync server to use the app?</h2> +<p>No. TaskWarrior Mobile is fully usable offline — tasks are stored in a local replica on +your device. A sync server is only needed if you want your tasks mirrored to another +device or backed up remotely.</p> +<h2 id="is-my-data-encrypted">Is my data encrypted?</h2> +<p>Yes. When you sync, your tasks are encrypted locally with your encryption secret before +being uploaded. The server stores only ciphertext; only clients that hold the secret can +read your tasks.</p> \ No newline at end of file diff --git a/docs/sync-setup/index.html b/docs/sync-setup/index.html new file mode 100644 index 00000000..6fb3fad9 --- /dev/null +++ b/docs/sync-setup/index.html @@ -0,0 +1,14 @@ +Setting up sync · TaskWarrior Mobile

Setting up sync

TaskWarrior Mobile syncs through a TaskChampion +sync server — the same protocol used by TaskWarrior 3.x on the desktop. Sync is +optional and offline-first: your tasks always live in a local replica, and syncing +simply reconciles that replica with the server when you are online.

What you need

Three values from your TaskChampion sync server:

FieldDescription
Sync server URLThe base URL of the TaskChampion server, e.g. https://example.org/taskchampion/.
Client IDA UUID identifying your replica on the server.
Encryption secretThe secret used to encrypt your data end-to-end. Only clients that share it can read your tasks.

The server never sees your task data in the clear — everything is encrypted locally with +your encryption secret before it is uploaded.

Connecting

  1. Open Profile → change the sync server → choose Taskchampion.
  2. Tap Configure and paste the three values above.
  3. Tap Save. The app performs a live sync against the server to confirm the +credentials work before it stores them — if anything is wrong, you find out +immediately instead of silently failing later.

Once connected, changes flow both ways whenever you have connectivity. You can keep +working offline at any time; the next sync catches the server up.

Self-hosting a server

Any TaskChampion-compatible sync server works. See the upstream +TaskChampion sync-server +project to run your own.

\ No newline at end of file diff --git a/downloads/index.html b/downloads/index.html new file mode 100644 index 00000000..03756309 --- /dev/null +++ b/downloads/index.html @@ -0,0 +1,9 @@ +Downloads · TaskWarrior Mobile

Downloads

Stable releases

Tagged releases are published on GitHub. Download the latest signed APK from the +releases page.

Nightly builds

A signed nightly APK is built automatically from the main branch every night and +published to the project’s F-Droid repository. Each entry below links to the APK produced +by that build, newest first.

No nightly builds have been recorded yet. Check back after the next scheduled build.

Nightly builds are unstable snapshots intended for testing. For everyday use, prefer a +stable release above.

\ No newline at end of file diff --git a/features/index.html b/features/index.html new file mode 100644 index 00000000..db69e6d9 --- /dev/null +++ b/features/index.html @@ -0,0 +1,12 @@ +Features · TaskWarrior Mobile

Features

TaskWarrior Mobile brings the full TaskWarrior task model to your phone and desktop, +without giving up speed or working offline.

Task management

  • Rich tasks — descriptions, projects, tags, priorities, and due dates.
  • Recurring tasks — repeat tasks on a schedule.
  • Dependencies — mark tasks as blocking or blocked by others so you always know +what is actionable.
  • Annotations — attach timestamped notes to any task.
  • Fast capture and edit — add or change a task in a couple of taps.

Offline-first

  • Every create, edit, and complete is written to a local replica instantly — no +spinner, no waiting on the network.
  • The app is fully usable with no connectivity at all; sync is optional.

Native TaskChampion sync

  • Syncs through a TaskChampion +sync server — the same protocol used by TaskWarrior 3.x on the desktop.
  • End-to-end encrypted: your tasks are encrypted locally before upload, so the server +only ever stores ciphertext.
  • Credentials are validated against the server before they are saved, so a bad URL or +secret is caught immediately.

Cross-platform

  • One Flutter codebase targeting Android, iOS, Windows, macOS, and Linux.
  • A shared native Rust core keeps behaviour consistent everywhere.

Open source

Ready to try it? Head to the downloads page.

\ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..18a410fc --- /dev/null +++ b/index.html @@ -0,0 +1,11 @@ +TaskWarrior Mobile

Your tasks, everywhere.

The cross-platform TaskWarrior mobile app by CCExtractor — offline-first task management with native TaskChampion sync.

Get the app +View source

Offline-first

Every create, edit, and complete is written to a local database instantly — no spinner, no network wait.

Native sync

Synchronise with a TaskChampion sync server through the native Rust bridge whenever you are online.

Cross-platform

One app across Android, iOS, Windows, macOS, and Linux, built with Flutter.

End-to-end encrypted

Your tasks are encrypted on-device before they sync — the server only ever stores ciphertext it cannot read.

Full task model

Projects, tags, priorities, due dates, recurrence, dependencies, and annotations — the complete TaskWarrior model.

Open source

Developed in the open on GitHub and maintained by CCExtractor, with automated nightly builds.

Explore all features →

TaskWarrior Mobile is the cross-platform Flutter client for +TaskWarrior, maintained by CCExtractor. +It brings TaskWarrior’s fast, unobtrusive task management to Android, iOS, Windows, +macOS, and Linux, backed by TaskChampion +for native, offline-first sync.

New here? Head to the downloads page to install the latest build, or browse +the nightly build history to see what has changed recently.

\ No newline at end of file diff --git a/index.xml b/index.xml new file mode 100644 index 00000000..c8c0f476 --- /dev/null +++ b/index.xml @@ -0,0 +1,130 @@ +TaskWarrior Mobilehttps://ccextractor.github.io/taskwarrior-flutter/Recent content on TaskWarrior MobileHugoen-usGetting startedhttps://ccextractor.github.io/taskwarrior-flutter/docs/getting-started/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/getting-started/<h2 id="install-the-app">Install the app</h2> +<p>Grab a build from the <a href="../../downloads/">downloads page</a>:</p> +<ul> +<li><strong>Stable</strong> — the latest signed APK from the GitHub <a href="https://github.com/CCExtractor/taskwarrior-flutter/releases">releases page</a>.</li> +<li><strong>Nightly</strong> — an automatically-built snapshot from the <code>main</code> branch, for testing the newest changes.</li> +</ul> +<p>On Android you may need to allow installation from your browser or file manager the +first time you sideload an APK.</p> +<h2 id="create-your-first-task">Create your first task</h2> +<ol> +<li>Open the app — you land on the task list.</li> +<li>Tap the <strong>+</strong> button.</li> +<li>Give the task a description, and optionally a project, tags, priority, and due date.</li> +<li>Save. The task is written to the local database immediately — no network required.</li> +</ol> +<p>Everything you do works fully offline. Sync is optional and layered on top; set it up +whenever you want your tasks mirrored to a server or another device.</p>Setting up synchttps://ccextractor.github.io/taskwarrior-flutter/docs/sync-setup/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/sync-setup/<p>TaskWarrior Mobile syncs through a <a href="https://github.com/GothenburgBitFactory/taskchampion">TaskChampion</a> +sync server — the same protocol used by TaskWarrior 3.x on the desktop. Sync is +<strong>optional and offline-first</strong>: your tasks always live in a local replica, and syncing +simply reconciles that replica with the server when you are online.</p> +<h2 id="what-you-need">What you need</h2> +<p>Three values from your TaskChampion sync server:</p> +<table> + <thead> + <tr> + <th>Field</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong>Sync server URL</strong></td> + <td>The base URL of the TaskChampion server, e.g. <code>https://example.org/taskchampion/</code>.</td> + </tr> + <tr> + <td><strong>Client ID</strong></td> + <td>A UUID identifying your replica on the server.</td> + </tr> + <tr> + <td><strong>Encryption secret</strong></td> + <td>The secret used to encrypt your data end-to-end. Only clients that share it can read your tasks.</td> + </tr> + </tbody> +</table> +<p>The server never sees your task data in the clear — everything is encrypted locally with +your encryption secret before it is uploaded.</p>Architecturehttps://ccextractor.github.io/taskwarrior-flutter/docs/architecture/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/architecture/<p>TaskWarrior Mobile is a Flutter app sitting on top of a native Rust core, so the same +task engine that powers TaskWarrior on the desktop runs unchanged on your phone.</p> +<h2 id="layers">Layers</h2> +<ul> +<li><strong>UI — Flutter (Dart).</strong> Screens, navigation, and state management use +<a href="https://pub.dev/packages/get">GetX</a>. This layer never talks to the network for task +data; it reads and writes through the core.</li> +<li><strong>Core — Rust (<code>tc_helper</code>).</strong> A thin wrapper around +<a href="https://github.com/GothenburgBitFactory/taskchampion">TaskChampion</a> exposed to Dart +through <a href="https://github.com/fzyzcjy/flutter_rust_bridge"><code>flutter_rust_bridge</code></a>. It +owns task storage, mutations, and sync.</li> +<li><strong>Storage &amp; sync — TaskChampion.</strong> Tasks live in a local replica on the device. +Syncing reconciles that replica with a TaskChampion sync server; there is no +bespoke HTTP API in between.</li> +</ul> +<h2 id="offline-first-data-flow">Offline-first data flow</h2> +<pre tabindex="0"><code>tap &#34;complete&#34; ─▶ Flutter ─▶ Rust FFI ─▶ local replica (instant, no network) + │ + (later, when online) + ▼ + TaskChampion sync server +</code></pre><p>Every create, edit, and complete is committed to the local replica synchronously, so the +UI never waits on the network. Synchronisation is a separate, best-effort step that runs +when connectivity is available.</p>FAQhttps://ccextractor.github.io/taskwarrior-flutter/docs/faq/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/docs/faq/<h2 id="do-i-need-a-sync-server-to-use-the-app">Do I need a sync server to use the app?</h2> +<p>No. TaskWarrior Mobile is fully usable offline — tasks are stored in a local replica on +your device. A sync server is only needed if you want your tasks mirrored to another +device or backed up remotely.</p> +<h2 id="is-my-data-encrypted">Is my data encrypted?</h2> +<p>Yes. When you sync, your tasks are encrypted locally with your encryption secret before +being uploaded. The server stores only ciphertext; only clients that hold the secret can +read your tasks.</p>Downloadshttps://ccextractor.github.io/taskwarrior-flutter/downloads/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/downloads/<h2 id="stable-releases">Stable releases</h2> +<p>Tagged releases are published on GitHub. Download the latest signed APK from the +<a href="https://github.com/CCExtractor/taskwarrior-flutter/releases">releases page</a>.</p> +<h2 id="nightly-builds">Nightly builds</h2> +<p>A signed nightly APK is built automatically from the <code>main</code> branch every night and +published to the project&rsquo;s F-Droid repository. Each entry below links to the APK produced +by that build, newest first.</p> + + + +<div class="nightly-builds"> + + <p class="empty">No nightly builds have been recorded yet. Check back after the next scheduled build.</p> + +</div> + +<p><em>Nightly builds are unstable snapshots intended for testing. For everyday use, prefer a +stable release above.</em></p>Featureshttps://ccextractor.github.io/taskwarrior-flutter/features/Mon, 01 Jan 0001 00:00:00 +0000https://ccextractor.github.io/taskwarrior-flutter/features/<p>TaskWarrior Mobile brings the full TaskWarrior task model to your phone and desktop, +without giving up speed or working offline.</p> +<h2 id="task-management">Task management</h2> +<ul> +<li><strong>Rich tasks</strong> — descriptions, projects, tags, priorities, and due dates.</li> +<li><strong>Recurring tasks</strong> — repeat tasks on a schedule.</li> +<li><strong>Dependencies</strong> — mark tasks as blocking or blocked by others so you always know +what is actionable.</li> +<li><strong>Annotations</strong> — attach timestamped notes to any task.</li> +<li><strong>Fast capture and edit</strong> — add or change a task in a couple of taps.</li> +</ul> +<h2 id="offline-first">Offline-first</h2> +<ul> +<li>Every create, edit, and complete is written to a <strong>local replica instantly</strong> — no +spinner, no waiting on the network.</li> +<li>The app is fully usable with no connectivity at all; sync is optional.</li> +</ul> +<h2 id="native-taskchampion-sync">Native TaskChampion sync</h2> +<ul> +<li>Syncs through a <a href="https://github.com/GothenburgBitFactory/taskchampion">TaskChampion</a> +sync server — the same protocol used by TaskWarrior 3.x on the desktop.</li> +<li><strong>End-to-end encrypted</strong>: your tasks are encrypted locally before upload, so the server +only ever stores ciphertext.</li> +<li>Credentials are <strong>validated against the server before they are saved</strong>, so a bad URL or +secret is caught immediately.</li> +</ul> +<h2 id="cross-platform">Cross-platform</h2> +<ul> +<li>One Flutter codebase targeting <strong>Android, iOS, Windows, macOS, and Linux</strong>.</li> +<li>A shared native Rust core keeps behaviour consistent everywhere.</li> +</ul> +<h2 id="open-source">Open source</h2> +<ul> +<li>Developed in the open on <a href="https://github.com/CCExtractor/taskwarrior-flutter">GitHub</a> +and maintained by <a href="https://ccextractor.org/">CCExtractor</a>.</li> +<li>Automated <a href="../downloads/#nightly-builds">nightly builds</a> let you try the latest changes.</li> +</ul> +<p>Ready to try it? Head to the <a href="../downloads/">downloads page</a>.</p> \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..55e9a3c3 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1 @@ +https://ccextractor.github.io/taskwarrior-flutter/docs/getting-started/2026-07-15T22:37:56+05:30https://ccextractor.github.io/taskwarrior-flutter/docs/sync-setup/2026-07-15T22:37:56+05:30https://ccextractor.github.io/taskwarrior-flutter/docs/architecture/2026-07-15T22:37:56+05:30https://ccextractor.github.io/taskwarrior-flutter/docs/faq/2026-07-15T22:37:56+05:30https://ccextractor.github.io/taskwarrior-flutter/categories/https://ccextractor.github.io/taskwarrior-flutter/docs/2026-07-15T22:37:56+05:30https://ccextractor.github.io/taskwarrior-flutter/downloads/2026-07-01T20:20:36+05:30https://ccextractor.github.io/taskwarrior-flutter/features/2026-07-15T22:37:56+05:30https://ccextractor.github.io/taskwarrior-flutter/tags/https://ccextractor.github.io/taskwarrior-flutter/2026-07-01T20:20:36+05:30 \ No newline at end of file diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 00000000..8693e4ed --- /dev/null +++ b/tags/index.html @@ -0,0 +1,5 @@ +Tags · TaskWarrior Mobile

Tags

\ No newline at end of file diff --git a/tags/index.xml b/tags/index.xml new file mode 100644 index 00000000..c549e3eb --- /dev/null +++ b/tags/index.xml @@ -0,0 +1 @@ +Tags on TaskWarrior Mobilehttps://ccextractor.github.io/taskwarrior-flutter/tags/Recent content in Tags on TaskWarrior MobileHugoen-us \ No newline at end of file From 616c97f60e1a35fc474444804efad221045feb0f Mon Sep 17 00:00:00 2001 From: Chinmay Chaudhari Date: Tue, 18 Aug 2026 00:26:59 +0530 Subject: [PATCH 2/2] site: move the Hugo source onto this branch so the site is self-contained The built output already lived here; the source that generates it stayed with the app. That split meant the site could not be rebuilt from the branch that serves it, and the app repository carried 19 files it never compiles or ships. Both halves now live together: website/ (content, layouts, static, hugo.toml, data) and scripts/update_build_log.py, which records each deploy into website/data/nightly_builds.json for the Downloads table. update_build_log.py now takes the commit sha and subject as arguments. It used to read git HEAD, which is wrong here: the deploy switches the work tree to this branch before recording, and at that point HEAD is this branch's own orphan commit rather than the app commit being released. Omitting the arguments still falls back to git, so running it by hand is unchanged. Verified: Hugo builds all 15 pages from this branch alone, and nothing in the branch's .gitignore (which excludes /android, /ios, /build and friends) swallows any of the added files. --- scripts/update_build_log.py | 77 ++++++++ website/.gitignore | 4 + website/README.md | 74 ++++++++ website/content/_index.md | 12 ++ website/content/docs/_index.md | 18 ++ website/content/docs/architecture.md | 42 +++++ website/content/docs/faq.md | 39 +++++ website/content/docs/getting-started.md | 31 ++++ website/content/docs/sync-setup.md | 40 +++++ website/content/downloads.md | 20 +++ website/content/features.md | 44 +++++ website/data/nightly_builds.json | 1 + website/hugo.toml | 34 ++++ website/layouts/_default/baseof.html | 29 +++ website/layouts/_default/list.html | 13 ++ website/layouts/_default/single.html | 6 + website/layouts/index.html | 45 +++++ .../layouts/shortcodes/nightly-builds.html | 37 ++++ website/static/CNAME | 1 + website/static/css/style.css | 165 ++++++++++++++++++ 20 files changed, 732 insertions(+) create mode 100644 scripts/update_build_log.py create mode 100644 website/.gitignore create mode 100644 website/README.md create mode 100644 website/content/_index.md create mode 100644 website/content/docs/_index.md create mode 100644 website/content/docs/architecture.md create mode 100644 website/content/docs/faq.md create mode 100644 website/content/docs/getting-started.md create mode 100644 website/content/docs/sync-setup.md create mode 100644 website/content/downloads.md create mode 100644 website/content/features.md create mode 100644 website/data/nightly_builds.json create mode 100644 website/hugo.toml create mode 100644 website/layouts/_default/baseof.html create mode 100644 website/layouts/_default/list.html create mode 100644 website/layouts/_default/single.html create mode 100644 website/layouts/index.html create mode 100644 website/layouts/shortcodes/nightly-builds.html create mode 100644 website/static/CNAME create mode 100644 website/static/css/style.css diff --git a/scripts/update_build_log.py b/scripts/update_build_log.py new file mode 100644 index 00000000..db47555d --- /dev/null +++ b/scripts/update_build_log.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Record a nightly build in the website's build-log data file. + +Prepends an entry describing the current commit to +``website/data/nightly_builds.json`` (newest first) and keeps only the most +recent ``MAX_ENTRIES``. The Hugo ``nightly-builds`` shortcode renders this file. + +Run from the repository root (as the CI workflow does): + + python3 update_build_log.py [sha] [msg] + +All arguments are optional: + status "success" (default) or "failure" + artifact_url link to the built APK ("" if none) + build_number CI run number ("" if unknown) + sha commit being deployed; read from git HEAD if omitted + msg its subject line; read from git HEAD if omitted + +`sha` and `msg` are passed explicitly by CI because the deploy switches the work +tree to the branch that hosts the site before this runs — at that point git HEAD +is that branch's own commit, not the app commit being released, so reading git +would record the wrong thing. Omitting them (running by hand) falls back to git. +""" + +import json +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path + +LOG = Path("website/data/nightly_builds.json") +MAX_ENTRIES = 90 + + +def _git(*args: str) -> str: + return subprocess.check_output(["git", *args]).decode().strip() + + +def main( + status: str = "success", + artifact: str = "", + build_number: str = "", + sha: str = "", + msg: str = "", +) -> None: + entry = { + "ts": datetime.now(timezone.utc).isoformat(timespec="seconds"), + "sha": (sha or _git("rev-parse", "HEAD"))[:8], + # Only the subject line; a passed-in message may carry a full body. + "msg": (msg or _git("log", "-1", "--pretty=%s")).strip().splitlines()[0] + if (msg or True) + else "", + "status": status, + "artifact": artifact, + "build": build_number, + } + + entries = [] + if LOG.exists(): + try: + entries = json.loads(LOG.read_text() or "[]") + except json.JSONDecodeError: + entries = [] + + entries.insert(0, entry) + entries = entries[:MAX_ENTRIES] + + LOG.parent.mkdir(parents=True, exist_ok=True) + LOG.write_text(json.dumps(entries, indent=2) + "\n") + print( + f"Recorded nightly build {entry['sha']} ({status}); " + f"{len(entries)} entr{'y' if len(entries) == 1 else 'ies'} in {LOG}." + ) + + +if __name__ == "__main__": + main(*sys.argv[1:]) diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 00000000..454de3e4 --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,4 @@ +# Hugo build output and caches +/public/ +/resources/ +.hugo_build.lock diff --git a/website/README.md b/website/README.md new file mode 100644 index 00000000..0240d6ba --- /dev/null +++ b/website/README.md @@ -0,0 +1,74 @@ +# TaskWarrior Mobile — Project Website + +A self-contained [Hugo](https://gohugo.io) static site for the TaskWarrior Mobile +app: landing page, downloads (with an auto-updated **nightly build log**), and a +documentation section. It has **no external theme** — all layouts live in +`layouts/`, so it builds from the single Hugo binary with no submodules or network +fetches. + +## Local development + +Requires Hugo **extended** (pinned to the version in +[`.github/workflows/deploy-website.yml`](../.github/workflows/deploy-website.yml)): + +```bash +# from this directory +hugo server # live-reload dev server at http://localhost:1313 +hugo --minify # production build into ./public +``` + +## Layout + +``` +website/ +├── hugo.toml # site config (baseURL, menus, params) +├── content/ # Markdown pages (_index, downloads, docs) +├── layouts/ # self-contained templates (no theme) +│ ├── _default/ # baseof / single / list +│ ├── index.html # homepage +│ └── shortcodes/ +│ └── nightly-builds.html # renders data/nightly_builds.json +├── data/ +│ └── nightly_builds.json # build log (populated by CI; ships empty) +└── static/ + ├── css/style.css + └── CNAME # custom domain +``` + +## Nightly build log + +[`scripts/update_build_log.py`](../scripts/update_build_log.py) prepends an entry +`{ts, sha, msg, status, artifact, build}` to `data/nightly_builds.json` (keeping the +90 most recent). The `{{}}` shortcode on the downloads page +renders it. The file ships as `[]`; CI fills it in. + +## CI + +- **`build-nightly.yml`** — daily at 02:00 UTC (and on demand): builds the signed + nightly APK, records the result via `update_build_log.py`, and commits the updated + log to `main`. +- **`deploy-website.yml`** — on any push touching `website/**` (including the nightly + log commit): builds the site with Hugo and deploys it to GitHub Pages. +- `nightlydepolyci.yml` now ignores `website/**` and `scripts/**` so a build-log + commit does not trigger a redundant APK rebuild. + +## One-time infrastructure setup (needs repo-admin / DNS access) + +These cannot be done from code and must be configured by a maintainer: + +1. **Enable GitHub Pages** → repo *Settings → Pages → Build and deployment → + Source: **GitHub Actions***. +2. **Custom domain / DNS** — point `taskwarrior.ccextractor.org` at GitHub Pages + with a `CNAME` DNS record to `.github.io`. The site already ships a + `static/CNAME`, so Pages will pick up the domain on first deploy. +3. **Branch protection** — if `main` is protected, allow `github-actions[bot]` to + push the nightly build-log commit (or point that commit at an unprotected branch). +4. The nightly signing secrets (`NIGHTLY_KEYSTORE_B64`, `NIGHTLY_PROPERTIES_B64`) are + the same ones the existing F-Droid workflow already uses. + +> **Note on the deploy target.** The proposal described renaming the `fdroid-repo` +> branch to `public-site` and serving from a branch. This implementation instead uses +> the modern **GitHub Actions Pages deployment** (`actions/deploy-pages`), which keeps +> the site source in `website/` on `main` and needs no dedicated deploy branch — fewer +> moving parts and no history juggling. The F-Droid APK repo on `fdroid-repo` is +> untouched. diff --git a/website/content/_index.md b/website/content/_index.md new file mode 100644 index 00000000..dd48717b --- /dev/null +++ b/website/content/_index.md @@ -0,0 +1,12 @@ +--- +title: "TaskWarrior Mobile" +--- + +**TaskWarrior Mobile** is the cross-platform [Flutter](https://flutter.dev) client for +[TaskWarrior](https://taskwarrior.org), maintained by [CCExtractor](https://ccextractor.org). +It brings TaskWarrior's fast, unobtrusive task management to Android, iOS, Windows, +macOS, and Linux, backed by [TaskChampion](https://github.com/GothenburgBitFactory/taskchampion) +for native, offline-first sync. + +New here? Head to the [downloads page](downloads/) to install the latest build, or browse +the [nightly build history](downloads/#nightly-builds) to see what has changed recently. diff --git a/website/content/docs/_index.md b/website/content/docs/_index.md new file mode 100644 index 00000000..d6268b0f --- /dev/null +++ b/website/content/docs/_index.md @@ -0,0 +1,18 @@ +--- +title: "Documentation" +description: "Developer and user documentation for TaskWarrior Mobile." +--- + +Documentation for TaskWarrior Mobile — a living set of guides that grows alongside the +app. New here? Start with [Getting started](getting-started/), then +[set up sync](sync-setup/) when you are ready to connect a server. Curious how it all +fits together? See the [architecture overview](architecture/). + +## Contributing + +Contributions are welcome. Start with the +[CONTRIBUTING guide](https://github.com/CCExtractor/taskwarrior-flutter/blob/main/CONTRIBUTING.md) +in the repository, and join the [CCExtractor community](https://ccextractor.org/) on Slack +or Zulip. + +## Guides diff --git a/website/content/docs/architecture.md b/website/content/docs/architecture.md new file mode 100644 index 00000000..394dc050 --- /dev/null +++ b/website/content/docs/architecture.md @@ -0,0 +1,42 @@ +--- +title: "Architecture" +description: "How TaskWarrior Mobile is built — Flutter UI over a native Rust core." +weight: 3 +--- + +TaskWarrior Mobile is a Flutter app sitting on top of a native Rust core, so the same +task engine that powers TaskWarrior on the desktop runs unchanged on your phone. + +## Layers + +- **UI — Flutter (Dart).** Screens, navigation, and state management use + [GetX](https://pub.dev/packages/get). This layer never talks to the network for task + data; it reads and writes through the core. +- **Core — Rust (`tc_helper`).** A thin wrapper around + [TaskChampion](https://github.com/GothenburgBitFactory/taskchampion) exposed to Dart + through [`flutter_rust_bridge`](https://github.com/fzyzcjy/flutter_rust_bridge). It + owns task storage, mutations, and sync. +- **Storage & sync — TaskChampion.** Tasks live in a local replica on the device. + Syncing reconciles that replica with a TaskChampion sync server; there is no + bespoke HTTP API in between. + +## Offline-first data flow + +``` +tap "complete" ─▶ Flutter ─▶ Rust FFI ─▶ local replica (instant, no network) + │ + (later, when online) + ▼ + TaskChampion sync server +``` + +Every create, edit, and complete is committed to the local replica synchronously, so the +UI never waits on the network. Synchronisation is a separate, best-effort step that runs +when connectivity is available. + +## Why a Rust core? + +Reusing TaskChampion means the mobile app shares TaskWarrior's battle-tested task model — +recurrence, dependencies, annotations, tags, and the sync protocol — instead of +reimplementing them. The Rust bridge surfaces those capabilities to the Flutter UI with a +small, typed FFI surface. diff --git a/website/content/docs/faq.md b/website/content/docs/faq.md new file mode 100644 index 00000000..c308c347 --- /dev/null +++ b/website/content/docs/faq.md @@ -0,0 +1,39 @@ +--- +title: "FAQ" +description: "Frequently asked questions about TaskWarrior Mobile." +weight: 4 +--- + +## Do I need a sync server to use the app? + +No. TaskWarrior Mobile is fully usable offline — tasks are stored in a local replica on +your device. A sync server is only needed if you want your tasks mirrored to another +device or backed up remotely. + +## Is my data encrypted? + +Yes. When you sync, your tasks are encrypted locally with your encryption secret before +being uploaded. The server stores only ciphertext; only clients that hold the secret can +read your tasks. + +## Which platforms are supported? + +The app targets Android, iOS, Windows, macOS, and Linux from a single Flutter codebase. +Android builds are published as APKs on the [downloads page](../../downloads/). + +## Does it work with my existing TaskWarrior setup? + +If your TaskWarrior 3.x desktop syncs through a TaskChampion sync server, point the app at +the same server, client ID, and encryption secret and your tasks will sync between them. +See [setting up sync](../sync-setup/). + +## What's the difference between stable and nightly builds? + +Stable builds are tagged releases meant for everyday use. Nightly builds are automatic +snapshots of the `main` branch — useful for trying the latest changes, but less tested. + +## Where do I report a bug or request a feature? + +Open an issue on the +[GitHub repository](https://github.com/CCExtractor/taskwarrior-flutter/issues). Pull +requests are welcome too — see the [contributing notes](../../docs/#contributing). diff --git a/website/content/docs/getting-started.md b/website/content/docs/getting-started.md new file mode 100644 index 00000000..cc9b29b9 --- /dev/null +++ b/website/content/docs/getting-started.md @@ -0,0 +1,31 @@ +--- +title: "Getting started" +description: "Install TaskWarrior Mobile and create your first task." +weight: 1 +--- + +## Install the app + +Grab a build from the [downloads page](../../downloads/): + +- **Stable** — the latest signed APK from the GitHub [releases page](https://github.com/CCExtractor/taskwarrior-flutter/releases). +- **Nightly** — an automatically-built snapshot from the `main` branch, for testing the newest changes. + +On Android you may need to allow installation from your browser or file manager the +first time you sideload an APK. + +## Create your first task + +1. Open the app — you land on the task list. +2. Tap the **+** button. +3. Give the task a description, and optionally a project, tags, priority, and due date. +4. Save. The task is written to the local database immediately — no network required. + +Everything you do works fully offline. Sync is optional and layered on top; set it up +whenever you want your tasks mirrored to a server or another device. + +## Next steps + +- [Set up sync](../sync-setup/) with a TaskChampion server. +- Learn how the app is [put together](../architecture/). +- Browse the [FAQ](../faq/) for common questions. diff --git a/website/content/docs/sync-setup.md b/website/content/docs/sync-setup.md new file mode 100644 index 00000000..535328b6 --- /dev/null +++ b/website/content/docs/sync-setup.md @@ -0,0 +1,40 @@ +--- +title: "Setting up sync" +description: "Connect TaskWarrior Mobile to a TaskChampion sync server." +weight: 2 +--- + +TaskWarrior Mobile syncs through a [TaskChampion](https://github.com/GothenburgBitFactory/taskchampion) +sync server — the same protocol used by TaskWarrior 3.x on the desktop. Sync is +**optional and offline-first**: your tasks always live in a local replica, and syncing +simply reconciles that replica with the server when you are online. + +## What you need + +Three values from your TaskChampion sync server: + +| Field | Description | +|---|---| +| **Sync server URL** | The base URL of the TaskChampion server, e.g. `https://example.org/taskchampion/`. | +| **Client ID** | A UUID identifying your replica on the server. | +| **Encryption secret** | The secret used to encrypt your data end-to-end. Only clients that share it can read your tasks. | + +The server never sees your task data in the clear — everything is encrypted locally with +your encryption secret before it is uploaded. + +## Connecting + +1. Open **Profile** → change the sync server → choose **Taskchampion**. +2. Tap **Configure** and paste the three values above. +3. Tap **Save**. The app performs a live sync against the server to confirm the + credentials work *before* it stores them — if anything is wrong, you find out + immediately instead of silently failing later. + +Once connected, changes flow both ways whenever you have connectivity. You can keep +working offline at any time; the next sync catches the server up. + +## Self-hosting a server + +Any TaskChampion-compatible sync server works. See the upstream +[TaskChampion sync-server](https://github.com/GothenburgBitFactory/taskchampion-sync-server) +project to run your own. diff --git a/website/content/downloads.md b/website/content/downloads.md new file mode 100644 index 00000000..5029cd30 --- /dev/null +++ b/website/content/downloads.md @@ -0,0 +1,20 @@ +--- +title: "Downloads" +description: "Install TaskWarrior Mobile — stable releases and automatically-built nightly APKs." +--- + +## Stable releases + +Tagged releases are published on GitHub. Download the latest signed APK from the +[releases page](https://github.com/CCExtractor/taskwarrior-flutter/releases). + +## Nightly builds {#nightly-builds} + +A signed nightly APK is built automatically from the `main` branch every night and +published to the project's F-Droid repository. Each entry below links to the APK produced +by that build, newest first. + +{{< nightly-builds limit="20" >}} + +_Nightly builds are unstable snapshots intended for testing. For everyday use, prefer a +stable release above._ diff --git a/website/content/features.md b/website/content/features.md new file mode 100644 index 00000000..6a0daf04 --- /dev/null +++ b/website/content/features.md @@ -0,0 +1,44 @@ +--- +title: "Features" +description: "What TaskWarrior Mobile can do — offline-first task management with native TaskChampion sync." +--- + +TaskWarrior Mobile brings the full TaskWarrior task model to your phone and desktop, +without giving up speed or working offline. + +## Task management + +- **Rich tasks** — descriptions, projects, tags, priorities, and due dates. +- **Recurring tasks** — repeat tasks on a schedule. +- **Dependencies** — mark tasks as blocking or blocked by others so you always know + what is actionable. +- **Annotations** — attach timestamped notes to any task. +- **Fast capture and edit** — add or change a task in a couple of taps. + +## Offline-first + +- Every create, edit, and complete is written to a **local replica instantly** — no + spinner, no waiting on the network. +- The app is fully usable with no connectivity at all; sync is optional. + +## Native TaskChampion sync + +- Syncs through a [TaskChampion](https://github.com/GothenburgBitFactory/taskchampion) + sync server — the same protocol used by TaskWarrior 3.x on the desktop. +- **End-to-end encrypted**: your tasks are encrypted locally before upload, so the server + only ever stores ciphertext. +- Credentials are **validated against the server before they are saved**, so a bad URL or + secret is caught immediately. + +## Cross-platform + +- One Flutter codebase targeting **Android, iOS, Windows, macOS, and Linux**. +- A shared native Rust core keeps behaviour consistent everywhere. + +## Open source + +- Developed in the open on [GitHub](https://github.com/CCExtractor/taskwarrior-flutter) + and maintained by [CCExtractor](https://ccextractor.org/). +- Automated [nightly builds](../downloads/#nightly-builds) let you try the latest changes. + +Ready to try it? Head to the [downloads page](../downloads/). diff --git a/website/data/nightly_builds.json b/website/data/nightly_builds.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/website/data/nightly_builds.json @@ -0,0 +1 @@ +[] diff --git a/website/hugo.toml b/website/hugo.toml new file mode 100644 index 00000000..ca8ebeb3 --- /dev/null +++ b/website/hugo.toml @@ -0,0 +1,34 @@ +baseURL = "https://taskwarrior.ccextractor.org/" +locale = "en-us" +title = "TaskWarrior Mobile" +enableGitInfo = true + +# Self-contained site: layouts live in ./layouts, so no external theme (and thus +# no submodule / network fetch) is required — Hugo builds it from its single +# binary alone. + +[params] + description = "The cross-platform TaskWarrior mobile app by CCExtractor — offline-first task management with native TaskChampion sync." + repo = "https://github.com/CCExtractor/taskwarrior-flutter" + org = "https://ccextractor.org/" + +[markup.goldmark.renderer] + unsafe = true + +[menu] + [[menu.main]] + name = "Home" + url = "/" + weight = 1 + [[menu.main]] + name = "Features" + url = "/features/" + weight = 2 + [[menu.main]] + name = "Downloads" + url = "/downloads/" + weight = 3 + [[menu.main]] + name = "Docs" + url = "/docs/" + weight = 4 diff --git a/website/layouts/_default/baseof.html b/website/layouts/_default/baseof.html new file mode 100644 index 00000000..0f314d08 --- /dev/null +++ b/website/layouts/_default/baseof.html @@ -0,0 +1,29 @@ + + + + + + {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} · {{ .Site.Title }}{{ end }} + + + + + + +
+ {{ block "main" . }}{{ end }} +
+ +
+

{{ .Site.Title }} · maintained by CCExtractor · built with Hugo

+
+ + diff --git a/website/layouts/_default/list.html b/website/layouts/_default/list.html new file mode 100644 index 00000000..8932309c --- /dev/null +++ b/website/layouts/_default/list.html @@ -0,0 +1,13 @@ +{{ define "main" }} +
+

{{ .Title }}

+ {{ .Content }} + {{ with .Pages }} + + {{ end }} +
+{{ end }} diff --git a/website/layouts/_default/single.html b/website/layouts/_default/single.html new file mode 100644 index 00000000..2eee85e2 --- /dev/null +++ b/website/layouts/_default/single.html @@ -0,0 +1,6 @@ +{{ define "main" }} +
+

{{ .Title }}

+ {{ .Content }} +
+{{ end }} diff --git a/website/layouts/index.html b/website/layouts/index.html new file mode 100644 index 00000000..92dd3063 --- /dev/null +++ b/website/layouts/index.html @@ -0,0 +1,45 @@ +{{ define "main" }} +
+

Your tasks, everywhere.

+

{{ .Site.Params.description }}

+

+ Get the app + View source +

+
+ +
+
+

Offline-first

+

Every create, edit, and complete is written to a local database instantly — no spinner, no network wait.

+
+
+

Native sync

+

Synchronise with a TaskChampion sync server through the native Rust bridge whenever you are online.

+
+
+

Cross-platform

+

One app across Android, iOS, Windows, macOS, and Linux, built with Flutter.

+
+
+

End-to-end encrypted

+

Your tasks are encrypted on-device before they sync — the server only ever stores ciphertext it cannot read.

+
+
+

Full task model

+

Projects, tags, priorities, due dates, recurrence, dependencies, and annotations — the complete TaskWarrior model.

+
+
+

Open source

+

Developed in the open on GitHub and maintained by CCExtractor, with automated nightly builds.

+
+
+ +
+ Explore all features → +
+ +
+ {{ .Content }} +
+{{ end }} diff --git a/website/layouts/shortcodes/nightly-builds.html b/website/layouts/shortcodes/nightly-builds.html new file mode 100644 index 00000000..3788f4ea --- /dev/null +++ b/website/layouts/shortcodes/nightly-builds.html @@ -0,0 +1,37 @@ +{{/* + nightly-builds shortcode — renders the build log collected by + scripts/update_build_log.py into website/data/nightly_builds.json. + Usage in content: {{< nightly-builds >}} (optionally {{< nightly-builds limit="15" >}}) +*/}} +{{ $builds := hugo.Data.nightly_builds }} +{{ $limit := int (.Get "limit" | default "20") }} +
+ {{ if $builds }} +
+ + + + + + + + + + + + {{ range first $limit $builds }} + + + + + + + + {{ end }} + +
Date (UTC)CommitMessageStatusAPK
{{ .ts }}{{ .sha }}{{ .msg }}{{ .status }}{{ with .artifact }}download{{ else }}—{{ end }}
+
+ {{ else }} +

No nightly builds have been recorded yet. Check back after the next scheduled build.

+ {{ end }} +
diff --git a/website/static/CNAME b/website/static/CNAME new file mode 100644 index 00000000..f71f76ef --- /dev/null +++ b/website/static/CNAME @@ -0,0 +1 @@ +taskwarrior.ccextractor.org diff --git a/website/static/css/style.css b/website/static/css/style.css new file mode 100644 index 00000000..37f1d885 --- /dev/null +++ b/website/static/css/style.css @@ -0,0 +1,165 @@ +:root { + --bg: #0f1419; + --surface: #171d26; + --text: #e6e9ee; + --muted: #9aa4b2; + --accent: #7c5cff; + --accent-2: #4dd0e1; + --border: #263041; + --ok: #3fb950; + --fail: #f85149; + --radius: 12px; + --maxw: 900px; +} + +* { box-sizing: border-box; } + +html { -webkit-text-size-adjust: 100%; } + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.6; +} + +a { color: var(--accent-2); text-decoration: none; } +a:hover { text-decoration: underline; } + +code { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 6px; + padding: 0.1em 0.4em; + font-size: 0.9em; +} + +/* Header */ +.site-header { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--border); + background: var(--surface); +} +.brand { font-weight: 700; font-size: 1.15rem; color: var(--text); } +.brand span { color: var(--accent); } +.site-header nav { display: flex; flex-wrap: wrap; gap: 1.1rem; } +.site-header nav a { color: var(--muted); font-weight: 500; } +.site-header nav a:hover { color: var(--text); text-decoration: none; } + +/* Layout */ +.content { max-width: var(--maxw); margin: 0 auto; padding: 2.5rem 1.5rem 4rem; } + +/* Hero */ +.hero { text-align: center; padding: 2rem 0 1rem; } +.hero h1 { font-size: clamp(2rem, 6vw, 3rem); margin: 0 0 0.5rem; } +.tagline { color: var(--muted); font-size: 1.15rem; max-width: 640px; margin: 0 auto 1.5rem; } +.cta { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; } + +.button { + display: inline-block; + background: var(--accent); + color: #fff; + padding: 0.7rem 1.4rem; + border-radius: var(--radius); + font-weight: 600; +} +.button:hover { filter: brightness(1.1); text-decoration: none; } +.button-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); } + +/* Features */ +.features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1rem; + margin: 2.5rem 0; +} +.feature { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1.25rem; +} +.feature h3 { margin: 0 0 0.4rem; color: var(--accent-2); } +.feature p { margin: 0; color: var(--muted); } + +/* Pages */ +.page h1 { border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; } +.page h2 { margin-top: 2rem; } +.page-list { padding-left: 1.2rem; } + +/* Content tables (markdown) */ +.page table { + width: 100%; + border-collapse: collapse; + margin: 1.25rem 0; + font-size: 0.95rem; +} +.page th, .page td { + text-align: left; + padding: 0.55rem 0.7rem; + border-bottom: 1px solid var(--border); + vertical-align: top; +} +.page th { color: var(--muted); font-weight: 600; } +.page thead tr { border-bottom: 2px solid var(--border); } + +/* Code blocks */ +.page pre { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1rem; + overflow-x: auto; +} +.page pre code { background: none; border: none; padding: 0; } +.page blockquote { + margin: 1.25rem 0; + padding: 0.25rem 1rem; + border-left: 3px solid var(--accent); + color: var(--muted); +} + +/* Nightly build table */ +.table-scroll { overflow-x: auto; } +.nightly-builds table { + width: 100%; + border-collapse: collapse; + margin: 1rem 0; + font-size: 0.92rem; +} +.nightly-builds th, .nightly-builds td { + text-align: left; + padding: 0.55rem 0.7rem; + border-bottom: 1px solid var(--border); + vertical-align: top; +} +.nightly-builds th { color: var(--muted); font-weight: 600; } +.nowrap { white-space: nowrap; } +.empty { color: var(--muted); font-style: italic; } + +.status { + display: inline-block; + padding: 0.1em 0.6em; + border-radius: 999px; + font-size: 0.8em; + font-weight: 600; + text-transform: capitalize; +} +.status-success { background: rgba(63,185,80,0.15); color: var(--ok); } +.status-failure { background: rgba(248,81,73,0.15); color: var(--fail); } + +/* Footer */ +.site-footer { + border-top: 1px solid var(--border); + padding: 1.5rem; + text-align: center; + color: var(--muted); + font-size: 0.9rem; +}