Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# OpenNotes environment variables (copy to .env.local for local overrides).

# None required to run. The Mac app uses your local git (and SSH/agent) for
# sync and the macOS Keychain for secrets; the AI Co-Writer keys are entered
# by you and stored encrypted on device (AES-GCM-256). Do not commit keys.

# Optional: app version surfaced in the Report-a-bug footer (set at build time).
# NEXT_PUBLIC_APP_VERSION=0.1.0
15 changes: 15 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# These are supported funding model platforms

github: harshmathurx # GitHub Sponsors
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# polar: # Replace with a single Polar username
# buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
# thanks_dev: # Replace with a single thanks.dev username
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
37 changes: 37 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
name: Bug report
about: Something is broken or behaving unexpectedly
title: "bug: "
labels: bug
assignees: ""
---

**Describe the bug**
A clear, concise description of what went wrong.

**To reproduce**
Steps to reproduce the behavior:

1. Go to "..."
2. Click on "..."
3. See error

**Expected behavior**
What you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain the problem.

**Environment**

- Surface: <!-- web app / Mac app -->
- OS and version: <!-- e.g. macOS 15.4, Windows 11 -->
- Browser (web app only): <!-- e.g. Chrome 126, Safari 17 -->
- OpenNotes version or commit: <!-- e.g. 0.1.0, or the commit SHA -->

**Data safety check**

- [ ] This bug does not involve loss or corruption of my notes (if it does, say so explicitly at the top — those reports get priority).

**Additional context**
Anything else relevant: console errors, whether it happens in a fresh browser profile, etc.
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/extension_idea.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: Extension idea
about: Propose a new extension — the friendliest way to contribute
title: "extension: "
labels: extension-idea
assignees: ""
---

Extensions are how OpenNotes grows. You don't need permission to have this idea, and a rough idea is a fine idea — the maintainer and community will help shape it.

**What would it do?**
One or two sentences. e.g. "A pomodoro timer in the sidebar that logs sessions to the daily journal."

**Who is it for?**
The workflow or kind of user it helps. Personal itches are the best ideas.

**How might it work? (optional, rough is fine)**
Which parts of the extension API you'd use — commands, slash items, panels, editor hooks. Skim [docs/extensions.md](../../docs/extensions.md) for what's available; if you need an API that doesn't exist yet, name it — that feedback is valuable too.

**Prior art (optional)**
Similar extensions or features in Obsidian, VS Code, Notion, or anywhere else.

**Want to build it?**

- [ ] I'd like to build this myself — point me at the starter template (`extensions/_starter/`)
- [ ] I'd like help or a co-builder
- [ ] I'm just donating the idea — anyone may pick it up
22 changes: 22 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an improvement to the core app
title: "feat: "
labels: enhancement
assignees: ""
---

**The problem or gap**
What are you trying to do that OpenNotes doesn't support today?

**Proposed solution**
What you'd like to see. Sketches or examples from other tools are welcome.

**Alternatives considered**
Other ways you've solved or worked around this.

**Core or extension?**
OpenNotes keeps the core small on purpose — capabilities that aren't universal ship as extensions. Do you see this as core behavior, or would it work as an extension? (If it's an extension idea, consider the "Extension idea" template instead.)

**Local-first check**
Does this fit the project's constraints — no backend, no account, no telemetry, no custody of user secrets? If it needs a server or third-party service, explain how it stays opt-in and user-controlled.
27 changes: 27 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Thanks for contributing. Keep the change minimal and focused — one concern per PR. -->

## What

<!-- A short description of the change and the problem it solves. Link the issue if one exists. -->

## How

<!-- The approach in a sentence or two. Note anything you deliberately did NOT do. -->

## Validation

Ran and passing:

- [ ] `pnpm exec eslint .`
- [ ] `pnpm exec tsc --noEmit`
- [ ] `pnpm exec vitest run`
- [ ] `pnpm exec next build`

<!-- Add tests for new logic where practical (pure, framework-free functions are easiest). -->

## Checklist

- [ ] Local-first preserved: nothing here uploads notes, requires an account, or adds telemetry.
- [ ] No secrets, tokens, or generated artifacts (`src-tauri/target`, `.next`, `dist`) committed.
- [ ] No emojis in UI or code; terminology is "notes folder" / "workspace".
- [ ] Docs updated if behavior or the extension API changed.
46 changes: 46 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# OpenNotes — fast CI for pushes to main and pull requests.
#
# Deliberately lightweight: typecheck + lint + unit tests only.
# The full Next.js build, Playwright e2e, and the Tauri desktop build run in
# release.yml (tag pushes / manual dispatch), not here — keep PR feedback fast.

name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
check:
name: Typecheck, lint, unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: |
corepack enable
pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm exec tsc --noEmit

- name: Lint
run: pnpm exec eslint .

