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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
deletion (#33, #43, #46, #48).
- Windows: six-second reversible deletion for individual and selected tasks
(#47).
- Windows: the shared app accelerators now work window-wide — Ctrl+1…4
switch smart views, Ctrl+N focuses quick add, Ctrl+F focuses search,
Ctrl+Shift+C toggles completed tasks, and Ctrl+Shift+N / Ctrl+O create
or open a database.
- Linux: the About dialog now carries the same website and Report-an-Issue
links as macOS and Windows (GtkAboutDialog cannot hold a second link,
so the dialog is built on the plain family layout); Windows and Linux
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ watch architecture.
actions and a one-step undo banner for deletes; ↑/↓ navigation,
Return expands the row in place (title, date/time chips, notes),
Esc collapses; completed tasks fold under a collapsible header.
Complete on macOS today; Linux ships the undo banner and the
keyboard flow, Windows is catching up
The full set — including the shared app accelerators (Ctrl/⌘+1…4,
N, F, ⇧C) — ships on all three platforms
([status](docs/UX-BENCHMARKS.md)).
- **Due-task notifications** — a 60 s poll plus a startup check, deduped,
capped at three separate banners (more → one summary).
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Taskly 构建在 [Rivet](https://github.com/turinglambdaai/rivet) 之上:全

- **Reminders 风格界面**——智能视图(今天 / 计划 / 全部 / 已完成),emoji 图标 + 12 色板的自定义清单,明暗主题跟随系统,中英双语实时切换。
- **自然语言快速添加**——输入 `明天买菜` 或 `standup @9am +1d`,回车前就能在实时预览 chip 里看到解析出的排程。
- **对齐 Reminders 的交互**——⌘/⇧ 多选、批量操作、删除一键撤销横幅;↑/↓ 导航,Return 行内展开(标题、日期/时间 chip、备注),Esc 收起;已完成任务收进可折叠分组。macOS 已全量对齐;Linux 已有撤销横幅与键盘流,Windows 正在补齐([进度](docs/UX-BENCHMARKS.md))。
- **对齐 Reminders 的交互**——⌘/⇧ 多选、批量操作、删除一键撤销横幅;↑/↓ 导航,Return 行内展开(标题、日期/时间 chip、备注),Esc 收起;已完成任务收进可折叠分组。全套能力(含共享应用快捷键 Ctrl/⌘+1…4、N、F、⇧C)已在三平台齐装([进度](docs/UX-BENCHMARKS.md))。
- **到期提醒通知**——60 秒轮询 + 启动检查,去重,超过三条合并为一条汇总。
- **单文件数据**——所有任务存在一个 SQLite 文件(`~/.taskly/tasks.db`,WAL),丢进 iCloud/OneDrive/Dropbox 即可同步。格式文档化且稳定:[DATA-FORMAT](shared/spec/DATA-FORMAT.md)。
- **面向 agent 的 CLI**——`taskly list|add|update|done|rm|search|mklist…`,`--json` 输出稳定、退出码固定、跨平台字节级一致(63 例 golden 套件钉死)。规格:[CLI-SPEC](shared/spec/CLI-SPEC.md)。
Expand Down
10 changes: 6 additions & 4 deletions docs/UX-BENCHMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ Things 的灵魂是把"什么时候做"压进四个默认视图,用 **start da
Things 几乎一切操作无需鼠标:全局快速录入、⌘N 新任务、⌘F 搜索、↑↓ 遍历、
⌘1…4 切视图、Enter 展开、Esc 收起。

- ✅ 已落地(Linux 宿主):Ctrl+1…4 切视图、Ctrl+N 聚焦快速添加、Ctrl+F 聚焦
搜索、Ctrl+Shift+C 切换显示已完成(PRODUCT-SPEC §8 的加速器全量接线);
Esc 阶梯(编辑器 → 搜索 → 放行)。
- ✅ 已落地(三平台):Ctrl+1…4 切视图、Ctrl+N 聚焦快速添加、Ctrl+F 聚焦
搜索、Ctrl+Shift+C 切换显示已完成——Linux 宿主全量接线并带 Esc 阶梯
(编辑器 → 搜索 → 放行);macOS 同一组命令走 ⌘ 系快捷键(⌘1…4/⌘N/
⌘F/⇧⌘C);Windows 用 KeyboardAccelerator 全窗口挂载同一契约,菜单上
另标 Ctrl+Shift+N / Ctrl+O 数据库快捷键提示。
- ✅ Linux 任务列表:GTK 原生多选支持 Ctrl/Shift 点选和方向键焦点移动;
Enter(或双击)激活选中行的行内编辑,Esc 先关闭编辑、再清除任务选择。
任务列表轮询重建时按任务 ID 恢复选中状态。
- 🔜 下一步:批量移动/完成/删除和跨视图选中策略仍须单独实现并实机验收。
- ✅ 批量移动/完成/删除与跨视图选中策略已在三平台落地(详见 §2.4)。

### 1.3 "安静"的哲学(quiet software)

Expand Down
73 changes: 73 additions & 0 deletions windows/MainWindow.xaml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ MainWindow::MainWindow() {
window->OnEffectiveThemeChanged();
}
});
InstallAppAccelerators();
InitializeBackendAsync();
}

