Skip to content
Open
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
35 changes: 35 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Bug report
description: Something in Until doesn't work as expected.
labels: [bug]
body:
- type: textarea
id: what
attributes:
label: What happened?
description: What you did, what you expected, and what you saw. Screenshots help.
validations:
required: true
- type: input
id: version
attributes:
label: Until version
description: Settings → the version under the sidebar.
placeholder: "1.2.0"
validations:
required: true
- type: input
id: macos
attributes:
label: macOS version
placeholder: "15.5"
- type: dropdown
id: source
attributes:
label: Calendar source
options:
- Calendars on this Mac
- Google account
- Both
- type: markdown
attributes:
value: Please remove private details (event titles, attendees, links) from anything you paste.
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions and ideas
url: https://github.com/combinatrix-ai/until/discussions
about: Ask a question or share how you use Until.
15 changes: 15 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Feature request
description: Suggest an idea for Until.
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: What are you trying to do?
description: The situation and what gets in the way today.
validations:
required: true
- type: textarea
id: idea
attributes:
label: What would help?
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/meeting_link.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Meeting link not detected
description: Until doesn't show a Join button for a meeting service.
labels: [meeting-service, good first issue]
body:
- type: input
id: service
attributes:
label: Service
placeholder: "e.g. Pumble, Butter, a self-hosted Jitsi"
validations:
required: true
- type: input
id: example
attributes:
label: Example link format
description: Replace IDs and passwords with placeholders.
placeholder: "https://meet.example.com/j/XXXX"
validations:
required: true
- type: dropdown
id: field
attributes:
label: Where the link is in the event
options:
- Location
- Description / notes
- URL field
- Not sure
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ jobs:
run: |
TAG="${GITHUB_REF_NAME:-$(git rev-parse --short HEAD)}"
cp .build/release/Until.dmg "Until-${TAG}.dmg"
# A stable name too, so releases/latest/download/Until.dmg always
# points at the newest installer (the website and README link it).
cp .build/release/Until.dmg Until.dmg
# Sparkle updates from a .zip of the notarized + stapled .app (the .dmg
# is only for first-time install). Stapled ticket rides inside the zip
# so the update validates offline.
Expand Down Expand Up @@ -110,7 +113,7 @@ jobs:
# releases/latest/download/appcast.xml URL that the app's SUFeedURL
# points at; the .zip is the update payload, the .dmg the installer.
gh release create "$GITHUB_REF_NAME" \
Until-*.dmg Until-*.zip appcast.xml \
Until.dmg Until-*.dmg Until-*.zip appcast.xml \
--title "$GITHUB_REF_NAME" --generate-notes

- name: Upload app artifact
Expand Down
31 changes: 27 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Until - build & run

macOS menubar calendar app (Swift + SwiftUI/AppKit). Shows your next Google
Calendar event in the menubar, filtered by user-authored structured rules.
macOS menubar calendar app (Swift + SwiftUI/AppKit). Shows your next event in
the menubar — from the Mac's Calendar app (EventKit) and/or Google accounts —
filtered by user-authored structured rules.
The SwiftPM package, target, and product are **Until** (bundle id `ai.combinatrix.until`).

## Development workflow
Expand Down Expand Up @@ -45,8 +46,13 @@ checklist. A release-status question stays read-only.
```
scripts/release.sh
```
Builds release, Developer ID-signs with the hardened runtime, notarizes, and
staples. Needs a notarytool keychain profile (`until-notary` by default) — see
Builds a universal (Apple Silicon + Intel) release, Developer ID-signs with the
hardened runtime, notarizes, and staples. `UNIVERSAL=0` builds only this Mac's
architecture. The release workflow uploads the installer twice: as
`Until-vX.Y.Z.dmg` and as `Until.dmg`, which `releases/latest/download/Until.dmg`
(website and README) resolves to. After a release, update the Homebrew cask:
copy `packaging/homebrew/until.rb` into `combinatrix-ai/homebrew-tap` as
`Casks/until.rb` with the new `version` and `sha256`. Needs a notarytool keychain profile (`until-notary` by default) — see
the header of `scripts/release.sh`. The app icon is generated by
`scripts/make-icon.swift` into `scripts/Until.icns` (delete it to regenerate).

