-
Notifications
You must be signed in to change notification settings - Fork 535
New updater behaviors and UI #2301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
✅ Deploy Preview for hyprnote-storybook ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughPre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Deploy Preview for hyprnote ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/updater2/Cargo.toml (1)
5-5: Fix invalid Rust edition.Rust edition "2024" is not valid. The supported editions are 2015, 2018, and 2021. This will prevent compilation.
-edition = "2024" +edition = "2021"
🧹 Nitpick comments (3)
plugins/updater2/src/job.rs (1)
43-49: Consider the state consistency on partial failure.If
set_pending_update_versionfails at line 43 but the update is already staged for restart, theUpdateReadyEventis still emitted at line 47. This could lead to:
- The UI showing an update is ready
- But
get_pending_update_versionreturningNoneConsider returning early if setting the pending version fails, or ensure the event emission is conditional on successful state persistence.
if let Err(e) = app.set_pending_update_version(Some(version.clone())) { tracing::error!("failed_to_set_pending_update_version: {}", e); + return; }plugins/updater2/src/commands.rs (1)
5-9: Consider making this command non-async (or normalize the return).
This is markedasyncbut doesn’tawait; making it sync reduces overhead/complexity. Also consider returningNonefor empty/whitespace versions to keep behavior consistent with the tray’sis_empty()guard.#[tauri::command] #[specta::specta] -pub(crate) async fn get_pending_update<R: tauri::Runtime>( +pub(crate) fn get_pending_update<R: tauri::Runtime>( app: tauri::AppHandle<R>, ) -> Result<Option<String>, String> { - app.get_pending_update_version().map_err(|e| e.to_string()) + app.get_pending_update_version() + .map(|opt| opt.and_then(|s| { + let s = s.trim().to_string(); + (!s.is_empty()).then_some(s) + })) + .map_err(|e| e.to_string()) }plugins/tray/src/lib.rs (1)
16-42: Add minimal error logging forTrayCheckUpdate::set_statefailures.
Right now failures are swallowed (let _ = ...). At least log atwarn/errorso broken menus aren’t silent (and consider logging event payload/version for easier support).tauri_plugin_updater2::UpdateReadyEvent::listen(app, move |_event| { - let _ = menu_items::TrayCheckUpdate::set_state( + if let Err(e) = menu_items::TrayCheckUpdate::set_state( &handle, UpdateMenuState::RestartToApply, - ); + ) { + tracing::warn!("failed_to_set_tray_update_state: {e}"); + } });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (7)
Cargo.lockis excluded by!**/*.lockplugins/updater2/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/updater2/permissions/autogenerated/commands/get_pending_update.tomlis excluded by!plugins/**/permissions/**plugins/updater2/permissions/autogenerated/commands/ping.tomlis excluded by!plugins/**/permissions/**plugins/updater2/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/updater2/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/updater2/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**
📒 Files selected for processing (19)
apps/desktop/src/components/main/body/index.tsx(2 hunks)apps/desktop/src/components/main/body/update.tsx(1 hunks)apps/web/content-collections.ts(1 hunks)apps/web/content/docs/_/0.installation.mdx(1 hunks)apps/web/content/docs/_/1.update.mdx(1 hunks)apps/web/content/docs/_/2.uninstall.mdx(1 hunks)apps/web/src/routes/_view/docs/-structure.ts(1 hunks)plugins/tray/Cargo.toml(1 hunks)plugins/tray/src/lib.rs(1 hunks)plugins/tray/src/menu_items/tray_check_update.rs(2 hunks)plugins/updater2/Cargo.toml(2 hunks)plugins/updater2/build.rs(1 hunks)plugins/updater2/src/commands.rs(1 hunks)plugins/updater2/src/error.rs(1 hunks)plugins/updater2/src/events.rs(1 hunks)plugins/updater2/src/ext.rs(3 hunks)plugins/updater2/src/job.rs(1 hunks)plugins/updater2/src/lib.rs(2 hunks)plugins/updater2/src/store.rs(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/desktop/src/components/main/body/update.tsxapps/web/content-collections.tsapps/web/src/routes/_view/docs/-structure.tsapps/desktop/src/components/main/body/index.tsx
plugins/*/src/lib.rs
📄 CodeRabbit inference engine (plugins/AGENTS.md)
After updating commands in
plugins/<NAME>/src/lib.rs, runcodegen, updateplugins/<NAME>/permissions/default.toml, andapps/desktop/src-tauri/capabilities/default.json
Files:
plugins/tray/src/lib.rsplugins/updater2/src/lib.rs
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/web/content-collections.tsapps/web/src/routes/_view/docs/-structure.ts
🧠 Learnings (1)
📚 Learning: 2025-11-27T11:40:22.782Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: plugins/AGENTS.md:0-0
Timestamp: 2025-11-27T11:40:22.782Z
Learning: Applies to plugins/*/src/lib.rs : After updating commands in `plugins/<NAME>/src/lib.rs`, run `codegen`, update `plugins/<NAME>/permissions/default.toml`, and `apps/desktop/src-tauri/capabilities/default.json`
Applied to files:
plugins/tray/Cargo.tomlplugins/tray/src/menu_items/tray_check_update.rsplugins/tray/src/lib.rsplugins/updater2/src/events.rsplugins/updater2/src/commands.rsplugins/updater2/build.rsplugins/updater2/src/ext.rsplugins/updater2/Cargo.tomlplugins/updater2/src/lib.rs
🧬 Code graph analysis (4)
apps/desktop/src/components/main/body/update.tsx (2)
extensions/shared/types/hypr-extension.d.ts (1)
Button(159-159)packages/utils/src/cn.ts (1)
cn(20-22)
plugins/updater2/src/events.rs (1)
packages/store/src/schema-external.ts (1)
Event(165-165)
plugins/updater2/src/lib.rs (2)
plugins/updater2/src/commands.rs (1)
get_pending_update(5-9)plugins/updater2/src/job.rs (1)
check_and_download_update(7-50)
apps/desktop/src/components/main/body/index.tsx (1)
apps/desktop/src/components/main/body/update.tsx (1)
Update(9-53)
🪛 LanguageTool
apps/web/content/docs/_/1.update.mdx
[grammar] ~8-~8: Ensure spelling is correct
Context: ...st version." --- ## Automatic Updates Hyprnote checks for updates automatically. When ...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~24-~24: Ensure spelling is correct
Context: ...ts to install it. ## Release Channels Hyprnote has two release channels: - *Nightly...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: fmt
- GitHub Check: desktop_ci (macos, depot-macos-14)
- GitHub Check: ci
- GitHub Check: desktop_ci (linux, depot-ubuntu-24.04-8)
- GitHub Check: desktop_ci (linux, depot-ubuntu-22.04-8)
🔇 Additional comments (18)
plugins/updater2/Cargo.toml (1)
19-19: New dependencies look good.The additions of
tauri-plugin-updaterandtokiowith the["time"]feature align with the PR's updater system implementation and the 30-second polling behavior described in the summary. Usingworkspace = trueis appropriate for monorepo consistency.Also applies to: 29-29
apps/web/content-collections.ts (1)
185-189: LGTM! Clean slug normalization.The filtering of "_" from path parts ensures that docs in the
_directory (getting started section) generate clean URLs without underscores in the slug.apps/web/src/routes/_view/docs/-structure.ts (1)
4-6: LGTM! Docs structure properly extended.The new "getting started" section and its default page mapping to "installation" correctly support the new documentation pages added in this PR.
apps/desktop/src/components/main/body/index.tsx (1)
46-46: LGTM! Clean UI composition.The Update component is properly imported and integrated into the header alongside the Search component, following the existing pattern.
Also applies to: 202-205
plugins/updater2/build.rs (1)
1-1: LGTM!Command registration correctly updated to
get_pending_update.Based on learnings, ensure you've also updated
plugins/updater2/permissions/default.tomlandapps/desktop/src-tauri/capabilities/default.jsonafter runningcodegen.plugins/updater2/src/error.rs (1)
9-10: LGTM!The new
Updatererror variant follows the existing pattern and correctly enables automatic conversion fromtauri_plugin_updater::Errorvia the#[from]attribute.plugins/updater2/src/events.rs (1)
1-10: LGTM!Event structures are well-defined with appropriate derives for Tauri event emission and TypeScript type generation via specta.
plugins/tray/Cargo.toml (2)
27-27: LGTM!The
tauri-plugin-updater2dependency addition correctly enables the tray plugin to integrate with the new update management functionality.
5-5: Edition 2024 is consistent across the entire workspace and intentional.All 100+ Cargo.toml files use
edition = "2024", confirming this is a deliberate project-wide decision, not a typo. No action required.plugins/updater2/src/job.rs (1)
28-34: Verify thedownloadmethod callback signature for the cancellation parameter.The second closure
|| {}in the download call appears to be a cancellation check callback. Based on standard Rust API patterns for download operations, this likely should return abool(e.g.,|| falseto indicate "don't cancel") rather than(). Confirm this against thetauri-plugin-updaterAPI documentation to ensure the callback types match the expected signature.plugins/tray/src/menu_items/tray_check_update.rs (1)
79-88: Keep updater “source of truth” consistent (UpdaterExt vs Updater2PluginExt).
This file now reads pending state viatauri_plugin_updater2::Updater2PluginExtbut still performs check/download/install viatauri_plugin_updater::UpdaterExt. If that’s intentional, fine—but please confirm there isn’t a split-brain where updater2 stages an update while the tray item simultaneously uses the legacy updater APIs.plugins/updater2/src/store.rs (1)
3-7: LGTM—just ensure store key naming stays stable across upgrades.
AddingPendingUpdateVersionis straightforward; ensure the serialized key name used by the store won’t change unintentionally (e.g., via rename/formatting changes), since that would effectively “lose” the pending-update flag across app upgrades.plugins/updater2/src/lib.rs (2)
3-13: [rewritten comment text]
[classification tag]
34-41: Unable to verify review comment—source files not found in repository.The
plugins/updater2/src/lib.rsandplugins/updater2/src/job.rsfiles referenced in this review are not accessible in the current repository state. Cannot verify:
- Whether
job::check_and_download_update()actually performs install or only download+staging- Whether concurrent execution safeguards (mutex/store flags) already exist
- Whether codegen and capability/permission files were updated per coding guidelines
To complete verification, the actual file contents and implementation details of the background update job must be available.
plugins/updater2/src/ext.rs (4)
4-4: LGTM!The import correctly references
UpdatedEventfrom thecrate::eventsmodule, aligning with the modularization of event types.
9-10: LGTM!The new trait methods follow the established pattern of the existing version tracking methods, with consistent signatures and return types.
51-53: LGTM!Clearing the pending update at app start is correct—any previously pending update should be cleared once the app launches with that version. The warning on failure is appropriate for debugging, and continuing execution is reasonable since this is a best-effort state cleanup rather than a critical operation.
27-40: Theset_pending_update_versionmethod's use ofunwrap_or_default()to convertNoneto an empty string is not actually problematic. The calling code inplugins/tray/src/menu_items/tray_check_update.rs(lines 82-86) explicitly checks both theOptionand the string's emptiness withif !pending.is_empty()before acting on the pending update. This two-level check ensures that empty strings are filtered out and never cause the update state to change. The semantic distinction between "no pending update" and "empty pending update string" is preserved through this validation at the call site.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/updater2/src/ext.rs (1)
42-54: Clearing pending update unconditionally at startup loses "restart needed" UI state
maybe_emit_updated()clears the pending update (lines 51-53) before checking versions. On app startup after a staged update is applied, this clears the pending version before the tray menu has a chance to check it.TrayCheckUpdate::build()relies on the pending version to display "Restart to Apply Update" state; clearing it early causes the UI to show "Check for Updates" instead, hiding the restart requirement from the user.Only clear pending when the staged version has been applied (pending version == current version).
- if let Err(e) = self.set_pending_update_version(None) { - tracing::warn!("failed_to_clear_pending_update: {}", e); - } + // Only clear pending update when the staged version has been applied. + if let Ok(Some(pending)) = self.get_pending_update_version() { + if pending == current_version { + if let Err(e) = self.set_pending_update_version(None) { + tracing::warn!("failed_to_clear_pending_update: {}", e); + } + } + }
🧹 Nitpick comments (2)
plugins/updater2/src/ext.rs (1)
9-10: Pending update persistence uses “empty string” as None sentinel—consider storing/removing Option directly
set_pending_update_version(None)writes"", andget_pending_update_version()filters empty strings. This works, but it’s a leaky abstraction (callers think it’s anOption, store actually containsString). Iftauri-plugin-store2supports storingOption<String>(or removing a key), that would be clearer and avoids sentinel values.fn set_pending_update_version(&self, version: Option<String>) -> Result<(), crate::Error> { let store = self.scoped_store(crate::PLUGIN_NAME)?; - store.set( - crate::StoreKey::PendingUpdateVersion, - version.unwrap_or_default(), - )?; + // Prefer storing the Option directly (or remove the key if store supports it). + store.set(crate::StoreKey::PendingUpdateVersion, version)?; Ok(()) }Also applies to: 27-40
plugins/updater2/src/lib.rs (1)
34-44: Background update loop: add cancellation/guardrails (and consider skipping when a pending update exists)
The spawned task runs forever with no shutdown signal. Also, hourlycheck_and_download_update()may repeatedly download/stage the same update unless the updater API prevents it; consider short-circuiting whenget_pending_update_version()isSome(_), and consider adding a timeout around network work.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
plugins/updater2/src/ext.rs(3 hunks)plugins/updater2/src/lib.rs(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
plugins/*/src/lib.rs
📄 CodeRabbit inference engine (plugins/AGENTS.md)
After updating commands in
plugins/<NAME>/src/lib.rs, runcodegen, updateplugins/<NAME>/permissions/default.toml, andapps/desktop/src-tauri/capabilities/default.json
Files:
plugins/updater2/src/lib.rs
🧠 Learnings (1)
📚 Learning: 2025-11-27T11:40:22.782Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: plugins/AGENTS.md:0-0
Timestamp: 2025-11-27T11:40:22.782Z
Learning: Applies to plugins/*/src/lib.rs : After updating commands in `plugins/<NAME>/src/lib.rs`, run `codegen`, update `plugins/<NAME>/permissions/default.toml`, and `apps/desktop/src-tauri/capabilities/default.json`
Applied to files:
plugins/updater2/src/lib.rs
🧬 Code graph analysis (1)
plugins/updater2/src/lib.rs (2)
plugins/updater2/src/commands.rs (1)
get_pending_update(5-9)plugins/updater2/src/job.rs (1)
check_and_download_update(7-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: ci
- GitHub Check: desktop_ci (linux, depot-ubuntu-24.04-8)
- GitHub Check: desktop_ci (macos, depot-macos-14)
- GitHub Check: desktop_ci (linux, depot-ubuntu-22.04-8)
- GitHub Check: fmt
🔇 Additional comments (1)
plugins/updater2/src/lib.rs (1)
16-25: Repo guideline: command changes inplugins/*/src/lib.rsrequire codegen + permissions/capabilities updates
You changed specta commands/events wiring here (Line 19-25). Per repo guidelines, ensure codegen was run and the permission/capabilities files were updated accordingly. Based on learnings, this should include updatingplugins/updater2/permissions/default.tomlandapps/desktop/src-tauri/capabilities/default.json.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
plugins/updater2/src/lib.rs (1)
29-45: 5-second update polling is too aggressive; use longer interval and de-duplicate event emissions.The 5-second interval causes excessive polling. Additionally,
UpdateReadyEventwill be emitted repeatedly for the same version on every poll cycle since there is no deduplication—theget_last_seen_version()andset_last_seen_version()methods exist inext.rsbut are not used injob.rs.Suggested approach:
- Increase interval to a reasonable duration (e.g., 1 hour)
- Track the last emitted version and skip emission if it hasn't changed
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> { let specta_builder = make_specta_builder(); tauri::plugin::Builder::new(PLUGIN_NAME) .invoke_handler(specta_builder.invoke_handler()) .setup(|app, _api| { let handle = app.clone(); tauri::async_runtime::spawn(async move { + let mut interval = + tokio::time::interval(std::time::Duration::from_secs(60 * 60)); // 1h loop { - tokio::time::sleep(std::time::Duration::from_secs(5)).await; + interval.tick().await; job::check_and_download_update(&handle).await; } }); Ok(()) }) .build() }Then in
job.rs, checkget_last_seen_version()against the detected version before emittingUpdateReadyEvent, and callset_last_seen_version()after emission.apps/desktop/src/components/changelog-listener.tsx (1)
12-33: Fix potential listener leak: handle unmount beforelisten()resolves.The
events.updatedEvent.listen()returns a Promise that resolves after the listener is registered. If the component unmounts before.then()executes, the cleanup runs withunlisten === null, and the listener is never removed. The stale callback may fire after unmount and call the oldopenNewclosure. This pattern is already correctly implemented inapps/desktop/src/contexts/network.tsx(lines 14-36) using acancelledflag—apply the same approach here:useEffect(() => { if (getCurrentWebviewWindowLabel() !== "main") { return; } let unlisten: null | UnlistenFn = null; + let cancelled = false; + events.updatedEvent .listen(({ payload: { previous, current } }) => { openNew({ type: "changelog", state: { previous, current }, }); }) .then((f) => { - unlisten = f; + if (cancelled) { + f(); + } else { + unlisten = f; + } }); return () => { + cancelled = true; unlisten?.(); unlisten = null; }; }, [openNew]);
♻️ Duplicate comments (2)
plugins/updater2/src/lib.rs (2)
1-13: Don’t forget the required permission/capability artifacts after command/event changes.
Per repo guidelines forplugins/*/src/lib.rs, please confirmcodegenwas run andplugins/updater2/permissions/default.toml+apps/desktop/src-tauri/capabilities/default.jsonwere updated. (Based on learnings.)Also applies to: 29-45
16-27: Generic specta builder shouldn’t hardcode::<tauri::Wry>for commands.
This is the same runtime-mismatch concern as previously noted: use::<R>to keep the command runtime consistent withBuilder<R>.fn make_specta_builder<R: tauri::Runtime>() -> tauri_specta::Builder<R> { @@ .commands(tauri_specta::collect_commands![ - commands::get_pending_update::<tauri::Wry>, + commands::get_pending_update::<R>, ])
🧹 Nitpick comments (2)
apps/desktop/src/components/main/body/changelog/index.tsx (1)
39-74: Harden external URLs: encode versions + avoid floating promise lint.
RecommendencodeURIComponentforprevious/currentin URLs andvoid openUrl(...)to avoid unhandled-promise lint. Also considertype="button"for safety if this ever renders inside a<form>.- onClick={() => - openUrl(`https://hyprnote.com/changelog/v${current}`) - } + type="button" + onClick={() => { + const v = encodeURIComponent(current); + void openUrl(`https://hyprnote.com/changelog/v${v}`); + }} @@ - onClick={() => - openUrl( - `https://github.com/fastrepl/hyprnote/compare/v${previous}...v${current}`, - ) - } + type="button" + onClick={() => { + const from = encodeURIComponent(previous); + const to = encodeURIComponent(current); + void openUrl( + `https://github.com/fastrepl/hyprnote/compare/v${from}...v${to}`, + ); + }}plugins/updater2/src/ext.rs (1)
10-11: LGTM! Consider adding doc comments.The new trait methods for managing pending update version are well-designed. The
Option<String>parameter inset_pending_update_versionappropriately allows clearing the pending update state.Optionally, consider adding doc comments to clarify the semantics of these public trait methods:
+ /// Returns the version of a pending update, or None if no update is pending. + /// Empty strings are treated as None. fn get_pending_update_version(&self) -> Result<Option<String>, crate::Error>; + /// Sets or clears the pending update version. Pass None to clear. fn set_pending_update_version(&self, version: Option<String>) -> Result<(), crate::Error>;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
plugins/updater2/js/bindings.gen.tsis excluded by!**/*.gen.ts
📒 Files selected for processing (9)
apps/desktop/src-tauri/src/lib.rs(1 hunks)apps/desktop/src/components/changelog-listener.tsx(2 hunks)apps/desktop/src/components/main/body/changelog/index.tsx(3 hunks)apps/desktop/src/components/main/body/update.tsx(1 hunks)apps/desktop/src/store/zustand/tabs/schema.ts(2 hunks)plugins/updater2/src/events.rs(1 hunks)plugins/updater2/src/ext.rs(3 hunks)plugins/updater2/src/job.rs(1 hunks)plugins/updater2/src/lib.rs(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/desktop/src/components/main/body/update.tsx
- plugins/updater2/src/job.rs
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/desktop/src/components/main/body/changelog/index.tsxapps/desktop/src/store/zustand/tabs/schema.tsapps/desktop/src/components/changelog-listener.tsx
plugins/*/src/lib.rs
📄 CodeRabbit inference engine (plugins/AGENTS.md)
After updating commands in
plugins/<NAME>/src/lib.rs, runcodegen, updateplugins/<NAME>/permissions/default.toml, andapps/desktop/src-tauri/capabilities/default.json
Files:
plugins/updater2/src/lib.rs
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/desktop/src/store/zustand/tabs/schema.ts
🧠 Learnings (3)
📚 Learning: 2025-11-24T16:32:29.314Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: apps/web/content/changelog/AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:29.314Z
Learning: Applies to apps/web/content/changelog/** : Only keep desktop-related changes when maintaining changelog entries from commits and diffs
Applied to files:
apps/desktop/src/components/main/body/changelog/index.tsx
📚 Learning: 2025-11-24T16:32:30.770Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: apps/web/content/changelog/AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:30.770Z
Learning: Applies to apps/web/content/changelog/** : Only include desktop-related changes in the changelog when reading through commits and diffs
Applied to files:
apps/desktop/src/components/main/body/changelog/index.tsx
📚 Learning: 2025-11-27T11:40:22.782Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: plugins/AGENTS.md:0-0
Timestamp: 2025-11-27T11:40:22.782Z
Learning: Applies to plugins/*/src/lib.rs : After updating commands in `plugins/<NAME>/src/lib.rs`, run `codegen`, update `plugins/<NAME>/permissions/default.toml`, and `apps/desktop/src-tauri/capabilities/default.json`
Applied to files:
plugins/updater2/src/lib.rsplugins/updater2/src/events.rs
🧬 Code graph analysis (3)
apps/desktop/src/components/main/body/changelog/index.tsx (1)
apps/desktop/src/components/main/body/shared.tsx (1)
TabItemBase(36-157)
plugins/updater2/src/lib.rs (2)
plugins/updater2/src/commands.rs (1)
get_pending_update(5-9)plugins/updater2/src/job.rs (1)
check_and_download_update(7-38)
plugins/updater2/src/events.rs (1)
packages/store/src/schema-external.ts (1)
Event(165-165)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: ci
- GitHub Check: desktop_ci (linux, depot-ubuntu-24.04-8)
- GitHub Check: desktop_ci (linux, depot-ubuntu-22.04-8)
- GitHub Check: desktop_ci (macos, depot-macos-14)
- GitHub Check: fmt
🔇 Additional comments (10)
apps/desktop/src/store/zustand/tabs/schema.ts (2)
125-130: Changelogpreviousnullable change looks consistent—just ensure it’s never “missing”.
Zod now requirespreviousto exist (but allowsnull). If any producer sendsprevious: undefined/ omits the field, parsing will fail; consider.default(null)if that’s a realistic payload/storage shape.
146-201:TabInputupdated correctly for nullableprevious.
Keeps the TS-side contract aligned with the zod schema and the event payload shape.apps/desktop/src-tauri/src/lib.rs (1)
158-161: Confirmmaybe_emit_updated()semantics didn’t regress first-run/onboarding UX.
Since the onboarding/existing-install signal is no longer passed here, please verify a fresh install on 2025-12-14 doesn’t incorrectly emit “updated” and open changelog.apps/desktop/src/components/main/body/changelog/index.tsx (1)
2-30: Tab item refactor is clean and keeps behavior unchanged.plugins/updater2/src/events.rs (1)
1-10: Event payloads and derives look correct and future-proofed (Option<String>forprevious).plugins/updater2/src/ext.rs (5)
1-5: LGTM!The imports are correct. The UpdatedEvent is now properly imported from
crate::events, aligning with the centralized event definitions mentioned in the PR summary.
28-32: Verify the empty string handling is intentional.The implementation filters out empty strings on line 31, treating them as
None. This pairs withset_pending_update_version(line 38) which convertsNoneto an empty string viaunwrap_or_default(). This creates a round-trip:None→""→None.While this pattern works correctly, please verify this is intentional and aligns with the store API's constraints for handling
Option<String>values.
52-54: Verify clearing pending update at function start is always correct.The pending update is cleared before checking if the version actually changed. This assumes that
maybe_emit_updatedis only called when the app has started (potentially with a new version installed).If this function could be called in other contexts, clearing the pending update optimistically might be incorrect. Please verify this function is only invoked at app startup.
56-65: Verify emitting UpdatedEvent on first run is intended.When there's no previous version stored (first run) or an empty previous version, the code emits an
UpdatedEventwithprevious: None(line 60).Please confirm this is the intended semantic—that an "updated" event should be emitted even when there's no previous version to compare against. This might be intentional for initialization or onboarding flows.
67-81: LGTM! Error handling is appropriate.The error handling strategy is consistent and appropriate for this "maybe" function:
- All errors are logged at appropriate levels (warning for pending update clear, errors for critical operations)
- Execution continues despite errors, which is correct for a best-effort update notification function
- Even if one step fails (e.g., emitting the event), subsequent steps (e.g., updating last seen version) still execute
This ensures the updater state remains as consistent as possible even when individual operations fail.
No description provided.