Expand Down Expand Up @@ -735,6 +736,11 @@ void MainWindow::ApplyLanguage() {
MenuHelp().Title(t("menuHelp"));
MenuNewDatabase().Text(t("menuNewDatabase"));
MenuOpenDatabase().Text(t("menuOpenDatabase"));
// Hint-only: the actual accelerators are installed window-wide in
// InstallAppAccelerators (these two also have on-menu hints because
// the File menu rows exist to carry them).
MenuNewDatabase().KeyboardAcceleratorTextOverride(L"Ctrl+Shift+N");
MenuOpenDatabase().KeyboardAcceleratorTextOverride(L"Ctrl+O");
MenuCloseDatabase().Text(t("menuCloseDatabase"));
MenuExit().Text(t("menuExit"));
MenuLangZh().Text(t("menuLangZh"));
Expand Down Expand Up @@ -2496,6 +2502,10 @@ void MainWindow::OnSmartTileClick(
auto const tag = sender.as<mxc::Button>().Tag();
auto const kind =
static_cast<ViewKind>(winrt::unbox_value<std::int32_t>(tag));
SwitchSmartView(kind);
}

void MainWindow::SwitchSmartView(ViewKind kind) {
if (view_ == kind) {
return;
}
Expand All @@ -2509,6 +2519,69 @@ void MainWindow::OnSmartTileClick(
ReloadTasksAsync();
}

// Ctrl+Shift+C mirrors the checkbox without double-firing its handler:
// a programmatic IsChecked change raises Checked/Unchecked in WinUI, so
// the handler applies the state first and this only finishes the job if
// the event never ran.
void MainWindow::ToggleShowCompleted() {
auto const boxed = ShowCompletedCheck().IsChecked();
bool const on = !(boxed != nullptr && boxed.Value());
ShowCompletedCheck().IsChecked(on);
if (show_completed_ == on) return;
show_completed_ = on;
ReloadTasksAsync();
}

// PRODUCT-SPEC §8: the accelerators work window-wide without opening the
// menu. KeyboardAccelerators are scoped to the attached element's
// XamlRoot, so RootGrid covers the whole window; each accelerator calls
// the same code path its on-screen control uses.
void MainWindow::InstallAppAccelerators() {
using winrt::Microsoft::UI::Xaml::Input::KeyboardAccelerator;
using winrt::Windows::System::VirtualKey;
auto const control = winrt::Windows::System::VirtualKeyModifiers::Control;
auto const control_shift =
control | winrt::Windows::System::VirtualKeyModifiers::Shift;
auto attach = [&](VirtualKey key,
winrt::Windows::System::VirtualKeyModifiers mods,
auto&& invoked) {
KeyboardAccelerator acc{};
acc.Modifiers(mods);
acc.Key(key);
acc.Invoked([invoked](auto const&,
winrt::Microsoft::UI::Xaml::Input::
KeyboardAcceleratorInvokedEventArgs const&
args) {
args.Handled(true);
invoked();
});
RootGrid().KeyboardAccelerators().Append(acc);
};
attach(VirtualKey::Number1, control,
[this] { SwitchSmartView(ViewKind::Today); });
attach(VirtualKey::Number2, control,
[this] { SwitchSmartView(ViewKind::Planned); });
attach(VirtualKey::Number3, control,
[this] { SwitchSmartView(ViewKind::All); });
attach(VirtualKey::Number4, control,
[this] { SwitchSmartView(ViewKind::Completed); });
attach(VirtualKey::N, control, [this] {
if (inline_task_id_.has_value()) CommitInlineTaskEditor();
NewTaskBox().Focus(winrt::Microsoft::UI::Xaml::FocusState::Keyboard);
});
attach(VirtualKey::F, control,
[this] { SearchBox().Focus(winrt::Microsoft::UI::Xaml::FocusState::Keyboard); });
attach(VirtualKey::C, control_shift, [this] { ToggleShowCompleted(); });
attach(VirtualKey::N, control_shift, [this] {
Microsoft::UI::Xaml::RoutedEventArgs args;
OnNewDatabase(nullptr, args);
});
attach(VirtualKey::O, control, [this] {
Microsoft::UI::Xaml::RoutedEventArgs args;
OnOpenDatabase(nullptr, args);
});
}

void MainWindow::OnUserListSelectionChanged(
winrt::Windows::Foundation::IInspectable const&,
Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const&) {
Expand Down
8 changes: 8 additions & 0 deletions windows/MainWindow.xaml.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ struct MainWindow : MainWindowT<MainWindow> {
private:
enum class ViewKind { Today, Planned, All, Completed, List };

// PRODUCT-SPEC §8 app accelerators (Ctrl+1…4 / Ctrl+N / Ctrl+F /
// Ctrl+Shift+C / Ctrl+Shift+N / Ctrl+O). They reuse the command paths
// of the tiles and menu items; Windows has no View menu, so the
// accelerators are installed on the root element in code.
void InstallAppAccelerators();
void SwitchSmartView(ViewKind kind);
void ToggleShowCompleted();

static std::string ViewStringFor(ViewKind kind);

winrt::fire_and_forget InitializeBackendAsync();
Expand Down
Loading