Expand Down Expand Up @@ -77,6 +83,11 @@ the header of `scripts/release.sh`. The app icon is generated by
- `Views.swift` — SwiftUI panel and preferences UI
- `GoogleAuth.swift` — Google OAuth PKCE + loopback, token refresh
- `CalendarClient.swift` — Calendar REST fetch + event normalization
- `MacCalendarSource.swift` — EventKit (Calendar app) calendars and events,
cross-source de-duplication, invite link extraction
- `EventLinks.swift` — meeting service detection and desktop-app links
- `StartAlert.swift` — the optional floating / full-screen start alert
- `HotkeyManager.swift` — Carbon global hotkeys (popover toggle, join)
- `RuleEngine.swift` — native structured filter evaluator
- `AppModel.swift` — config, polling, derived `AppState`, notification sync
- `EventNotifier.swift` — native UserNotifications reminders
Expand Down Expand Up @@ -115,6 +126,18 @@ working tomorrow. Anything wrong in the file stops the app with a message on
stderr instead of falling back, so a typo cannot hand back a subtly different
shot. See `scripts/demo-data-example.json` for the schema.

## Website and README art

The site lives in `docs/` (GitHub Pages deploys it on every push to `main`;
`docs/ja/` is the Japanese page). Its UI illustrations are SVGs in `docs/art/`,
drawn by `scripts/make-site-art.py` from one layout with English and Japanese
copy; the README uses the same files. After changing the UI or its wording:

```
python3 scripts/make-site-art.py # docs/art/*-en.svg, *-ja.svg
scripts/render-og.sh # docs/og-image.png, docs/og-image-ja.png
```

## Filter language
The Swift build evaluates structured rule conditions natively.

Expand Down
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Changelog