- name: Unit tests
run: pnpm exec vitest run
176 changes: 176 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# OpenNotes — Release workflow
#
# Builds the Tauri v2 Mac app (DMG) for Apple Silicon + Intel and publishes
# both DMGs to GitHub Releases. See RELEASE.md for how to cut a release.
#
# Triggers:
# - push of a version tag: git tag v0.1.0 && git push origin v0.1.0
# - manual: Actions → Release → Run workflow (supply a tag like v0.1.0)
#
# ─────────────────────────────────────────────────────────────────────────────
# CODE SIGNING & NOTARIZATION (currently DISABLED — builds are unsigned)
#
# The app is not signed or notarized yet, so macOS Gatekeeper warns users on
# first open (see the "Unsigned app" section in RELEASE.md). The build below
# is structured so signing turns on without any other changes:
#
# 1. Get an Apple Developer ID "Developer ID Application" certificate.
# 2. Add these repository secrets (Settings → Secrets and variables → Actions):
# APPLE_CERTIFICATE base64-encoded .p12 of the certificate
# APPLE_CERTIFICATE_PASSWORD password for the .p12
# APPLE_SIGNING_IDENTITY e.g. "Developer ID Application: Name (TEAMID)"
# APPLE_ID Apple ID email (for notarization)
# APPLE_PASSWORD app-specific password (for notarization)
# APPLE_TEAM_ID 10-char Apple team ID
# (Tauri reads these exact env var names natively — no other wiring needed:
# https://v2.tauri.app/distribute/sign/macos/)
# 3. Flip ENABLE_SIGNING below from "false" to "true".
#
# Until then the env vars are passed through only when present, and Tauri
# simply produces an unsigned DMG. The workflow succeeds either way.
# ─────────────────────────────────────────────────────────────────────────────

name: Release

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Version tag for the release (e.g. v0.1.0)"
required: true
default: "v0.1.0"

env:
# Flip to "true" once the APPLE_* secrets above are configured.
ENABLE_SIGNING: "false"
# Release tag: from the pushed tag, or from the manual-dispatch input.
RELEASE_TAG: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tag }}

permissions:
contents: write # required to create the GitHub Release and upload assets

jobs:
build-dmg:
name: Build DMG (${{ matrix.label }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # if one arch fails, still keep the other's artifacts
matrix:
include:
- os: macos-14 # Apple Silicon runner
target: aarch64-apple-darwin
label: Apple Silicon (aarch64)
artifact: opennotes-dmg-aarch64
- os: macos-13 # Intel runner
target: x86_64-apple-darwin
label: Intel (x86_64)
artifact: opennotes-dmg-x86_64
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4 # reads the packageManager field / installs standalone pnpm

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Setup Rust (${{ matrix.target }})
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Cache Rust/Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo
src-tauri/target
key: ${{ runner.os }}-rust-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock', '**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-rust-${{ matrix.target }}-

- name: Install frontend dependencies
run: |
corepack enable
pnpm install --frozen-lockfile

- name: Build Tauri app (frontend static export + DMG)
# TAURI_BUILD=1 makes next.config.mjs emit the static export (output: "export",
# distDir: "out") that tauri.conf.json's frontendDist points at. tauri.conf's
# beforeBuildCommand sets it too; we also set it here so a bare `pnpm build`
# in this step's environment can never produce a server build by accident.
env:
TAURI_BUILD: "1"
# Signing env vars: read from secrets when present, empty otherwise.
# Tauri only signs when a signing identity is configured, so the build
# succeeds unsigned when these are unset. See the header comment.
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
if [ "${{ env.ENABLE_SIGNING }}" = "true" ] && [ -n "$APPLE_CERTIFICATE" ]; then
echo "Building SIGNED + notarized DMG for ${{ matrix.target }}"
else
echo "Building UNSIGNED DMG for ${{ matrix.target }} (signing disabled or secrets absent)"
fi
pnpm tauri build -- --target ${{ matrix.target }}

- name: Locate built DMG
# aarch64 (native) lands in target/release; x86_64 (cross on the Intel
# runner, or explicit --target) lands under the target-triple dir.
# Grep for *.dmg so we fail loudly here instead of at upload time.
run: |
DMG_PATH=$(ls src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg 2>/dev/null \
|| ls src-tauri/target/release/bundle/dmg/*.dmg)
echo "Found DMG: $DMG_PATH"
echo "dmg_path=$DMG_PATH" >> "$GITHUB_ENV"

- name: Upload DMG artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
# Covers both layouts; upload-artifact errors if nothing matches,
# which doubles as a build sanity check.
path: |
src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
src-tauri/target/release/bundle/dmg/*.dmg
if-no-files-found: error
retention-days: 7

release:
name: Publish GitHub Release
needs: build-dmg
runs-on: ubuntu-latest
steps:
- name: Download DMG artifacts
uses: actions/download-artifact@v4
with:
pattern: opennotes-dmg-*
path: dist
merge-multiple: true

- name: List artifacts
run: ls -la dist/

- name: Publish release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.RELEASE_TAG }}
name: OpenNotes ${{ env.RELEASE_TAG }}
# Auto-generate notes from merged PRs/commits since the last tag.
generate_release_notes: true
# All 0.x releases ship as prereleases until 1.0.
prerelease: ${{ !startsWith(env.RELEASE_TAG, 'v1.') }}
files: dist/*.dmg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@

# testing
/coverage
/test-results/
/playwright-report/

# local scratch
/ignored/

# next.js
/.next/
Expand All @@ -19,6 +24,9 @@
# production
/build

# tauri
src-tauri/target/

# misc
.DS_Store
*.pem
Expand Down
Loading
Loading