From c5a79562a0860382edeed1ec464a1d9f34daab61 Mon Sep 17 00:00:00 2001 From: turinglambdaai Date: Fri, 23 Oct 2026 13:57:52 +0800 Subject: [PATCH 1/4] feat(about): portal links and copy-diagnostics on every host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Raycast teardown (2.9.0.0, docs/UX-BENCHMARKS.md) showed a pattern worth adopting: the About surface as a one-stop hub for updates, links, feedback, and diagnostics. PRODUCT-SPEC §8 now specifies it; this lands it on all three hosts. - About gains User Manual and Changelog links beside Website and Report an Issue (i18n: aboutManual, aboutChangelog). - A diagnostics block — release identity, platform + arch, OS version, and the ~/.taskly contract paths — rendered selectable, with a Copy Diagnostics button that puts the block on the clipboard for pasting into an issue (aboutCopyDiagnostics). Confirmation is a transient status message on macOS/Linux and a temporary button-label swap on Windows. Labels are English fixed strings, machine-oriented like the CLI's --json output. - GTK notes: the diagnostics string is a function-local static so the clicked-callback's user_data outlives the dialog; the loopback HTTP server lesson from the release suite does not apply here. - docs/UX-BENCHMARKS.md records the full Raycast teardown: adopted, adapted (silent auto-install declined — a data tool keeps the user's install confirmation), and rejected (accounts, store, web-shell settings, English-only, macOS 26 floor). Verified on the macOS leg: raco rivet build clean, About sheet renders all four links plus the diagnostics block via the AX tree, Copy Diagnostics puts the exact block on the clipboard, and raco rivet acceptance passes 6/6 suites including the release-update suite merged in #61. --- CHANGELOG.md | 5 ++ docs/UX-BENCHMARKS.md | 42 ++++++++++++++++ linux/src/main.cpp | 50 ++++++++++++++++++- macos-host/Sources/RivetHost/Sheets.swift | 50 ++++++++++++++++++- shared/i18n/en.json | 4 ++ shared/i18n/zh.json | 4 ++ shared/spec/PRODUCT-SPEC.md | 20 +++++--- windows/MainWindow.xaml.cpp | 61 +++++++++++++++++++++++ 8 files changed, 228 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 292e4191..555e5544 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- About dialog becomes the update/link/feedback portal on every platform: + User Manual and Changelog links join Website and Report an Issue, and a + diagnostics block (release identity, platform, database and config + paths) carries a Copy Diagnostics button so a bug report can include + the full context verbatim (#58). - Release-kind acceptance: the update chain — signed-feed rejection, download cancellation, the N→N+1 lifecycle, data preservation, and the Linux AppImage in-place swap — is now an executable `release` diff --git a/docs/UX-BENCHMARKS.md b/docs/UX-BENCHMARKS.md index adc6bb1f..39b5506d 100644 --- a/docs/UX-BENCHMARKS.md +++ b/docs/UX-BENCHMARKS.md @@ -146,3 +146,45 @@ Scheduled(有日期的)。 - Manual acceptance: move a task from 工作 to 生活, confirm it disappears from 工作, appears in 生活 and keeps all other fields; then restart and verify ownership persists. Exercise both languages and all smart-list views. + +## Raycast 对照(商业标杆,2026-10-23 实拆 2.9.0.0) + +> 对象:Raycast(macOS 启动器,收费商业软件)。实拆官方 DMG:Info.plist、 +> 自研更新器(Updater/UpdaterDaemon,无 Sparkle)、IPC 面、Settings 前端 +> 包、InternetAccessPolicy.plist,对照 manual.raycast.com。结论按 +> 「学 / 参考 / 不学」记录,落地见 PRODUCT-SPEC §8 与对应切片。 + +### 学到的(已落地或已排期) + +- **About = 更新/链接/反馈门户**:Raycast 的 Settings → About 收口 + Raycast Updates、Open Changelog、Website、社区、Send Feedback 五个入口。 + Taskly 采纳:About 增加 User Manual 与 Changelog 链接(Website/Report an + Issue 已有),构成「更新(菜单)+ 手册 + 日志 + 反馈」门户。 +- **复制诊断信息**:Raycast IPC 有 CopyLogFiles。Taskly 采纳:About 增加 + 诊断块(版本/平台/架构/OS/DB 路径/配置路径,英文固定标签,可选中)+ + 「复制诊断信息」按钮,粘贴进 issue 即可带全上下文。 +- **网络透明度**:Raycast 随包分发 InternetAccessPolicy.plist,按主机声明 + 用途与拒绝后果(Little Snitch 等防火墙直接呈现给用户)。Taskly 采纳为 + 安装链切片:声明 github.com 更新源——把「本地优先、只连更新源」的信任 + 故事写进包里。 +- **更新行为显性化**:Raycast 把更新入口明示在 About。Taskly 排期:设置页 + 增加「自动检查更新」开关(默认开,写 config),把已有的 4h 静默检查 + 显性化;下载后仍确认再装(与 Raycast 的退出静默安装分叉——任务管理器 + 是数据工具,保留用户控制感)。 +- **手册结构**:manual.raycast.com 的 Keyboard Shortcuts / Import & + Export / Troubleshooting 独立成章、changelog 挂首页。Taskly 排期并入 + 手册切片:快捷键全表(§8 面向用户版)与「数据在哪/如何备份」章节。 + +### 参考但不照搬 + +- 退出时静默自动安装(CanShutDownForAutoInstall):不采纳,理由同上。 +- Sentry 崩溃上报内置于包:v1 无遥测、crash 仅 opt-in,按自己的清单走。 +- 社区/Slack 入口:单机 + 云盘同步路线,GitHub issues 即反馈渠道。 +- 更新后一次性亮点提示(changelog 一等公民):条件比 Raycast 更好 + (结构化 CHANGELOG 自动进 release),列为后续增量切片。 + +### 明确不学 + +账号/Store/云同步/AI(与本地优先正面冲突);纯英文零本地化(双语是 +Taskly 契约优势);设置页做 WebKit 壳(宿主原生 + i18n 单源);macOS 26 +最低版本门槛(14+ 是真实覆盖面差异)。 diff --git a/linux/src/main.cpp b/linux/src/main.cpp index 4d714cb5..22ff61aa 100644 --- a/linux/src/main.cpp +++ b/linux/src/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1813,11 +1814,58 @@ void show_about_dialog() { gtk_button_set_label(GTK_BUTTON(website), tr("aboutWebsite").c_str()); gtk_widget_set_hexpand(website, FALSE); gtk_box_append(GTK_BOX(links), website); + auto* manual = gtk_link_button_new("https://taskly.jrtx.site/manual.html"); + gtk_button_set_label(GTK_BUTTON(manual), tr("aboutManual").c_str()); + gtk_widget_set_hexpand(manual, FALSE); + gtk_box_append(GTK_BOX(links), manual); + auto* links_row2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 24); + gtk_widget_set_halign(links_row2, GTK_ALIGN_CENTER); + gtk_box_append(GTK_BOX(content), links_row2); + auto* changelog = gtk_link_button_new( + "https://github.com/turinglambdaai/taskly/blob/main/CHANGELOG.md"); + gtk_button_set_label(GTK_BUTTON(changelog), tr("aboutChangelog").c_str()); + gtk_widget_set_hexpand(changelog, FALSE); + gtk_box_append(GTK_BOX(links_row2), changelog); auto* issue = gtk_link_button_new( "https://github.com/turinglambdaai/taskly/issues"); gtk_button_set_label(GTK_BUTTON(issue), tr("aboutReportIssue").c_str()); gtk_widget_set_hexpand(issue, FALSE); - gtk_box_append(GTK_BOX(links), issue); + gtk_box_append(GTK_BOX(links_row2), issue); + // Diagnostics block (PRODUCT-SPEC §8): the release identity and the + // ~/.taskly contract paths a bug report starts from. Labels are English + // fixed strings — machine-oriented like the CLI's --json. + static const std::string diagnostics = [] { + utsname names{}; + uname(&names); + return std::string("Taskly v") + rivet_app::kVersion + " (" + + names.sysname + " " + names.release + ", " + names.machine + ")" + + "\nDatabase: " + g_get_home_dir() + "/.taskly/tasks.db" + + "\nConfig: " + g_get_home_dir() + "/.taskly/config.ini"; + }(); + names.sysname + " " + names.release + ", " + names.machine + ")" + + "\nDatabase: " + home + "/.taskly/tasks.db" + + "\nConfig: " + home + "/.taskly/config.ini"; + auto* diagnostics_label = gtk_label_new(diagnostics.c_str()); + gtk_label_set_selectable(GTK_LABEL(diagnostics_label), TRUE); + gtk_label_set_justify(GTK_LABEL(diagnostics_label), GTK_JUSTIFY_LEFT); + PangoAttrList* mono = pango_attr_list_new(); + pango_attr_list_insert(mono, pango_attr_family_new("monospace")); + gtk_label_set_attributes(diagnostics_label, mono); + pango_attr_list_unref(mono); + gtk_widget_set_halign(diagnostics_label, GTK_ALIGN_CENTER); + gtk_box_append(GTK_BOX(content), diagnostics_label); + auto* copy_diagnostics = gtk_button_new_with_label( + tr("aboutCopyDiagnostics").c_str()); + gtk_widget_set_halign(copy_diagnostics, GTK_ALIGN_CENTER); + g_signal_connect( + copy_diagnostics, "clicked", + G_CALLBACK(+[](GtkButton*, gpointer user_data) { + auto* text = static_cast(user_data); + auto* clipboard = gdk_display_get_clipboard(gdk_display_get_default()); + gdk_clipboard_set_text(clipboard, text->c_str()); + }), + new std::string(diagnostics)); + gtk_box_append(GTK_BOX(content), copy_diagnostics); g_signal_connect( dialog, "response", G_CALLBACK(+[](GtkDialog* dialog, gint, gpointer) { diff --git a/macos-host/Sources/RivetHost/Sheets.swift b/macos-host/Sources/RivetHost/Sheets.swift index c8aee334..7a967ab9 100644 --- a/macos-host/Sources/RivetHost/Sheets.swift +++ b/macos-host/Sources/RivetHost/Sheets.swift @@ -1,3 +1,4 @@ +import AppKit import SwiftUI /// Signed ARGB int as stored in the DB color column (DATA-FORMAT §2): @@ -244,7 +245,8 @@ private struct ColorSwatchGrid: View { } } -/// About box (Help ▸ About) — `Taskly v` (PRODUCT-SPEC §8). +/// About box (Help ▸ About) — the update/link/feedback hub and the +/// diagnostics surface (PRODUCT-SPEC §8). struct AboutSheet: View { @Environment(AppModel.self) private var model @Environment(\.dismiss) private var dismiss @@ -254,6 +256,34 @@ struct AboutSheet: View { ?? RivetGeneratedConfig.version } + /// The diagnostics block a bug report starts from (PRODUCT-SPEC §8): + /// release identity plus the `~/.taskly` contract paths. Labels are + /// English fixed strings — machine-oriented like the CLI's --json. + private var diagnosticsText: String { + let os = ProcessInfo.processInfo.operatingSystemVersion + let arch: String + #if arch(arm64) + arch = "arm64" + #elseif arch(x86_64) + arch = "x86_64" + #else + arch = "unknown" + #endif + let home = FileManager.default.homeDirectoryForCurrentUser.path + return """ + Taskly \(version) (macOS \(os.majorVersion).\(os.minorVersion), \(arch)) + Database: \(home)/.taskly/tasks.db + Config: \(home)/.taskly/config.ini + """ + } + + private func copyDiagnostics() { + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + pasteboard.setString(diagnosticsText, forType: .string) + model.flashStatus(model.t("statusDiagnosticsCopied")) + } + var body: some View { VStack(spacing: 12) { Image(systemName: "checklist") @@ -275,11 +305,29 @@ struct AboutSheet: View { HStack(spacing: 20) { Link(model.t("aboutWebsite"), destination: URL(string: "https://taskly.jrtx.site")!) + Link(model.t("aboutManual"), + destination: URL(string: "https://taskly.jrtx.site/manual.html")!) + } + .font(.system(size: 12)) + .tint(model.theme.accent) + HStack(spacing: 20) { + Link(model.t("aboutChangelog"), + destination: URL(string: "https://github.com/turinglambdaai/taskly/blob/main/CHANGELOG.md")!) Link(model.t("aboutReportIssue"), destination: URL(string: "https://github.com/turinglambdaai/taskly/issues")!) } .font(.system(size: 12)) .tint(model.theme.accent) + Text(diagnosticsText) + .font(.system(size: 10, design: .monospaced)) + .foregroundStyle(model.theme.secondaryText) + .textSelection(.enabled) + .multilineTextAlignment(.leading) + .lineLimit(3) + Button(model.t("aboutCopyDiagnostics")) { copyDiagnostics() } + .font(.system(size: 12)) + .buttonStyle(.bordered) + .tint(model.theme.accent) Button(model.t("dialogConfirm")) { dismiss() } .keyboardShortcut(.defaultAction) .buttonStyle(.borderedProminent) diff --git a/shared/i18n/en.json b/shared/i18n/en.json index 07ba6310..d19d0b2b 100644 --- a/shared/i18n/en.json +++ b/shared/i18n/en.json @@ -131,5 +131,9 @@ "aboutLicense": "Released under the GNU AGPL-3.0", "aboutWebsite": "Website", "aboutReportIssue": "Report an Issue", + "aboutManual": "User Manual", + "aboutChangelog": "Changelog", + "aboutCopyDiagnostics": "Copy Diagnostics", + "statusDiagnosticsCopied": "Diagnostics copied — paste it into your issue", "updateCopyError": "Copy error" } diff --git a/shared/i18n/zh.json b/shared/i18n/zh.json index 68c19ee4..c287cc06 100644 --- a/shared/i18n/zh.json +++ b/shared/i18n/zh.json @@ -131,5 +131,9 @@ "aboutLicense": "以 GNU AGPL-3.0 授权发布", "aboutWebsite": "访问官网", "aboutReportIssue": "问题反馈", + "aboutManual": "使用手册", + "aboutChangelog": "更新日志", + "aboutCopyDiagnostics": "复制诊断信息", + "statusDiagnosticsCopied": "已复制诊断信息,可直接粘贴到 issue", "updateCopyError": "复制错误信息" } diff --git a/shared/spec/PRODUCT-SPEC.md b/shared/spec/PRODUCT-SPEC.md index 9d1efbab..73db8dc8 100644 --- a/shared/spec/PRODUCT-SPEC.md +++ b/shared/spec/PRODUCT-SPEC.md @@ -166,12 +166,20 @@ pickers. Save (blank text silently ignored), Delete (confirm first), Cancel Check for Updates… (manual online-update check; auto-check runs at launch and every 4 h — contract: `UPDATE.md`). - Help: About — `Taskly v` + `© 2026 Taskly Team` + - `aboutContent` + license line (`aboutLicense`) + two link buttons on - every host: Website (`aboutWebsite` → taskly.jrtx.site) and Report an - Issue (`aboutReportIssue` → the GitHub issue tracker). macOS and - Windows render the fields inside their sheet/dialog; Linux builds the - same layout on a plain GtkDialog — GtkAboutDialog cannot carry a - second link, so the native about dialog is not used. + `aboutContent` + license line (`aboutLicense`) + link buttons on every + host: Website (`aboutWebsite` → taskly.jrtx.site), User Manual + (`aboutManual` → taskly.jrtx.site/manual.html), Changelog + (`aboutChangelog` → the CHANGELOG on GitHub), and Report an Issue + (`aboutReportIssue` → the GitHub issue tracker). Below the links, a + diagnostics block shows the release identity and the data locations — + app version, platform + architecture, OS version, database path, + config path — with a `aboutCopyDiagnostics` button that puts the same + block on the clipboard so a bug report can include it verbatim; the + copy confirmation is a transient status message. Diagnostics labels + are English fixed strings (machine-oriented, like the CLI's `--json`). + macOS and Windows render the fields inside their sheet/dialog; Linux + builds the same layout on a plain GtkDialog — GtkAboutDialog cannot + carry multiple links, so the native about dialog is not used. - All accelerators work window-wide without opening the menu. ## 9. Reminders (all platforms) diff --git a/windows/MainWindow.xaml.cpp b/windows/MainWindow.xaml.cpp index 91066ebb..a7ef0653 100644 --- a/windows/MainWindow.xaml.cpp +++ b/windows/MainWindow.xaml.cpp @@ -2454,16 +2454,77 @@ winrt::fire_and_forget MainWindow::OnAbout( L"https://taskly.jrtx.site"}); website.HorizontalAlignment(mx::HorizontalAlignment::Center); website.Padding({0, 0, 0, 0}); + auto manual = mxc::HyperlinkButton{}; + manual.Content(winrt::box_value(t("aboutManual"))); + manual.NavigateUri(winrt::Windows::Foundation::Uri{ + L"https://taskly.jrtx.site/manual.html"}); + manual.HorizontalAlignment(mx::HorizontalAlignment::Center); + manual.Padding({0, 0, 0, 0}); + auto changelog = mxc::HyperlinkButton{}; + changelog.Content(winrt::box_value(t("aboutChangelog"))); + changelog.NavigateUri(winrt::Windows::Foundation::Uri{ + L"https://github.com/turinglambdaai/taskly/blob/main/CHANGELOG.md"}); + changelog.HorizontalAlignment(mx::HorizontalAlignment::Center); + changelog.Padding({0, 0, 0, 0}); auto issue = mxc::HyperlinkButton{}; issue.Content(winrt::box_value(t("aboutReportIssue"))); issue.NavigateUri(winrt::Windows::Foundation::Uri{ L"https://github.com/turinglambdaai/taskly/issues"}); issue.HorizontalAlignment(mx::HorizontalAlignment::Center); issue.Padding({0, 0, 0, 0}); + // Diagnostics block (PRODUCT-SPEC §8): the release identity and the + // ~/.taskly contract paths a bug report starts from. Labels are English + // fixed strings — machine-oriented like the CLI's --json. + std::wstring profile; + if (const char* env = std::getenv("USERPROFILE")) { + profile = wide(env); + } + const std::wstring diagnostics = + std::wstring(L"Taskly v") + wide(rivet_app::kVersion) + + L" (Windows, x64)\nDatabase: " + profile + L"\\.taskly\\tasks.db" + + L"\nConfig: " + profile + L"\\.taskly\\config.ini"; + auto diagnostics_text = mxc::TextBlock{}; + diagnostics_text.Text(diagnostics); + diagnostics_text.FontFamily(mx::Media::FontFamily{L"Consolas"}); + diagnostics_text.FontSize(11); + diagnostics_text.Margin({0, 8, 0, 0}); + diagnostics_text.IsTextSelectionEnabled(true); + auto copy_diagnostics = mxc::Button{}; + copy_diagnostics.Content(winrt::box_value(t("aboutCopyDiagnostics"))); + copy_diagnostics.HorizontalAlignment(mx::HorizontalAlignment::Center); + copy_diagnostics.Padding({12, 4, 12, 4}); + copy_diagnostics.Click( + [text = winrt::weak_ref(copy_diagnostics), diagnostics]( + auto&&, auto&&) { + auto package = + winrt::Windows::ApplicationModel::DataTransfer::DataPackage{}; + package.RequestedOperation( + winrt::Windows::ApplicationModel::DataTransfer:: + DataPackageOperation::Copy); + package.SetText(diagnostics); + winrt::Windows::ApplicationModel::DataTransfer::Clipboard::SetContent( + package); + if (auto button = text.get()) { + button.Content(winrt::box_value(t("statusDiagnosticsCopied"))); + auto restore = mx::DispatcherTimer(); + restore.Interval(std::chrono::milliseconds{2500}); + restore.Tick([restore, text](auto&&, auto&&) { + if (auto b = text.get()) { + b.Content(winrt::box_value(t("aboutCopyDiagnostics"))); + } + restore.Stop(); + }); + restore.Start(); + } + }); panel.Children().Append(tagline); panel.Children().Append(license); panel.Children().Append(website); + panel.Children().Append(manual); + panel.Children().Append(changelog); panel.Children().Append(issue); + panel.Children().Append(diagnostics_text); + panel.Children().Append(copy_diagnostics); auto dialog = mxc::ContentDialog{}; dialog.Title(winrt::box_value(L"Taskly v" + wide(rivet_app::kVersion))); From 2ad37539c075784c2977f1cf43842a98d379bea2 Mon Sep 17 00:00:00 2001 From: turinglambdaai Date: Fri, 23 Oct 2026 14:04:17 +0800 Subject: [PATCH 2/4] fix(windows): read USERPROFILE via GetEnvironmentVariableW MSVC (with the project's warning-as-error settings) rejects getenv as C4996; the Win32 call is the project-idiomatic replacement. --- windows/MainWindow.xaml.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/windows/MainWindow.xaml.cpp b/windows/MainWindow.xaml.cpp index a7ef0653..43e4bd75 100644 --- a/windows/MainWindow.xaml.cpp +++ b/windows/MainWindow.xaml.cpp @@ -2476,8 +2476,11 @@ winrt::fire_and_forget MainWindow::OnAbout( // ~/.taskly contract paths a bug report starts from. Labels are English // fixed strings — machine-oriented like the CLI's --json. std::wstring profile; - if (const char* env = std::getenv("USERPROFILE")) { - profile = wide(env); + wchar_t profile_buf[MAX_PATH]; + const DWORD profile_len = GetEnvironmentVariableW( + L"USERPROFILE", profile_buf, MAX_PATH); + if (profile_len > 0 && profile_len < MAX_PATH) { + profile.assign(profile_buf, profile_len); } const std::wstring diagnostics = std::wstring(L"Taskly v") + wide(rivet_app::kVersion) + From 750acc24e6585a90a2b306ad40bc82df70ba1f60 Mon Sep 17 00:00:00 2001 From: turinglambdaai Date: Fri, 23 Oct 2026 14:10:59 +0800 Subject: [PATCH 3/4] fix(linux): drop leftover diagnostics lines, stop leaking user_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lambda rewrite left three stale lines from the non-static version behind (undeclared names/home, cascade type error), and the clicked callback still owned a heap-allocated copy — the diagnostics string is a function-local static now, so &diagnostics is the right pointer. --- linux/src/main.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/linux/src/main.cpp b/linux/src/main.cpp index 22ff61aa..5a75d577 100644 --- a/linux/src/main.cpp +++ b/linux/src/main.cpp @@ -1842,9 +1842,6 @@ void show_about_dialog() { "\nDatabase: " + g_get_home_dir() + "/.taskly/tasks.db" + "\nConfig: " + g_get_home_dir() + "/.taskly/config.ini"; }(); - names.sysname + " " + names.release + ", " + names.machine + ")" + - "\nDatabase: " + home + "/.taskly/tasks.db" + - "\nConfig: " + home + "/.taskly/config.ini"; auto* diagnostics_label = gtk_label_new(diagnostics.c_str()); gtk_label_set_selectable(GTK_LABEL(diagnostics_label), TRUE); gtk_label_set_justify(GTK_LABEL(diagnostics_label), GTK_JUSTIFY_LEFT); @@ -1864,7 +1861,7 @@ void show_about_dialog() { auto* clipboard = gdk_display_get_clipboard(gdk_display_get_default()); gdk_clipboard_set_text(clipboard, text->c_str()); }), - new std::string(diagnostics)); + &diagnostics); gtk_box_append(GTK_BOX(content), copy_diagnostics); g_signal_connect( dialog, "response", From 15aaed7f4f93310f8e45b78ddde917205b1f519a Mon Sep 17 00:00:00 2001 From: turinglambdaai Date: Fri, 23 Oct 2026 14:22:51 +0800 Subject: [PATCH 4/4] fix(linux): cast the diagnostics label, drop const for gpointer gtk_label_set_attributes needs the GTK_LABEL cast like every other label call in this file, and the clicked callback's gpointer user_data cannot take a pointer-to-const. --- linux/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/src/main.cpp b/linux/src/main.cpp index 5a75d577..beab5d8d 100644 --- a/linux/src/main.cpp +++ b/linux/src/main.cpp @@ -1834,7 +1834,7 @@ void show_about_dialog() { // Diagnostics block (PRODUCT-SPEC §8): the release identity and the // ~/.taskly contract paths a bug report starts from. Labels are English // fixed strings — machine-oriented like the CLI's --json. - static const std::string diagnostics = [] { + static std::string diagnostics = [] { utsname names{}; uname(&names); return std::string("Taskly v") + rivet_app::kVersion + " (" + @@ -1847,7 +1847,7 @@ void show_about_dialog() { gtk_label_set_justify(GTK_LABEL(diagnostics_label), GTK_JUSTIFY_LEFT); PangoAttrList* mono = pango_attr_list_new(); pango_attr_list_insert(mono, pango_attr_family_new("monospace")); - gtk_label_set_attributes(diagnostics_label, mono); + gtk_label_set_attributes(GTK_LABEL(diagnostics_label), mono); pango_attr_list_unref(mono); gtk_widget_set_halign(diagnostics_label, GTK_ALIGN_CENTER); gtk_box_append(GTK_BOX(content), diagnostics_label);