Release notes for each version are on
[GitHub Releases](https://github.com/combinatrix-ai/until/releases). This file
collects what is ready for the next one.

## 1.2.0 (unreleased)

### New
- **Calendars on this Mac.** Until can show everything the Calendar app
already syncs — iCloud, Google, Outlook/Exchange, CalDAV — without signing
in. Mac calendars and Google accounts work side by side, and events that
appear through both are merged.
- **Try it with a sample day.** The first-run popover can show a sample day
before you connect a calendar.
- **Start alert.** Optionally put the meeting on screen as it starts, as a
floating card or a full-screen takeover on every display, with Join, Snooze
1 min, and Dismiss.
- **Join shortcut.** A global shortcut joins the meeting in the menubar, or
the one that is running or about to start.
- **Desktop widgets** with today's agenda, in medium and large sizes.
- **Today at a glance.** The popover footer shows how many events are left,
how much of the day is booked, and the longest free stretch.
- **Links from the invite.** The upcoming card shows up to three docs,
designs, or pull requests linked from the event.

### Changed
- Meeting links from 30+ services are recognized, including Slack huddles,
Discord, Jitsi, FaceTime, Amazon Chime, Gather, Tuple, Lark, and Tencent
Meeting. Zoom and Teams links can open in their desktop apps.
- The menubar can show only the countdown, hiding the event title.
- Until runs natively on Intel Macs as well as Apple Silicon.
42 changes: 42 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Contributing to Until

Thanks for helping. Focused fixes, new meeting services, translations, and
documentation are all welcome.

## Build and run

Until is a plain SwiftPM package (Swift + SwiftUI/AppKit), no Xcode project.

```sh
swift build # compile
swift test # run the tests
scripts/dev.sh # rebuild and relaunch as a real .app bundle
scripts/dev.sh --demo-mode # the same, with a synthetic calendar
```

Mac calendars work without any setup. Google sign-in needs your own OAuth
desktop client:

1. In Google Cloud Console, create a project, enable the **Google Calendar
API** (and the Drive and Docs APIs for meeting notes), and create an OAuth
client ID of type **Desktop app**.
2. `cp .env.example .env` and fill in `GOOGLE_OAUTH_CLIENT_ID` and
`GOOGLE_OAUTH_CLIENT_SECRET`. The packaging script bakes them into the app's
`Info.plist`; `.env` is gitignored.

[AGENTS.md](AGENTS.md) has the full development notes: layout, demo mode for
screenshots, and the release and Mac App Store builds.

## Good first contributions

- **A meeting service Until doesn't recognize.** Add its domains to
`EventLinks.MeetingProvider` in `Sources/Until/EventLinks.swift`, plus a test
in `Tests/UntilTests/MeetingWorkflowTests.swift`.
- **A translation.** Strings live in `Sources/Until/Resources/<lang>.lproj/`;
English keys double as the source text.

## Pull requests

Work on a branch and open a pull request against `main`. Please run
`swift test` and `swiftlint` before you push, and keep each pull request to one
change.
77 changes: 77 additions & 0 deletions README.ja.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<p align="center">
<img src="assets/logo.png" alt="Until のロゴ" width="96" />
</p>

<h1 align="center">Until</h1>

<p align="center">
<strong>次の予定を、いつもメニューバーに。</strong><br />
macOS のメニューバーに次の会議までのカウントダウン。クリックすれば一日がひとつのタイムラインに。議事録はワンクリックで。
</p>

<p align="center">
<a href="https://github.com/combinatrix-ai/until/releases/latest/download/Until.dmg"><strong>macOS 版をダウンロード</strong></a>
· <a href="https://until.combinatrix.ai/ja/">Webサイト</a>
· <a href="README.md">English</a>
</p>

<p align="center">
<img src="docs/art/hero-ja.svg" alt="メニューバーのカウントダウンから一日のタイムラインを開いたところ。次の予定のカードに参加ボタンと議事録ボタンがある" width="720" />
</p>

## インストール

**ダウンロード:** [`Until.dmg`](https://github.com/combinatrix-ai/until/releases/latest/download/Until.dmg) を開き、`Until.app` をアプリケーションフォルダへドラッグします。Apple の公証済みで、アップデートは自動です。

**Homebrew:**

```sh
brew install --cask combinatrix-ai/tap/until
```

無料・オープンソース。macOS 13 以降、Apple シリコンと Intel の両方に対応。

## Until の特徴

- **カウントダウンはいつも目に入る場所に。** 次の予定をメニューバーに表示し、始まったら残り時間に切り替わります。⌥クリックか参加ショートカットで、すぐ会議に入れます。
- **一日をひとつのタイムラインで。** メニューバーをクリックすると、終わった予定・いまの時刻・この先の予定が一本のレールに並び、会議の間の空き時間も見えます。下部には今日の予定の件数・合計時間・残りの最長の空き時間を表示します。
- **議事録はワンクリック。** Google アカウントを接続すると、テンプレートから Google ドキュメントを作り、参加者と共有して、予定に添付するまでをワンクリックで行います。次の予定のカードには、招待に含まれる資料・デザイン・PR へのリンクも表示します。

## 使えるカレンダー

- **このMacのカレンダー** — カレンダーAppに入っている予定(iCloud・Google・Outlook/Exchange・CalDAV)をそのまま表示します。サインイン不要で、予定がこのMacの外に送られることはありません。
- **Google アカウント** — 直接サインインすると(複数アカウント可)、議事録の作成と Google Meet リンクの追加ができます。

どちらか一方でも、両方でも使えます。両方に出てくる同じ予定は1つにまとめます。

## 機能

- **メニューバーのカウントダウン**。開始後は残り時間を表示。発表中は予定名を隠せます。
- **開始アラート**(任意)。会議の開始時に、浮動カードか、すべてのディスプレイへの全画面表示で知らせます。参加・1分後に再通知・閉じるを選べます。
- **ネイティブの通知**(スヌーズ付き)。
- **ワンクリック参加**。Google Meet・Zoom・Microsoft Teams・Webex・Slack ハドル・Discord・Jitsi・FaceTime など30以上のサービスに対応。Zoom と Teams はデスクトップアプリで開けます。
- **デスクトップウィジェット**で今日の予定を一覧。
- **細かいフィルター**。カレンダー・タイトル・参加者・自分の返答・長さなどのルールで、表示する予定を決められます。
- **接続前に試せる**サンプルの一日。
- グローバルショートカット、ログイン時に起動、アイコンの右クリックで表示を折りたたみ。
- **日本語と英語**に対応。

## よくある質問

**無料ですか?** はい。MIT ライセンスで、アカウント登録もサブスクリプションもありません。

**予定のデータはどこかに送られますか?** いいえ。Until のサーバーはありません。Mac のカレンダーは Mac の中で読み取り、Google のデータは Google の API から直接 Mac に届きます。サインイン情報は macOS のキーチェーンに保存します。詳しくは[プライバシーポリシー](https://until.combinatrix.ai/privacy.html)をご覧ください。

**Mac のカレンダーで使えるなら、Google にサインインする理由は?** 議事録ドキュメントの作成と Meet リンクの追加のためだけです。それ以外は Mac のカレンダーだけで使えます。

**会社の Google Workspace でサードパーティ製アプリが許可されていません。** 「このMacのカレンダー」を使ってください。仕事用カレンダーがカレンダーAppに入っていれば、Google へのサインインなしで表示できます。

**Outlook は使えますか?** Exchange や Microsoft 365 のアカウントをカレンダーApp(システム設定 → インターネットアカウント)に追加し、「このMacのカレンダー」をオンにしてください。

## ソースからのビルド

[CONTRIBUTING.md](CONTRIBUTING.md) を参照してください。

## ライセンス

[MIT](LICENSE)
Loading
Loading