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

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:

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

Documentation

Documentation for TaskWarrior Mobile — a living set of guides that grows alongside the +app. New here? Start with Getting started, then +set up sync when you are ready to connect a server. Curious how it all +fits together? See the architecture overview.

Contributing

Contributions are welcome. Start with the +CONTRIBUTING guide +in the repository, and join the CCExtractor community on Slack +or Zulip.

Guides

\ 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

Offline-first

Native TaskChampion sync

Cross-platform

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/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/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

TaskWarrior Mobile · maintained by CCExtractor · built with Hugo

\ 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 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; +}