diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 000000000..35d6dd192 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,102 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + actions: read + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build-demo: + runs-on: ubuntu-latest + # Only rebuild Wasm when app code or build files change; site-only edits skip this job + if: | + github.event_name == 'workflow_dispatch' || + contains(toJSON(github.event.head_commit.modified), 'kmp/') || + contains(toJSON(github.event.head_commit.added), 'kmp/') || + contains(toJSON(github.event.head_commit.modified), 'gradle') || + contains(toJSON(github.event.head_commit.modified), 'settings.gradle') + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + - uses: gradle/actions/setup-gradle@v4 + with: + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + - name: Build wasmJs demo + run: ./gradlew :kmp:wasmJsBrowserDistribution --no-daemon --build-cache -PenableJs=true + - name: Assert expected output exists + run: test -d kmp/build/dist/wasmJs/productionExecutable/ + - name: Assert no single file exceeds 90 MB + run: | + find kmp/build/dist/wasmJs/productionExecutable -type f -size +90M \ + -exec echo "FILE TOO LARGE: {}" \; | grep . && exit 1 || true + - uses: actions/upload-artifact@v4 + with: + name: demo-dist + path: kmp/build/dist/wasmJs/productionExecutable/ + retention-days: 7 + + build-site: + runs-on: ubuntu-latest + needs: build-demo + if: always() && (needs.build-demo.result == 'success' || needs.build-demo.result == 'skipped') + steps: + - uses: actions/checkout@v4 + # Try current run first (when build-demo ran); fall back to last successful run + # so site-only edits keep the previously-built demo live. + - id: download-demo + name: Download demo artifact (current run) + uses: actions/download-artifact@v4 + continue-on-error: true + with: + name: demo-dist + path: site/public/demo/ + - id: download-demo-fallback + name: Download demo artifact (last successful run) + if: steps.download-demo.outcome == 'failure' + continue-on-error: true + run: | + LAST_RUN=$(gh run list --workflow pages.yml --status success --limit 1 \ + --json databaseId --jq '.[0].databaseId // empty') + if [ -n "$LAST_RUN" ]; then + gh run download "$LAST_RUN" --name demo-dist --dir site/public/demo/ + fi + env: + GH_TOKEN: ${{ github.token }} + - uses: actions/configure-pages@v5 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: site/package-lock.json + - run: npm ci + working-directory: site + - run: npm run build + working-directory: site + env: + DEMO_AVAILABLE: ${{ steps.download-demo.outcome == 'success' }} + - uses: actions/upload-pages-artifact@v3 + with: + path: site/dist/ + + deploy: + runs-on: ubuntu-latest + needs: build-site + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 4fad5d584..f8e0f7a3a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# SteleKit +
+
+
Your knowledge, carved in stone.
A local-first outliner that keeps your notes as plain markdown on your disk — forever — and runs natively on every platform you use. @@ -33,6 +33,41 @@ SteleKit exists because of Logseq's ideas, not in spite of them. Your existing L --- +## Why I built this + +I've been a Logseq user for years. The outliner-first, block-based approach shaped by Zettelkasten — as laid out in *How to Take Smart Notes* and *How to Read a Book* — is central to how I think about notes. Obsidian is excellent software, but it treats notes as flat documents. That missing outliner layer is not a minor UX preference for me; it changes how ideas connect. + +The problem was Logseq's Android app. Over time it got slower, and the team's attention moved toward hosted sync and collaborative editing — reasonable bets for a business, wrong bets for my workflow. I work offline. I don't want a server. I just want a fast, reliable editor on every device I own. + +At the same time, I'd watched Kotlin Multiplatform mature into the clearest answer to "write once, run natively everywhere." The developer tooling gap between KMP and ClojureScript is not subtle. Type safety, incremental compilation, first-class IDE support, native rendering via Compose — none of that is available in the Electron/CLJS stack. I wanted a foundation I could maintain and build on without fighting the toolchain. + +SteleKit is the editor I wanted to exist. It reads your existing Logseq markdown without migration, runs natively on the platforms I care about (Desktop and Android, primarily), and stores the graph in persistent SQLite so opening a large graph is instant rather than a cold-scan. The sync story is a git repo with an auto-committing cron job — not glamorous, but it works everywhere and costs nothing. + +--- + +## SteleKit is not for you if… + +- **You're happy with Logseq or Obsidian.** Both are great projects with active teams, large plugin ecosystems, and paying users funding their development. If they're working for you, please keep using them and support the developers. +- **You rely on Logseq's hosted sync or multiplayer editing.** SteleKit has no sync service and no plans for one. You manage your own files. +- **You're primarily on Windows or iOS.** Desktop (JVM) and Android are the focus. Windows works but gets less attention. iOS is planned but not there yet. +- **You need a mature plugin ecosystem today.** The plugin scaffolding exists but it's early-stage. +- **You don't want to run software built by one person in their spare time.** That's a completely fair reason. + +--- + +## Acknowledgements + +SteleKit stands on the shoulders of several projects and their communities: + +- **[Logseq](https://logseq.com)** — the original inspiration. The block-based, bidirectional-linking, local-first model is Logseq's vision. SteleKit exists because that vision is right. +- **[Obsidian](https://obsidian.md)** — for proving that a local-first, file-based knowledge tool can be polished, fast, and sustainable as a business. +- **[Roam Research](https://roamresearch.com)** — for popularising the outliner-as-knowledge-graph idea and proving people would pay for it. +- **[Athens Research](https://github.com/athensresearch/athens)** — an open-source Roam alternative that explored similar technical territory before shutting down. Its existence showed the problem was worth solving. +- **[JetBrains](https://www.jetbrains.com)** — for Kotlin, Compose Multiplatform, and SQLDelight (via Cash App). The KMP ecosystem made this project feasible. +- **Niklas Luhmann** — for the Zettelkasten method, and Sönke Ahrens (*How to Take Smart Notes*) for explaining why it works. + +--- + ## Quick Start ```bash diff --git a/docs/tasks/TODO.md b/docs/tasks/TODO.md index cf7e81d2e..ab43d7f00 100644 --- a/docs/tasks/TODO.md +++ b/docs/tasks/TODO.md @@ -1,5 +1,7 @@ # Tasks +- [ ] SteleKit Site — Astro + Starlight GitHub Pages site with CI/CD pipeline, landing page, user docs, developer docs, and demo page; depends on browser-wasm-demo.md for demo embed (docs/tasks/stelekit-site.md) +- [ ] Browser Wasm Demo — migrate `js(IR)` to `wasmJs` + `CanvasBasedWindow`, rendering the real Compose UI in-browser via Skia canvas; switch SQLDelight to IN_MEMORY backend for demo (docs/tasks/browser-wasm-demo.md) - [ ] Recent Pages — per-graph visit history in left sidebar showing last 10 visited pages, persists across sessions (docs/tasks/recent-pages.md) - [ ] Copy / Cut / Paste Blocks — Ctrl+C/X/V block-tree clipboard with undo, external Markdown paste, and Ctrl+Shift+V block reference paste (docs/tasks/copy-cut-paste-blocks.md) - [ ] All Pages View — sortable table of every page with backlink counts, name filter, journal/page toggle, and multi-select bulk delete (docs/tasks/all-pages-view.md) diff --git a/docs/tasks/browser-wasm-demo.md b/docs/tasks/browser-wasm-demo.md new file mode 100644 index 000000000..f15eb3286 --- /dev/null +++ b/docs/tasks/browser-wasm-demo.md @@ -0,0 +1,331 @@ +# Browser Wasm Demo + +**Status**: Planned +**Depends on**: nothing — self-contained migration + +--- + +## Problem + +The current `js(IR)` build produces a status page with raw DOM `innerHTML` writes — not the actual Compose app. To demonstrate SteleKit in the browser, the build must switch to the `wasmJs` Gradle target with `CanvasBasedWindow`, which renders the real Compose UI via Skia on a `