From 653945a5e883b5a287ce74867c8d251524c6258d Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 15:34:24 -0500 Subject: [PATCH 01/51] Select a profile by clicking anywhere on its card --- frontend/src/screens/ProfilesScreen.tsx | 8 +++++++ frontend/src/styles.css | 8 +++++++ frontend/src/test/ProfilesScreen.test.tsx | 28 +++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/frontend/src/screens/ProfilesScreen.tsx b/frontend/src/screens/ProfilesScreen.tsx index 813c58e..c75902b 100644 --- a/frontend/src/screens/ProfilesScreen.tsx +++ b/frontend/src/screens/ProfilesScreen.tsx @@ -132,6 +132,14 @@ export function ProfilesScreen({ onCreateProfile }: ProfilesScreenProps) {
{ + if ((event.target as HTMLElement).closest('button, a, input')) return; + void onSelect(profile.npub); + } + } >
{ }); }); + it('selects a profile when its card is clicked', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await screen.findByText('Bob'); + await user.click(screen.getByText('Bob')); + + await waitFor(() => { + expect(backend.state.active_profile?.npub).toBe(BOB); + }); + }); + + it('does not select a profile when an action button inside the card is clicked', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await screen.findByText('Bob'); + const copyButtons = screen.getAllByRole('button', { name: 'Copy public key' }); + await user.click(copyButtons[copyButtons.length - 1]); + + expect(backend.copied).toContain(BOB); + expect(backend.state.active_profile?.npub).toBe(ALICE); + }); + it('disables Select for the active profile and copies public keys', async () => { const backend = createFakeBackend(); installFakeBackend(backend); From 355e07f40f74bbba48581c59d8467e0f7a6629f0 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 15:35:00 -0500 Subject: [PATCH 02/51] Checkpoint: card-click profile selection --- CHECKPOINT-encryption.md | 73 +++++++++++++++------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 6597edd..71b755e 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,54 +1,34 @@ -# Checkpoint — Profile metadata publishing + profile pictures (2026-08-22) +# Checkpoint — Card-click profile selection + rename to Nostr_Keynctr (2026-08-23) **App renamed to Keynectr (2026-08-23).** A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. ## Where things are -- Project: `/home/avi/Projects/Nostr_Keynctr` (renamed from `0_Nostr` after commit `1116dfd`) -- Git repo: `master` @ `a6329d5` ("Publish profile metadata (name + picture) so external - clients show it"). Before it: `db81f8d` (profile deletion with undo), `9a8f334` - (audit checkpoint refresh), and the 2026-08-21 audit-fix commits (`f7db29e`, `d90b6e5`, - `130d7e2`). +- Project: `/home/avi/Projects/Nostr_Keynctr` (renamed from `0_Nostr` after commit `1116dfd`; + folder itself renamed from `Nostr-Keynectr` — see `2e4005d`) +- Git repo: `master` @ `653945a` ("Select a profile by clicking anywhere on its card"). + Before it: `2e4005d` (folder-rename checkpoint), `24b34b6`, `6ead6f4`, `31ef305` + (Keynectr rebrand), `a6329d5` (metadata publishing), `db81f8d` (delete + undo). - Working tree is **clean** apart from this checkpoint update, which is committed right after. ## What was completed -**Problem:** profiles created in the app never published a Nostr kind 0 metadata event, -so other clients showed generated petnames ("evil iguana", "homeless leech") or a -truncated npub instead of the user's chosen name. +1. **Click anywhere on a profile card to select it (2026-08-23)** — on the Profiles + screen, clicking an inactive profile's card now does the same as pressing its + "Select" button (`653945a`). Clicks on buttons inside the card (Copy / Publish name / + Picture / Delete / Secret key / Select) do not trigger selection. The active card is + not clickable and shows a hover highlight on inactive cards. +2. **Project folder renamed** to `Nostr_Keynctr` (`2e4005d`) — old path + `Nostr-Keynectr` no longer exists; same project, new name. +3. Earlier in the session: Keynectr rebrand across UI/package/binary (`31ef305`, + `6ead6f4`, `24b34b6`), metadata/picture publishing (`a6329d5`) — see previous + checkpoint notes below for details. -1. **Automatic metadata on creation** — `create_profile` now publishes a kind 0 event - with the label as `name`/`display_name` to all enabled relays (best-effort; relay - failures never block creation). -2. **"Publish name" action for existing profiles** — new button on every Profiles-screen - card plus CLI `publish-name `. Returns a per-relay report shown in the UI. -3. **Profile pictures end-to-end** — - - Vault: optional `picture: Option` per profile (serde default → old vaults - load unchanged). - - Backend: `set_profile_picture` validates http(s) URLs only, stores the URL, and - publishes kind 0 including `picture`; clearing supported (`None`). - - GUI: "Picture" button opens a modal — paste a URL, upload a file via the existing - nostr.build pipeline, or remove; avatar shows the picture everywhere in-app. - - CLI: `set-picture `. -4. **Nested-runtime safety** — metadata publishing runs on a dedicated OS thread with its - own tokio runtime, so both sync (CLI) and async (IPC server) callers are safe. -5. **Finished prior session's delete/undo work** — exposed `undo_history` in - `AppStateView`, fixed invalid Button variants / missing icon / null-safety errors so - the frontend typechecks again. -6. **Test hygiene fix** — `Settings::default()` points at real relays and tests were - silently publishing events to them (one got rate-limited by damus.io). All test suites - now use offline settings; test time dropped from ~126 s to ~3 s. +## Commits added most recently -## Commits added in this session - -- `7c6a085` Rename the app to Keynectr -- `ae5ddda` Allow new profile methods through the Electron IPC allowlist -- `a6329d5` Publish profile metadata (name + picture) so external clients show it - -**Gotcha for future work:** any new backend request type must also be added to -`RENDERER_METHODS` in `frontend/electron/main.ts`, or the main process rejects it with -"rejected renderer method" before it reaches the Rust backend. +- `653945a` Select a profile by clicking anywhere on its card +- `2e4005d` Checkpoint: project folder renamed to Nostr_Keynctr ## Verification commands run (all green) @@ -56,7 +36,8 @@ Rust (repo root): ``` cargo test # 96 passed; 0 failed -cargo clippy --all-targets # 0 errors/warnings from session code +cargo clippy --all-targets # finishes; 2 pre-existing warnings in src/profiles.rs + # (drop of reference at :170, unused delete_profile at :761) cargo fmt --check # clean cargo build --release # success ``` @@ -64,7 +45,7 @@ cargo build --release # success Frontend (`frontend/`): ``` -npm test # 14 files, 78 tests passed +npm test # 14 files, 80 tests passed (incl. 2 new card-click tests) npm run typecheck # clean npm run lint # 0 errors npm run format:check # clean @@ -80,6 +61,8 @@ GUI: cd ~/Projects/Nostr_Keynctr/frontend && npm start ``` +- Profiles screen: click an inactive profile's card (anywhere except its buttons) to + make it active; the Select button still works as before. - New profiles publish their name automatically on creation. - Existing profiles: **Profiles → Publish name** button, or **Picture** button to set a photo (URL paste or file upload) which publishes immediately. @@ -94,12 +77,10 @@ $B publish-name # republish stored name $B set-picture https://…/img.png # set + publish picture ``` -Verified live during the session: kind 0 events confirmed present on nos.lol, -relay.primal.net, relay.damus.io and the user's own wss://nostr.l484.com via direct -websocket queries; Iris shows the stored names after publish. - ## Notes & next steps +- Pre-existing clippy warnings in `src/profiles.rs` (drop of a reference, unused + `delete_profile`) are untouched — worth cleaning up in a future pass. - Nostr has no relay-to-relay sync: names/pictures are only visible on relays they were published to. Keep major relays enabled so clients that don't read your own relay can see the profile. @@ -116,7 +97,7 @@ websocket queries; Iris shows the stored names after publish. - **Rename details:** binary is now `keynectr`; data lives in `~/.local/share/keynectr` (auto-migrated from `nost-feed-manager` on first run — verified live). The KDF verifier string in crypto.rs was deliberately NOT renamed so old encrypted backups stay readable. - If a GitHub remote is ever added, rename the repo to `Nostr_Keynctr` there too. + The git remote is `https://git.atitlan.io/avi/Nostr_Keynctr`. - Relay config at time of writing (all enabled): nos.lol, relay.primal.net, l484.com (user's own), damus.io, snort.social, soloco.nl; relay.nostr.band added but was unreachable (handshake timeout) — retry enabling later. From 64502c1b14bef491bf7909689ddfb80afe90bc27 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 16:25:07 -0500 Subject: [PATCH 03/51] Wrap profile card action buttons on narrow windows --- frontend/src/styles.css | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 877d8ed..e59e42f 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1088,6 +1088,7 @@ select { .profile-card-actions { display: flex; + flex-wrap: wrap; align-items: center; gap: 8px; margin-top: auto; From 808f01187656ccc0fd1d9ee4fdabeb88347cdc1c Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 16:25:30 -0500 Subject: [PATCH 04/51] Checkpoint: narrow-window button wrap fix --- CHECKPOINT-encryption.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 71b755e..3590562 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,4 @@ -# Checkpoint — Card-click profile selection + rename to Nostr_Keynctr (2026-08-23) +# Checkpoint — Card-click selection + narrow-window button wrap (2026-08-23) **App renamed to Keynectr (2026-08-23).** A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. @@ -7,9 +7,10 @@ verified green at the moment this file was written. - Project: `/home/avi/Projects/Nostr_Keynctr` (renamed from `0_Nostr` after commit `1116dfd`; folder itself renamed from `Nostr-Keynectr` — see `2e4005d`) -- Git repo: `master` @ `653945a` ("Select a profile by clicking anywhere on its card"). - Before it: `2e4005d` (folder-rename checkpoint), `24b34b6`, `6ead6f4`, `31ef305` - (Keynectr rebrand), `a6329d5` (metadata publishing), `db81f8d` (delete + undo). +- Git repo: `master` @ `64502c1` ("Wrap profile card action buttons on narrow windows"). + Before it: `653945a` (card-click selection), `2e4005d` (folder-rename checkpoint), + `24b34b6`, `6ead6f4`, `31ef305` (Keynectr rebrand), `a6329d5` (metadata publishing), + `db81f8d` (delete + undo). - Working tree is **clean** apart from this checkpoint update, which is committed right after. ## What was completed @@ -19,14 +20,19 @@ verified green at the moment this file was written. "Select" button (`653945a`). Clicks on buttons inside the card (Copy / Publish name / Picture / Delete / Secret key / Select) do not trigger selection. The active card is not clickable and shows a hover highlight on inactive cards. -2. **Project folder renamed** to `Nostr_Keynctr` (`2e4005d`) — old path +2. **Action buttons wrap on narrow windows (2026-08-23)** — `.profile-card-actions` now + uses `flex-wrap: wrap`, so at half-screen width the Copy/Publish/Picture/Delete/ + Secret key/Select buttons wrap onto extra rows inside the card instead of spilling + past its border (`64502c1`). +3. **Project folder renamed** to `Nostr_Keynctr` (`2e4005d`) — old path `Nostr-Keynectr` no longer exists; same project, new name. -3. Earlier in the session: Keynectr rebrand across UI/package/binary (`31ef305`, +4. Earlier in the session: Keynectr rebrand across UI/package/binary (`31ef305`, `6ead6f4`, `24b34b6`), metadata/picture publishing (`a6329d5`) — see previous checkpoint notes below for details. ## Commits added most recently +- `64502c1` Wrap profile card action buttons on narrow windows - `653945a` Select a profile by clicking anywhere on its card - `2e4005d` Checkpoint: project folder renamed to Nostr_Keynctr From d618a5a4a06b6b441b9e68523b0b944b3a426b61 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 18:48:45 -0500 Subject: [PATCH 05/51] Add profile rename with metadata republish (GUI + CLI) --- frontend/electron/main.ts | 1 + frontend/src/lib/api.ts | 5 + frontend/src/screens/ProfilesScreen.tsx | 100 +++++++++++++++++ frontend/src/state/AppProvider.tsx | 12 +++ frontend/src/test/ProfilesScreen.test.tsx | 21 ++++ frontend/src/test/fakeBackend.ts | 19 ++++ src/ipc.rs | 19 ++++ src/main.rs | 23 ++++ src/profiles.rs | 124 ++++++++++++++++++++++ 9 files changed, 324 insertions(+) diff --git a/frontend/electron/main.ts b/frontend/electron/main.ts index ce51864..b8f0637 100644 --- a/frontend/electron/main.ts +++ b/frontend/electron/main.ts @@ -185,6 +185,7 @@ const RENDERER_METHODS: ReadonlySet = new Set([ 'select_profile', 'publish_profile_metadata', 'set_profile_picture', + 'rename_profile', 'delete_profile', 'undo_delete', 'publish_note', diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index d340b7f..9ee9f7f 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -58,6 +58,11 @@ export const api = { 'set_profile_picture', { npub, url }, ), + renameProfile: (npub: string, label: string) => + call<{ profile: ProfileSummary; report: MetadataPublishReport; state: AppState }>( + 'rename_profile', + { npub, label }, + ), publishNote: (content: string) => call('publish_note', { content }), feedGet: (limit?: number, contactsOnly = false) => call('feed_get', { diff --git a/frontend/src/screens/ProfilesScreen.tsx b/frontend/src/screens/ProfilesScreen.tsx index c75902b..d437d94 100644 --- a/frontend/src/screens/ProfilesScreen.tsx +++ b/frontend/src/screens/ProfilesScreen.tsx @@ -24,6 +24,7 @@ export function ProfilesScreen({ onCreateProfile }: ProfilesScreenProps) { const [errorId] = useState(() => `profiles-error-${Math.random().toString(36).slice(2)}`); const [revealTarget, setRevealTarget] = useState<{ label: string; npub: string } | null>(null); const [pictureTarget, setPictureTarget] = useState(null); + const [renameTarget, setRenameTarget] = useState<{ npub: string; label: string } | null>(null); const profiles = state?.profiles ?? []; const shorten = state?.settings.shorten_npub ?? true; @@ -169,6 +170,13 @@ export function ProfilesScreen({ onCreateProfile }: ProfilesScreenProps) { > Publish name +
); @@ -250,6 +271,85 @@ interface PictureTarget { url: string; } +function RenameModal({ + target, + onClose, + onSaved, + onError, + onSavingChange, +}: { + target: { npub: string; label: string }; + onClose: () => void; + onSaved: (message: string) => void; + onError: (message: string | null) => void; + onSavingChange: (npub: string | null) => void; +}) { + const { renameProfile } = useApp(); + const [label, setLabel] = useState(target.label); + const [saving, setSaving] = useState(false); + + const trimmed = label.trim(); + const canSave = trimmed.length > 0 && trimmed !== target.label; + + const save = async () => { + if (!canSave) { + return; + } + onError(null); + setSaving(true); + onSavingChange(target.npub); + try { + const report = await renameProfile(target.npub, trimmed); + onSaved( + report.failed.length === 0 + ? `Renamed to "${trimmed}" and published to ${report.succeeded.length} relay(s). It may take a minute to appear on other clients.` + : `Renamed to "${trimmed}", but ${report.failed.length} relay(s) did not accept it. Use "Publish name" to retry.`, + ); + } catch (err) { + onError(err instanceof Error ? err.message : String(err)); + } finally { + setSaving(false); + onSavingChange(null); + } + }; + + return ( + +
+
+ + setLabel(event.target.value)} + placeholder="My Profile" + autoComplete="off" + autoFocus + /> +
+

+ The name is stored on this computer and published to your enabled relays so other Nostr + clients show it. +

+
+ + +
+
+
+ ); +} + function PictureModal({ target, onClose, diff --git a/frontend/src/state/AppProvider.tsx b/frontend/src/state/AppProvider.tsx index b248a2d..6e971e5 100644 --- a/frontend/src/state/AppProvider.tsx +++ b/frontend/src/state/AppProvider.tsx @@ -41,6 +41,7 @@ interface AppContextValue { selectProfile: (npub: string) => Promise; publishProfileMetadata: (npub: string) => Promise; setProfilePicture: (npub: string, url: string | null) => Promise; + renameProfile: (npub: string, label: string) => Promise; publishNote: (content: string) => Promise; recordPublishFailure: (message: string, details?: string | null) => void; clearLastPublish: () => void; @@ -134,6 +135,15 @@ export function AppProvider({ children }: { children: ReactNode }) { [], ); + const renameProfile = useCallback( + async (npub: string, label: string): Promise => { + const result = await api.renameProfile(npub, label); + setState(result.state); + return result.report; + }, + [], + ); + const publishNote = useCallback(async (content: string): Promise => { const report = await api.publishNote(content); setLastPublish({ report, error: null, details: null, at: Date.now() }); @@ -250,6 +260,7 @@ export function AppProvider({ children }: { children: ReactNode }) { undoDelete, publishProfileMetadata, setProfilePicture, + renameProfile, copyText, }), [ @@ -262,6 +273,7 @@ export function AppProvider({ children }: { children: ReactNode }) { selectProfile, publishProfileMetadata, setProfilePicture, + renameProfile, publishNote, deleteProfile, undoDelete, diff --git a/frontend/src/test/ProfilesScreen.test.tsx b/frontend/src/test/ProfilesScreen.test.tsx index 0b9cdc6..ff5fa99 100644 --- a/frontend/src/test/ProfilesScreen.test.tsx +++ b/frontend/src/test/ProfilesScreen.test.tsx @@ -65,6 +65,27 @@ describe('ProfilesScreen', () => { expect(backend.state.active_profile?.npub).toBe(ALICE); }); + it('renames a profile from the Edit name modal and publishes it', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await screen.findByText('Bob'); + await user.click(screen.getAllByRole('button', { name: 'Edit name' })[1]); + + const input = screen.getByLabelText('Profile name'); + expect(input).toHaveValue('Bob'); + await user.clear(input); + await user.type(input, 'Bobby'); + await user.click(screen.getByRole('button', { name: 'Save & publish' })); + + await waitFor(() => { + expect(backend.state.profiles.find((p) => p.npub === BOB)?.label).toBe('Bobby'); + }); + expect(await screen.findByRole('status')).toHaveTextContent(/Renamed to "Bobby"/); + }); + it('disables Select for the active profile and copies public keys', async () => { const backend = createFakeBackend(); installFakeBackend(backend); diff --git a/frontend/src/test/fakeBackend.ts b/frontend/src/test/fakeBackend.ts index 314c1e4..0e916a0 100644 --- a/frontend/src/test/fakeBackend.ts +++ b/frontend/src/test/fakeBackend.ts @@ -163,6 +163,25 @@ export function createFakeBackend(initial?: AppState): FakeBackend { return next; } + case 'rename_profile': { + const npub = String(params.npub); + const label = String(params.label ?? '').trim(); + if (!label) { + throw new Error('The profile name cannot be empty.'); + } + if (!state.profiles.some((p) => p.npub === npub)) { + throw new Error('That profile is not stored on this computer.'); + } + const updated: ProfileSummary = { ...state.profiles.find((p) => p.npub === npub)!, label }; + const next: AppState = { + ...state, + profiles: state.profiles.map((p) => (p.npub === npub ? updated : p)), + active_profile: state.active_profile?.npub === npub ? updated : state.active_profile, + }; + backend.setState(next); + return { profile: updated, report: makePublishReport(), state: next }; + } + case 'publish_note': { if (publishFailure) { const failure = publishFailure; diff --git a/src/ipc.rs b/src/ipc.rs index e51c80c..89c75ea 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -49,6 +49,12 @@ pub enum Request { npub: String, url: Option, }, + /// Change a profile's label and publish it as part of the profile's kind 0 + /// metadata. + RenameProfile { + npub: String, + label: String, + }, PublishNote { content: String, }, @@ -325,6 +331,19 @@ async fn run_with_app(app: &mut App, request: Request) -> Result { + let key = app.vault_key().copied(); + let (summary, report) = profiles::rename_profile( + &mut app.vault, + &npub, + label, + key.as_ref(), + &app.settings, + )?; + app.save_vault()?; + Ok(json!({ "profile": summary, "report": report, "state": app.state_view() })) + } + Request::PublishNote { content } => { let report = publish::publish_active(&app.vault, &app.settings, &content, app.vault_key()) diff --git a/src/main.rs b/src/main.rs index 92cf91a..725c999 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ Commands: publish Publish a text note as a specific profile publish-name Publish the profile's stored name so other clients show it set-picture Set the profile picture (http(s) URL) and publish it + rename Rename a profile and publish the new name feed [--contacts] [limit] Fetch recent notes from enabled relays (default 50); --contacts filters to the active profile's contacts relays list List configured relays @@ -70,6 +71,7 @@ async fn main() -> ExitCode { "publish" => cli_publish(&args).await, "publish-name" => cli_publish_name(&args), "set-picture" => cli_set_picture(&args), + "rename" => cli_rename(&args), "feed" => cli_feed(&args).await, "relays" => cli_relays(&args).await, "settings" => cli_settings(&args), @@ -187,6 +189,27 @@ fn cli_set_picture(args: &[String]) -> Result { )) } +fn cli_rename(args: &[String]) -> Result { + let npub = args + .get(2) + .ok_or_else(|| AppError::config("Usage: keynectr rename "))?; + let label = args[3..].join(" "); + if label.trim().is_empty() { + return Err(AppError::config("Usage: keynectr rename ")); + } + + let mut app = load_app_with_unlock()?; + let key = app.vault_key().copied(); + let (summary, report) = + profiles::rename_profile(&mut app.vault, npub, label, key.as_ref(), &app.settings)?; + app.save_vault()?; + Ok(format!( + "Renamed to \"{}\"; accepted by {} relay(s).", + summary.label, + report.succeeded.len() + )) +} + fn cli_publish_name(args: &[String]) -> Result { let npub = args .get(2) diff --git a/src/profiles.rs b/src/profiles.rs index 2973246..3d42a3f 100644 --- a/src/profiles.rs +++ b/src/profiles.rs @@ -195,6 +195,62 @@ pub fn set_profile_picture( Ok((summary, report)) } +/// Change a profile's label and immediately republish it as the profile's +/// kind 0 metadata so external clients show the new name. +/// +/// Returns the updated summary plus the per-relay publish report. +pub fn rename_profile( + vault: &mut Vault, + npub: &str, + label: String, + key: Option<&VaultKey>, + settings: &Settings, +) -> Result<(ProfileSummary, MetadataPublishReport), AppError> { + let trimmed = label.trim(); + if trimmed.is_empty() { + return Err(AppError::config("The profile name cannot be empty.")); + } + + // Resolve and sign before mutating so a locked vault or bad key changes + // nothing on disk. + let secret_hex = resolve_secret_key(vault, npub, key)?; + let secret_key = parse_secret_key(&secret_hex)?; + + let stored = find_profile_mut(vault, npub)?; + stored.label = trimmed.to_string(); + let (label, created_at, public_key, picture) = ( + stored.label.clone(), + stored.created_at, + stored.public_key.clone(), + stored.picture.clone(), + ); + let summary = ProfileSummary { + label, + npub: public_key.clone(), + created_at, + is_active: vault.active_profile.as_deref() == Some(public_key.as_str()), + picture, + }; + + let relay_urls = relays::enabled_urls(settings); + if relay_urls.is_empty() { + // The vault change stands; publishing can be retried later via the + // explicit "publish name" action once a relay is enabled. + return Ok(( + summary, + MetadataPublishReport { + succeeded: Vec::new(), + failed: Vec::new(), + }, + )); + } + + let keys = Keys::new(secret_key); + let report = + publish_metadata_blocking(&keys, &summary.label, summary.picture.clone(), relay_urls); + Ok((summary, report)) +} + /// Validate that a picture URL is a well-formed http(s) URL. fn validate_picture_url(url: &str) -> Result<(), AppError> { let parsed = Url::parse(url) @@ -756,6 +812,74 @@ mod tests { assert_eq!(err.kind(), crate::errors::ErrorKind::ProfileNotFound); } + #[test] + fn rename_profile_updates_label_and_skips_publish_without_relays() { + let mut vault = Vault::empty(); + let summary = + create_profile(&mut vault, "Alice".to_string(), None, &offline_settings()).unwrap(); + + let (renamed, report) = rename_profile( + &mut vault, + &summary.npub, + "Alicia".to_string(), + None, + &offline_settings(), + ) + .expect("renaming must work offline"); + + assert_eq!(renamed.label, "Alicia"); + assert_eq!( + vault.profiles[0].label, "Alicia", + "vault must remember the label" + ); + // No relays enabled: nothing published, but the change still stands. + assert!(report.succeeded.is_empty()); + assert!(report.failed.is_empty()); + + // Leading/trailing whitespace is trimmed. + let (trimmed, _) = rename_profile( + &mut vault, + &summary.npub, + " Ace ".to_string(), + None, + &offline_settings(), + ) + .unwrap(); + assert_eq!(trimmed.label, "Ace"); + assert_eq!(vault.profiles[0].label, "Ace"); + } + + #[test] + fn rename_profile_rejects_empty_names() { + let mut vault = Vault::empty(); + let summary = + create_profile(&mut vault, "Alice".to_string(), None, &offline_settings()).unwrap(); + + for bad in [String::new(), " ".to_string()] { + let err = rename_profile(&mut vault, &summary.npub, bad, None, &offline_settings()) + .expect_err("empty name must error"); + assert_eq!(err.kind(), crate::errors::ErrorKind::Config); + } + assert_eq!( + vault.profiles[0].label, "Alice", + "nothing stored on failure" + ); + } + + #[test] + fn rename_profile_missing_profile_errors() { + let mut vault = Vault::empty(); + let err = rename_profile( + &mut vault, + "npub1ghost", + "Ghost".to_string(), + None, + &offline_settings(), + ) + .expect_err("missing profile must error"); + assert_eq!(err.kind(), crate::errors::ErrorKind::ProfileNotFound); + } + /// Delete a profile by npub, returning the deleted profile for undo. /// The vault must not be encrypted, or the key must be provided. pub fn delete_profile(vault: &mut Vault, npub: &str) -> Result { From 6df690fe8fcb9c27409d170b20bfff79039b2143 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 18:49:50 -0500 Subject: [PATCH 06/51] Checkpoint: profile rename feature --- CHECKPOINT-encryption.md | 49 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 3590562..eaf1f1b 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,4 @@ -# Checkpoint — Card-click selection + narrow-window button wrap (2026-08-23) +# Checkpoint — Profile rename + card-click selection + button wrap (2026-08-23) **App renamed to Keynectr (2026-08-23).** A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. @@ -7,31 +7,39 @@ verified green at the moment this file was written. - Project: `/home/avi/Projects/Nostr_Keynctr` (renamed from `0_Nostr` after commit `1116dfd`; folder itself renamed from `Nostr-Keynectr` — see `2e4005d`) -- Git repo: `master` @ `64502c1` ("Wrap profile card action buttons on narrow windows"). - Before it: `653945a` (card-click selection), `2e4005d` (folder-rename checkpoint), - `24b34b6`, `6ead6f4`, `31ef305` (Keynectr rebrand), `a6329d5` (metadata publishing), - `db81f8d` (delete + undo). +- Git repo: `master` @ `d618a5a` ("Add profile rename with metadata republish (GUI + + CLI)"). Before it: `64502c1` (button wrap), `653945a` (card-click selection), + `2e4005d` (folder-rename checkpoint), `24b34b6`, `6ead6f4`, `31ef305` (Keynectr + rebrand), `a6329d5` (metadata publishing), `db81f8d` (delete + undo). - Working tree is **clean** apart from this checkpoint update, which is committed right after. ## What was completed -1. **Click anywhere on a profile card to select it (2026-08-23)** — on the Profiles +1. **Rename a profile (2026-08-23)** — new "Edit name" button on every Profiles card + opens a modal (prefilled with the current name); saving stores the new label and + republishes kind 0 metadata to enabled relays (`d618a5a`). Wired end-to-end: + `profiles::rename_profile` (Rust), `rename_profile` IPC request, Electron + RENDERER_METHODS allowlist, `api.renameProfile`, AppProvider `renameProfile`, + RenameModal UI, fake-backend support + test. CLI: `keynectr rename + `. Empty names rejected; whitespace trimmed; works offline (change saved, + publish retried later via "Publish name"). +2. **Click anywhere on a profile card to select it (2026-08-23)** — on the Profiles screen, clicking an inactive profile's card now does the same as pressing its "Select" button (`653945a`). Clicks on buttons inside the card (Copy / Publish name / - Picture / Delete / Secret key / Select) do not trigger selection. The active card is - not clickable and shows a hover highlight on inactive cards. -2. **Action buttons wrap on narrow windows (2026-08-23)** — `.profile-card-actions` now - uses `flex-wrap: wrap`, so at half-screen width the Copy/Publish/Picture/Delete/ - Secret key/Select buttons wrap onto extra rows inside the card instead of spilling - past its border (`64502c1`). -3. **Project folder renamed** to `Nostr_Keynctr` (`2e4005d`) — old path + Picture / Edit name / Delete / Secret key / Select) do not trigger selection. The + active card is not clickable and shows a hover highlight on inactive cards. +3. **Action buttons wrap on narrow windows (2026-08-23)** — `.profile-card-actions` now + uses `flex-wrap: wrap`, so at half-screen width the action buttons wrap onto extra + rows inside the card instead of spilling past its border (`64502c1`). +4. **Project folder renamed** to `Nostr_Keynctr` (`2e4005d`) — old path `Nostr-Keynectr` no longer exists; same project, new name. -4. Earlier in the session: Keynectr rebrand across UI/package/binary (`31ef305`, +5. Earlier in the session: Keynectr rebrand across UI/package/binary (`31ef305`, `6ead6f4`, `24b34b6`), metadata/picture publishing (`a6329d5`) — see previous checkpoint notes below for details. ## Commits added most recently +- `d618a5a` Add profile rename with metadata republish (GUI + CLI) - `64502c1` Wrap profile card action buttons on narrow windows - `653945a` Select a profile by clicking anywhere on its card - `2e4005d` Checkpoint: project folder renamed to Nostr_Keynctr @@ -41,9 +49,9 @@ verified green at the moment this file was written. Rust (repo root): ``` -cargo test # 96 passed; 0 failed +cargo test # 99 passed; 0 failed cargo clippy --all-targets # finishes; 2 pre-existing warnings in src/profiles.rs - # (drop of reference at :170, unused delete_profile at :761) + # (drop of reference at :170 area, unused delete_profile) cargo fmt --check # clean cargo build --release # success ``` @@ -51,7 +59,7 @@ cargo build --release # success Frontend (`frontend/`): ``` -npm test # 14 files, 80 tests passed (incl. 2 new card-click tests) +npm test # 14 files, 81 tests passed (incl. rename + card-click tests) npm run typecheck # clean npm run lint # 0 errors npm run format:check # clean @@ -69,6 +77,8 @@ cd ~/Projects/Nostr_Keynctr/frontend && npm start - Profiles screen: click an inactive profile's card (anywhere except its buttons) to make it active; the Select button still works as before. +- Profiles screen: **Edit name** button opens a modal to rename a profile and publish + the new name. - New profiles publish their name automatically on creation. - Existing profiles: **Profiles → Publish name** button, or **Picture** button to set a photo (URL paste or file upload) which publishes immediately. @@ -81,6 +91,7 @@ $B list # show profiles $B relays enable wss://nos.lol # enable at least one relay first $B publish-name # republish stored name $B set-picture https://…/img.png # set + publish picture +$B rename # rename + publish the new name ``` ## Notes & next steps @@ -98,8 +109,8 @@ $B set-picture https://…/img.png # set + publish picture identifier field per profile (GUI + CLI), include it in published metadata, plus docs or a helper for serving `.well-known/nostr.json` on the user's domain (the HTTP side cannot be done by the app alone). -- Other candidates: rename profiles (edit label + republish), banner/about fields in the - edit UI. +- Other candidates: banner/about fields in the edit UI. (Rename profiles was completed + 2026-08-23.) - **Rename details:** binary is now `keynectr`; data lives in `~/.local/share/keynectr` (auto-migrated from `nost-feed-manager` on first run — verified live). The KDF verifier string in crypto.rs was deliberately NOT renamed so old encrypted backups stay readable. From 4d70b25628f907448a18e96ba89df0dcdc402ce5 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 19:03:16 -0500 Subject: [PATCH 07/51] Make IPC concurrent so relay checks and feeds never block the UI --- src/ipc.rs | 138 ++++++++++++++++++++++++++++-------------------- src/main.rs | 4 +- src/profiles.rs | 66 ++++++++++++++++------- src/signer.rs | 17 +++--- 4 files changed, 136 insertions(+), 89 deletions(-) diff --git a/src/ipc.rs b/src/ipc.rs index 89c75ea..c0c1b04 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -1,8 +1,9 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::time::Duration; use serde::{Deserialize, Serialize}; use serde_json::json; +use tokio::sync::Mutex; use crate::app::App; use crate::errors::AppError; @@ -157,20 +158,26 @@ pub struct ReplyEnvelope { /// Run the JSON-lines IPC server on stdin/stdout. /// -/// The Electron main process spawns `keynectr serve` and -/// exchanges one JSON object per line. Requests are processed sequentially so -/// the shared state never sees concurrent mutations. +/// The Electron main process spawns `keynectr serve` and exchanges one JSON +/// object per line. Requests are handled concurrently — replies are +/// id-correlated, so they may arrive out of order — while every handler that +/// touches shared state locks it for the duration, so mutations remain +/// serialized and never interleave. Long network-only requests (relay tests, +/// feed reads) run without holding that lock so they cannot delay interactive +/// ones such as selecting a profile. pub async fn serve() -> Result<(), AppError> { use tokio::io::AsyncBufReadExt; + use tokio::task::JoinSet; // Shared state, so the NIP-46 signer's background task and the request loop // both see the same vault (including its unlock key) without racing writes. let app = Arc::new(Mutex::new(App::load()?)); - let signer = Signer::new(); + let signer = Arc::new(Signer::new()); + let stdout = Arc::new(tokio::sync::Mutex::new(tokio::io::stdout())); let stdin = tokio::io::stdin(); let mut lines = tokio::io::BufReader::new(stdin).lines(); - let mut stdout = tokio::io::stdout(); + let mut tasks = JoinSet::new(); while let Some(line) = lines .next_line() @@ -189,29 +196,40 @@ pub async fn serve() -> Result<(), AppError> { message: "The request could not be understood.".to_string(), details: Some(e.to_string()), }; - write_line(&mut stdout, ReplyEnvelope { id: 0, reply }).await?; + write_line(&stdout, ReplyEnvelope { id: 0, reply }).await?; continue; } }; - let reply = handle(app.clone(), &signer, envelope.request).await; - write_line( - &mut stdout, - ReplyEnvelope { - id: envelope.id, - reply, - }, - ) - .await?; + let task_app = app.clone(); + let task_signer = signer.clone(); + let task_stdout = stdout.clone(); + tasks.spawn(async move { + let reply = handle(task_app, task_signer, envelope.request).await; + let _ = write_line( + &task_stdout, + ReplyEnvelope { + id: envelope.id, + reply, + }, + ) + .await; + }); } + // Finish in-flight requests before returning so the GUI never sees the + // backend disappear mid-request. + while tasks.join_next().await.is_some() {} Ok(()) } -async fn write_line(writer: &mut W, envelope: ReplyEnvelope) -> Result<(), AppError> -where - W: tokio::io::AsyncWriteExt + Unpin, -{ +async fn write_line( + writer: &tokio::sync::Mutex, + envelope: ReplyEnvelope, +) -> Result<(), AppError> { + use tokio::io::AsyncWriteExt; + + let mut writer = writer.lock().await; let mut line = serde_json::to_string(&envelope) .map_err(|e| AppError::json("Could not prepare a response", e))?; line.push('\n'); @@ -228,10 +246,10 @@ where async fn handle( app: Arc>, - signer: &Signer, + signer: Arc, request: Request, ) -> Reply { - let result = run(&app, signer, request).await; + let result = run(&app, &signer, request).await; match result { Ok(value) => Reply::Ok { data: value }, Err(err) => Reply::Error { @@ -255,19 +273,16 @@ fn error_code(err: &AppError) -> String { } /// Signer control commands never touch the vault directly, so they take the -/// shared handle (a clone) rather than locking the state. Everything else -/// locks the vault for the duration of the call, mirroring the old -/// single-threaded model. -#[allow(clippy::await_holding_lock)] +/// shared handle (a clone) rather than locking the state. Read-only network +/// requests (relay tests, feed reads) grab what they need under a short lock +/// and then run without it, so slow relays cannot delay interactive requests. +/// Everything else locks the state for the duration of the call, so mutations +/// remain serialized and never interleave. async fn run( app: &Arc>, signer: &Signer, request: Request, ) -> Result { - // Signer control commands never touch the vault directly, so they take the - // shared handle (a clone) rather than locking the state. Everything else - // locks the vault for the duration of the call, mirroring the old - // single-threaded model. match request { Request::SignerConnect { uri } => { signer.connect(app.clone(), &uri)?; @@ -282,16 +297,49 @@ async fn run( signer.approve(&id, approved)?; Ok(json!(signer.status())) } + Request::RelayTest { url } => { + // Pure network probe against the given URL; no shared state. + let result = relays::test_connection(&url, RELAY_TEST_TIMEOUT).await?; + Ok(json!(result)) + } + Request::FeedGet { + limit, + contacts_only, + } => { + let limit = limit.unwrap_or(feed::DEFAULT_LIMIT); + let contacts_only = contacts_only.unwrap_or(false); + // Copy the inputs out of shared state under a short lock so the + // multi-second relay fetches below never block a Select or save. + let (settings, owner_hex) = { + let guard = app.lock().await; + let owner_hex = if contacts_only { + let npub = guard + .vault + .active_profile + .as_deref() + .ok_or_else(AppError::no_active_profile)?; + Some(feed::owner_pubkey(npub)?.to_hex()) + } else { + None + }; + (guard.settings.clone(), owner_hex) + }; + let items = match owner_hex { + Some(hex) => feed::contact_feed(&settings, limit, &hex).await?, + None => feed::aggregate_feed(&settings, limit).await?, + }; + Ok(json!(items)) + } other => { - let mut guard = app.lock().expect("app mutex poisoned"); + let mut guard = app.lock().await; run_with_app(&mut guard, other).await } } } /// Requests dispatched to the vault state. The shared mutex guard is held across -/// the awaited operation on purpose: requests remain effectively sequential, and -/// a concurrent `await` never yields back into a state the loop expects to own. +/// the awaited operation on purpose: mutations stay serialized against each +/// other even though requests themselves are handled concurrently. async fn run_with_app(app: &mut App, request: Request) -> Result { match request { Request::Init | Request::GetState => Ok(json!(app.state_view())), @@ -351,25 +399,6 @@ async fn run_with_app(app: &mut App, request: Request) -> Result { - let limit = limit.unwrap_or(feed::DEFAULT_LIMIT); - let items = if contacts_only.unwrap_or(false) { - let npub = app - .vault - .active_profile - .as_deref() - .ok_or_else(AppError::no_active_profile)?; - let pubkey = feed::owner_pubkey(npub)?; - feed::contact_feed(&app.settings, limit, &pubkey.to_hex()).await? - } else { - feed::aggregate_feed(&app.settings, limit).await? - }; - Ok(json!(items)) - } - Request::RelayAdd { url } => { relays::add_relay(&mut app.settings, &url)?; app.save_settings()?; @@ -388,11 +417,6 @@ async fn run_with_app(app: &mut App, request: Request) -> Result { - let result = relays::test_connection(&url, RELAY_TEST_TIMEOUT).await?; - Ok(json!(result)) - } - Request::SettingsGet => Ok(json!(app.settings)), Request::BackupNow => { diff --git a/src/main.rs b/src/main.rs index 725c999..56a53e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use std::process::ExitCode; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use keynectr::app::App; use keynectr::errors::{AppError, ErrorKind}; @@ -509,7 +509,7 @@ async fn cli_signer(args: &[String]) -> Result { let uri = args .get(3) .ok_or_else(|| AppError::config("Usage: signer connect "))?; - let app = Arc::new(Mutex::new(load_app_with_unlock()?)); + let app = Arc::new(tokio::sync::Mutex::new(load_app_with_unlock()?)); let signer = Signer::new(); signer.connect(app, uri)?; println!("Connecting to the NIP-46 app… (interrupt with Ctrl-C to stop)"); diff --git a/src/profiles.rs b/src/profiles.rs index 3d42a3f..5f542d4 100644 --- a/src/profiles.rs +++ b/src/profiles.rs @@ -11,9 +11,10 @@ use crate::settings::Settings; use crate::vault::{unix_timestamp, StoredProfile, Vault}; /// How long to wait for relays to accept a connection attempt. -const METADATA_CONNECT_TIMEOUT: Duration = Duration::from_secs(10); -/// How long to wait for a single relay to accept the metadata event. -const METADATA_SEND_TIMEOUT: Duration = Duration::from_secs(15); +const METADATA_CONNECT_TIMEOUT: Duration = Duration::from_secs(5); +/// How long to wait for a single relay to accept the metadata event. Relays +/// are sent to in parallel, so this caps the whole publish, not each relay. +const METADATA_SEND_TIMEOUT: Duration = Duration::from_secs(8); /// A safe view of a profile that contains no secret key material. #[derive(Debug, Clone, Serialize, PartialEq, Eq)] @@ -343,29 +344,54 @@ async fn publish_metadata_async( client.connect().await; let _ = client.wait_for_connection(METADATA_CONNECT_TIMEOUT).await; - let mut succeeded = Vec::new(); - let mut failed = Vec::new(); - for url in &relay_urls { - match client.relay(url.as_str()).await { - Ok(relay) => { - match tokio::time::timeout(METADATA_SEND_TIMEOUT, relay.send_event(&event)).await { - Ok(Ok(_)) => succeeded.push(url.clone()), - Ok(Err(err)) => failed.push(failure_for(url, &err)), - Err(_) => failed.push(RelayFailure { - url: url.clone(), - error: "The relay did not respond in time.".to_string(), - details: Some( - "Timed out while waiting for the relay to accept the metadata." - .to_string(), + // Send to every relay in parallel so one slow or dead relay cannot drag + // the whole publish out to (relays x timeout). + let mut outcomes: Vec>> = + (0..relay_urls.len()).map(|_| None).collect(); + let mut sends = tokio::task::JoinSet::new(); + for (index, url) in relay_urls.iter().cloned().enumerate() { + let client = client.clone(); + let event = event.clone(); + sends.spawn(async move { + match client.relay(url.as_str()).await { + Ok(relay) => { + match tokio::time::timeout(METADATA_SEND_TIMEOUT, relay.send_event(&event)) + .await + { + Ok(Ok(_)) => (index, Ok(url)), + Ok(Err(err)) => (index, Err(failure_for(&url, &err))), + Err(_) => ( + index, + Err(RelayFailure { + url, + error: "The relay did not respond in time.".to_string(), + details: Some( + "Timed out while waiting for the relay to accept the metadata." + .to_string(), + ), + }), ), - }), + } } + Err(err) => (index, Err(failure_for(&url, &err))), } - Err(err) => failed.push(failure_for(url, &err)), - } + }); + } + while let Some(joined) = sends.join_next().await { + let (index, outcome) = joined.expect("metadata send task panicked"); + outcomes[index] = Some(outcome); } client.disconnect().await; + let mut succeeded = Vec::new(); + let mut failed = Vec::new(); + for outcome in outcomes.into_iter().flatten() { + match outcome { + Ok(url) => succeeded.push(url), + Err(failure) => failed.push(failure), + } + } + MetadataPublishReport { succeeded, failed } } diff --git a/src/signer.rs b/src/signer.rs index 086f0b3..84e8d7a 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -238,7 +238,7 @@ impl Signer { /// /// `app` is the shared vault state, so the signer reflects the current unlock /// key and active profile. A locked vault cannot sign until it is unlocked. - pub fn connect(&self, app: Arc>, uri: &str) -> Result<(), AppError> { + pub fn connect(&self, app: Arc>, uri: &str) -> Result<(), AppError> { if uri.trim().starts_with("bunker://") { return Err(AppError::config( "That is a bunker:// link, which means routing through *another* signer. \ @@ -596,16 +596,10 @@ fn nip44(keys: &Keys, request: &RawRequest) -> Result { /// The background loop: connect to the client's relays, announce ourselves, /// subscribe to kind 24133 events, and answer requests until stopped. -async fn run_sign_task(signer: Signer, app: Arc>, uri: ConnectUri) { +async fn run_sign_task(signer: Signer, app: Arc>, uri: ConnectUri) { // 1. Resolve the active profile's key under the current vault lock. let keys = { - let guard = match app.lock() { - Ok(guard) => guard, - Err(_) => { - signer.fail("The vault could not be read."); - return; - } - }; + let guard = app.lock().await; let hex = match profiles::resolve_active_secret_key(&guard.vault, guard.vault_key()) { Ok(hex) => hex, Err(err) => { @@ -764,7 +758,10 @@ mod tests { fn bunker_link_is_rejected() { let signer = Signer::new(); assert!(signer - .connect(Arc::new(Mutex::new(App::load().unwrap())), "bunker://abc") + .connect( + Arc::new(tokio::sync::Mutex::new(App::load().unwrap())), + "bunker://abc" + ) .is_err()); } From ad70890efcb177026ea7a9b7eb12befebe566367 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 19:04:11 -0500 Subject: [PATCH 08/51] Checkpoint: concurrent IPC responsiveness fix --- CHECKPOINT-encryption.md | 49 ++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index eaf1f1b..0e9c4ab 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,4 @@ -# Checkpoint — Profile rename + card-click selection + button wrap (2026-08-23) +# Checkpoint — Responsive backend (concurrent IPC) + rename (2026-08-23) **App renamed to Keynectr (2026-08-23).** A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. @@ -7,38 +7,57 @@ verified green at the moment this file was written. - Project: `/home/avi/Projects/Nostr_Keynctr` (renamed from `0_Nostr` after commit `1116dfd`; folder itself renamed from `Nostr-Keynectr` — see `2e4005d`) -- Git repo: `master` @ `d618a5a` ("Add profile rename with metadata republish (GUI + - CLI)"). Before it: `64502c1` (button wrap), `653945a` (card-click selection), - `2e4005d` (folder-rename checkpoint), `24b34b6`, `6ead6f4`, `31ef305` (Keynectr - rebrand), `a6329d5` (metadata publishing), `db81f8d` (delete + undo). +- Git repo: `master` @ `4d70b25` ("Make IPC concurrent so relay checks and feeds never + block the UI"). Before it: `d618a5a` (rename), `64502c1` (button wrap), `653945a` + (card-click selection), `2e4005d` (folder-rename checkpoint), `24b34b6`, `6ead6f4`, + `31ef305` (Keynectr rebrand), `a6329d5` (metadata publishing), `db81f8d` + (delete + undo). - Working tree is **clean** apart from this checkpoint update, which is committed right after. ## What was completed -1. **Rename a profile (2026-08-23)** — new "Edit name" button on every Profiles card +1. **Backend made responsive (2026-08-23, `4d70b25`)** — user-visible lag fixed: + Selecting a profile took ~10 s and renaming ~30 s. Root causes: the IPC server + processed requests strictly one-at-a-time; the Home screen fires a relay test per + enabled relay on mount (a dead relay burns the full 8 s timeout each), and + `feed_get` held the shared lock during multi-second relay fetches — so Select queued + behind them; metadata publishing also waited 10 s for connections and sent to relays + one-by-one with 15 s timeouts each (~30 s with one dead relay). + Changes: + - IPC server now handles requests **concurrently** (task per request; replies are + id-correlated so out-of-order is fine). Mutations stay serialized via the shared + mutex, which moved from `std::sync` to `tokio::sync` (`App`, `Signer::connect`, + `run_sign_task`, CLI `signer connect`). + - `relay_test` and `feed_get` no longer hold the shared lock during network I/O + (they copy settings / active-npub under a short lock first). + - Metadata publish now sends to all relays **in parallel** with shorter timeouts + (connect 5 s, send 8 s) → worst case ~13 s instead of ~100 s; typical rename is a + few seconds. +2. **Rename a profile (2026-08-23)** — new "Edit name" button on every Profiles card opens a modal (prefilled with the current name); saving stores the new label and republishes kind 0 metadata to enabled relays (`d618a5a`). Wired end-to-end: `profiles::rename_profile` (Rust), `rename_profile` IPC request, Electron RENDERER_METHODS allowlist, `api.renameProfile`, AppProvider `renameProfile`, - RenameModal UI, fake-backend support + test. CLI: `keynectr rename + RenameModal UI, fake-backend support + test. CLI: `keynectr rename `. Empty names rejected; whitespace trimmed; works offline (change saved, publish retried later via "Publish name"). -2. **Click anywhere on a profile card to select it (2026-08-23)** — on the Profiles +3. **Click anywhere on a profile card to select it (2026-08-23)** — on the Profiles screen, clicking an inactive profile's card now does the same as pressing its "Select" button (`653945a`). Clicks on buttons inside the card (Copy / Publish name / Picture / Edit name / Delete / Secret key / Select) do not trigger selection. The active card is not clickable and shows a hover highlight on inactive cards. -3. **Action buttons wrap on narrow windows (2026-08-23)** — `.profile-card-actions` now +4. **Action buttons wrap on narrow windows (2026-08-23)** — `.profile-card-actions` now uses `flex-wrap: wrap`, so at half-screen width the action buttons wrap onto extra rows inside the card instead of spilling past its border (`64502c1`). -4. **Project folder renamed** to `Nostr_Keynctr` (`2e4005d`) — old path +5. **Project folder renamed** to `Nostr_Keynctr` (`2e4005d`) — old path `Nostr-Keynectr` no longer exists; same project, new name. -5. Earlier in the session: Keynectr rebrand across UI/package/binary (`31ef305`, +6. Earlier in the session: Keynectr rebrand across UI/package/binary (`31ef305`, `6ead6f4`, `24b34b6`), metadata/picture publishing (`a6329d5`) — see previous checkpoint notes below for details. ## Commits added most recently +- `4d70b25` Make IPC concurrent so relay checks and feeds never block the UI - `d618a5a` Add profile rename with metadata republish (GUI + CLI) - `64502c1` Wrap profile card action buttons on narrow windows - `653945a` Select a profile by clicking anywhere on its card @@ -51,7 +70,7 @@ Rust (repo root): ``` cargo test # 99 passed; 0 failed cargo clippy --all-targets # finishes; 2 pre-existing warnings in src/profiles.rs - # (drop of reference at :170 area, unused delete_profile) + # (drop of reference, unused delete_profile) cargo fmt --check # clean cargo build --release # success ``` @@ -96,6 +115,12 @@ $B rename # rename + publish the new name ## Notes & next steps +- **Architecture note (2026-08-23):** the shared `App` state mutex is now + `tokio::sync::Mutex` (`Arc>` in ipc.rs/signer.rs). Guards are held across + awaits by design; new handlers that touch vault/settings must lock it, while pure + network requests should copy inputs under a short lock and run lock-free. +- If rename/publish latency still feels long, next step would be background + publishing (return immediately, report via notice later) instead of shorter timeouts. - Pre-existing clippy warnings in `src/profiles.rs` (drop of a reference, unused `delete_profile`) are untouched — worth cleaning up in a future pass. - Nostr has no relay-to-relay sync: names/pictures are only visible on relays they were From cb23a5e32d374f8dc457265b353b22c8fd25fdb4 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 19:09:46 -0500 Subject: [PATCH 09/51] Trim metadata publish latency: skip global connection wait, 6s send cap --- src/profiles.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/profiles.rs b/src/profiles.rs index 5f542d4..35ed677 100644 --- a/src/profiles.rs +++ b/src/profiles.rs @@ -10,11 +10,9 @@ use crate::relays; use crate::settings::Settings; use crate::vault::{unix_timestamp, StoredProfile, Vault}; -/// How long to wait for relays to accept a connection attempt. -const METADATA_CONNECT_TIMEOUT: Duration = Duration::from_secs(5); /// How long to wait for a single relay to accept the metadata event. Relays /// are sent to in parallel, so this caps the whole publish, not each relay. -const METADATA_SEND_TIMEOUT: Duration = Duration::from_secs(8); +const METADATA_SEND_TIMEOUT: Duration = Duration::from_secs(6); /// A safe view of a profile that contains no secret key material. #[derive(Debug, Clone, Serialize, PartialEq, Eq)] @@ -342,7 +340,10 @@ async fn publish_metadata_async( let _ = client.add_relay(url.as_str()).await; } client.connect().await; - let _ = client.wait_for_connection(METADATA_CONNECT_TIMEOUT).await; + // No explicit wait for connections here: `send_event` waits for each relay + // to become writable itself, and the per-send timeout below already bounds + // the whole publish. Waiting for *all* relays first would burn the full + // timeout whenever a single relay is unreachable. // Send to every relay in parallel so one slow or dead relay cannot drag // the whole publish out to (relays x timeout). From f2c30d397f901913a65c4399d9288ca64876f777 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 19:10:12 -0500 Subject: [PATCH 10/51] Checkpoint: publish latency trim --- CHECKPOINT-encryption.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 0e9c4ab..07730f8 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -7,16 +7,22 @@ verified green at the moment this file was written. - Project: `/home/avi/Projects/Nostr_Keynctr` (renamed from `0_Nostr` after commit `1116dfd`; folder itself renamed from `Nostr-Keynectr` — see `2e4005d`) -- Git repo: `master` @ `4d70b25` ("Make IPC concurrent so relay checks and feeds never - block the UI"). Before it: `d618a5a` (rename), `64502c1` (button wrap), `653945a` - (card-click selection), `2e4005d` (folder-rename checkpoint), `24b34b6`, `6ead6f4`, - `31ef305` (Keynectr rebrand), `a6329d5` (metadata publishing), `db81f8d` - (delete + undo). +- Git repo: `master` @ `cb23a5e` ("Trim metadata publish latency: skip global + connection wait, 6s send cap"). Before it: `4d70b25` (concurrent IPC), `d618a5a` + (rename), `64502c1` (button wrap), `653945a` (card-click selection), `2e4005d` + (folder-rename checkpoint), `24b34b6`, `6ead6f4`, `31ef305` (Keynectr rebrand), + `a6329d5` (metadata publishing), `db81f8d` (delete + undo). - Working tree is **clean** apart from this checkpoint update, which is committed right after. ## What was completed -1. **Backend made responsive (2026-08-23, `4d70b25`)** — user-visible lag fixed: +1. **Publish latency trimmed further (2026-08-23, `cb23a5e`)** — after the concurrent + IPC fix, rename still took ~10 s: `wait_for_connection` always burned its full + timeout when any relay was unreachable, and the dead relay's send burned 8 s. + Removed the global connection wait (`send_event` waits for writability itself) and + capped sends at 6 s. Typical rename now completes in ~1–3 s even with one dead + relay. If still too slow, next step is fully background publishing. +2. **Backend made responsive (2026-08-23, `4d70b25`)** — user-visible lag fixed: Selecting a profile took ~10 s and renaming ~30 s. Root causes: the IPC server processed requests strictly one-at-a-time; the Home screen fires a relay test per enabled relay on mount (a dead relay burns the full 8 s timeout each), and @@ -57,6 +63,7 @@ verified green at the moment this file was written. ## Commits added most recently +- `cb23a5e` Trim metadata publish latency: skip global connection wait, 6s send cap - `4d70b25` Make IPC concurrent so relay checks and feeds never block the UI - `d618a5a` Add profile rename with metadata republish (GUI + CLI) - `64502c1` Wrap profile card action buttons on narrow windows From 045fa474760572d740f799d8da787042cd193bf7 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 21:59:11 -0500 Subject: [PATCH 11/51] Add NIP-05 identifiers: store, publish, GUI modal, CLI helpers - Store an optional nip05 on each profile; publish it in kind 0 metadata - set-nip05 CLI (+ validation, lower-casing, clear) and nip05-file helper that prints the .well-known/nostr.json document for a domain - Profiles screen: NIP-05 button, handle shown on cards, Nip05Modal with client-side validation and Remove action - Fix Modal stealing focus from autoFocus inputs one frame after open --- docs/NIP-05.md | 77 ++++++++ frontend/electron/main.ts | 1 + frontend/src/components/Modal.tsx | 6 +- frontend/src/lib/api.ts | 5 + frontend/src/lib/types.ts | 2 + frontend/src/screens/ProfilesScreen.tsx | 145 ++++++++++++++ frontend/src/state/AppProvider.tsx | 12 ++ frontend/src/test/ProfilesScreen.test.tsx | 63 ++++++ frontend/src/test/fakeBackend.ts | 33 ++++ src/app.rs | 3 +- src/ipc.rs | 14 ++ src/main.rs | 80 +++++++- src/profiles.rs | 230 +++++++++++++++++++++- src/vault.rs | 6 + 14 files changed, 666 insertions(+), 11 deletions(-) create mode 100644 docs/NIP-05.md diff --git a/docs/NIP-05.md b/docs/NIP-05.md new file mode 100644 index 0000000..441951e --- /dev/null +++ b/docs/NIP-05.md @@ -0,0 +1,77 @@ +# NIP-05 identifiers in Keynectr + +A NIP-05 identifier is a human-readable Nostr address that looks like an email +address — for example `boo@l484.com`. When a profile has one, clients such as +Iris, Yakihonne, Amethyst and snort show the handle instead of a raw `npub1…` +key, and users can find and tag you by typing it. + +NIP-05 has two halves. Keynectr does the first half; you do the second half +once on your own domain. + +## 1. Publish it from Keynectr (the app side) + +- **GUI:** Profiles → *NIP-05* button → enter `name@domain.com` → *Save & publish*. + The identifier is stored with the profile and published to your enabled relays + as part of your kind 0 metadata. +- **CLI:** + ```bash + B=~/Projects/Nostr_Keynctr/target/release/keynectr + $B set-nip05 boo@l484.com # set + publish + $B set-nip05 clear # remove + publish the removal + ``` +- The special form `_@domain.com` claims the bare domain itself (the whole + domain shows as your handle). + +## 2. Serve `.well-known/nostr.json` (the domain side) + +Clients verify a NIP-05 claim by fetching: + +``` +https:///.well-known/nostr.json?name= +``` + +That file must live on the domain in the identifier — publishing alone is not +enough. Keynectr prints the exact document to serve: + +```bash +$B nip05-file boo@l484.com +``` + +which outputs something like: + +```json +{ + "names": { + "boo": "3bf0c6…(64-char hex public key)" + }, + "relays": { + "3bf0c6…": ["wss://nos.lol", "wss://relay.primal.net"] + } +} +``` + +Serve it at `https:///.well-known/nostr.json` with: + +- `Content-Type: application/json` (or `application/json; charset=utf-8`) +- CORS header `Access-Control-Allow-Origin: *` (clients fetch it from browsers) + +### nginx example + +```nginx +location = /.well-known/nostr.json { + include snippets/cors.conf; # or add_header Access-Control-Allow-Origin *; + default_type application/json; + root /var/www/static; +} +``` + +Then place the printed document at `/var/www/static/.well-known/nostr.json`. +Regenerate it if you switch profiles or change your relay list. + +## Notes + +- The identifier is lower-cased when stored; validation matches NIP-05's + limited character set (`a-z`, `0-9`, `-`, `_`; `_` for the bare domain). +- Clients cache profiles aggressively — hard-refresh after changing anything. +- Without the well-known file, most clients will not display the handle even + though the metadata was published successfully. diff --git a/frontend/electron/main.ts b/frontend/electron/main.ts index b8f0637..22ff4f6 100644 --- a/frontend/electron/main.ts +++ b/frontend/electron/main.ts @@ -186,6 +186,7 @@ const RENDERER_METHODS: ReadonlySet = new Set([ 'publish_profile_metadata', 'set_profile_picture', 'rename_profile', + 'set_nip05', 'delete_profile', 'undo_delete', 'publish_note', diff --git a/frontend/src/components/Modal.tsx b/frontend/src/components/Modal.tsx index d3a678e..431c352 100644 --- a/frontend/src/components/Modal.tsx +++ b/frontend/src/components/Modal.tsx @@ -19,7 +19,11 @@ export function Modal({ open, title, onClose, children }: ModalProps) { const container = containerRef.current; const frame = requestAnimationFrame(() => { - container?.focus(); + // Keep keyboard focus where the user (or autoFocus) put it; only pull + // focus to the dialog itself as a fallback for content without inputs. + if (!container?.contains(document.activeElement)) { + container?.focus(); + } }); const onKeyDown = (event: KeyboardEvent) => { diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 9ee9f7f..f5f78e4 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -63,6 +63,11 @@ export const api = { 'rename_profile', { npub, label }, ), + setNip05: (npub: string, nip05: string | null) => + call<{ profile: ProfileSummary; report: MetadataPublishReport; state: AppState }>('set_nip05', { + npub, + nip05, + }), publishNote: (content: string) => call('publish_note', { content }), feedGet: (limit?: number, contactsOnly = false) => call('feed_get', { diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 0ea3c99..15c241e 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -36,6 +36,8 @@ export interface ProfileSummary { is_active: boolean; /** Public URL of the profile picture, when one has been set. */ picture?: string | null; + /** NIP-05 identifier (e.g. `boo@l484.com`), when one has been set. */ + nip05?: string | null; } export interface RelayConfig { diff --git a/frontend/src/screens/ProfilesScreen.tsx b/frontend/src/screens/ProfilesScreen.tsx index d437d94..87045b1 100644 --- a/frontend/src/screens/ProfilesScreen.tsx +++ b/frontend/src/screens/ProfilesScreen.tsx @@ -25,6 +25,7 @@ export function ProfilesScreen({ onCreateProfile }: ProfilesScreenProps) { const [revealTarget, setRevealTarget] = useState<{ label: string; npub: string } | null>(null); const [pictureTarget, setPictureTarget] = useState(null); const [renameTarget, setRenameTarget] = useState<{ npub: string; label: string } | null>(null); + const [nip05Target, setNip05Target] = useState(null); const profiles = state?.profiles ?? []; const shorten = state?.settings.shorten_npub ?? true; @@ -151,6 +152,11 @@ export function ProfilesScreen({ onCreateProfile }: ProfilesScreenProps) { />

{profile.label}

+ {profile.nip05 && ( + + {profile.nip05} + + )} {shortenNpub(profile.npub, shorten)} @@ -177,6 +183,19 @@ export function ProfilesScreen({ onCreateProfile }: ProfilesScreenProps) { > Edit name +
); @@ -271,6 +303,119 @@ interface PictureTarget { url: string; } +interface Nip05Target { + npub: string; + label: string; + nip05: string; +} + +/** Client-side mirror of the backend's NIP-05 validation, for fast feedback. */ +function nip05Problem(value: string): string | null { + const trimmed = value.trim().toLowerCase(); + if (!trimmed) { + return null; // Empty clears the identifier. + } + const at = trimmed.indexOf('@'); + if (at <= 0 || at === trimmed.length - 1) { + return 'Use the form name@domain.com.'; + } + const [local, domain] = [trimmed.slice(0, at), trimmed.slice(at + 1)]; + if (local !== '_' && !/^[a-z0-9_-]+$/.test(local)) { + return 'The part before @ may only use letters, numbers, dashes and underscores.'; + } + if (!/^[a-z0-9.-]+\.[a-z]{2,}$/.test(domain)) { + return 'The part after @ must be a domain like example.com.'; + } + return null; +} + +function Nip05Modal({ + target, + onClose, + onSaved, + onError, + onSavingChange, +}: { + target: Nip05Target; + onClose: () => void; + onSaved: (message: string) => void; + onError: (message: string | null) => void; + onSavingChange: (npub: string | null) => void; +}) { + const { setNip05 } = useApp(); + const [nip05, setNip05Value] = useState(target.nip05); + const [saving, setSaving] = useState(false); + + const trimmed = nip05.trim(); + const changed = trimmed !== target.nip05; + const problem = nip05Problem(trimmed); + const canSave = changed && !problem; + + const save = async (next: string | null) => { + onError(null); + setSaving(true); + onSavingChange(target.npub); + try { + const report = await setNip05(target.npub, next); + onSaved( + report.failed.length === 0 + ? next + ? `NIP-05 "${next}" published to ${report.succeeded.length} relay(s). Remember it must also be served from your domain (see docs/NIP-05.md).` + : 'NIP-05 removed and the change published.' + : `NIP-05 saved for "${target.label}", but ${report.failed.length} relay(s) did not accept it. Use "Publish name" to retry.`, + ); + } catch (err) { + onError(err instanceof Error ? err.message : String(err)); + } finally { + setSaving(false); + onSavingChange(null); + } + }; + + return ( + +
+
+ + setNip05Value(event.target.value)} + placeholder="name@domain.com" + autoComplete="off" + autoFocus + /> + {problem && trimmed && {problem}} +
+

+ A NIP-05 address (like an email handle) makes clients show a proper username instead of a + raw key. It is published to your relays and must also be served from your domain as + /.well-known/nostr.json — see docs/NIP-05.md. +

+
+ {target.nip05 && ( + + )} + + +
+
+
+ ); +} + function RenameModal({ target, onClose, diff --git a/frontend/src/state/AppProvider.tsx b/frontend/src/state/AppProvider.tsx index 6e971e5..cab380d 100644 --- a/frontend/src/state/AppProvider.tsx +++ b/frontend/src/state/AppProvider.tsx @@ -42,6 +42,7 @@ interface AppContextValue { publishProfileMetadata: (npub: string) => Promise; setProfilePicture: (npub: string, url: string | null) => Promise; renameProfile: (npub: string, label: string) => Promise; + setNip05: (npub: string, nip05: string | null) => Promise; publishNote: (content: string) => Promise; recordPublishFailure: (message: string, details?: string | null) => void; clearLastPublish: () => void; @@ -144,6 +145,15 @@ export function AppProvider({ children }: { children: ReactNode }) { [], ); + const setNip05 = useCallback( + async (npub: string, nip05: string | null): Promise => { + const result = await api.setNip05(npub, nip05); + setState(result.state); + return result.report; + }, + [], + ); + const publishNote = useCallback(async (content: string): Promise => { const report = await api.publishNote(content); setLastPublish({ report, error: null, details: null, at: Date.now() }); @@ -261,6 +271,7 @@ export function AppProvider({ children }: { children: ReactNode }) { publishProfileMetadata, setProfilePicture, renameProfile, + setNip05, copyText, }), [ @@ -274,6 +285,7 @@ export function AppProvider({ children }: { children: ReactNode }) { publishProfileMetadata, setProfilePicture, renameProfile, + setNip05, publishNote, deleteProfile, undoDelete, diff --git a/frontend/src/test/ProfilesScreen.test.tsx b/frontend/src/test/ProfilesScreen.test.tsx index ff5fa99..20759a0 100644 --- a/frontend/src/test/ProfilesScreen.test.tsx +++ b/frontend/src/test/ProfilesScreen.test.tsx @@ -86,6 +86,69 @@ describe('ProfilesScreen', () => { expect(await screen.findByRole('status')).toHaveTextContent(/Renamed to "Bobby"/); }); + it('sets a NIP-05 address from the NIP-05 modal and publishes it', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await screen.findByText('Bob'); + await user.click(screen.getAllByRole('button', { name: 'NIP-05' })[1]); + + const input = screen.getByLabelText('NIP-05 address'); + expect(input).toHaveValue(''); + await user.type(input, 'Bob@Example.com'); + await user.click(screen.getByRole('button', { name: 'Save & publish' })); + + await waitFor(() => { + expect(backend.state.profiles.find((p) => p.npub === BOB)?.nip05).toBe('bob@example.com'); + }); + expect(await screen.findByRole('status')).toHaveTextContent(/NIP-05 "bob@example.com"/); + expect(await screen.findByText('bob@example.com')).toBeInTheDocument(); + }); + + it('rejects a malformed NIP-05 address without calling the backend', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await screen.findByText('Bob'); + await user.click(screen.getAllByRole('button', { name: 'NIP-05' })[1]); + + const input = screen.getByLabelText('NIP-05 address'); + await user.type(input, 'not-an-address'); + expect(screen.getByRole('button', { name: 'Save & publish' })).toBeDisabled(); + + await user.clear(input); + await user.type(input, 'boo@nodot'); + expect(screen.getByRole('button', { name: 'Save & publish' })).toBeDisabled(); + expect(backend.requests.filter((r) => r.method === 'set_nip05')).toHaveLength(0); + }); + + it('removes an existing NIP-05 address', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + backend.setState({ + ...backend.state, + profiles: backend.state.profiles.map((p) => + p.npub === BOB ? { ...p, nip05: 'bob@example.com' } : p, + ), + active_profile: backend.state.active_profile, + }); + const user = userEvent.setup(); + renderWithApp(); + + await screen.findByText('bob@example.com'); + await user.click(screen.getAllByRole('button', { name: 'NIP-05' })[1]); + await user.click(screen.getByRole('button', { name: 'Remove' })); + + await waitFor(() => { + expect(backend.state.profiles.find((p) => p.npub === BOB)?.nip05).toBeNull(); + }); + expect(await screen.findByRole('status')).toHaveTextContent(/NIP-05 removed/i); + }); + it('disables Select for the active profile and copies public keys', async () => { const backend = createFakeBackend(); installFakeBackend(backend); diff --git a/frontend/src/test/fakeBackend.ts b/frontend/src/test/fakeBackend.ts index 0e916a0..77fcd42 100644 --- a/frontend/src/test/fakeBackend.ts +++ b/frontend/src/test/fakeBackend.ts @@ -182,6 +182,39 @@ export function createFakeBackend(initial?: AppState): FakeBackend { return { profile: updated, report: makePublishReport(), state: next }; } + case 'set_nip05': { + const npub = String(params.npub); + const raw = params.nip05; + const nip05 = typeof raw === 'string' ? raw.trim().toLowerCase() : null; + if (nip05) { + const at = nip05.indexOf('@'); + const [local, domain] = [nip05.slice(0, at), nip05.slice(at + 1)]; + if (at <= 0 || at === nip05.length - 1 || nip05.includes('@', at + 1)) { + throw new Error('A NIP-05 address must look like name@domain.com.'); + } + if (local !== '_' && !/^[a-z0-9_-]+$/.test(local)) { + throw new Error( + 'The part before @ may only use letters, numbers, dashes and underscores.', + ); + } + if (!domain.includes('.') || domain.split('.').some((part) => !part)) { + throw new Error('The part after @ must be a domain like example.com.'); + } + } + const existing = state.profiles.find((p) => p.npub === npub); + if (!existing) { + throw new Error('That profile is not stored on this computer.'); + } + const updated: ProfileSummary = { ...existing, nip05: nip05 || null }; + const next: AppState = { + ...state, + profiles: state.profiles.map((p) => (p.npub === npub ? updated : p)), + active_profile: state.active_profile?.npub === npub ? updated : state.active_profile, + }; + backend.setState(next); + return { profile: updated, report: makePublishReport(), state: next }; + } + case 'publish_note': { if (publishFailure) { const failure = publishFailure; diff --git a/src/app.rs b/src/app.rs index d60fc2b..88b569a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -113,7 +113,8 @@ impl App { public_key: restored.npub.clone(), secret_key: "".to_string(), created_at: restored.created_at, - picture: None, + picture: restored.picture.clone(), + nip05: restored.nip05.clone(), }; self.vault.profiles.push(stored); // If no active profile, this restored one becomes active diff --git a/src/ipc.rs b/src/ipc.rs index c0c1b04..823eabc 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -56,6 +56,12 @@ pub enum Request { npub: String, label: String, }, + /// Store a NIP-05 identifier (or clear it with `None`) and publish it as + /// part of the profile's kind 0 metadata. + SetNip05 { + npub: String, + nip05: Option, + }, PublishNote { content: String, }, @@ -392,6 +398,14 @@ async fn run_with_app(app: &mut App, request: Request) -> Result { + let key = app.vault_key().copied(); + let (summary, report) = + profiles::set_nip05(&mut app.vault, &npub, nip05, key.as_ref(), &app.settings)?; + app.save_vault()?; + Ok(json!({ "profile": summary, "report": report, "state": app.state_view() })) + } + Request::PublishNote { content } => { let report = publish::publish_active(&app.vault, &app.settings, &content, app.vault_key()) diff --git a/src/main.rs b/src/main.rs index 56a53e9..8c74756 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,10 @@ Commands: publish-name Publish the profile's stored name so other clients show it set-picture Set the profile picture (http(s) URL) and publish it rename Rename a profile and publish the new name + set-nip05 Set the NIP-05 address (name@domain) and publish it; + pass 'clear' to remove it + nip05-file Print the .well-known/nostr.json document to serve + on your domain for a NIP-05 address feed [--contacts] [limit] Fetch recent notes from enabled relays (default 50); --contacts filters to the active profile's contacts relays list List configured relays @@ -72,6 +76,8 @@ async fn main() -> ExitCode { "publish-name" => cli_publish_name(&args), "set-picture" => cli_set_picture(&args), "rename" => cli_rename(&args), + "set-nip05" => cli_set_nip05(&args), + "nip05-file" => cli_nip05_file(&args), "feed" => cli_feed(&args).await, "relays" => cli_relays(&args).await, "settings" => cli_settings(&args), @@ -210,6 +216,76 @@ fn cli_rename(args: &[String]) -> Result { )) } +/// Print the `.well-known/nostr.json` document that serves a NIP-05 +/// identifier for a stored profile. Read-only: never unlocks the vault. +fn cli_nip05_file(args: &[String]) -> Result { + let npub = args + .get(2) + .ok_or_else(|| AppError::config("Usage: keynectr nip05-file "))?; + let identifier = args + .get(3) + .ok_or_else(|| AppError::config("Usage: keynectr nip05-file "))?; + let name = profiles::validate_nip05(identifier)?; + let local = name.split('@').next().unwrap_or(&name); + + let app = App::load()?; + let stored = app + .vault + .profiles + .iter() + .find(|p| p.public_key == npub.as_str()) + .ok_or_else(|| AppError::profile_not_found(npub))?; + let hex = keynectr::feed::owner_pubkey(&stored.public_key)?.to_hex(); + let relays = relays::enabled_urls(&app.settings); + + let mut names = serde_json::Map::new(); + names.insert( + local.to_string(), + serde_json::Value::String(hex.to_string()), + ); + let mut doc = serde_json::Map::new(); + doc.insert("names".to_string(), serde_json::Value::Object(names)); + if !relays.is_empty() { + let urls: Vec = + relays.into_iter().map(serde_json::Value::String).collect(); + let mut relay_map = serde_json::Map::new(); + relay_map.insert(hex.to_string(), serde_json::Value::Array(urls)); + doc.insert("relays".to_string(), serde_json::Value::Object(relay_map)); + } + let pretty = serde_json::to_string_pretty(&serde_json::Value::Object(doc)) + .map_err(|e| AppError::internal(format!("Could not render the document: {e}")))?; + Ok(format!( + "Serve this as https:///.well-known/nostr.json (Content-Type: application/json):\n\n{pretty}\n" + )) +} + +fn cli_set_nip05(args: &[String]) -> Result { + let npub = args + .get(2) + .ok_or_else(|| AppError::config("Usage: keynectr set-nip05 "))?; + let raw = args + .get(3) + .ok_or_else(|| AppError::config("Usage: keynectr set-nip05 "))?; + let nip05 = if raw.eq_ignore_ascii_case("clear") { + None + } else { + Some(raw.clone()) + }; + + let mut app = load_app_with_unlock()?; + let key = app.vault_key().copied(); + let (summary, report) = + profiles::set_nip05(&mut app.vault, npub, nip05, key.as_ref(), &app.settings)?; + app.save_vault()?; + match summary.nip05 { + Some(id) => Ok(format!( + "NIP-05 set to \"{id}\"; accepted by {} relay(s).", + report.succeeded.len() + )), + None => Ok("NIP-05 cleared.".to_string()), + } +} + fn cli_publish_name(args: &[String]) -> Result { let npub = args .get(2) @@ -571,6 +647,7 @@ fn delete_profile_direct(vault: &mut Vault, npub: &str) -> Result Result { public_key: restored.npub.clone(), secret_key: "".to_string(), created_at: restored.created_at, - picture: None, + picture: restored.picture, + nip05: restored.nip05, }; app.vault.profiles.push(stored); if app.vault.active_profile.is_none() { diff --git a/src/profiles.rs b/src/profiles.rs index 35ed677..885ba0c 100644 --- a/src/profiles.rs +++ b/src/profiles.rs @@ -25,6 +25,8 @@ pub struct ProfileSummary { pub is_active: bool, /// Public URL of the profile picture, when one has been set. pub picture: Option, + /// NIP-05 identifier (e.g. `boo@l484.com`), when one has been set. + pub nip05: Option, } /// A secret key revealed after the vault is unlocked, in both the raw hex and @@ -75,6 +77,7 @@ pub fn create_profile( secret_key: stored_secret, created_at, picture: None, + nip05: None, }; let is_active = vault.active_profile.is_none(); @@ -88,7 +91,7 @@ pub fn create_profile( // Best-effort: relay failures here never block profile creation. let relay_urls = relays::enabled_urls(settings); if !relay_urls.is_empty() { - publish_metadata_blocking(&keys, &label, None, relay_urls); + publish_metadata_blocking(&keys, &label, None, None, relay_urls); } Ok(ProfileSummary { @@ -97,6 +100,7 @@ pub fn create_profile( created_at, is_active, picture: None, + nip05: None, }) } @@ -132,6 +136,7 @@ pub fn publish_profile_metadata( &keys, &stored.label, stored.picture.clone(), + stored.nip05.clone(), relay_urls, )) } @@ -159,12 +164,13 @@ pub fn set_profile_picture( let stored = find_profile_mut(vault, npub)?; stored.picture = url; - let (label, npub, created_at, public_key, picture) = ( + let (label, npub, created_at, public_key, picture, nip05) = ( stored.label.clone(), stored.public_key.clone(), stored.created_at, stored.public_key.clone(), stored.picture.clone(), + stored.nip05.clone(), ); drop(stored); let summary = ProfileSummary { @@ -173,6 +179,7 @@ pub fn set_profile_picture( created_at, is_active: vault.active_profile.as_deref() == Some(public_key.as_str()), picture, + nip05, }; let relay_urls = relays::enabled_urls(settings); @@ -189,8 +196,13 @@ pub fn set_profile_picture( } let keys = Keys::new(secret_key); - let report = - publish_metadata_blocking(&keys, &summary.label, summary.picture.clone(), relay_urls); + let report = publish_metadata_blocking( + &keys, + &summary.label, + summary.picture.clone(), + summary.nip05.clone(), + relay_urls, + ); Ok((summary, report)) } @@ -217,11 +229,12 @@ pub fn rename_profile( let stored = find_profile_mut(vault, npub)?; stored.label = trimmed.to_string(); - let (label, created_at, public_key, picture) = ( + let (label, created_at, public_key, picture, nip05) = ( stored.label.clone(), stored.created_at, stored.public_key.clone(), stored.picture.clone(), + stored.nip05.clone(), ); let summary = ProfileSummary { label, @@ -229,6 +242,7 @@ pub fn rename_profile( created_at, is_active: vault.active_profile.as_deref() == Some(public_key.as_str()), picture, + nip05, }; let relay_urls = relays::enabled_urls(settings); @@ -245,11 +259,110 @@ pub fn rename_profile( } let keys = Keys::new(secret_key); - let report = - publish_metadata_blocking(&keys, &summary.label, summary.picture.clone(), relay_urls); + let report = publish_metadata_blocking( + &keys, + &summary.label, + summary.picture.clone(), + summary.nip05.clone(), + relay_urls, + ); Ok((summary, report)) } +/// Store a NIP-05 identifier (e.g. `boo@l484.com`) and immediately publish it +/// as part of the profile's kind 0 metadata. +/// +/// Pass `None` to clear the identifier. Returns the updated summary plus the +/// per-relay publish report. +pub fn set_nip05( + vault: &mut Vault, + npub: &str, + nip05: Option, + key: Option<&VaultKey>, + settings: &Settings, +) -> Result<(ProfileSummary, MetadataPublishReport), AppError> { + let normalised = match &nip05 { + Some(value) => Some(validate_nip05(value)?), + None => None, + }; + + // Resolve and sign before mutating so a locked vault or bad key changes + // nothing on disk. + let secret_hex = resolve_secret_key(vault, npub, key)?; + let secret_key = parse_secret_key(&secret_hex)?; + + let stored = find_profile_mut(vault, npub)?; + stored.nip05 = normalised; + let (label, created_at, public_key, picture, nip05) = ( + stored.label.clone(), + stored.created_at, + stored.public_key.clone(), + stored.picture.clone(), + stored.nip05.clone(), + ); + let summary = ProfileSummary { + label, + npub: public_key.clone(), + created_at, + is_active: vault.active_profile.as_deref() == Some(public_key.as_str()), + picture, + nip05, + }; + + let relay_urls = relays::enabled_urls(settings); + if relay_urls.is_empty() { + // The vault change stands; publishing can be retried later via the + // explicit "publish name" action once a relay is enabled. + return Ok(( + summary, + MetadataPublishReport { + succeeded: Vec::new(), + failed: Vec::new(), + }, + )); + } + + let keys = Keys::new(secret_key); + let report = publish_metadata_blocking( + &keys, + &summary.label, + summary.picture.clone(), + summary.nip05.clone(), + relay_urls, + ); + Ok((summary, report)) +} + +/// Validate a NIP-05 identifier (`@`), returning the +/// lower-cased trimmed form. `_@domain` (the bare-domain form) is allowed. +/// Exposed for the CLI's `.well-known/nostr.json` helper. +pub fn validate_nip05(raw: &str) -> Result { + let trimmed = raw.trim().to_lowercase(); + let (local, domain) = trimmed + .split_once('@') + .ok_or_else(|| AppError::config("A NIP-05 address must look like name@domain.com."))?; + if local.is_empty() || domain.is_empty() || domain.contains('@') { + return Err(AppError::config( + "A NIP-05 address must look like name@domain.com.", + )); + } + if local != "_" + && !local + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') + { + return Err(AppError::config( + "The part before @ may only use letters, numbers, dashes and underscores.", + )); + } + if domain.split('.').any(|label| label.is_empty()) || !domain.contains('.') { + return Err(AppError::config( + "The part after @ must be a domain like example.com.", + )); + } + Ok(trimmed) +} + /// Validate that a picture URL is a well-formed http(s) URL. fn validate_picture_url(url: &str) -> Result<(), AppError> { let parsed = Url::parse(url) @@ -290,6 +403,7 @@ fn publish_metadata_blocking( keys: &Keys, label: &str, picture: Option, + nip05: Option, relay_urls: Vec, ) -> MetadataPublishReport { let keys = keys.clone(); @@ -297,7 +411,9 @@ fn publish_metadata_blocking( std::thread::spawn(move || { tokio::runtime::Runtime::new() .expect("metadata runtime") - .block_on(publish_metadata_async(&keys, &label, picture, relay_urls)) + .block_on(publish_metadata_async( + &keys, &label, picture, nip05, relay_urls, + )) }) .join() .expect("metadata publish thread panicked") @@ -307,6 +423,7 @@ async fn publish_metadata_async( keys: &Keys, label: &str, picture: Option, + nip05: Option, relay_urls: Vec, ) -> MetadataPublishReport { let mut metadata = Metadata::new().name(label).display_name(label); @@ -315,6 +432,9 @@ async fn publish_metadata_async( metadata = metadata.picture(parsed); } } + if let Some(nip05) = &nip05 { + metadata = metadata.nip05(nip05); + } let event = match EventBuilder::new(Kind::Metadata, metadata.as_json()) .sign(keys) .await @@ -432,6 +552,7 @@ fn summary_for(vault: &Vault, profile: &StoredProfile) -> ProfileSummary { created_at: profile.created_at, is_active: vault.active_profile.as_deref() == Some(profile.public_key.as_str()), picture: profile.picture.clone(), + nip05: profile.nip05.clone(), } } @@ -547,6 +668,7 @@ mod tests { secret_key: "00".repeat(32), created_at: 1, picture: None, + nip05: None, }); vault.profiles.push(StoredProfile { label: "Bob".to_string(), @@ -554,6 +676,7 @@ mod tests { secret_key: "11".repeat(32), created_at: 2, picture: None, + nip05: None, }); vault } @@ -907,6 +1030,96 @@ mod tests { assert_eq!(err.kind(), crate::errors::ErrorKind::ProfileNotFound); } + #[test] + fn set_nip05_stores_identifier_and_skips_publish_without_relays() { + let mut vault = Vault::empty(); + let summary = + create_profile(&mut vault, "Boo".to_string(), None, &offline_settings()).unwrap(); + + let (updated, report) = set_nip05( + &mut vault, + &summary.npub, + Some("Boo@L484.com".to_string()), + None, + &offline_settings(), + ) + .expect("setting a NIP-05 must work offline"); + + assert_eq!( + updated.nip05.as_deref(), + Some("boo@l484.com"), + "lower-cased" + ); + assert_eq!( + vault.profiles[0].nip05.as_deref(), + Some("boo@l484.com"), + "vault must remember the identifier" + ); + // No relays enabled: nothing published, but the change still stands. + assert!(report.succeeded.is_empty()); + assert!(report.failed.is_empty()); + + // Clearing the identifier also persists. + let (cleared, _) = + set_nip05(&mut vault, &summary.npub, None, None, &offline_settings()).unwrap(); + assert!(cleared.nip05.is_none()); + assert!(vault.profiles[0].nip05.is_none()); + } + + #[test] + fn set_nip05_rejects_malformed_identifiers() { + let mut vault = Vault::empty(); + let summary = + create_profile(&mut vault, "Boo".to_string(), None, &offline_settings()).unwrap(); + + for bad in [ + "just-a-name", + "@l484.com", + "boo@", + "bo o@l484.com", + "boo@nodot", + "boo@@l484.com", + ] { + let err = set_nip05( + &mut vault, + &summary.npub, + Some(bad.to_string()), + None, + &offline_settings(), + ) + .expect_err("malformed NIP-05 must error"); + assert_eq!(err.kind(), crate::errors::ErrorKind::Config); + } + assert!( + vault.profiles[0].nip05.is_none(), + "nothing stored on failure" + ); + + // The bare-domain form `_@domain` is valid. + set_nip05( + &mut vault, + &summary.npub, + Some("_@l484.com".to_string()), + None, + &offline_settings(), + ) + .expect("bare-domain form must be accepted"); + } + + #[test] + fn set_nip05_missing_profile_errors() { + let mut vault = Vault::empty(); + let err = set_nip05( + &mut vault, + "npub1ghost", + Some("ghost@example.com".to_string()), + None, + &offline_settings(), + ) + .expect_err("missing profile must error"); + assert_eq!(err.kind(), crate::errors::ErrorKind::ProfileNotFound); + } + /// Delete a profile by npub, returning the deleted profile for undo. /// The vault must not be encrypted, or the key must be provided. pub fn delete_profile(vault: &mut Vault, npub: &str) -> Result { @@ -926,6 +1139,7 @@ mod tests { created_at: stored.created_at, is_active: false, picture: stored.picture, + nip05: stored.nip05, }) } } diff --git a/src/vault.rs b/src/vault.rs index 72e1f77..e4b078c 100644 --- a/src/vault.rs +++ b/src/vault.rs @@ -39,6 +39,11 @@ pub struct StoredProfile { /// profiles stored before pictures were introduced. #[serde(default)] pub picture: Option, + /// NIP-05 identifier (e.g. `boo@l484.com`), when one has been set. + /// Absent for profiles stored before NIP-05 was introduced. Published as + /// part of kind 0 metadata so clients show a human handle. + #[serde(default)] + pub nip05: Option, } /// KDF parameters that encrypted a vault. Stored so future key-derivation @@ -457,6 +462,7 @@ mod tests { secret_key: "00ff".to_string(), created_at: 1_700_000_000, picture: None, + nip05: None, } } From 2ad054e6e19521991a166026c87298ebe0a11179 Mon Sep 17 00:00:00 2001 From: Avi Date: Sun, 23 Aug 2026 22:00:13 -0500 Subject: [PATCH 12/51] Checkpoint: NIP-05 identifiers --- CHECKPOINT-encryption.md | 121 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 07730f8..ea8575b 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,123 @@ -# Checkpoint — Responsive backend (concurrent IPC) + rename (2026-08-23) +# Checkpoint — NIP-05 identifiers (2026-08-23) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `045fa47` ("Add NIP-05 identifiers: store, publish, GUI + modal, CLI helpers"). Before it: `f2c30d3` (checkpoint), `cb23a5e` (publish + latency trim), `4d70b25` (concurrent IPC), `d618a5a` (rename), and earlier + rebrand/UI commits listed in the previous checkpoint section below. +- Working tree is **clean** apart from this checkpoint update, which is committed right after. + +## What was completed + +1. **NIP-05 identifiers (2026-08-23, `045fa47`)** — the feature agreed on + 2026-08-22 (see "Next feature" note in the older checkpoint below). A session + that crashed mid-edit had left most of the Rust side written but uncommitted + and non-compiling; this session finished it end-to-end: + - **Backend (Rust):** `nip05: Option` on `StoredProfile` + + `ProfileSummary`; `set_nip05()` validates (`name@domain`, `_@domain` bare + form allowed; lower-cased; rejects malformed input without storing), + stores, and republishes kind 0 metadata with the identifier included; + wired through IPC (`set_nip05` request) and CLI (`set-nip05 + `); all other publish paths (create/rename/picture/publish-name) + now include the stored nip05. + - **GUI:** new **NIP-05** button on every Profiles card opens a modal + (prefilled) with live validation, *Save & publish* and *Remove*; the + handle is displayed on the profile card under the name. Wired: + Electron allowlist, `api.setNip05`, AppProvider, fake backend + tests. + - **CLI helper:** `keynectr nip05-file ` prints the exact + `.well-known/nostr.json` document (names + relays maps, hex pubkey) to + serve on your domain. Verified against the real vault. + - **Docs:** `docs/NIP-05.md` explains both halves (app publish + domain + serving, nginx example, CORS requirement). + - **Bug fix:** `Modal.tsx` stole focus from `autoFocus` inputs one frame + after opening (deferred `container.focus()`) — now it only focuses the + dialog when nothing inside has focus. This also fixes flaky typing in + modals generally. +2. Previous session work (rename with metadata republish, concurrent IPC, + publish latency trim, card-click selection, button wrap, rebrand) — see the + older notes below. + +## Commits added most recently + +- `045fa47` Add NIP-05 identifiers: store, publish, GUI modal, CLI helpers + +## Verification commands run (all green) + +Rust (repo root): + +``` +cargo test # 102 passed; 0 failed (3 new NIP-05 tests) +cargo clippy --all-targets # only 2 pre-existing warnings in src/profiles.rs + # (drop of reference, unused delete_profile) +cargo fmt --check # clean +cargo build --release # success +``` + +Frontend (`frontend/`): + +``` +npm test # 14 files, 84 tests passed (4 new NIP-05 tests) +npm run typecheck # clean +npm run lint # 0 errors +npm run format:check # clean +npm run build # vite build success +npm run electron:build # tsc electron main success +``` + +## How to use / reproduce + +GUI: + +```bash +cd ~/Projects/Nostr_Keynctr/frontend && npm start +``` + +- Profiles screen: **NIP-05** button → type `name@domain.com` → Save & publish. + Remove clears it. The handle shows on the profile card. +- Everything else as before (Edit name, Picture, Publish name, card-click + selection, delete + undo). + +CLI: + +```bash +B=~/Projects/Nostr_Keynctr/target/release/keynectr +$B list # show profiles +$B set-nip05 boo@l484.com # set + publish +$B set-nip05 clear # remove + publish removal +$B nip05-file boo@l484.com # print .well-known/nostr.json JSON +``` + +Domain side (one-time): serve the printed document at +`https:///.well-known/nostr.json` with `Content-Type: application/json` +and `Access-Control-Allow-Origin: *` — details + nginx snippet in +`docs/NIP-05.md`. + +## Notes & next steps + +- The `.well-known/nostr.json` HTTP half cannot be done by the app alone; users + must host the file themselves (`nip05-file` prints it for them). +- If rename/publish latency still feels long, next step would be background + publishing (return immediately, report via notice later). +- Pre-existing clippy warnings in `src/profiles.rs` (drop of a reference, unused + `delete_profile`) are untouched — worth cleaning up in a future pass. +- Nostr has no relay-to-relay sync: names/pictures/nip05 are only visible on + relays they were published to. Keep major relays enabled. +- Clients cache profiles; hard-refresh (Ctrl+Shift+R) after republishing. +- Candidate next features: banner/about fields in the edit UI. +- Binary is `keynectr`; data lives in `~/.local/share/keynectr`. The KDF verifier + string in crypto.rs was deliberately NOT renamed so old encrypted backups stay readable. + The git remote is `https://git.atitlan.io/avi/Nostr_Keynctr`. +- Relay config at time of writing: nos.lol, relay.primal.net, nostr.l484.com + (user's own), damus.io, snort.social, soloco.nl, relay.nostr.band. + +--- + +# Older checkpoint — Responsive backend (concurrent IPC) + rename (2026-08-23) **App renamed to Keynectr (2026-08-23).** A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From b001b3c7b1d2c0b9a624021302a8f7f3c58791ad Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 00:03:12 -0500 Subject: [PATCH 13/51] Trim note publish latency: skip global connection wait, parallel sends, 6s cap --- src/publish.rs | 102 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/src/publish.rs b/src/publish.rs index 5e4f476..23187e6 100644 --- a/src/publish.rs +++ b/src/publish.rs @@ -11,10 +11,9 @@ use crate::relays; use crate::settings::Settings; use crate::vault::Vault; -/// How long to wait for relays to accept a connection attempt. -const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); -/// How long to wait for a single relay to accept an event. -const RELAY_SEND_TIMEOUT: Duration = Duration::from_secs(15); +/// How long to wait for a single relay to accept an event. Relays are sent +/// to in parallel, so this caps the whole publish, not each relay. +const RELAY_SEND_TIMEOUT: Duration = Duration::from_secs(6); /// A relay that rejected a published note. #[derive(Debug, Clone, Serialize)] @@ -185,40 +184,73 @@ async fn publish_with_keys( .map_err(|e| AppError::network(format!("Could not add relay {url}: {e}")))?; } client.connect().await; - client.wait_for_connection(CONNECT_TIMEOUT).await; + // No explicit wait for connections here: `send_event` waits for each relay + // to become writable itself, and the per-send timeout below already bounds + // the whole publish. Waiting for *all* relays first would burn the full + // timeout whenever a single relay is unreachable. - // Send to each relay individually so partial failures are fully reported. - let mut succeeded: Vec = Vec::new(); - let mut failed: Vec = Vec::new(); - for url in &relay_urls { - let relay = match client.relay(url.as_str()).await { - Ok(relay) => relay, - Err(err) => { - let (message, details) = relay_error_message(&err); - failed.push(RelayFailure { - url: url.clone(), - error: message, - details: Some(details), - }); - continue; + // Send to every relay in parallel so one slow or dead relay cannot drag + // the whole publish out to (relays x timeout). + let mut outcomes: Vec>> = + (0..relay_urls.len()).map(|_| None).collect(); + let mut sends = tokio::task::JoinSet::new(); + for (index, url) in relay_urls.iter().cloned().enumerate() { + let client = client.clone(); + let event = event.clone(); + sends.spawn(async move { + match client.relay(url.as_str()).await { + Ok(relay) => { + match tokio::time::timeout(RELAY_SEND_TIMEOUT, relay.send_event(&event)).await { + Ok(Ok(_)) => (index, Ok(url)), + Ok(Err(err)) => { + let (message, details) = relay_error_message(&err); + ( + index, + Err(RelayFailure { + url, + error: message, + details: Some(details), + }), + ) + } + Err(_) => ( + index, + Err(RelayFailure { + url, + error: "The relay did not respond in time.".to_string(), + details: Some( + "Timed out while waiting for the relay to accept the note." + .to_string(), + ), + }), + ), + } + } + Err(err) => { + let (message, details) = relay_error_message(&err); + ( + index, + Err(RelayFailure { + url, + error: message, + details: Some(details), + }), + ) + } } - }; + }); + } + while let Some(joined) = sends.join_next().await { + let (index, outcome) = joined.expect("publish send task panicked"); + outcomes[index] = Some(outcome); + } - match tokio::time::timeout(RELAY_SEND_TIMEOUT, relay.send_event(&event)).await { - Ok(Ok(_)) => succeeded.push(url.clone()), - Ok(Err(err)) => { - let (message, details) = relay_error_message(&err); - failed.push(RelayFailure { - url: url.clone(), - error: message, - details: Some(details), - }); - } - Err(_) => failed.push(RelayFailure { - url: url.clone(), - error: "The relay did not respond in time.".to_string(), - details: Some("Timed out while waiting for the relay to accept the note.".into()), - }), + let mut succeeded = Vec::new(); + let mut failed = Vec::new(); + for outcome in outcomes.into_iter().flatten() { + match outcome { + Ok(url) => succeeded.push(url), + Err(failure) => failed.push(failure), } } From 8f637618bcc312e8adcb47dda74e48b6a7e05905 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 00:04:01 -0500 Subject: [PATCH 14/51] Checkpoint: note publish latency trim --- CHECKPOINT-encryption.md | 92 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index ea8575b..40adc19 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,94 @@ -# Checkpoint — NIP-05 identifiers (2026-08-23) +# Checkpoint — Note publish latency trim (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `b001b3c` ("Trim note publish latency: skip global + connection wait, parallel sends, 6s cap"). Before it: `2ad054e` (checkpoint), + `045fa47` (NIP-05), `cb23a5e` (metadata latency trim), `4d70b25` (concurrent IPC). +- Working tree is **clean** apart from this checkpoint update, which is committed right after. + +## What was completed + +1. **Diagnosed a slow/failing publish (2026-08-24, `b001b3c`).** User reported a + 10+ s note publish with two relay failures (`relay.snort.social` "Not + connected", `relay.nostr.band` "did not respond in time"). Live probing of the + user's relays showed: + - The note itself **did land** on nos.lol, nostr.l484.com, relay.primal.net, + soloco.nl (why phoenix.social showed it). Kind-0 metadata ("Boo" + photo) + is on those relays too — Primal/Iris visibility is third-party indexing lag + or their differing relay sets, not lost data. + - `wss://relay.damus.io` is **down** (HTTP 503) and `wss://relay.nostr.band` + **times out on the WebSocket handshake** right now. + - Root cause of the stall: the *note* path still had the pattern removed from + the metadata path in `cb23a5e` — a global `wait_for_connection(10s)` that + burned its full timeout whenever any single relay was unreachable, then + sequential per-relay sends (15s timeout each). +2. **Fix (`b001b3c`)** — `src/publish.rs` now mirrors `src/profiles.rs`: + - No global connection wait (`send_event` waits for each relay's writable + state itself). + - Sends run in parallel via `tokio::task::JoinSet`; per-relay timeout cut + 15s → 6s, so worst case ≈ 6 s regardless of how many relays are dead. + - Failure reporting unchanged (per-relay concise error + technical detail). + +## Commits added most recently + +- `b001b3c` Trim note publish latency: skip global connection wait, parallel sends, 6s cap + +## Verification commands run (all green) + +Rust (repo root): + +``` +cargo test # 102 passed; 0 failed +cargo clippy --all-targets # only pre-existing warnings in src/profiles.rs +cargo fmt --check # clean +cargo build --release # success +``` + +Frontend (`frontend/`) — untouched by this change but re-run: + +``` +npm test # 14 files, 84 tests passed +npm run typecheck # clean +npm run lint # 0 errors +npm run format:check # clean +npm run build # vite build success +npm run electron:build # tsc electron main success +``` + +## How to use / reproduce + +GUI: `cd ~/Projects/Nostr_Keynctr/frontend && npm start`, compose a note, +publish. Expect ~1–6 s even when some relays are dead; failures are listed +per-relay under the composer. + +CLI: + +```bash +B=~/Projects/Nostr_Keynctr/target/release/keynectr +$B publish "hello" # text note as active profile +``` + +## Notes & next steps + +- **Relay health (checked 2026-08-24):** damus.io returning 503; nostr.band WS + handshake times out. Consider disabling nostr.band until it recovers, and + adding healthy high-traffic relays (e.g. offchain.pub, nostr.mom) so clients + whose default sets don't include nos.lol/primal.net can see your notes. +- Profile/photo on Primal: metadata + notes are on relay.primal.net; if Primal + still shows nothing later, re-check via primal.net (their crawler indexes with + delay). Iris/Yakihonne need the note on relays *they* read. +- Pre-existing clippy warnings in `src/profiles.rs` remain (drop of reference, + unused `delete_profile`). +- Candidate next features: banner/about fields in the edit UI. + +--- + +# Older checkpoint — NIP-05 identifiers (2026-08-23) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From 55a49b442baa850a02928fde03873a7ca1e596bf Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 00:13:58 -0500 Subject: [PATCH 15/51] Feed: 'My notes' scope with per-profile dropdown filter --- frontend/src/lib/api.ts | 3 +- frontend/src/screens/FeedScreen.tsx | 81 ++++++++++++++++++++++++--- frontend/src/state/AppProvider.tsx | 6 +- frontend/src/styles.css | 4 ++ frontend/src/test/FeedScreen.test.tsx | 51 +++++++++++++++++ frontend/src/test/fakeBackend.ts | 19 +++++++ src/feed.rs | 37 ++++++++++++ src/ipc.rs | 28 +++++++-- 8 files changed, 212 insertions(+), 17 deletions(-) diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index f5f78e4..ba56bd5 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -69,10 +69,11 @@ export const api = { nip05, }), publishNote: (content: string) => call('publish_note', { content }), - feedGet: (limit?: number, contactsOnly = false) => + feedGet: (limit?: number, contactsOnly = false, authorNpub?: string) => call('feed_get', { ...(limit ? { limit } : {}), ...(contactsOnly ? { contacts_only: true } : {}), + ...(authorNpub ? { author: authorNpub } : {}), }), relayAdd: (url: string) => call('relay_add', { url }), relayRemove: (url: string) => call('relay_remove', { url }), diff --git a/frontend/src/screens/FeedScreen.tsx b/frontend/src/screens/FeedScreen.tsx index 4dff080..816ba4d 100644 --- a/frontend/src/screens/FeedScreen.tsx +++ b/frontend/src/screens/FeedScreen.tsx @@ -15,21 +15,33 @@ interface FeedScreenProps { onNavigate: (screen: Screen) => void; } -type FeedScope = 'everyone' | 'contacts'; +type FeedScope = 'everyone' | 'contacts' | 'profile'; export function FeedScreen({ onNavigate }: FeedScreenProps) { const { state, feedGet } = useApp(); const [items, setItems] = useState([]); const [scope, setScope] = useState('everyone'); + const [pickedProfile, setPickedProfile] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const enabledRelays = (state?.settings.relays ?? []).filter((r) => r.enabled); const shorten = state?.settings.shorten_npub ?? true; + const profiles = state?.profiles ?? []; const hasActiveProfile = state?.active_profile != null; + const hasProfiles = profiles.length > 0; + // Default the author filter to the active profile until one is picked. + const selectedProfileNpub = + pickedProfile ?? state?.active_profile?.npub ?? profiles[0]?.npub ?? null; const contactsScope = scope === 'contacts'; - const effectiveScope: FeedScope = contactsScope && !hasActiveProfile ? 'everyone' : scope; + const profileScope = scope === 'profile'; + const effectiveScope: FeedScope = + contactsScope && !hasActiveProfile + ? 'everyone' + : profileScope && !hasProfiles + ? 'everyone' + : scope; const load = useCallback( async (background: boolean) => { @@ -40,7 +52,13 @@ export function FeedScreen({ onNavigate }: FeedScreenProps) { setLoading(true); } try { - setItems(await feedGet(undefined, effectiveScope === 'contacts')); + setItems( + await feedGet( + undefined, + effectiveScope === 'contacts', + effectiveScope === 'profile' ? (selectedProfileNpub ?? undefined) : undefined, + ), + ); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { @@ -48,7 +66,7 @@ export function FeedScreen({ onNavigate }: FeedScreenProps) { setRefreshing(false); } }, - [feedGet, effectiveScope], + [feedGet, effectiveScope, selectedProfileNpub], ); useEffect(() => { @@ -122,7 +140,37 @@ export function FeedScreen({ onNavigate }: FeedScreenProps) { My contacts + + {effectiveScope === 'profile' && ( + + )} + + + + {updateError && ( + + {updateError} + + )} + + {checking && } + + {applyMessage && ( + +
    + {applyMessage.map((line) => ( +
  • {line}
  • + ))} +
+
+ )} + + {updateReport && !checking && ( + <> + {updateReport.advisories.length > 0 ? ( + +
    + {updateReport.advisories.map((advisory) => ( +
  • + + {advisory.severity} + {' '} + {advisory.package} + {advisory.title ? ` — ${advisory.title}` : ''} +
  • + ))} +
+
+ ) : ( + + )} + + {updateReport.outdated_npm.length > 0 && ( +
+ JavaScript packages +
    + {updateReport.outdated_npm.map((pkg) => ( +
  • + {pkg.name} {pkg.current} →{' '} + {pkg.available} +
  • + ))} +
+
+ )} + + {updateReport.outdated_cargo.length > 0 && ( +
+ Rust crates +
    + {updateReport.outdated_cargo.map((crateName) => ( +
  • + {crateName.name} {crateName.current} →{' '} + {crateName.available} +
  • + ))} +
+
+ )} + + {updateReport.notes.map((note) => ( +

+ {note} +

+ ))} + + {updateReport.advisories.length === 0 && + updateReport.outdated_npm.length === 0 && + updateReport.outdated_cargo.length === 0 && ( + Everything is up to date. + )} + + )} + + +

Advanced

diff --git a/frontend/src/state/AppProvider.tsx b/frontend/src/state/AppProvider.tsx index 421cd26..0b5541d 100644 --- a/frontend/src/state/AppProvider.tsx +++ b/frontend/src/state/AppProvider.tsx @@ -21,6 +21,8 @@ import type { Settings, SignerStatus, Theme, + UpdateApplyReport, + UpdateCheckReport, UploadedImage, } from '../lib/types'; @@ -51,6 +53,8 @@ interface AppContextValue { relayRemove: (url: string) => Promise; relaySetEnabled: (url: string, enabled: boolean) => Promise; relayTest: (url: string) => Promise; + updateCheck: () => Promise; + updateApply: () => Promise; updateSettings: ( patch: Partial>, ) => Promise; @@ -190,6 +194,8 @@ export function AppProvider({ children }: { children: ReactNode }) { [applySettings], ); const relayTest = useCallback((url: string) => api.relayTest(url), []); + const updateCheck = useCallback(() => api.updateCheck(), []); + const updateApply = useCallback(() => api.updateApply(), []); const updateSettings = useCallback( async (patch: Partial>) => applySettings(await api.settingsUpdate(patch)), @@ -252,6 +258,8 @@ export function AppProvider({ children }: { children: ReactNode }) { relayRemove, relaySetEnabled, relayTest, + updateCheck, + updateApply, updateSettings, backupNow, setVaultPassword, @@ -296,6 +304,8 @@ export function AppProvider({ children }: { children: ReactNode }) { relayRemove, relaySetEnabled, relayTest, + updateCheck, + updateApply, updateSettings, backupNow, setVaultPassword, diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 6029927..d8541ee 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -256,6 +256,21 @@ a { height: 34px; } +.update-list { + margin: 6px 0 0; + padding-left: 18px; + display: grid; + gap: 4px; + font-size: 14px; +} + +.update-summary { + margin: 6px 0 0; + padding-left: 18px; + display: grid; + gap: 4px; +} + /* ------------------------------------------------------------------------- Sidebar ------------------------------------------------------------------------- */ diff --git a/frontend/src/test/SettingsScreen.test.tsx b/frontend/src/test/SettingsScreen.test.tsx index 928f5aa..bc5a4e6 100644 --- a/frontend/src/test/SettingsScreen.test.tsx +++ b/frontend/src/test/SettingsScreen.test.tsx @@ -64,4 +64,64 @@ describe('SettingsScreen', () => { expect(await screen.findByText(/Keynectr v/)).toBeInTheDocument(); expect(screen.getByText('Rust (nostr-sdk)')).toBeInTheDocument(); }); + + it('checks for updates and lists security advisories first', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await user.click(await screen.findByRole('button', { name: /Check for updates/i })); + + expect(await screen.findByText('1 security issue(s) found')).toBeInTheDocument(); + expect(screen.getByText(/minimist/)).toBeInTheDocument(); + expect(screen.getByText('JavaScript packages')).toBeInTheDocument(); + expect(screen.getByText('Rust crates')).toBeInTheDocument(); + const checkRequest = backend.requests.find((r) => r.method === 'update_check'); + expect(checkRequest).toBeDefined(); + }); + + it('reports an up-to-date app when the scan finds nothing', async () => { + const backend = createFakeBackend(); + backend.updateReport = { + outdated_npm: [], + advisories: [], + outdated_cargo: [], + notes: [], + }; + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await user.click(await screen.findByRole('button', { name: /Check for updates/i })); + + expect(await screen.findByText('Everything is up to date.')).toBeInTheDocument(); + }); + + it('installs updates and asks for a rebuild + restart', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await user.click(await screen.findByRole('button', { name: /Install updates/i })); + + expect(await screen.findByText(/Rebuild and restart the app/)).toBeInTheDocument(); + expect(screen.getByText('JavaScript security fixes applied')).toBeInTheDocument(); + const applyRequest = backend.requests.find((r) => r.method === 'update_apply'); + expect(applyRequest).toBeDefined(); + }); + + it('surfaces update failures as errors', async () => { + const backend = createFakeBackend(); + backend.nextErrors.update_check = { message: 'npm was not found on this computer.' }; + installFakeBackend(backend); + const user = userEvent.setup(); + renderWithApp(); + + await user.click(await screen.findByRole('button', { name: /Check for updates/i })); + + expect(await screen.findByText('Update problem')).toBeInTheDocument(); + expect(screen.getByText('npm was not found on this computer.')).toBeInTheDocument(); + }); }); diff --git a/frontend/src/test/fakeBackend.ts b/frontend/src/test/fakeBackend.ts index 5850434..0819a58 100644 --- a/frontend/src/test/fakeBackend.ts +++ b/frontend/src/test/fakeBackend.ts @@ -7,6 +7,8 @@ import type { RelayTestResult, Settings, SignerStatus, + UpdateApplyReport, + UpdateCheckReport, } from '../lib/types'; import { ALICE, makePublishReport, makeRelayTest, makeSignerStatus, makeState } from './apiMock'; @@ -45,6 +47,10 @@ export interface FakeBackend { contactFeedItems: FeedItem[]; /** Notes returned by `feed_get` with an `author` filter. */ profileFeedItems: FeedItem[]; + /** Report returned by `update_check`. */ + updateReport: UpdateCheckReport; + /** Result returned by `update_apply`. */ + updateApplyResult: UpdateApplyReport; } export function createFakeBackend(initial?: AppState): FakeBackend { @@ -139,6 +145,23 @@ export function createFakeBackend(initial?: AppState): FakeBackend { relays: ['wss://relay.damus.io'], }, ], + updateReport: { + outdated_npm: [{ name: 'vite', current: '4.0.0', available: '5.1.0' }], + advisories: [ + { + package: 'minimist', + severity: 'high', + title: 'Prototype Pollution', + }, + ], + outdated_cargo: [{ name: 'serde', current: '1.0.200', available: '1.0.219' }], + notes: [], + }, + updateApplyResult: { + applied: ['JavaScript security fixes applied', 'Rust crates updated in Cargo.lock'], + failed: [], + restart_required: true, + }, }; async function dispatch(method: string, params: Record): Promise { @@ -361,6 +384,12 @@ export function createFakeBackend(initial?: AppState): FakeBackend { return result; } + case 'update_check': + return backend.updateReport; + + case 'update_apply': + return backend.updateApplyResult; + case 'settings_update': { const nextSettings: Settings = { ...state.settings, ...params }; backend.setState({ ...state, settings: nextSettings }); diff --git a/src/ipc.rs b/src/ipc.rs index ccb4ca8..e1ec407 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -13,6 +13,7 @@ use crate::publish; use crate::relays; use crate::settings::Theme; use crate::signer::Signer; +use crate::updates; /// How long to wait for a relay connection test. const RELAY_TEST_TIMEOUT: Duration = Duration::from_secs(8); @@ -90,6 +91,11 @@ pub enum Request { RelayTest { url: String, }, + /// Scan both dependency sets (npm + cargo) for available updates and + /// security advisories, without changing anything. + UpdateCheck, + /// Install compatible dependency updates (security fixes first). + UpdateApply, SettingsGet, SettingsUpdate { theme: Option, @@ -312,6 +318,16 @@ async fn run( let result = relays::test_connection(&url, RELAY_TEST_TIMEOUT).await?; Ok(json!(result)) } + Request::UpdateCheck => { + // Long-running package-manager scan; never touches shared state. + let report = updates::check().await?; + Ok(json!(report)) + } + Request::UpdateApply => { + // Installs updates on disk; a rebuild + restart picks them up. + let report = updates::apply().await?; + Ok(json!(report)) + } Request::FeedGet { limit, contacts_only, diff --git a/src/lib.rs b/src/lib.rs index b489c08..7ca39ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod publish; pub mod relays; pub mod settings; pub mod signer; +pub mod updates; pub mod uploads; pub mod vault; diff --git a/src/updates.rs b/src/updates.rs new file mode 100644 index 0000000..5ab6c8b --- /dev/null +++ b/src/updates.rs @@ -0,0 +1,429 @@ +//! Dependency update scanning and installation for both halves of the app. +//! +//! The app runs from a source checkout, so "updating" means refreshing the +//! JavaScript dependencies (`frontend/`) and the Rust crates (`Cargo.lock`) +//! to their newest compatible versions. Security advisories from `npm audit` +//! are surfaced first; `cargo update` picks up semver-compatible patches for +//! the Rust side. +//! +//! Nothing here touches secret material: only fixed, read-mostly package +//! manager commands are run in the project directories. + +use std::path::{Path, PathBuf}; +use std::time::Duration; + +use serde::Serialize; +use tokio::process::Command; + +use crate::errors::{AppError, ErrorKind}; + +/// Upper bound for a single package-manager command. Installs can be slow on +/// cold caches, but a hung command must still be reaped eventually. +const COMMAND_TIMEOUT: Duration = Duration::from_secs(150); + +/// One dependency with a newer compatible version available. +#[derive(Debug, Clone, Serialize, PartialEq, Eq)] +pub struct PackageUpdate { + pub name: String, + pub current: String, + pub available: String, +} + +/// A known security advisory affecting an npm dependency. +#[derive(Debug, Clone, Serialize, PartialEq, Eq)] +pub struct SecurityAdvisory { + pub package: String, + /// npm severity label: critical / high / moderate / low / info. + pub severity: String, + /// Short advisory title, when npm reported one. + pub title: Option, +} + +/// Everything the scan found, ready to show in one screen. +#[derive(Debug, Clone, Serialize)] +pub struct UpdateCheckReport { + pub outdated_npm: Vec, + pub advisories: Vec, + pub outdated_cargo: Vec, + /// Hints about optional tooling or skipped parts of the scan. + pub notes: Vec, +} + +/// Result of applying updates. +#[derive(Debug, Clone, Serialize)] +pub struct UpdateApplyReport { + pub applied: Vec, + pub failed: Vec, + /// The running binary/bundle cannot hot-swap; the app must be rebuilt + /// and restarted to load the refreshed dependencies. + pub restart_required: bool, +} + +/// The directory this backend was compiled from (the project root). +fn source_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) +} + +/// The frontend source directory (where `package.json` lives). +fn frontend_dir() -> PathBuf { + source_dir().join("frontend") +} + +/// Verify the app is running from its source checkout before shelling out. +fn require_source_checkout() -> Result<(), AppError> { + let manifest = source_dir().join("Cargo.toml"); + let package = frontend_dir().join("package.json"); + if manifest.exists() && package.exists() { + return Ok(()); + } + Err(AppError::simple( + ErrorKind::Config, + "Updates need the app's source folder, which was not found next to the running program.", + )) +} + +/// Run a command with a timeout, capturing stdout/stderr separately. +async fn run(dir: &Path, program: &str, args: &[&str]) -> Result { + let mut command = Command::new(program); + command.current_dir(dir).args(args).kill_on_drop(true); + let future = command.output(); + match tokio::time::timeout(COMMAND_TIMEOUT, future).await { + Ok(Ok(output)) => Ok(output), + Ok(Err(err)) => { + let hint = if err.kind() == std::io::ErrorKind::NotFound { + format!("'{program}' was not found on this computer.") + } else { + format!("Could not run '{program}': {err}") + }; + Err(AppError::simple(ErrorKind::Internal, hint)) + } + Err(_) => Err(AppError::with_details( + ErrorKind::Network, + format!("'{program}' took too long and was stopped."), + "The command exceeded its time limit while updating dependencies.", + )), + } +} + +/// Decode command output as UTF-8, falling back to lossy text. +fn text(bytes: &[u8]) -> String { + String::from_utf8_lossy(bytes).into_owned() +} + +/// Parse `npm outdated --json`. +/// +/// npm exits non-zero when anything is outdated, so exit status is ignored: +/// the JSON body is the data. Unparseable or missing output means no data. +fn parse_npm_outdated(json: &str) -> Vec { + let Ok(value) = serde_json::from_str::(json) else { + return Vec::new(); + }; + let Some(map) = value.as_object() else { + return Vec::new(); + }; + let mut updates = Vec::new(); + for (name, info) in map { + let get = |key: &str| { + info.get(key) + .and_then(|v| v.as_str()) + .unwrap_or_default() + .to_string() + }; + let current = get("current"); + let available = if get("latest").is_empty() { + get("wanted") + } else { + get("latest") + }; + updates.push(PackageUpdate { + name: name.clone(), + current, + available, + }); + } + updates.sort_by(|a, b| a.name.cmp(&b.name)); + updates +} + +/// Parse `npm audit --json` into a flat advisory list. +fn parse_npm_audit(json: &str) -> Vec { + let Ok(value) = serde_json::from_str::(json) else { + return Vec::new(); + }; + let Some(vulnerabilities) = value.get("vulnerabilities").and_then(|v| v.as_object()) else { + return Vec::new(); + }; + let mut advisories = Vec::new(); + for (name, info) in vulnerabilities { + let severity = info + .get("severity") + .and_then(|v| v.as_str()) + .unwrap_or("unknown") + .to_string(); + // `via` holds either plain title strings or full advisory objects. + let title = info.get("via").and_then(|v| v.as_array()).and_then(|list| { + list.iter().find_map(|entry| match entry { + serde_json::Value::String(text) => Some(text.clone()), + serde_json::Value::Object(object) => object + .get("title") + .and_then(|t| t.as_str()) + .map(|t| t.to_string()), + _ => None, + }) + }); + advisories.push(SecurityAdvisory { + package: name.clone(), + severity, + title, + }); + } + const ORDER: [&str; 5] = ["critical", "high", "moderate", "low", "info"]; + advisories.sort_by(|a, b| { + let rank = |s: &str| { + ORDER + .iter() + .position(|known| known.eq_ignore_ascii_case(s)) + .unwrap_or(ORDER.len()) + }; + rank(&a.severity) + .cmp(&rank(&b.severity)) + .then_with(|| a.package.cmp(&b.package)) + }); + advisories +} + +/// Parse `cargo update --dry-run` status lines into crate updates. +fn parse_cargo_updates(text: &str) -> Vec { + let mut updates = Vec::new(); + for line in text.lines() { + let tokens: Vec<&str> = line.split_whitespace().collect(); + // e.g. "Updating serde v1.0.200 -> v1.0.219" (+ optional suffixes). + if tokens.len() >= 5 && tokens[3] == "->" { + let verb = tokens.first().copied().unwrap_or_default(); + if matches!(verb, "Updating" | "Downgrading") { + updates.push(PackageUpdate { + name: tokens[1].to_string(), + current: tokens[2].trim_start_matches('v').to_string(), + available: tokens[4].trim_start_matches('v').to_string(), + }); + } + } + } + updates.sort_by(|a, b| a.name.cmp(&b.name)); + updates +} + +/// Whether the optional RustSec scanner is installed. +async fn cargo_audit_available() -> bool { + run(Path::new("."), "cargo", &["audit", "--version"]) + .await + .map(|output| output.status.success()) + .unwrap_or(false) +} + +/// Scan both dependency sets without changing anything. +pub async fn check() -> Result { + require_source_checkout()?; + let root = source_dir(); + let frontend = frontend_dir(); + + let outdated = run(&frontend, "npm", &["outdated", "--json"]).await?; + let outdated_npm = parse_npm_outdated(&text(&outdated.stdout)); + + let audit = run(&frontend, "npm", &["audit", "--json"]).await?; + let advisories = parse_npm_audit(&text(&audit.stdout)); + + let dry_run = run(&root, "cargo", &["update", "--dry-run"]).await?; + let cargo_text = format!("{}{}", text(&dry_run.stdout), text(&dry_run.stderr)); + let outdated_cargo = parse_cargo_updates(&cargo_text); + + let mut notes = Vec::new(); + if !cargo_audit_available().await { + notes.push( + "Rust advisory scan unavailable: install cargo-audit (cargo install cargo-audit) \ + to also check Rust crates against the RustSec database." + .to_string(), + ); + } + + Ok(UpdateCheckReport { + outdated_npm, + advisories, + outdated_cargo, + notes, + }) +} + +/// Install compatible updates: npm security fixes, then general bumps, then +/// the Rust lockfile. +pub async fn apply() -> Result { + require_source_checkout()?; + let root = source_dir(); + let frontend = frontend_dir(); + + let steps: [(&Path, &str, &[&str], &str); 3] = [ + ( + &frontend, + "npm", + &["audit", "fix"], + "JavaScript security fixes applied", + ), + ( + &frontend, + "npm", + &["update"], + "JavaScript packages updated to their newest compatible versions", + ), + ( + &root, + "cargo", + &["update"], + "Rust crates updated in Cargo.lock", + ), + ]; + + let mut applied = Vec::new(); + let mut failed = Vec::new(); + for (dir, program, args, summary) in steps { + match run(dir, program, args).await { + Ok(output) => { + if output.status.success() { + applied.push(summary.to_string()); + } else { + let stderr = text(&output.stderr); + let tail: String = stderr + .lines() + .filter(|line| !line.trim().is_empty()) + .rev() + .take(3) + .collect::>() + .join(" | "); + failed.push(format!("{program} {args:?}: {tail}")); + } + } + Err(err) => failed.push(format!("{program} {args:?}: {}", err.message())), + } + } + + if applied.is_empty() && !failed.is_empty() { + return Err(AppError::with_details( + ErrorKind::Internal, + "No updates could be installed.", + failed.join("\n"), + )); + } + + let restart_required = !applied.is_empty(); + Ok(UpdateApplyReport { + applied, + failed, + restart_required, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_npm_outdated_entries() { + let json = r#"{ + "left-pad": { "current": "1.0.0", "wanted": "1.3.0", "latest": "1.3.0" }, + "react": { "current": "18.2.0", "wanted": "18.2.0", "latest": "19.0.0" } + }"#; + let updates = parse_npm_outdated(json); + assert_eq!(updates.len(), 2); + assert_eq!( + updates[0], + PackageUpdate { + name: "left-pad".to_string(), + current: "1.0.0".to_string(), + available: "1.3.0".to_string(), + } + ); + assert_eq!(updates[1].name, "react"); + assert_eq!(updates[1].available, "19.0.0"); + } + + #[test] + fn empty_or_garbage_outdated_output_yields_nothing() { + assert!(parse_npm_outdated("").is_empty()); + assert!(parse_npm_outdated("not json at all").is_empty()); + assert!(parse_npm_outdated("{}").is_empty()); + } + + #[test] + fn parses_npm_audit_with_title_objects_and_strings() { + let json = r#"{ + "vulnerabilities": { + "minimist": { + "severity": "high", + "via": [{ "title": "Prototype Pollution", "url": "https://example.com/a" }] + }, + "tar": { "severity": "low", "via": ["Regular Expression Denial of Service"] } + } + }"#; + let advisories = parse_npm_audit(json); + assert_eq!(advisories.len(), 2); + assert_eq!(advisories[0].package, "minimist"); + assert_eq!(advisories[0].severity, "high"); + assert_eq!(advisories[0].title.as_deref(), Some("Prototype Pollution")); + assert_eq!(advisories[1].package, "tar"); + assert_eq!( + advisories[1].title.as_deref(), + Some("Regular Expression Denial of Service") + ); + } + + #[test] + fn sorts_advisories_by_severity_then_name() { + let json = r#"{ + "vulnerabilities": { + "zeta": { "severity": "critical", "via": [] }, + "alpha": { "severity": "high", "via": [] }, + "beta": { "severity": "critical", "via": [] } + } + }"#; + let advisories = parse_npm_audit(json); + let order: Vec<&str> = advisories.iter().map(|a| a.package.as_str()).collect(); + assert_eq!(order, vec!["beta", "zeta", "alpha"]); + } + + #[test] + fn empty_audit_yields_no_advisories() { + assert!(parse_npm_audit("").is_empty()); + assert!(parse_npm_audit("{}").is_empty()); + assert!(parse_npm_audit("{\"vulnerabilities\":{}}").is_empty()); + } + + #[test] + fn parses_cargo_update_lines() { + let text = "\ + Updating crates.io index\n\ + Locking 2 packages to their latest compatible version\n\ + Updating serde v1.0.200 -> v1.0.219\n\ + Downgrading leftpad v2.0.0 -> v1.9.0 (features: [\"std\"])\n\ + Adding newcrate v0.1.0\n"; + let updates = parse_cargo_updates(text); + assert_eq!(updates.len(), 2); + assert_eq!(updates[0].name, "leftpad"); + assert_eq!(updates[0].current, "2.0.0"); + assert_eq!(updates[0].available, "1.9.0"); + assert_eq!(updates[1].name, "serde"); + assert_eq!(updates[1].available, "1.0.219"); + } + + #[test] + fn unchanged_lockfile_yields_no_updates() { + assert!(parse_cargo_updates("Unchanged").is_empty()); + assert!(parse_cargo_updates("").is_empty()); + } + + #[test] + fn source_layout_holds_for_this_repo() { + // The compile-time paths must exist in the real checkout, otherwise + // every runtime check would fail with a misleading error. + assert!(source_dir().join("Cargo.toml").exists()); + assert!(frontend_dir().join("package.json").exists()); + } +} From a1915bb1c1464db080df850b8ea424cdbad90057 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 09:55:00 -0500 Subject: [PATCH 18/51] Checkpoint: updates card --- CHECKPOINT-encryption.md | 101 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index c2fa125..c42a2a7 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,103 @@ -# Checkpoint — Feed "My notes" profile filter (2026-08-24) +# Checkpoint — Updates card (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `8bce428` ("Updates card: scan npm/cargo deps, surface + security advisories, install compatible updates"). Before it: `55a49b4` + (feed profile filter), `3dfd15f` (checkpoint), `b001b3c` (publish latency). +- Working tree is **clean** apart from this checkpoint update, which is committed right after. + +## What was completed + +1. **Updates card in Settings (2026-08-24, `8bce428`).** New **Updates** section + on the Settings screen with two buttons: + - **Check for updates** — scans without changing anything: + `npm outdated --json` + `npm audit --json` (frontend) and + `cargo update --dry-run` (Rust). Known security advisories are listed + first with severity badges (critical/high → danger, moderate → warning); + outdated npm packages and Rust crates are listed with current → available + versions. + - **Install updates** — applies compatible (non-breaking) updates: + `npm audit fix`, then `npm update`, then `cargo update`. Reports what was + applied and reminds that a **rebuild + restart** is required to load them. + - First real scan found 20 advisories (2 critical: tar, vitest; several high + in the Electron build chain), 18 outdated npm packages, 42 outdated crates. +2. **Backend:** new `src/updates.rs` module (parsers unit-tested; commands run + with timeouts and never touch secret material). IPC methods `update_check` + and `update_apply`, dispatched outside the state lock so slow installs never + block other requests. Electron allowlist extended; backend round-trip cap + raised 120 s → 300 s to fit cold-cache installs. +3. Optional hardening noted in-app: installing `cargo-audit` adds RustSec + advisory scanning for the Rust side (currently not installed on this machine; + the app says so under Notes when absent). + +## Commits added most recently + +- `8bce428` Updates card: scan npm/cargo deps, surface security advisories, install compatible updates + +## Verification commands run (all green) + +Rust (repo root): + +``` +cargo test # 112 passed; 0 failed (8 new updates tests) +cargo clippy --all-targets # only pre-existing warnings in src/profiles.rs +cargo fmt --check # clean +cargo build --release # success +# end-to-end: echo '{"id":1,"method":"update_check"}' | ./target/release/keynectr serve +# returned real advisories/outdated lists for this machine +``` + +Frontend (`frontend/`): + +``` +npm test # 14 files, 91 tests passed (4 new Settings tests) +npm run typecheck # clean +npm run lint # 0 errors +npm run format:check # clean +npm run build # vite build success +npm run electron:build # tsc electron main success +``` + +## How to use / reproduce + +GUI: `cd ~/Projects/Nostr_Keynctr/frontend && npm start` → **Settings → +Updates → Check for updates**, review the list (security first), then +**Install updates**. Afterwards rebuild and relaunch: + +```bash +cd ~/Projects/Nostr_Keynctr && cargo build --release +cd frontend && npm run build && npm start +``` + +CLI equivalent: + +```bash +cd frontend && npm audit fix && npm update +cd .. && cargo update +``` + +## Notes & next steps + +- `Install updates` only applies semver-compatible bumps. Major version jumps + shown by the check (e.g. electron 33 → 43, eslint 9 → 10) still need manual + attention — deliberate, to avoid breaking the app silently. +- Consider running the first real "Install updates" soon: 2 critical npm + advisories were found (tar, vitest dev chain) plus high-severity Electron + packaging tools. +- Install `cargo-audit` (`cargo install cargo-audit`) for RustSec coverage; the + card picks it up automatically once present. +- Possible follow-ups: CLI `updates` subcommand; auto-check on startup behind a + setting. +- Relay health (earlier today): damus.io 503, nostr.band WS handshake timing out. + +--- + +# Older checkpoint — Feed "My notes" profile filter (2026-08-24) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From c11ab9f735098e8582c4e389dcfc04d80e1bd796 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 11:00:18 -0500 Subject: [PATCH 19/51] Security: major dependency upgrades clearing all npm advisories; show per-advisory fix paths --- Cargo.lock | 185 +- frontend/package-lock.json | 5530 ++++++--------------- frontend/package.json | 10 +- frontend/src/lib/types.ts | 2 + frontend/src/screens/SettingsScreen.tsx | 1 + frontend/src/test/SettingsScreen.test.tsx | 8 +- frontend/src/test/fakeBackend.ts | 7 + src/updates.rs | 69 + 8 files changed, 1722 insertions(+), 4090 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1bc0190..c501d9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "bitcoin-consensus-encoding" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" dependencies = [ "bitcoin-internals", "hex-conservative 1.2.0", @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.0" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "shlex", @@ -326,14 +326,14 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] name = "either" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "errno" @@ -347,9 +347,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "form_urlencoded" @@ -362,9 +362,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -376,9 +376,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -386,33 +386,33 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -543,9 +543,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -557,9 +557,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -570,9 +570,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -584,16 +584,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -604,15 +605,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -674,9 +675,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -710,9 +711,9 @@ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "lock_api" @@ -725,9 +726,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lru" @@ -760,9 +761,9 @@ checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe" [[package]] name = "negentropy" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d" +checksum = "81c353b400a5503efdcf398f11a83fb7aa84f59f5d76fc4bf5bbc1e4f5366caa" [[package]] name = "nostr" @@ -811,7 +812,7 @@ dependencies = [ "atomic-destructor", "lru", "negentropy 0.3.1", - "negentropy 0.5.0", + "negentropy 0.5.1", "nostr", "nostr-database", "tokio", @@ -925,9 +926,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -1053,9 +1054,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -1128,9 +1129,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -1217,7 +1218,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1318,9 +1319,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -1349,11 +1350,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.20", ] [[package]] @@ -1369,20 +1370,20 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -1428,7 +1429,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1515,7 +1516,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.19", + "thiserror 2.0.20", "utf-8", ] @@ -1583,9 +1584,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -1615,9 +1616,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -1628,9 +1629,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -1638,9 +1639,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1648,9 +1649,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", @@ -1661,18 +1662,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -1801,9 +1802,9 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "yoke" @@ -1830,18 +1831,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.55" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.55" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", @@ -1877,9 +1878,9 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -1888,9 +1889,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -1899,13 +1900,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.4", ] [[package]] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index adf8c5c..6e127ff 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,17 +20,17 @@ "@types/node": "^26.1.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "@vitejs/plugin-react": "^4.3.4", - "electron": "^33.2.0", - "electron-builder": "^25.1.8", + "@vitejs/plugin-react": "^6.1.0", + "electron": "^43.4.1", + "electron-builder": "^26.15.3", "eslint": "^9.15.0", "eslint-plugin-react-hooks": "^5.0.0", "jsdom": "^25.0.1", "prettier": "^3.3.3", "typescript": "^5.6.3", "typescript-eslint": "^8.15.0", - "vite": "^5.4.11", - "vitest": "^2.1.8" + "vite": "^8.2.2", + "vitest": "^4.1.11" } }, "node_modules/@adobe/css-tools": { @@ -76,143 +76,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", @@ -223,78 +86,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", - "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", - "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/runtime": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", @@ -305,54 +96,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -468,22 +211,14 @@ "node": ">=18" } }, - "node_modules/@develar/schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "node_modules/@electron-internal/extract-zip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.5.tgz", + "integrity": "sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==", "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=22.12.0" } }, "node_modules/@electron/asar": { @@ -535,26 +270,56 @@ "node": "*" } }, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" + } + }, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.1.0.tgz", + "integrity": "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==", "dev": true, "license": "MIT", "dependencies": { "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", "progress": "^2.0.3", - "semver": "^6.2.0", + "semver": "^7.6.3", "sumchecker": "^3.0.1" }, "engines": { - "node": ">=12" + "node": ">=22.12.0" }, "optionalDependencies": { - "global-agent": "^3.0.0" + "undici": "^7.24.4" } }, "node_modules/@electron/notarize": { @@ -588,33 +353,10 @@ "node": ">=10" } }, - "node_modules/@electron/notarize/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/osx-sign": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", - "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -633,21 +375,6 @@ "node": ">=12.0.0" } }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", @@ -661,117 +388,35 @@ "url": "https://github.com/sponsors/gjtorikian/" } }, - "node_modules/@electron/osx-sign/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/rebuild": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz", - "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.2.0.tgz", + "integrity": "sha512-RKL/O+jGoXJMxrx/5771y1n0xTKmFuOYGO3gMmwypBM6rsH0kou0mswwdXA2JrhIkE4xyC7v9vGk0n6NPzgOxQ==", "dev": true, "license": "MIT", "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", "debug": "^4.1.1", - "detect-libc": "^2.0.1", - "fs-extra": "^10.0.0", - "got": "^11.7.0", - "node-abi": "^3.45.0", - "node-api-version": "^0.2.0", - "node-gyp": "^9.0.0", - "ora": "^5.1.0", - "read-binary-file-arch": "^1.0.6", - "semver": "^7.3.5", - "tar": "^6.0.5", - "yargs": "^17.0.1" + "node-abi": "^4.2.0", + "node-api-version": "^0.2.1", + "node-gyp": "^12.2.0", + "read-binary-file-arch": "^1.0.6" }, "bin": { "electron-rebuild": "lib/cli.js" }, "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/@electron/rebuild/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@electron/rebuild/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/rebuild/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/rebuild/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "node": ">=22.12.0" } }, "node_modules/@electron/universal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", - "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", "dev": true, "license": "MIT", "dependencies": { - "@electron/asar": "^3.2.7", + "@electron/asar": "^3.3.1", "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.3.1", "dir-compare": "^4.2.0", @@ -815,19 +460,6 @@ "node": ">=14.14" } }, - "node_modules/@electron/universal/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/@electron/universal/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", @@ -844,405 +476,43 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=14.14" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "aix" - ], + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1451,13 +721,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true, - "license": "MIT" - }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1524,139 +787,17 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dev": true, "license": "ISC", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "minipass": "^7.0.4" }, "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" + "node": ">=18.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -1666,17 +807,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -1732,110 +862,88 @@ "node": ">=10" } }, - "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "node_modules/@noble/hashes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.3.0.tgz", + "integrity": "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.146.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz", + "integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.9.4.tgz", + "integrity": "sha512-GjzePcT9Iw8NzeOPf73iNS9xM+TBhd/FilAfP+RQGkTMQJTVWtytN3JHJACCjf/ABNau5S7mS3g+DcuxmRgYEg==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, "engines": { "node": ">=14" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", - "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz", + "integrity": "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "tslib": "^2.8.1", + "webcrypto-core": "^1.9.2" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", + "integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==", "cpu": [ "arm" ], @@ -1844,12 +952,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", - "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", + "integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==", "cpu": [ "arm64" ], @@ -1858,12 +969,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", - "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", + "integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==", "cpu": [ "arm64" ], @@ -1872,12 +986,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", - "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", + "integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==", "cpu": [ "x64" ], @@ -1886,26 +1003,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", - "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", - "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", + "integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==", "cpu": [ "x64" ], @@ -1914,12 +1020,15 @@ "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", - "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", + "integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==", "cpu": [ "arm" ], @@ -1928,26 +1037,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", - "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", - "cpu": [ - "arm" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", - "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", + "integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==", "cpu": [ "arm64" ], @@ -1956,12 +1054,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", - "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", + "integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==", "cpu": [ "arm64" ], @@ -1970,40 +1071,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", - "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", - "cpu": [ - "loong64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", - "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", - "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", + "integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==", "cpu": [ "ppc64" ], @@ -2012,54 +1088,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", - "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", - "cpu": [ - "ppc64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", - "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", - "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", - "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", + "integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==", "cpu": [ "s390x" ], @@ -2068,12 +1105,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", - "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", + "integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==", "cpu": [ "x64" ], @@ -2082,12 +1122,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", - "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", + "integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==", "cpu": [ "x64" ], @@ -2096,26 +1139,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", - "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", - "cpu": [ - "x64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", - "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", + "integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==", "cpu": [ "arm64" ], @@ -2124,12 +1156,15 @@ "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", - "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", + "integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==", "cpu": [ "arm64" ], @@ -2138,26 +1173,15 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", - "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", - "cpu": [ - "ia32" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", - "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", + "integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==", "cpu": [ "x64" ], @@ -2166,21 +1190,17 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", - "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", - "cpu": [ - "x64" ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, "node_modules/@sindresorhus/is": { "version": "4.6.0", @@ -2195,6 +1215,13 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -2284,9 +1311,9 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "version": "14.6.6", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.6.tgz", + "integrity": "sha512-Jbs9FpkkIDw8FgSc6kOVsOv8JuuqGAL7J4X1oot77JxAoDlkNn2GRkd0aYRVuQ+pVQAiHWVkE4rX/dkF5fBiCw==", "dev": true, "license": "MIT", "engines": { @@ -2297,16 +1324,6 @@ "@testing-library/dom": ">=7.21.4" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz", - "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -2314,51 +1331,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -2372,6 +1344,17 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -2382,6 +1365,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -2431,27 +1421,15 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", + "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~8.3.0" } }, - "node_modules/@types/plist": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", - "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" - } - }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -2490,37 +1468,18 @@ "@types/node": "*" } }, - "node_modules/@types/verror": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", - "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz", - "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/type-utils": "8.66.0", - "@typescript-eslint/utils": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2533,7 +1492,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.66.0", + "@typescript-eslint/parser": "^8.67.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -2549,16 +1508,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz", - "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3" }, "engines": { @@ -2574,14 +1533,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz", - "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.66.0", - "@typescript-eslint/types": "^8.66.0", + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", "debug": "^4.4.3" }, "engines": { @@ -2596,14 +1555,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz", - "integrity": "sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0" + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2614,9 +1573,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz", - "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", "dev": true, "license": "MIT", "engines": { @@ -2631,15 +1590,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz", - "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/utils": "8.66.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2656,9 +1615,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz", - "integrity": "sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", "dev": true, "license": "MIT", "engines": { @@ -2670,16 +1629,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz", - "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.66.0", - "@typescript-eslint/tsconfig-utils": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2697,30 +1656,17 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz", - "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0" + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2735,13 +1681,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz", - "integrity": "sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/types": "8.67.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2766,59 +1712,70 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.0.tgz", + "integrity": "sha512-qd2BzUBehkov86WFhg0JkEFEYyCLG9uPCe6qWTY/kRlss9OvJrOF2UbIWT7p+8IzZHkEu0DNGHc4HSv+JdDLsw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.28.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.145.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } } }, "node_modules/@vitest/expect": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", - "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", - "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.9", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" + "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -2830,98 +1787,92 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", - "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.9", - "pathe": "^1.1.2" + "@vitest/utils": "4.1.11", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.9", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", - "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", - "dependencies": { - "tinyspy": "^3.0.2" - }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", - "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.9", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" + "@vitest/pretty-format": "4.1.11", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@xmldom/xmldom": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", - "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.6" + "node": ">=10.0.0" } }, - "node_modules/7zip-bin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", - "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", - "dev": true, - "license": "MIT" - }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "dev": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, "node_modules/acorn": { "version": "8.18.0", @@ -2956,33 +1907,6 @@ "node": ">= 14" } }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -3000,16 +1924,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3036,93 +1950,184 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/app-builder-bin": { - "version": "5.0.0-alpha.10", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", - "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==", - "dev": true, - "license": "MIT" - }, "node_modules/app-builder-lib": { - "version": "25.1.8", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-25.1.8.tgz", - "integrity": "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.3.tgz", + "integrity": "sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow==", "dev": true, "license": "MIT", "dependencies": { - "@develar/schema-utils": "~2.6.5", + "@electron/asar": "3.4.1", + "@electron/fuses": "^1.8.0", + "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", - "@electron/osx-sign": "1.3.1", - "@electron/rebuild": "3.6.1", - "@electron/universal": "2.0.1", + "@electron/osx-sign": "1.3.3", + "@electron/rebuild": "^4.0.4", + "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", + "@noble/hashes": "^2.2.0", + "@peculiar/webcrypto": "^1.7.1", "@types/fs-extra": "9.0.13", + "ajv": "^8.18.0", + "asn1js": "^3.0.10", "async-exit-hook": "^2.0.1", - "bluebird-lst": "^1.0.9", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chromium-pickle-js": "^0.2.0", - "config-file-ts": "0.2.8-rc1", + "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", - "electron-publish": "25.1.7", - "form-data": "^4.0.0", + "electron-publish": "26.15.3", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", - "is-ci": "^3.0.0", "isbinaryfile": "^5.0.0", + "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", - "minimatch": "^10.0.0", + "minimatch": "^10.2.5", + "pkijs": "^3.4.0", + "plist": "3.1.0", + "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", - "sanitize-filename": "^1.6.3", - "semver": "^7.3.8", - "tar": "^6.1.12", - "temp-file": "^3.4.0" + "semver": "~7.7.3", + "tar": "^7.5.7", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0", + "unzipper": "^0.12.3", + "which": "^5.0.0" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { - "dmg-builder": "25.1.8", - "electron-builder-squirrel-windows": "25.1.8" + "dmg-builder": "26.15.3", + "electron-builder-squirrel-windows": "26.15.3" } }, - "node_modules/app-builder-lib/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/app-builder-lib/node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=6 <7 || >=8" } }, - "node_modules/app-builder-lib/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/app-builder-lib/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/app-builder-lib/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/app-builder-lib/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/app-builder-lib/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/app-builder-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/app-builder-lib/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -3133,114 +2138,29 @@ } }, "node_modules/app-builder-lib/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, - "node_modules/aproba": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", - "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", - "dev": true, - "license": "ISC" - }, - "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", + "node_modules/app-builder-lib/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/argparse": { @@ -3260,15 +2180,19 @@ "dequal": "^2.0.3" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", "dev": true, - "license": "MIT", - "optional": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, "engines": { - "node": ">=0.8" + "node": ">=12.0.0" } }, "node_modules/assertion-error": { @@ -3281,17 +2205,6 @@ "node": ">=12" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -3326,6 +2239,13 @@ "node": ">= 4.0.0" } }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -3357,31 +2277,6 @@ ], "license": "MIT" }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.12", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", - "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3389,16 +2284,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bluebird-lst": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", - "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bluebird": "^3.5.5" - } - }, "node_modules/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -3421,75 +2306,6 @@ "node": "20 || >=22" } }, - "node_modules/browserslist": { - "version": "4.28.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", - "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.44", - "caniuse-lite": "^1.0.30001806", - "electron-to-chromium": "^1.5.393", - "node-releases": "^2.0.51", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3498,34 +2314,35 @@ "license": "MIT" }, "node_modules/builder-util": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-25.1.7.tgz", - "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.15.3.tgz", + "integrity": "sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==", "dev": true, "license": "MIT", "dependencies": { "@types/debug": "^4.1.6", - "7zip-bin": "~5.2.0", - "app-builder-bin": "5.0.0-alpha.10", - "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.2.10", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", - "is-ci": "^3.0.0", "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", - "temp-file": "^3.4.0" + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/builder-util-runtime": { - "version": "9.2.10", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", - "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz", + "integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==", "dev": true, "license": "MIT", "dependencies": { @@ -3536,143 +2353,14 @@ "node": ">=12.0.0" } }, - "node_modules/builder-util/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/builder-util/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/builder-util/node_modules/universalify": { + "node_modules/bytestreamjs": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cacache": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cacache/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" + "node": ">=6.0.0" } }, "node_modules/cacheable-lookup": { @@ -3728,40 +2416,12 @@ "node": ">=6" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001806", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", - "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { "node": ">=18" } @@ -3783,24 +2443,14 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/chromium-pickle-js": { @@ -3811,9 +2461,9 @@ "license": "MIT" }, "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -3826,60 +2476,6 @@ "node": ">=8" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3895,16 +2491,6 @@ "node": ">=12" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -3938,16 +2524,6 @@ "dev": true, "license": "MIT" }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3981,23 +2557,6 @@ "node": ">=0.10.0" } }, - "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4005,89 +2564,6 @@ "dev": true, "license": "MIT" }, - "node_modules/config-file-ts": { - "version": "0.2.8-rc1", - "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", - "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^10.3.12", - "typescript": "^5.4.3" - } - }, - "node_modules/config-file-ts/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/config-file-ts/node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/config-file-ts/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/config-file-ts/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/config-file-ts/node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, - "license": "ISC" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4096,51 +2572,20 @@ "license": "MIT" }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, "license": "MIT" }, - "node_modules/crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "buffer": "^5.1.0" - } - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -4260,16 +2705,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4277,19 +2712,6 @@ "dev": true, "license": "MIT" }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -4348,13 +2770,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "license": "MIT" - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -4426,86 +2841,16 @@ } }, "node_modules/dmg-builder": { - "version": "25.1.8", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.1.8.tgz", - "integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.3.tgz", + "integrity": "sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "25.1.8", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", "fs-extra": "^10.1.0", - "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" - }, - "optionalDependencies": { - "dmg-license": "^1.0.11" - } - }, - "node_modules/dmg-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dmg-builder/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/dmg-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/dmg-license": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "@types/plist": "^3.0.1", - "@types/verror": "^1.10.3", - "ajv": "^6.10.0", - "crc": "^3.8.0", - "iconv-corefoundation": "^1.1.7", - "plist": "^3.0.4", - "smart-buffer": "^4.0.2", - "verror": "^1.10.0" - }, - "bin": { - "dmg-license": "bin/dmg-license.js" - }, - "engines": { - "node": ">=8" } }, "node_modules/dom-accessibility-api": { @@ -4559,12 +2904,15 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } }, "node_modules/ejs": { "version": "3.1.10", @@ -4583,38 +2931,38 @@ } }, "node_modules/electron": { - "version": "33.4.11", - "resolved": "https://registry.npmjs.org/electron/-/electron-33.4.11.tgz", - "integrity": "sha512-xmdAs5QWRkInC7TpXGNvzo/7exojubk+72jn1oJL7keNeIlw7xNglf8TGtJtkR4rWC5FJq0oXiIXPS9BcK2Irg==", + "version": "43.4.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-43.4.1.tgz", + "integrity": "sha512-5b+EuiwkgG5iRcsEL34rimgRpkYp15SsfZOa0pC5kXs0Tb82TH4n95rpQzTZa7yRCbA7tm0WoEbuBL6NaAhAcA==", "dev": true, - "hasInstallScript": true, "license": "MIT", "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", - "extract-zip": "^2.0.1" + "@electron-internal/extract-zip": "^1.0.1", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0" }, "bin": { - "electron": "cli.js" + "electron": "cli.js", + "install-electron": "install.js" }, "engines": { - "node": ">= 12.20.55" + "node": ">= 22.12.0" } }, "node_modules/electron-builder": { - "version": "25.1.8", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.8.tgz", - "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.3.tgz", + "integrity": "sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "25.1.8", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", - "dmg-builder": "25.1.8", + "ci-info": "^4.2.0", + "dmg-builder": "26.15.3", "fs-extra": "^10.1.0", - "is-ci": "^3.0.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" @@ -4628,173 +2976,110 @@ } }, "node_modules/electron-builder-squirrel-windows": { - "version": "25.1.8", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz", - "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.3.tgz", + "integrity": "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "app-builder-lib": "25.1.8", - "archiver": "^5.3.1", - "builder-util": "25.1.7", - "fs-extra": "^10.1.0" - } - }, - "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-builder/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "electron-winstaller": "5.4.0" } }, "node_modules/electron-publish": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-25.1.7.tgz", - "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.3.tgz", + "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", "dev": true, "license": "MIT", "dependencies": { "@types/fs-extra": "^9.0.11", - "builder-util": "25.1.7", - "builder-util-runtime": "9.2.10", + "aws4": "^1.13.2", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", + "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, - "node_modules/electron-publish/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" }, "engines": { - "node": ">=12" + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" } }, - "node_modules/electron-publish/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, - "node_modules/electron-publish/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", + "peer": true, "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.399", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.399.tgz", - "integrity": "sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==", - "dev": true, - "license": "ISC" - }, "node_modules/electron/node_modules/@types/node": { - "version": "20.19.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", - "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.18.0" } }, "node_modules/electron/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, @@ -4805,17 +3090,6 @@ "dev": true, "license": "MIT" }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -4840,13 +3114,16 @@ } }, "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/err-code": { @@ -4877,9 +3154,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, @@ -4920,45 +3197,6 @@ "license": "MIT", "optional": true }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4986,6 +3224,7 @@ "version": "9.39.5", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { @@ -5207,38 +3446,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "optional": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5260,15 +3467,22 @@ "dev": true, "license": "MIT" }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "node_modules/fast-uri": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz", + "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/fdir": { "version": "6.5.0", @@ -5379,36 +3593,6 @@ "dev": true, "license": "ISC" }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", @@ -5426,40 +3610,19 @@ "node": ">= 6" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" + "node": ">=12" } }, "node_modules/fs.realpath": { @@ -5494,37 +3657,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -5675,20 +3807,6 @@ "node": ">=10.0" } }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -5819,13 +3937,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "license": "ISC" - }, "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -5852,26 +3963,6 @@ "node": ">=10" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hosted-git-info/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -5934,34 +4025,6 @@ "node": ">= 14" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - }, - "engines": { - "node": "^8.11.2 || >=10" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -5975,27 +4038,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6043,13 +4085,6 @@ "node": ">=8" } }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true, - "license": "ISC" - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -6069,29 +4104,6 @@ "dev": true, "license": "ISC" }, - "node_modules/ip-address": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", - "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -6125,23 +4137,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -6149,26 +4144,12 @@ "dev": true, "license": "MIT" }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/isbinaryfile": { "version": "5.0.7", @@ -6190,22 +4171,6 @@ "dev": true, "license": "ISC" }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jake": { "version": "10.9.4", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", @@ -6224,6 +4189,16 @@ "node": ">=10" } }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6294,19 +4269,6 @@ } } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -6350,11 +4312,14 @@ } }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "dev": true, "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -6376,56 +4341,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6440,6 +4355,267 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6463,38 +4639,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6502,31 +4646,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -6539,13 +4658,6 @@ "loose-envify": "cli.js" } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -6557,13 +4669,16 @@ } }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "license": "ISC", "dependencies": { - "yallist": "^3.0.2" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/lz-string": { @@ -6586,86 +4701,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dev": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -6726,16 +4761,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -6783,127 +4808,40 @@ } }, "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", - "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, "license": "BlueOak-1.0.0", - "dependencies": { - "minipass": "^3.0.0" - }, "engines": { - "node": ">= 8" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">= 8" + "node": ">= 18" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, "bin": { "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/ms": { @@ -6914,9 +4852,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.17", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", - "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -6939,50 +4877,19 @@ "dev": true, "license": "MIT" }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/node-abi": { - "version": "3.94.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz", - "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==", + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.33.0.tgz", + "integrity": "sha512-vLBWCKb+7LWsX+TbfzWOkw0W81m377tyx3hOweBTjO43CXZnRGS1/JPWs20fr0PgZyDXk6ROYrylsEycK8raDA==", "dev": true, "license": "MIT", "dependencies": { - "semver": "^7.3.5" + "semver": "^7.6.3" }, "engines": { - "node": ">=10" + "node": ">=22.12.0" } }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/node-api-version": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", @@ -6993,93 +4900,98 @@ "semver": "^7.3.5" } }, - "node_modules/node-api-version/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-gyp": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", - "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-releases": { - "version": "2.0.51", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", - "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "node_modules/node-gyp/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=6" } }, - "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/undici": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "dev": true, "license": "ISC", "dependencies": { - "abbrev": "^1.0.0" + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/normalize-url": { @@ -7095,23 +5007,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/nwsapi": { "version": "2.2.24", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz", @@ -7130,6 +5025,20 @@ "node": ">= 0.4" } }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7140,22 +5049,6 @@ "wrappy": "1" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -7174,30 +5067,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -7240,29 +5109,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7319,57 +5165,13 @@ "node": ">=8" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, "node_modules/pe-library": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", @@ -7385,13 +5187,6 @@ "url": "https://github.com/sponsors/jet2jet" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7400,9 +5195,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -7412,14 +5207,45 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/plist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz", - "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, "license": "MIT", "dependencies": { - "@xmldom/xmldom": "^0.9.10", + "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" }, @@ -7428,9 +5254,9 @@ } }, "node_modules/postcss": { - "version": "8.5.25", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", - "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "dev": true, "funding": [ { @@ -7448,7 +5274,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -7456,6 +5282,36 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7510,13 +5366,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", @@ -7528,13 +5393,6 @@ "node": ">=0.4.0" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true, - "license": "ISC" - }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", @@ -7549,6 +5407,18 @@ "node": ">=10" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -7570,6 +5440,26 @@ "node": ">=6" } }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz", + "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -7615,16 +5505,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-binary-file-arch": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", @@ -7639,62 +5519,19 @@ } }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "node_modules/redent": { @@ -7721,6 +5558,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resedit": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", @@ -7769,20 +5616,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -7794,20 +5627,18 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, "node_modules/roarr": { @@ -7829,50 +5660,38 @@ "node": ">=8.0" } }, - "node_modules/rollup": { - "version": "4.62.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", - "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "node_modules/rolldown": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz", + "integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.9" + "@oxc-project/types": "=0.146.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.62.4", - "@rollup/rollup-android-arm64": "4.62.4", - "@rollup/rollup-darwin-arm64": "4.62.4", - "@rollup/rollup-darwin-x64": "4.62.4", - "@rollup/rollup-freebsd-arm64": "4.62.4", - "@rollup/rollup-freebsd-x64": "4.62.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", - "@rollup/rollup-linux-arm-musleabihf": "4.62.4", - "@rollup/rollup-linux-arm64-gnu": "4.62.4", - "@rollup/rollup-linux-arm64-musl": "4.62.4", - "@rollup/rollup-linux-loong64-gnu": "4.62.4", - "@rollup/rollup-linux-loong64-musl": "4.62.4", - "@rollup/rollup-linux-ppc64-gnu": "4.62.4", - "@rollup/rollup-linux-ppc64-musl": "4.62.4", - "@rollup/rollup-linux-riscv64-gnu": "4.62.4", - "@rollup/rollup-linux-riscv64-musl": "4.62.4", - "@rollup/rollup-linux-s390x-gnu": "4.62.4", - "@rollup/rollup-linux-x64-gnu": "4.62.4", - "@rollup/rollup-linux-x64-musl": "4.62.4", - "@rollup/rollup-openbsd-x64": "4.62.4", - "@rollup/rollup-openharmony-arm64": "4.62.4", - "@rollup/rollup-win32-arm64-msvc": "4.62.4", - "@rollup/rollup-win32-ia32-msvc": "4.62.4", - "@rollup/rollup-win32-x64-gnu": "4.62.4", - "@rollup/rollup-win32-x64-msvc": "4.62.4", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm-eabi": "1.2.5", + "@rolldown/binding-android-arm64": "1.2.5", + "@rolldown/binding-darwin-arm64": "1.2.5", + "@rolldown/binding-darwin-x64": "1.2.5", + "@rolldown/binding-freebsd-x64": "1.2.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", + "@rolldown/binding-linux-arm64-gnu": "1.2.5", + "@rolldown/binding-linux-arm64-musl": "1.2.5", + "@rolldown/binding-linux-ppc64-gnu": "1.2.5", + "@rolldown/binding-linux-s390x-gnu": "1.2.5", + "@rolldown/binding-linux-x64-gnu": "1.2.5", + "@rolldown/binding-linux-x64-musl": "1.2.5", + "@rolldown/binding-openharmony-arm64": "1.2.5", + "@rolldown/binding-win32-arm64-msvc": "1.2.5", + "@rolldown/binding-win32-x64-msvc": "1.2.5" } }, "node_modules/rrweb-cssom": { @@ -7883,24 +5702,10 @@ "license": "MIT" }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT" }, "node_modules/safer-buffer": { @@ -7953,13 +5758,16 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-compare": { @@ -7987,13 +5795,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8044,89 +5845,6 @@ "node": ">=10" } }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", - "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ip-address": "^10.1.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8166,19 +5884,6 @@ "license": "BSD-3-Clause", "optional": true }, - "node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -8197,20 +5902,20 @@ } }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, "node_modules/string-width": { @@ -8228,22 +5933,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -8257,20 +5946,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -8331,59 +6006,47 @@ "license": "MIT" }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" }, "engines": { - "node": ">=6" + "node": ">=6.0.0" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -8395,42 +6058,24 @@ "fs-extra": "^10.0.0" } }, - "node_modules/temp-file/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" + "semver": "^5.5.0" } }, - "node_modules/temp-file/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/temp-file/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, "node_modules/tinybench": { @@ -8441,11 +6086,14 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { "version": "0.2.17", @@ -8464,30 +6112,10 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { @@ -8583,6 +6211,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8625,16 +6260,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.66.0.tgz", - "integrity": "sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.66.0", - "@typescript-eslint/parser": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/utils": "8.66.0" + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8648,6 +6283,17 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", @@ -8655,71 +6301,43 @@ "dev": true, "license": "MIT" }, - "node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "node_modules/unzipper": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.5.tgz", + "integrity": "sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "11.3.1", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" + } + }, + "node_modules/unzipper/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "engines": { + "node": ">=14.14" } }, "node_modules/uri-js": { @@ -8746,38 +6364,24 @@ "dev": true, "license": "MIT" }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -8786,23 +6390,33 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "less": { + "@vitejs/devtools": { "optional": true }, - "lightningcss": { + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { "optional": true }, "sass": { @@ -8819,85 +6433,89 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, - "node_modules/vite-node": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", - "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/vitest": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", - "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.9", - "@vitest/mocker": "2.1.9", - "@vitest/pretty-format": "^2.1.9", - "@vitest/runner": "2.1.9", - "@vitest/snapshot": "2.1.9", - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.9", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.9", - "@vitest/ui": "2.1.9", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, + "@opentelemetry/api": { + "optional": true + }, "@types/node": { "optional": true }, - "@vitest/browser": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { @@ -8908,6 +6526,9 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, @@ -8924,14 +6545,18 @@ "node": ">=18" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "node_modules/webcrypto-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", + "integrity": "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==", "dev": true, "license": "MIT", "dependencies": { - "defaults": "^1.0.3" + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" } }, "node_modules/webidl-conversions": { @@ -9015,16 +6640,6 @@ "node": ">=8" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -9053,25 +6668,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -9080,9 +6676,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", - "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "dev": true, "license": "MIT", "engines": { @@ -9139,9 +6735,9 @@ } }, "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, "license": "ISC" }, @@ -9174,17 +6770,6 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -9197,45 +6782,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } } } } diff --git a/frontend/package.json b/frontend/package.json index 52f1075..7002d7b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -32,17 +32,17 @@ "@types/node": "^26.1.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "@vitejs/plugin-react": "^4.3.4", - "electron": "^33.2.0", - "electron-builder": "^25.1.8", + "@vitejs/plugin-react": "^6.1.0", + "electron": "^43.4.1", + "electron-builder": "^26.15.3", "eslint": "^9.15.0", "eslint-plugin-react-hooks": "^5.0.0", "jsdom": "^25.0.1", "prettier": "^3.3.3", "typescript": "^5.6.3", "typescript-eslint": "^8.15.0", - "vite": "^5.4.11", - "vitest": "^2.1.8" + "vite": "^8.2.2", + "vitest": "^4.1.11" }, "build": { "appId": "dev.nostfeedmanager.desktop", diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 2dba643..d0b3576 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -107,6 +107,8 @@ export interface SecurityAdvisory { /** npm severity label: critical / high / moderate / low / info. */ severity: string; title: string | null; + /** What is needed to clear it; null when a compatible fix exists. */ + fix: string | null; } /** Result of scanning both dependency sets for updates. */ diff --git a/frontend/src/screens/SettingsScreen.tsx b/frontend/src/screens/SettingsScreen.tsx index 421bf70..95c4578 100644 --- a/frontend/src/screens/SettingsScreen.tsx +++ b/frontend/src/screens/SettingsScreen.tsx @@ -258,6 +258,7 @@ export function SettingsScreen() { {' '} {advisory.package} {advisory.title ? ` — ${advisory.title}` : ''} + {advisory.fix && {advisory.fix}} ))} diff --git a/frontend/src/test/SettingsScreen.test.tsx b/frontend/src/test/SettingsScreen.test.tsx index bc5a4e6..e88e83d 100644 --- a/frontend/src/test/SettingsScreen.test.tsx +++ b/frontend/src/test/SettingsScreen.test.tsx @@ -73,7 +73,13 @@ describe('SettingsScreen', () => { await user.click(await screen.findByRole('button', { name: /Check for updates/i })); - expect(await screen.findByText('1 security issue(s) found')).toBeInTheDocument(); + expect(await screen.findByText('2 security issue(s) found')).toBeInTheDocument(); + expect(screen.getByText(/minimist/)).toBeInTheDocument(); + // Advisories needing a major upgrade explain themselves instead of + // silently surviving an install. + expect( + screen.getByText('Needs electron@43.4.1, a major upgrade — not auto-installed.'), + ).toBeInTheDocument(); expect(screen.getByText(/minimist/)).toBeInTheDocument(); expect(screen.getByText('JavaScript packages')).toBeInTheDocument(); expect(screen.getByText('Rust crates')).toBeInTheDocument(); diff --git a/frontend/src/test/fakeBackend.ts b/frontend/src/test/fakeBackend.ts index 0819a58..cd5d0d7 100644 --- a/frontend/src/test/fakeBackend.ts +++ b/frontend/src/test/fakeBackend.ts @@ -152,6 +152,13 @@ export function createFakeBackend(initial?: AppState): FakeBackend { package: 'minimist', severity: 'high', title: 'Prototype Pollution', + fix: null, + }, + { + package: 'electron', + severity: 'critical', + title: 'ASAR Integrity Bypass', + fix: 'Needs electron@43.4.1, a major upgrade — not auto-installed.', }, ], outdated_cargo: [{ name: 'serde', current: '1.0.200', available: '1.0.219' }], diff --git a/src/updates.rs b/src/updates.rs index 5ab6c8b..ef75d9b 100644 --- a/src/updates.rs +++ b/src/updates.rs @@ -37,6 +37,9 @@ pub struct SecurityAdvisory { pub severity: String, /// Short advisory title, when npm reported one. pub title: Option, + /// What npm says is needed to clear it. `None` means a compatible fix + /// exists that "Install updates" can apply. + pub fix: Option, } /// Everything the scan found, ready to show in one screen. @@ -171,10 +174,37 @@ fn parse_npm_audit(json: &str) -> Vec { _ => None, }) }); + let fix = match info.get("fixAvailable") { + // A compatible fix exists; "Install updates" handles it. + Some(serde_json::Value::Bool(true)) | None => None, + Some(serde_json::Value::Bool(false)) => { + Some("No fix has been published yet.".to_string()) + } + Some(details @ serde_json::Value::Object(_)) => { + let name = details + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or("a dependency"); + let version = details + .get("version") + .and_then(|v| v.as_str()) + .unwrap_or("latest"); + let major = details + .get("isSemVerMajor") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + Some(format!( + "Needs {name}@{version}{} — not auto-installed.", + if major { ", a major upgrade" } else { "" } + )) + } + Some(_) => None, + }; advisories.push(SecurityAdvisory { package: name.clone(), severity, title, + fix, }); } const ORDER: [&str; 5] = ["critical", "high", "moderate", "low", "info"]; @@ -375,6 +405,45 @@ mod tests { ); } + #[test] + fn advisory_fix_paths_are_classified() { + let json = r#"{ + "vulnerabilities": { + "auto": { "severity": "low", "via": [], "fixAvailable": true }, + "stuck": { "severity": "high", "via": [], "fixAvailable": false }, + "electron": { + "severity": "critical", "via": [], + "fixAvailable": { "name": "electron", "version": "43.4.1", "isSemVerMajor": true } + }, + "minor": { + "severity": "moderate", "via": [], + "fixAvailable": { "name": "left-pad", "version": "1.3.0", "isSemVerMajor": false } + } + } + }"#; + let advisories = parse_npm_audit(json); + let fix_of = |name: &str| { + advisories + .iter() + .find(|a| a.package == name) + .and_then(|a| a.fix.clone()) + }; + // Compatible fixes need no hint: Install updates clears them. + assert_eq!(fix_of("auto"), None); + assert_eq!( + fix_of("stuck").as_deref(), + Some("No fix has been published yet.") + ); + assert_eq!( + fix_of("electron").as_deref(), + Some("Needs electron@43.4.1, a major upgrade — not auto-installed.") + ); + assert_eq!( + fix_of("minor").as_deref(), + Some("Needs left-pad@1.3.0 — not auto-installed.") + ); + } + #[test] fn sorts_advisories_by_severity_then_name() { let json = r#"{ From 48efaad0d92c50f4dd3c08a341f90a465a68b7d7 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 11:01:18 -0500 Subject: [PATCH 20/51] Checkpoint: security dependency upgrades --- CHECKPOINT-encryption.md | 85 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index c42a2a7..0407a61 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,87 @@ -# Checkpoint — Updates card (2026-08-24) +# Checkpoint — Security dependency upgrades (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `c11ab9f` ("Security: major dependency upgrades clearing + all npm advisories; show per-advisory fix paths"). Before it: `8bce428` + (updates card), `a1915bb` (checkpoint), `55a49b4` (feed filter), `b001b3c` + (publish latency). +- Working tree is **clean** apart from this checkpoint update, which is committed right after. + +## What was completed + +1. **All npm security advisories cleared (2026-08-24, `c11ab9f`).** The user + ran *Install updates*, but 20 advisories remained — `npm audit fix` only + applies semver-compatible fixes, and npm's own `fixAvailable` data showed + every remaining issue needed one of four **major** upgrades. All applied and + verified: + - `vite` ^5.4 → **^8.2.2** (fixes vite path traversal, esbuild dev-server) + - `vitest` ^2.1 → **^4.1.11** (fixes critical vitest/@vitest/mocker/vite-node) + - `electron` ^33.2 → **^43.4.1** (fixes ~30 runtime CVEs incl. ASAR bypass) + - `electron-builder` ^25.1 → **^26.15.3** (fixes critical tar chain, + node-gyp/cacache/make-fetch-happen/builder-util cluster) + - `npm audit` now reports **0 vulnerabilities**. +2. **Updates card now explains residual advisories (`c11ab9f`).** Each advisory + can show a fix-path hint from npm (e.g. "Needs electron@43.4.1, a major + upgrade — not auto-installed."), or "No fix has been published yet." Hints + only appear when *Install updates* cannot clear the item by itself. +3. Full toolchain verified on the new majors: vitest 4, vite 8, plugin-react 6, + electron 43 all pass the existing suite with no config changes. + +## Commits added most recently + +- `c11ab9f` Security: major dependency upgrades clearing all npm advisories; show per-advisory fix paths + +## Verification commands run (all green) + +Frontend (`frontend/`): + +``` +npm audit # found 0 vulnerabilities +npm test # 14 files, 91 tests passed +npm run typecheck # clean +npm run lint # 0 errors +npm run format:check # clean +npm run build # vite 8 build success +npm run electron:build # tsc electron main success +npx electron --version # v43.4.1 +``` + +Rust (repo root): + +``` +cargo test # 113 passed; 0 failed (new fix-path parser test) +cargo clippy --all-targets # only pre-existing warnings in src/profiles.rs +cargo fmt --check # clean +cargo build --release # success +``` + +## How to use / reproduce + +```bash +cd ~/Projects/Nostr_Keynctr/frontend && npm start +``` + +Settings → Updates → Check for updates should show "No known security +advisories". If new ones appear later, Install handles compatible fixes; +anything left lists exactly what major upgrade it needs. + +## Notes & next steps + +- Electron jumped 10 majors (33 → 43): compile-level checks and the renderer + test suite pass, but give the app one manual smoke test (launch, unlock, + publish, feed) when convenient. +- The Updates card's Install button remains deliberately conservative: majors + are never auto-applied; they are surfaced as explicit hints instead. +- Relay health (earlier today): damus.io 503, nostr.band WS handshake timing out. + +--- + +# Older checkpoint — Updates card (2026-08-24) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From 8a644afd80c280a735d2cac59db6ca2342c60f57 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 11:33:45 -0500 Subject: [PATCH 21/51] App icon: hummingbird-key mark for window and packaged Linux builds --- frontend/build/icon.png | Bin 0 -> 10477 bytes frontend/electron/main.ts | 13 +++++++++++++ frontend/package.json | 1 + frontend/public/icon.png | Bin 0 -> 10477 bytes 4 files changed, 14 insertions(+) create mode 100644 frontend/build/icon.png create mode 100644 frontend/public/icon.png diff --git a/frontend/build/icon.png b/frontend/build/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d02f1d6303d0f7e307c9b0ce7889fea392d63159 GIT binary patch literal 10477 zcmeAS@N?(olHy`uVBq!ia0y~yU}6Aa4h9AWhKs`8Y77hv3dtTpz6=aiY77hwEes65 z7#J8DUNA6}8Za=tN?>5Hn!&&zUNC1@pbbc8lDE4H!~gdFGy54B7}!fZeO=jKaYzWr zN*(w&;V}aPgKCLuL`h0wNvc(HQEFmIDua=Mk(sW6g|3lFh@qL4k*SrbrM7{Am4Sh# z*e^X44Y~O#nQ4{i8ceK=EFc=Bt+g2$7#K9*Hk4%MrWThZ<`$sqv9vNWgXnqr{+BTW zgZf2J7srr_TW@PCXQZT-e*Zc5yI$zs&C1G$wGRQV7(`hcx|keX85b;K2nb|Y z(bI0R^go+Z%e^@M(CVoS46j1}ay4o(I#eBMSn+H1Q)Z8E`7a+{Ev{eApkd4E5x?N& zr>Tn=B$69BR`oS>Sv0=*s$cTfz-a-4#wSVF{Pli|8!rB8TwM3x%aEa}iz%Q%!-2!q z%WcX%mIc?t`+uDDKIY)MQRnG*Mz&7fg?nNdPC6`5TOcB+=CNv?=_P{#7PiG63xe1r z8Y49)33I&VmdFnjUce9_nXWmP|HAFB>%FcrC}>2;tYwhc^slvJ0f$7gyu+3m$rU^c z*vj`X26!~DZjecrXL!Z(?5|7iNuP$Or4J1q$}V)3-V6D8xSMG~f6bj*hO2wq8o8KK z8KxS3WyBgJI6?|0Jk^k`;al!Bzd12i*SjuB=4FUIW^hl<;R@FZ{ssIYiy1)jRSb{c zuq-<*A;K)hx}ryu;R?$N;T1+U6Pb(}P6=G?kKtLzv|!0Ecfa30S6Ei?FIW_KAj4L& zSN7O6Miu}1u%$IT3np{?t}7DlWGeZR|36)Q{_$mg37v`ojOqffSo+FE7N{lcVO*fy zaLkV1Ak6$kkVEnEaz^L2s*4QvMGQS>e~M2KSeW4&9K-UqpP!@ad+gjzf(^%df7-3z z6w5Z%CQs&{`ui_?ypA!b_DdVS>0W>C{OX!OhsejrzPwBO5$DkUd}-nXf&Tr^7?0WH z9nrYA@$hWDeU@7m#7$Y&H~mS~xxziCk1vek!GT8S%dePUc?;O=8Sv*!eUViWWy4s#)&iFs*LKL(L>5Wx>q(>rG}YGicy=-FH=4%|FvlVKbjgenN`d7g}&NBw9Dlm@YkGMmyh6Wi3&;|KlXju{(o)EafP>o*EuH| z#mlbkI3e^ZTYTM=Lpz=-y9Wy>xv*sK+ikccqprlj>Cms_e<}ZW^{!#>4l$h>dGZdo zF0ZyWLy$;B^^L!AI}SV-dVHhf4s%@5rFBB;PlP-w+`j7WPbdyq>BgPHWL4PkwJ&Oe zztf4fjnR{T>sdWnWBpjF-BVk9!Y$F6$E%nxR3@h;GB5_dsrx)bBl1nCTTjGk{fet6 zZ>ua!6q~p9rzOvVa_dxP29<>mKD;kp&d;E}@k@_=<;~B&cfQEnxWcx@xLNnn^s)^X zG&uq_n>ZDe9%bCTC~ME4p>)6chqLs#J;w8%Sa>#b3khb5^u z^p}>LJnxev+YP3~=ot-?Yyn&nrVK$xIfcG|k^Zyd_xS^7JU2a3U4Oq_sZsIW!At3h zOu8)+y-aO=wu~L;9>m!-sVuxAJ^yaOw{ltY&Wqo^J`a69X&v_zMz(Vd0uD*lKmPjN zInCZSBkIZ=n}@yZ6Iod%yvu5x!q|0$Vdi7E3AZ#;stivisb;Fg6+Av>EN^nD<8j2U z*}I!{YF&=zIZn7{A6m||IaqSHz5NUROUK2_*37MW+Iqgz`T7F({p&uc+h5<+?(~~M zNl9hd&etEWed&r45c>9Lb>EIJ--U&4n;e*&chB&wqSj||1_p;!=2e>CrDk*sc*-38 zdOyDI#MFi2>R~Qc%WE(5G(2PEN@iklQ8JXRoG-mle{=eshHtxHgp{8$Khah&weD9% z-qrK1{LGw6Oq>2?cdoRL@=H4Z|DU$s@9U{D%LNrZU%s!OP$Z zBp4EmuB_SnX;N%bc0|UG<;%H@R_$(x%yZb(^X|iv^Cymb<;ea%pu2VNo)eoGsu&~m z*cv)i{AK?=KKtr$H4mqyCtv09iJmWJm>4hN&}ij%U=W;djwdwQTw{4AlvnVKo?Su{Eg;&K# zbiSVG>iy%cZ}s!|*AusnOwv=;XJ+2As^pP+{15p#%-Pb*e{R`6KYLajle)Cq%Agds zjL_2K(XkG54tH)1s(y6n^1sz`oXbVkPcgNoHEeqb=;Pc(#D4@{?*4{-;jzb z;A!z{tZCSCarWfYde3$Hzo_g`DSTl2t8Mzsdd_NQX23MF zJZh#FqmRSt)8Z}9woSir$4il=@%NL~^gs8y6(^J|V==LhIbaYFwRY~bH95Pg7fv+% z+hO={-h3O21hM8R={ngF5j!*-Iy_ho@w|^c{ppW`=aYX=Ze(W(Z9XAX++@7;m(o=& zZMjU>fNZOjYpWBRVwb)DHp%AYS>FAhZg9U&yj8^{^kwIKE~X{xSfYy6wGOGco?zLg z{$1Uu_6l>Hk7b?OaZbKnpDKCV85i04I#h4VsV>NEjX9;%()e<5ONVDz+&k}e(K(MU z{Qq{*{OaQkcWtkj*K#YqP4LKNSR!;S;fmS<+pCTYDtBW3x93+DuFDtveL0?k-+W_Y z*4d61ksmYVm0emcg@p)g5(&Gx)=|*w&=S)}?k+Dr_S%12WF!2;!0piVx|-JqrfK5 zaCo&a!ow-SU4S*%-|zFMQ0)i>p~jBJqJCK|5e9}$y{Y;CI^>^bM;+Xnu-rd?PTAbB zTMK_5<`?Qb`Sio}@Ae-qB>UT3^mw3qzwY(c^S7$MFYh#YnAV+tI;uKIL)gK^=q>B< zn(MuvzpiDlUse0ykHDN`#kQ6I4W<8l+GZ{H=WyDVO^54_%Wcy>el2C%f;nvqBvcbO z%dR`I`@5^>oF~6^IoGv_@11_a^LXdN?tWJRAnZM&ir!*+Jtc7?7zr}fvGe>!L0dHU?-^>aM_lqmbS=ccYb9h7~i>}vV! zoHY@%syFhqJ0JY_d~b@YL*8o3NX88%lNC7kzF24Tp5;;t`($C6uRlwD&9fC^dp2&? z3zn|@afyAOvflA&lg}Eex*VGt@NM^#qZw5(mr|8kSs0GKYj(HiI(ePTU}DAD+WdQ# z1($!iaBFMxdg>;v{Qn`hJfi3t=czO4=&|VxEbX(7*Z#P9 z`PkB%pI4olD61jEz>=hC=r8wuZs)Qme;ztKX+6EpI`fpN!|5yeJ8#a=(Q*Cy`+U`j zeyhG5W=;;ZTfdT*yI%zl0=@pf6b*i*IbqA4i((AN<}pU~HO!p1Q?qb8NFXBr;;du_=EPo0ZT zcI95r+ncm$Usch~Cr6%qN}Vis{#KQ=I@1P)*_{)`R&TRER1mpf27?Fh<|Fq~->))d ze0V`e;DS9|nH*U41vu(i>~JFCvF zx_bKVwRxLc)(G&p9JqPGhV_7s(1yL}eEO%MFFMk)yCZ;Xo6I}1r%l+*T z=A0HVl}*v?kjH@}-DfO+v~9Pk)h23E{|A_EopOLG&n7~W#a!M zoZopnm}9Je9#vjen`d9rsNgAeyneOVg`h>}ORn&?Y?3{mxr9?qNbpR^?w$WW$!@#* z>*nrvdk!0gD{NS4KC6{8nXTdHrZlFBNB+)VT=$Z_xx%~b(bagnj8%4z=Wym2RK8pO zC+&CrSAGTo!R4m~H~-ae%4FtQzP3v#Oh{2FZs*m@WfkYjtlw_RbJB58vMFQ8_~{nj`1Q-2W?2TAZnWB%QG1=eg~+FD+HBsU5%n*!;IIgMp>U zwSJ4QALd31oj#Ho*ST`8;Ihb_2XwSDQstxmXR|z2zRQ$w;Hxk9{|ZGF!P7U*ef$F7 zl^*^J+E`S z9DYHSfi3d>6~^c$70){lrpAAAI`=zG)+lA9}pQxDS zT>iK{ljDZwO5x69ir z;_inat&?f;(>?yPu4BJ(h<&Sw^v*BJc8gTrw=}%W?$PNu!Fzu9A)(^c$Cj7eUAUCN z;k1v+#vawcMUwG`Qf_gSf)3$bg0R|_PX>01wvMxwcU?`Y<{QiUETd!Rd zmlZJd5N24!B62m){`WalRDy|ep#UHCFCjDpTcy`HBKtm=HL2ob?>sj7xQ;Boo4@*x65g1oL8_I!;;+Z3L7jA%EjBQ z$}}iqFgag)bxR{hNzjW2^Ub!-TIu2y+v4 zld8qlt-kN%UDYt9m+=12%R zZh8Lad(%7yh8>ry|2=j!-05t_WbkFyTOs!qp_kV4Oz8jjTrBwHqNk>j&l7z9ofZFg zt+#zYo#h_4IqxL=&7JPGYg2N>IB}uHvgTG^TNe^=F9&9 z3{P?yZY}*f*DFVnDM_tJ>a|_{)l(Owxfo8Yvr>{~xH9kiDuxrCzb@B%Zr`TxHY}+v z|8enq+m)H^7oRu%IPkU6`|VWa;^+0+4AatBGW@g_?3S4?F!6A2`n{^7vo0jdZAe!0 zasTmP%UAPk2JJ$#2KVYm=GImdzLk{f{5usH=HTK`kTq$~!*h?7vnvj?9k|x7qPAt0 zgV#QXnk5Vw>%DLI%vXv&(;YQ|dy~DOVte!R+K02|E@rG(o&08Z`jd_&M?IJJv$&sQ zn5~?!xujs(|D(5#w6Q9jUl`P6&g@!OoO*C$DUbuit#%TL?rWjr$* zyfXG@`6WmRH}T%sdbz#k>8*6uN0O88|MP5I=$qhla`IgTG6w9Qv_4oarR5fY&`aJ%7nkf@s88C#u{;_=J z&MWce%hWEvzW4d7tnTw4JIpQytm11^^IQ7m{<(80zb$U&&$9lve)9Ewf(2W?9!y!{ z#js+Au*S{0ch`7pO+NlBx85oG?$CvOyIH1OU6L&k@xa4@Vd9att@~?lyVSjI*L`tR zFZ6V7*{Y?#Z?*^J*!=unpEgNFy`J|`i*=q*yK$9cd4f*GV~42>4QH>NKRUU5Z=6rv z>tzx}pNysCZp(d|u3^Qdb&O%N>N@+9bHZ^m;`gxW)Uz7iTxZOX$(_7$^RcVD`26NN zC^g<->7V;`!|cWXPIC6&SpWQ6^P=b6TGKcd&6L`E;CJ`$zZU1+r(VeHdwcl5{ubc? zUEv9)uQ@mmJq?d}tX?_K-m}d5)UFW2hhB64hb>jpSatWanEAQl$2!(uCgmo*lbw8Y z8&l{m9xwNkT!L#pyzl$7a{h8=hRHU6=Y2IS)NgJy<&p@r@Zi+@lc{^Y^HRczov&vv z+b$*`V$UD2*GECYY*po#@zICo>GAhVHeOitTgZFqA_fkZH5YEzJ#O9k zFNW`W+_b#rxP*&)XEiicFgOWa&EI>o*Yf-a?sl+I?<*ZI8z{I0uxTv&w>eW*spfCJ-;+~UV)EyeIzBM+SkV;3EAMdWZtMh?e;*Fb z-)*+vR7t_y=EG|3waTJ1xMSXGICOAye!n^YcVM5I!<>}atHaD?J<}UQ{Iw>td#Jn4 zydUekv#R0PuK722KfI+S_m9hvVUtS1o4D`X`u>aA%y<6z;dVD*s~*ED2{s|l$ht?j zYS_15Yr4MQ@PqEuDxM1w3=A$RoxjiX?<(xG-+ayD;U~556upY2wG0yuL}lln%kNj` zs4MvK{M9uMjzxA3oM#&TU${OYT2|u$Yx}(&p&44Q|A+E22o(Q*>{mB;fnUJ|-)L4n zMy0E!jG{~p7Qy8Kd2<)UaL=!QY*jcjczuuv18YB1%N+K)2hUs?-mvTM$ZbDi!08z} zHC~Z{gTtxZq)3|ep;=tT={{fOsk0Y_`Z92|u=w9~-npZp;bT*B-c2(Trb#-iYXTh_ zuj(r(z5esUedmsbgydQ4_hkA$Y0TU#s>apeBv;?TFzp?;kDmPnk3ZK|tFQ5N7w~?X z|*)_Ei`ybzLo9r>P z_%+p#@uiQ0fvrc_v<-&@nI5xL|2^Aw;7YV~*D(f`&_srA`+p@Xe@2-dT>b9Og@WD? zyC19N7>-mwnZCiBlj(+cSlu(@)ywDJ6R}tK&`CTJ!O6hk_~C>6&i5t)2j;mfuX{Xa z*+<{}ulI|m#WU^In>ba(WO1`E>mdh@MxDR?1@fHMZQW^oRZTw?Gr1%=t&l#i}}AE1u9Nx zF4%qAfB7=!X**5RSpGJ4{!&t8J#Z=TLd}lz$Iq+Hv3Q(oZT?Vbso0+1$u{NF6@Ohw z?b;&vZ1(C)5=nBuL+cp|aw<;T?%#A-uGyvP`G=doUc1kfDBQP@sZ&8|LTTOqp|yP2owb4)4;yc6uX{@Cp! zXRqI>o_1umzL=~V-wxBO5t0hxObU#?-#b#jHO;mAaA`%Lu%q=9nMGGG*)Tk{cHh1H z?*ak;CgJrrG#lo}{p4O1^D8!j;fW;E_bb9$b;VbY=C3iIdqpI?=!d_jgu?P|-T@c> zW;0&C^S!HCYUAxs$1`eDx7a!8O%rEgVvSub_=Y9Dwj}j1GpE6%Q~Vx@%lg8fSVdHQ zVB}%X-Cd~fFXvZY zF5z|Feq34q_X;zHUH>;-R*$>3Vrl3S4J|KIeglKT!|C;hQl=j>D)}mYg?1{bZQSx6L4SkJ&lB|Dk`Meqyw835<39qGs{s%d*33nUto!GH?k0z31SU=aX;V zV82@ba!~`{)LnC5Yd z{@VRq4SPxrJbq8;psT$6F=i~7HcX~E-@X}jy%FRa+4&DC&a zi~c{?YyL+rNHYEojmox{$FI0taPV=s__DP9 z8i&HvRc?$CZ~Qmtt(CZV*hTl zqca=R8J0dZOlz1U{cOVw@dca>hGk5~|NR96OxPqI-RQYyBcR%pWa!{${$*wRevjJ? z#~P0IFje(2K3mK8{>E!o0gjigtwIhf=N|a^-)Z?{-IoRyN*7Zc4oWPL55Bis)Ze)A zuR$ME0*7CM{I$A&_K_;W9!^TSs{%ws+87OV7HsrvFUbfDIO*$9KSA(Dnw#7yhNE1} zzndK1bD4bP*F1H|@8Xx@PR0xAiOZKpD+_sG7s7yY%*ZI+(&r`pq>8!oVFzLB^ zxV%Dt4#P!-;;+;0{+~Q2->JQWb>~-((-V&KuI79p&nOaG@}{;fM|Y(N55r3q13}3r z_td0a@7e0_T9!BSQe$#u$!4$L?o)3Hh%_>$aP$W}ihAt(|B<@N%iB*ctIzqn!l+zU zoRPtSFImL;Z!PDYm$y%RllLkSoH}J9hub@rBo4oXS3lLi{LR!~%X4z6(SoncmrkE} zb4l_PL#yb*b>hF>?=fRTeuEv4VpZ@+_yU97VfvK&YDb3#V zUddaPnFlN9sjfAX>Ry|ebm7kP!|m+XCWx{0sxsVp8En5d?Nsu`RU0<4F!;Z(Jo?H_ z)@jLar(=d(30AeY7aY65w$YHaAtApgdwdcjhmTn{pHL~|vGbf~ zv`kBqZ`?3C&|?@N>CKi=$?%H3Pm`gc#IB*}zz(Jc&+ucq3=OMN+{Dwqm#m%R*B-^d z@c5BbcOK7ddoGFDp?Z{A_qYLP3GE(^ca%hbGT6BZrss-H-R_ zsPCJf@a6n^i)!cVKh2Q_q=i9Ta z*Y!Db4r9Z{AJaPPzZS+9whD#*h+txvQt|)SBV+l((EHK9+#i2XZJl)EL+}6h-omrE zB>aUFIww7QV!kh9QqGF*M+tiv9GqMjYxU>yE)h;%&DL;K+%2Fw@r1GzYsf|R7iKHO ztBjI#IaVkzmmGhQP{dKCZ!^!1LGZwqYn!wFt(IUbi{oZZl(32l?+BmFc;P22Pkzw)nY1|?&L!G9r2X39U!5GrYA?s|LW#}5VC%Ef`*!3$TFq`T&2PaDo7$J9bva+e zDr^`VF8uvboxGU$(9|BLT@OosFDzbO8}>JR3gd=~z3LMzswXpgOm+}G%bXCXW$@tH z<0ArZc~)nLC~VST<}fe6?K>mn@6=Kz@w?`S{EGXd%lS$zLRuL&d`LC8QE|35|MupS zRsXc#FzXntk*@jXBB?bDIZ@jIq)3wR{B&N{tr zhf2w!mX4dbIlr3HZBy#kU7E?b>+v_Gw&1mK{`Id8&7LoLpgd-}lzXi8M2ku1SORs+ z(-e>G#s(#|>*=;|#|F}d;Gu}}3Tz+`=4`69rkSCz5EMq$DfHWS7x)(jz6*cVLt&7jrH)R47**CJtI zY)ivn3!czcj3Ei15^@z`c)aFM$+o#n&?RM(b1i;xxG5O(_MBo!?q(2C=DF}JfV_oP mxa~z=z=q@;`U=ZF{m<<7B|k5>J0b$Kj?vTA&t;ucLK6Tss`S_Z literal 0 HcmV?d00001 diff --git a/frontend/electron/main.ts b/frontend/electron/main.ts index 6759ddc..dde0544 100644 --- a/frontend/electron/main.ts +++ b/frontend/electron/main.ts @@ -82,6 +82,18 @@ function resolveBackendPath(): string { return path.join(app.getAppPath(), '..', 'target', 'release', 'keynectr'); } +/** + * The app icon ships inside the bundle: Vite copies `public/icon.png` into + * `dist/`, so the packaged app finds it next to the loaded page. In dev the + * Vite project's `public/` folder is the same file. + */ +function resolveWindowIcon(): string { + const base = app.isPackaged + ? path.join(app.getAppPath(), 'dist') + : path.join(app.getAppPath(), 'public'); + return path.join(base, 'icon.png'); +} + function startBackend(): void { if (backendStarted && backend && backend.exitCode === null) { return; @@ -500,6 +512,7 @@ function createWindow(): void { minWidth: 920, minHeight: 640, title: 'Keynectr', + icon: resolveWindowIcon(), backgroundColor: '#f6f4f0', autoHideMenuBar: true, webPreferences: { diff --git a/frontend/package.json b/frontend/package.json index 7002d7b..6dadc90 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -65,6 +65,7 @@ "target": [ "dir" ], + "icon": "build/icon.png", "category": "Network", "executableName": "keynectr" } diff --git a/frontend/public/icon.png b/frontend/public/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d02f1d6303d0f7e307c9b0ce7889fea392d63159 GIT binary patch literal 10477 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 literal 0 HcmV?d00001 From 4d9489b5ac308e6384e9036570147999ee4a2e15 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 11:34:47 -0500 Subject: [PATCH 22/51] Checkpoint: app icon --- CHECKPOINT-encryption.md | 73 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 0407a61..c95413f 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,75 @@ -# Checkpoint — Security dependency upgrades (2026-08-24) +# Checkpoint — App icon (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `8a644af` ("App icon: hummingbird-key mark for window + and packaged Linux builds"). Before it: `c11ab9f` (security dependency + upgrades), `48efaad` (checkpoint), `8bce428` (updates card). +- Working tree: clean apart from this checkpoint update. Note: `concept3.svg` + shows as deleted in the working tree and `KeynectrAppIconPossibility02.jpeg` + (the icon source art) is untracked — both left as the user had them, not committed. + +## What was completed + +1. **App icon (2026-08-24, `8a644af`).** The hummingbird-key artwork + (`KeynectrAppIconPossibility02.jpeg`) is now the app icon everywhere: + - `frontend/build/icon.png` — 512×512 PNG used by electron-builder for + packaged Linux builds (`build.linux.icon`), i.e. desktop entries/installers. + - `frontend/public/icon.png` — copied by Vite into `dist/`, shipped inside + the packaged asar; `BrowserWindow({ icon })` loads it at runtime in both + dev (`public/`) and packaged (`dist/`) modes. + - Verified end-to-end: `npm run dist` produced `release/linux-unpacked` + with the icon inside `app.asar` (`/dist/icon.png`). +2. Earlier today: all npm security advisories cleared via major upgrades + (`c11ab9f`), Updates card (`8bce428`), feed profile filter (`55a49b4`), + publish latency trim (`b001b3c`). + +## Commits added most recently + +- `8a644af` App icon: hummingbird-key mark for window and packaged Linux builds + +## Verification commands run (all green) + +Frontend (`frontend/`): + +``` +npm test # 14 files, 91 tests passed +npm run typecheck # clean +npm run lint # 0 errors +npm run format:check # clean +npm run build # vite build success (dist/icon.png present) +npm run electron:build # tsc electron main success +npm run dist # electron-builder linux dir target packaged OK (electron 43.4.1) +``` + +## How to use / reproduce + +```bash +cd ~/Projects/Nostr_Keynctr/frontend +npm start # dev window shows the icon +npm run dist # packaged app in release/linux-unpacked (icon in asar) +``` + +To change the artwork later: overwrite `frontend/build/icon.png` and +`frontend/public/icon.png` (512×512 PNG), rebuild. + +## Notes & next steps + +- The source JPEG has a white background (kept as-is). If a transparent + version is ever wanted for dark taskbars, regenerate the PNGs from a + transparent source. +- Sharing decision (this session): user chose "shared access on same machine" — + the current single-vault + passcode model already covers it; no new sharing + feature needed. Distribution to others = `npm run dist` + their own passcode. +- Relay health (earlier today): damus.io 503, nostr.band WS handshake timing out. + +--- + +# Older checkpoint — Security dependency upgrades (2026-08-24) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From cbbe779faa45b9c23c2f36aa19deafbc558ea0dd Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 11:46:48 -0500 Subject: [PATCH 23/51] Desktop entry + Wayland app id so the taskbar shows the app icon --- frontend/electron/main.ts | 7 ++++ frontend/package.json | 1 + frontend/scripts/install-desktop-entry.sh | 42 +++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100755 frontend/scripts/install-desktop-entry.sh diff --git a/frontend/electron/main.ts b/frontend/electron/main.ts index dde0544..c0fe1f1 100644 --- a/frontend/electron/main.ts +++ b/frontend/electron/main.ts @@ -563,6 +563,13 @@ function createWindow(): void { }); } +// Wayland has no per-window icons: the taskbar resolves the running app's id +// through an installed .desktop file instead (see +// scripts/install-desktop-entry.sh). Pin the identity before `ready`. +if (process.platform === 'linux') { + app.setDesktopName('keynectr.desktop'); +} + app.whenReady().then(() => { protocol.handle('app', (request) => { const { pathname } = new URL(request.url); diff --git a/frontend/package.json b/frontend/package.json index 6dadc90..8ed1c4c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "format:check": "prettier --check .", "electron:build": "tsc -p tsconfig.electron.json", "start": "npm run electron:build && electron .", + "desktop:install": "bash scripts/install-desktop-entry.sh", "dist": "npm run build && npm run electron:build && electron-builder --linux dir" }, "dependencies": { diff --git a/frontend/scripts/install-desktop-entry.sh b/frontend/scripts/install-desktop-entry.sh new file mode 100755 index 0000000..c2ceeb5 --- /dev/null +++ b/frontend/scripts/install-desktop-entry.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Install the Keynectr .desktop entry and icon for the current user. +# +# On Linux (especially Wayland) the taskbar icon does not come from the +# BrowserWindow icon: the desktop environment resolves the running app's id +# against an installed .desktop file and its Icon= key. This script installs +# both so the hummingbird-key icon shows up in launchers and taskbars. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ICON_SRC="$ROOT/build/icon.png" +ICON_DIR="$HOME/.local/share/icons/hicolor/512x512/apps" +DESKTOP_DIR="$HOME/.local/share/applications" + +if [[ ! -f "$ICON_SRC" ]]; then + echo "Icon not found at $ICON_SRC" >&2 + exit 1 +fi + +mkdir -p "$ICON_DIR" "$DESKTOP_DIR" +cp "$ICON_SRC" "$ICON_DIR/keynectr.png" + +cat > "$DESKTOP_DIR/keynectr.desktop" </dev/null || true +command -v kbuildsycoca6 >/dev/null 2>&1 && kbuildsycoca6 --noincremental >/dev/null 2>&1 || true + +echo "Installed:" +echo " $ICON_DIR/keynectr.png" +echo " $DESKTOP_DIR/keynectr.desktop" +echo "Restart Keynectr to see the icon in the taskbar." From 5f27c4585b45c47d3fb0c5a9fbf848193e9757e5 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 11:48:34 -0500 Subject: [PATCH 24/51] Checkpoint: taskbar icon via desktop entry --- CHECKPOINT-encryption.md | 61 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index c95413f..e34db7c 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -6,30 +6,39 @@ verified green at the moment this file was written. ## Where things are - Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `8a644af` ("App icon: hummingbird-key mark for window - and packaged Linux builds"). Before it: `c11ab9f` (security dependency - upgrades), `48efaad` (checkpoint), `8bce428` (updates card). +- Git repo: `master` @ `cbbe779` ("Desktop entry + Wayland app id so the + taskbar shows the app icon"). Before it: `8a644af` (app icon), `4d9489b` + (checkpoint), `c11ab9f` (security upgrades). - Working tree: clean apart from this checkpoint update. Note: `concept3.svg` shows as deleted in the working tree and `KeynectrAppIconPossibility02.jpeg` (the icon source art) is untracked — both left as the user had them, not committed. ## What was completed -1. **App icon (2026-08-24, `8a644af`).** The hummingbird-key artwork - (`KeynectrAppIconPossibility02.jpeg`) is now the app icon everywhere: - - `frontend/build/icon.png` — 512×512 PNG used by electron-builder for - packaged Linux builds (`build.linux.icon`), i.e. desktop entries/installers. - - `frontend/public/icon.png` — copied by Vite into `dist/`, shipped inside - the packaged asar; `BrowserWindow({ icon })` loads it at runtime in both - dev (`public/`) and packaged (`dist/`) modes. - - Verified end-to-end: `npm run dist` produced `release/linux-unpacked` - with the icon inside `app.asar` (`/dist/icon.png`). -2. Earlier today: all npm security advisories cleared via major upgrades +1. **Taskbar icon on KDE Wayland (2026-08-24, `cbbe779`).** The user reported + no icon after restart. Root cause: on Wayland there are no per-window + icons — the KDE taskbar resolves the running app through an installed + `.desktop` file, which Keynectr did not have. Fix: + - `frontend/scripts/install-desktop-entry.sh` (npm script + `desktop:install`) installs + `~/.local/share/icons/hicolor/512x512/apps/keynectr.png` and + `~/.local/share/applications/keynectr.desktop` (Icon=keynectr, + StartupWMClass=keynectr) and refreshes the desktop database. **Run once + per machine.** + - `app.setDesktopName('keynectr.desktop')` in `electron/main.ts` (called + before `ready`; Electron 43 renamed the old `setDesktopFileName`) pins + the app id so the desktop file always matches. +2. **Window + packaged icon (2026-08-24, `8a644af`).** Hummingbird-key artwork + as `frontend/build/icon.png` (electron-builder `linux.icon`) and + `frontend/public/icon.png` (shipped in the asar, loaded by BrowserWindow — + effective on X11 titlebars and inside packaged builds). +3. Earlier today: all npm security advisories cleared via major upgrades (`c11ab9f`), Updates card (`8bce428`), feed profile filter (`55a49b4`), publish latency trim (`b001b3c`). ## Commits added most recently +- `cbbe779` Desktop entry + Wayland app id so the taskbar shows the app icon - `8a644af` App icon: hummingbird-key mark for window and packaged Linux builds ## Verification commands run (all green) @@ -37,34 +46,34 @@ verified green at the moment this file was written. Frontend (`frontend/`): ``` +npm run desktop:install # installed icon + keynectr.desktop for this user npm test # 14 files, 91 tests passed npm run typecheck # clean npm run lint # 0 errors npm run format:check # clean -npm run build # vite build success (dist/icon.png present) -npm run electron:build # tsc electron main success -npm run dist # electron-builder linux dir target packaged OK (electron 43.4.1) +npm run electron:build # tsc electron main success (setDesktopName compiled) +npm run dist # electron-builder linux dir target packaged OK (earlier) ``` ## How to use / reproduce ```bash cd ~/Projects/Nostr_Keynctr/frontend -npm start # dev window shows the icon -npm run dist # packaged app in release/linux-unpacked (icon in asar) +npm run desktop:install # once per machine +npm start # restart the app -> taskbar shows the icon ``` -To change the artwork later: overwrite `frontend/build/icon.png` and -`frontend/public/icon.png` (512×512 PNG), rebuild. - ## Notes & next steps -- The source JPEG has a white background (kept as-is). If a transparent - version is ever wanted for dark taskbars, regenerate the PNGs from a - transparent source. +- On Wayland, KDE may need the app restarted (and occasionally the panel to + refresh) before the icon swaps from the generic one. +- The source JPEG has a white background (kept as-is); regenerate the PNGs + from a transparent source if dark-taskbar polish is wanted. +- For real installer targets (deb/AppImage), electron-builder embeds the icon + and desktop file automatically from `build/`; the manual install above only + covers running from source. - Sharing decision (this session): user chose "shared access on same machine" — - the current single-vault + passcode model already covers it; no new sharing - feature needed. Distribution to others = `npm run dist` + their own passcode. + the current single-vault + passcode model already covers it. - Relay health (earlier today): damus.io 503, nostr.band WS handshake timing out. --- From a00c7c9897b93a57d58acd6bf1d6daa879f27777 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 11:56:16 -0500 Subject: [PATCH 25/51] Sidebar brand mark: hummingbird-key artwork replaces the shield icon --- frontend/src/assets/logo.png | Bin 0 -> 10477 bytes frontend/src/components/Sidebar.tsx | 3 ++- frontend/src/styles.css | 17 +++++++++++++++-- frontend/src/vite-env.d.ts | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 frontend/src/assets/logo.png create mode 100644 frontend/src/vite-env.d.ts diff --git a/frontend/src/assets/logo.png b/frontend/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d02f1d6303d0f7e307c9b0ce7889fea392d63159 GIT binary patch literal 10477 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 literal 0 HcmV?d00001 diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 4d06c24..cce4bca 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -4,6 +4,7 @@ import { shortenNpub } from '../lib/format'; import { Avatar } from './Avatar'; import { CopyButton } from './CopyButton'; import { Icon, type IconName } from './Icon'; +import logoUrl from '../assets/logo.png'; const NAV_ITEMS: { id: Screen; label: string; icon: IconName }[] = [ { id: 'home', label: 'Home', icon: 'home' }, @@ -29,7 +30,7 @@ export function Sidebar({ screen, onNavigate }: SidebarProps) {
diff --git a/frontend/src/state/AppProvider.tsx b/frontend/src/state/AppProvider.tsx index 0b5541d..6167031 100644 --- a/frontend/src/state/AppProvider.tsx +++ b/frontend/src/state/AppProvider.tsx @@ -337,6 +337,10 @@ export function useApp(): AppContextValue { /** Apply the requested theme (respecting system preference for `system`). */ export function applyTheme(theme: Theme): void { + if (theme === 'neon') { + document.documentElement.dataset.theme = 'neon'; + return; + } const prefersDark = theme === 'dark' || (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches); diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 06e610c..61bdf7d 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -58,6 +58,35 @@ --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.6); } +/* "Neon" — pure-black Matrix/Cyberpunk palette inspired by the CAJAFUERTE + Obsidian theme (MIT): black panels, deep-pink primary (#ff1493), purple + secondary (#8b00ff family), yellow highlights (#ffd700). */ +:root[data-theme='neon'] { + --bg: #000000; + --surface: #0b0b0e; + --surface-2: #131318; + --surface-hover: #191921; + --border: #2b1224; + --border-strong: #4d1c3d; + --text: #f5f2f7; + --text-muted: #a89bb0; + --primary: #ff1493; + --primary-hover: #ff4fb0; + --primary-soft: #2b0a1e; + --on-primary: #ffffff; + --danger: #ff3355; + --danger-soft: #330810; + --warning: #ffd700; + --warning-soft: #332b03; + --success: #00e68a; + --success-soft: #04301f; + --info: #b388ff; + --info-soft: #1c1035; + --focus: #ff1493; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 8px 24px rgba(255, 20, 147, 0.07); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.85), 0 0 32px rgba(255, 20, 147, 0.12); +} + * { box-sizing: border-box; } @@ -312,7 +341,8 @@ a { /* The artwork is black-on-white; flip it in dark themes so it stays black bird on dark tile instead of a glaring white square. */ -html[data-theme='dark'] .sidebar-logo img { +html[data-theme='dark'] .sidebar-logo img, +html[data-theme='neon'] .sidebar-logo img { filter: invert(1); } diff --git a/frontend/src/test/SettingsScreen.test.tsx b/frontend/src/test/SettingsScreen.test.tsx index e88e83d..68ecbf2 100644 --- a/frontend/src/test/SettingsScreen.test.tsx +++ b/frontend/src/test/SettingsScreen.test.tsx @@ -29,6 +29,19 @@ describe('SettingsScreen', () => { expect(document.documentElement.dataset.theme).toBe('dark'); }); + it('applies the neon theme to the document', async () => { + const backend = createFakeBackend(); + installFakeBackend(backend); + renderWithApp(); + + const themeSelect = await screen.findByLabelText('Theme'); + fireEvent.change(themeSelect, { target: { value: 'neon' } }); + await waitFor(() => { + expect(backend.state.settings.theme).toBe('neon'); + }); + expect(document.documentElement.dataset.theme).toBe('neon'); + }); + it('toggles publish confirmation and npub shortening', async () => { const backend = createFakeBackend(); installFakeBackend(backend); diff --git a/src/main.rs b/src/main.rs index 8c74756..6f630a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ Commands: relays disable Disable a relay relays test Test a relay connection settings get Show application settings - settings set theme + settings set theme settings set confirm settings set shorten delete-profile Delete a profile (moves it to undo stack) @@ -442,7 +442,7 @@ fn cli_settings(args: &[String]) -> Result { match key.as_str() { "theme" => { let theme = Theme::parse(value).ok_or_else(|| { - AppError::config("Theme must be one of: light, dark, system") + AppError::config("Theme must be one of: light, dark, system, neon") })?; app.settings.theme = theme; } diff --git a/src/settings.rs b/src/settings.rs index 8cddc71..55ee740 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -23,6 +23,7 @@ pub enum Theme { Light, Dark, System, + Neon, } impl Theme { @@ -32,6 +33,7 @@ impl Theme { "light" => Some(Self::Light), "dark" => Some(Self::Dark), "system" => Some(Self::System), + "neon" => Some(Self::Neon), _ => None, } } From 4722ecdabd26f12e7ba4a67c07ed80b8eb1678ad Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 12:15:36 -0500 Subject: [PATCH 28/51] Checkpoint: neon theme --- CHECKPOINT-encryption.md | 82 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index f3f68f3..3f903fb 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,84 @@ -# Checkpoint — App icon (2026-08-24) +# Checkpoint — Neon theme (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `74072b7` ("Neon theme: pure-black Matrix palette + (CAJAFUERTE-inspired) alongside light/dark/system"). Before it: `ef6ea17` + (checkpoint), `a00c7c9` (sidebar brand mark), `cbbe779` (desktop entry), + `8a644af` (app icon). +- Working tree: clean apart from this checkpoint update. Note: `concept3.svg` + shows as deleted in the working tree and `KeynectrAppIconPossibility02.jpeg` + (icon source art) is untracked — left as the user had them, not committed. + +## What was completed + +1. **Neon theme (2026-08-24, `74072b7`).** New fourth theme adapted from the + CAJAFUERTE Obsidian theme (MIT, github.com/AreAjoErda/CAJAFUERTE): pure + black interface (`#000`), deep-pink primary (`#ff1493`), purple secondary + (`#8b00ff` family), yellow warnings (`#ffd700`), Matrix-green success. + - Settings → Appearance → Theme → **Neon** (with hint text). + - `applyTheme` maps neon straight to `data-theme='neon'` (not affected by + the system light/dark preference); sidebar logo auto-inverts like dark. + - Backend: `Theme::Neon` variant (serde `neon`), CLI + `settings set theme `. + - Old settings files keep loading; new value round-trips through + settings.json. + +## Commits added most recently + +- `74072b7` Neon theme: pure-black Matrix palette (CAJAFUERTE-inspired) alongside light/dark/system + +## Verification commands run (all green) + +Rust (repo root): + +``` +cargo test # 113 passed; 0 failed +cargo clippy --all-targets # only pre-existing warnings in src/profiles.rs +cargo fmt --check # clean +cargo build --release # success +``` + +Frontend (`frontend/`): + +``` +npm test # 14 files, 92 tests passed (new neon theme test) +npm run typecheck # clean +npm run lint # 0 errors +npm run format:check # clean +npm run build # vite build success +npm run electron:build # tsc electron main success +``` + +## How to use / reproduce + +GUI: Settings → Appearance → Theme → **Neon**. Applies immediately and +persists in `~/.local/share/keynectr/settings.json`. + +CLI: + +```bash +B=~/Projects/Nostr_Keynctr/target/release/keynectr +$B settings set theme neon +``` + +## Notes & next steps + +- Neon shadows carry a faint pink glow (CAJAFUERTE vibe); if it is too much, + dial `--shadow`/`--shadow-modal` in the `:root[data-theme='neon']` block of + `frontend/src/styles.css`. +- The theme only restyles the app UI; the Electron window chrome stays + system-styled on Wayland. +- Earlier today: sidebar brand mark (`a00c7c9`), taskbar icon via desktop + entry (`cbbe779`), app icon (`8a644af`), security upgrades (`c11ab9f`). + +--- + +# Older checkpoint — App icon (2026-08-24) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From 90d699cff1920afc06539e114cd67fdcb1a1248b Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 12:30:37 -0500 Subject: [PATCH 29/51] App icon: subtle white border around black artwork --- frontend/build/icon.png | Bin 10477 -> 11216 bytes frontend/public/icon.png | Bin 10477 -> 11216 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/build/icon.png b/frontend/build/icon.png index d02f1d6303d0f7e307c9b0ce7889fea392d63159..e4387ea68bc20af4fb3298c19405be1ba3a0824e 100644 GIT binary patch delta 11119 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 DHxgO9 delta 10374 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 DHxgO9 delta 10374 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 Date: Mon, 24 Aug 2026 12:32:39 -0500 Subject: [PATCH 30/51] Checkpoint: app icon --- CHECKPOINT-encryption.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 3f903fb..289ff65 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -6,7 +6,7 @@ verified green at the moment this file was written. ## Where things are - Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `74072b7` ("Neon theme: pure-black Matrix palette +- Git repo: `master` @ `90d699c` ("Neon theme: pure-black Matrix palette (CAJAFUERTE-inspired) alongside light/dark/system"). Before it: `ef6ea17` (checkpoint), `a00c7c9` (sidebar brand mark), `cbbe779` (desktop entry), `8a644af` (app icon). @@ -16,7 +16,7 @@ verified green at the moment this file was written. ## What was completed -1. **Neon theme (2026-08-24, `74072b7`).** New fourth theme adapted from the +1. **Neon theme (2026-08-24, `90d699c`).** New fourth theme adapted from the CAJAFUERTE Obsidian theme (MIT, github.com/AreAjoErda/CAJAFUERTE): pure black interface (`#000`), deep-pink primary (`#ff1493`), purple secondary (`#8b00ff` family), yellow warnings (`#ffd700`), Matrix-green success. @@ -30,7 +30,7 @@ verified green at the moment this file was written. ## Commits added most recently -- `74072b7` Neon theme: pure-black Matrix palette (CAJAFUERTE-inspired) alongside light/dark/system +- `90d699c` Neon theme: pure-black Matrix palette (CAJAFUERTE-inspired) alongside light/dark/system ## Verification commands run (all green) From c080a8d1c83142b5f62fe60ffb903634a78dae26 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 13:52:47 -0500 Subject: [PATCH 31/51] App icon: remove white border, restore original 512x512 dimensions --- frontend/build/icon.png | Bin 11216 -> 11216 bytes frontend/public/icon.png | Bin 11216 -> 11216 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/build/icon.png b/frontend/build/icon.png index e4387ea68bc20af4fb3298c19405be1ba3a0824e..18ff052e226917f9a04739da3c31d173f7535109 100644 GIT binary patch delta 72 zcmcZ*ej$89I=hgG@V^JihbHE!@)(^PsgaeT RB}CCK(fq%gSs2e~0RVx87ft{G delta 62 zcmcZ*ej$89I=i5((n~uhuZel8JVvJ41_o9J2Ff=Vcubs+;0Luxe3^Jbd14^PsgaeT RB}CCK(fq%gSs2e~0RVx87ft{G delta 62 zcmcZ*ej$89I=i5((n~uhuZel8JVvJ41_o9J2Ff=Vcubs+;0Luxe3^Jbd14 Date: Mon, 24 Aug 2026 14:23:15 -0500 Subject: [PATCH 32/51] Remove system theme, keep light/dark/neon only --- frontend/src/lib/types.ts | 2 +- frontend/src/screens/SettingsScreen.tsx | 5 ++--- frontend/src/state/AppProvider.tsx | 7 ++----- frontend/src/test/FeedScreen.test.tsx | 2 +- frontend/src/test/apiMock.ts | 2 +- src/main.rs | 2 +- src/settings.rs | 2 -- 7 files changed, 8 insertions(+), 14 deletions(-) diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 2581bfe..f8e3884 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -1,4 +1,4 @@ -export type Theme = 'light' | 'dark' | 'system' | 'neon'; +export type Theme = 'light' | 'dark' | 'neon'; /** Lifecycle of the NIP-46 remote signer. */ export type SignerPhase = 'stopped' | 'connecting' | 'connected'; diff --git a/frontend/src/screens/SettingsScreen.tsx b/frontend/src/screens/SettingsScreen.tsx index 2ea58e1..c5c8ae2 100644 --- a/frontend/src/screens/SettingsScreen.tsx +++ b/frontend/src/screens/SettingsScreen.tsx @@ -104,12 +104,11 @@ export function SettingsScreen() { > -

- “System” follows your desktop's light or dark preference. “Neon” is a pure-black - theme with pink and purple accents. + “Light” and “Dark” follow your manual selection. “Neon” is a pure-black theme with + pink and purple accents.

diff --git a/frontend/src/state/AppProvider.tsx b/frontend/src/state/AppProvider.tsx index 6167031..d910afe 100644 --- a/frontend/src/state/AppProvider.tsx +++ b/frontend/src/state/AppProvider.tsx @@ -335,16 +335,13 @@ export function useApp(): AppContextValue { return context; } -/** Apply the requested theme (respecting system preference for `system`). */ +/** Apply the requested theme. */ export function applyTheme(theme: Theme): void { if (theme === 'neon') { document.documentElement.dataset.theme = 'neon'; return; } - const prefersDark = - theme === 'dark' || - (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches); - document.documentElement.dataset.theme = prefersDark ? 'dark' : 'light'; + document.documentElement.dataset.theme = theme; } /** Keep the document theme in sync with settings, watching system changes. */ diff --git a/frontend/src/test/FeedScreen.test.tsx b/frontend/src/test/FeedScreen.test.tsx index 09be4c5..bb2a93c 100644 --- a/frontend/src/test/FeedScreen.test.tsx +++ b/frontend/src/test/FeedScreen.test.tsx @@ -24,7 +24,7 @@ describe('FeedScreen', () => { it('disable relays shows an empty state that can navigate to relays', async () => { const settings = { - theme: 'system' as const, + theme: 'light' as const, confirm_before_publish: true, shorten_npub: true, relays: [ diff --git a/frontend/src/test/apiMock.ts b/frontend/src/test/apiMock.ts index c5de46c..21b89ee 100644 --- a/frontend/src/test/apiMock.ts +++ b/frontend/src/test/apiMock.ts @@ -25,7 +25,7 @@ export function makeState(overrides?: Partial): AppState { is_active: false, }; const settings: Settings = { - theme: 'system', + theme: 'light', confirm_before_publish: true, shorten_npub: true, relays: [ diff --git a/src/main.rs b/src/main.rs index 6f630a7..6a66b31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ Commands: relays disable Disable a relay relays test Test a relay connection settings get Show application settings - settings set theme + settings set theme settings set confirm settings set shorten delete-profile Delete a profile (moves it to undo stack) diff --git a/src/settings.rs b/src/settings.rs index 55ee740..7293ee0 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -22,7 +22,6 @@ impl RelayConfig { pub enum Theme { Light, Dark, - System, Neon, } @@ -32,7 +31,6 @@ impl Theme { match s { "light" => Some(Self::Light), "dark" => Some(Self::Dark), - "system" => Some(Self::System), "neon" => Some(Self::Neon), _ => None, } From db2d22f999ac395b5a2216d9e8bfdac60d7dced8 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 14:34:35 -0500 Subject: [PATCH 33/51] Theme: add glass theme (CAJAFUERTE-inspired) alongside light/dark/neon --- frontend/src/lib/types.ts | 2 +- frontend/src/screens/SettingsScreen.tsx | 5 +- frontend/src/state/AppProvider.tsx | 4 +- frontend/src/styles.css | 649 ++++++++++++++++++++++++ src/main.rs | 2 +- src/settings.rs | 2 + 6 files changed, 658 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index f8e3884..08455e1 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -1,4 +1,4 @@ -export type Theme = 'light' | 'dark' | 'neon'; +export type Theme = 'light' | 'dark' | 'glass' | 'neon'; /** Lifecycle of the NIP-46 remote signer. */ export type SignerPhase = 'stopped' | 'connecting' | 'connected'; diff --git a/frontend/src/screens/SettingsScreen.tsx b/frontend/src/screens/SettingsScreen.tsx index c5c8ae2..a107a98 100644 --- a/frontend/src/screens/SettingsScreen.tsx +++ b/frontend/src/screens/SettingsScreen.tsx @@ -104,11 +104,12 @@ export function SettingsScreen() { > +

- “Light” and “Dark” follow your manual selection. “Neon” is a pure-black theme with - pink and purple accents. + “Light” and “Dark” follow your manual selection. “Glass” and “Neon” are aesthetic + themes with distinctive color palettes.

diff --git a/frontend/src/state/AppProvider.tsx b/frontend/src/state/AppProvider.tsx index d910afe..d7a5c5d 100644 --- a/frontend/src/state/AppProvider.tsx +++ b/frontend/src/state/AppProvider.tsx @@ -337,8 +337,8 @@ export function useApp(): AppContextValue { /** Apply the requested theme. */ export function applyTheme(theme: Theme): void { - if (theme === 'neon') { - document.documentElement.dataset.theme = 'neon'; + if (theme === 'glass' || theme === 'neon') { + document.documentElement.dataset.theme = theme; return; } document.documentElement.dataset.theme = theme; diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 61bdf7d..9708f92 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -62,29 +62,678 @@ Obsidian theme (MIT): black panels, deep-pink primary (#ff1493), purple secondary (#8b00ff family), yellow highlights (#ffd700). */ :root[data-theme='neon'] { + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --bg: #000000; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --surface: #0b0b0e; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --surface-2: #131318; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --surface-hover: #191921; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --border: #2b1224; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --border-strong: #4d1c3d; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --text: #f5f2f7; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --text-muted: #a89bb0; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --primary: #ff1493; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --primary-hover: #ff4fb0; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --primary-soft: #2b0a1e; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --on-primary: #ffffff; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --danger: #ff3355; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --danger-soft: #330810; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --warning: #ffd700; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --warning-soft: #332b03; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --success: #00e68a; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --success-soft: #04301f; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --info: #b388ff; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --info-soft: #1c1035; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --focus: #ff1493; + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 8px 24px rgba(255, 20, 147, 0.07); + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.85), 0 0 32px rgba(255, 20, 147, 0.12); + + /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ + :root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + } +} + +/* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ +:root[data-theme='glass'] { + --bg: #0d0d0d; + --surface: #1a1a1f; + --surface-2: #24242a; + --border: #3a3a44; + --border-strong: #4a4a5a; + --text: #e0e0e7; + --text-muted: #888899; + --primary: #7fdbff; + --primary-hover: #5bc0de; + --primary-soft: #1e2a3a; + --on-primary: #050512; + --danger: #ff6b6b; + --danger-soft: #332a2a; + --warning: #f0ad4f; + --warning-soft: #332a2a; + --success: #5cb85c; + --success-soft: #2ecc71; + --info: #5bc0de; + --info-soft: #1e2c3c; + --focus: #7fdbff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); + --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); } * { diff --git a/src/main.rs b/src/main.rs index 6a66b31..c919b04 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ Commands: relays disable Disable a relay relays test Test a relay connection settings get Show application settings - settings set theme + settings set theme settings set confirm settings set shorten delete-profile Delete a profile (moves it to undo stack) diff --git a/src/settings.rs b/src/settings.rs index 7293ee0..7af6ffe 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -22,6 +22,7 @@ impl RelayConfig { pub enum Theme { Light, Dark, + Glass, Neon, } @@ -31,6 +32,7 @@ impl Theme { match s { "light" => Some(Self::Light), "dark" => Some(Self::Dark), + "glass" => Some(Self::Glass), "neon" => Some(Self::Neon), _ => None, } From bfe14f0d97594a204423d36f78cabd2f3a022a5a Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 19:39:32 -0500 Subject: [PATCH 34/51] Fix glass theme: repair broken Default impl that blocked all builds Commit a01f258 removed Theme::System but missed Settings::default(), so the crate failed to compile and every desktop launch kept spawning a stale backend binary that rejected 'glass'. Also untangles the glass palette CSS that was duplicated as nested blocks inside the neon rule, and re-applies the theme reactively when settings change. --- frontend/src/state/AppProvider.tsx | 6 +- frontend/src/styles.css | 626 +---------------------------- src/main.rs | 2 +- src/settings.rs | 2 +- 4 files changed, 9 insertions(+), 627 deletions(-) diff --git a/frontend/src/state/AppProvider.tsx b/frontend/src/state/AppProvider.tsx index d7a5c5d..0970bf4 100644 --- a/frontend/src/state/AppProvider.tsx +++ b/frontend/src/state/AppProvider.tsx @@ -239,7 +239,11 @@ export function AppProvider({ children }: { children: ReactNode }) { const copyText = useCallback((text: string) => api.copyText(text), []); - useThemeSync(state?.settings.theme); + useEffect(() => { + if (state?.settings.theme) { + applyTheme(state.settings.theme); + } + }, [state?.settings.theme]); const value = useMemo( () => ({ diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 9708f92..60604c0 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -62,655 +62,33 @@ Obsidian theme (MIT): black panels, deep-pink primary (#ff1493), purple secondary (#8b00ff family), yellow highlights (#ffd700). */ :root[data-theme='neon'] { - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --bg: #000000; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --surface: #0b0b0e; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --surface-2: #131318; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --surface-hover: #191921; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --border: #2b1224; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --border-strong: #4d1c3d; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --text: #f5f2f7; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --text-muted: #a89bb0; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --primary: #ff1493; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --primary-hover: #ff4fb0; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --primary-soft: #2b0a1e; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --on-primary: #ffffff; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --danger: #ff3355; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --danger-soft: #330810; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --warning: #ffd700; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --warning-soft: #332b03; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --success: #00e68a; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --success-soft: #04301f; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --info: #b388ff; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --info-soft: #1c1035; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --focus: #ff1493; - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 8px 24px rgba(255, 20, 147, 0.07); - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.85), 0 0 32px rgba(255, 20, 147, 0.12); - - /* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ - :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; - --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; - --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); - } } -/* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ +/* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme + (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ :root[data-theme='glass'] { --bg: #0d0d0d; --surface: #1a1a1f; diff --git a/src/main.rs b/src/main.rs index c919b04..920afe9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -442,7 +442,7 @@ fn cli_settings(args: &[String]) -> Result { match key.as_str() { "theme" => { let theme = Theme::parse(value).ok_or_else(|| { - AppError::config("Theme must be one of: light, dark, system, neon") + AppError::config("Theme must be one of: light, dark, glass, neon") })?; app.settings.theme = theme; } diff --git a/src/settings.rs b/src/settings.rs index 7af6ffe..75ea91e 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -60,7 +60,7 @@ fn default_true() -> bool { impl Default for Settings { fn default() -> Self { Self { - theme: Theme::System, + theme: Theme::Light, confirm_before_publish: true, shorten_npub: true, relays: crate::relays::default_relays(), From 1aae81ca040612aaf0242816f22aac7f968534d8 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 19:40:05 -0500 Subject: [PATCH 35/51] Checkpoint: glass theme fix --- CHECKPOINT-encryption.md | 96 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 289ff65..0722ccd 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,98 @@ -# Checkpoint — Neon theme (2026-08-24) +# Checkpoint — Glass theme fix (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `bfe14f0` ("Fix glass theme: repair broken Default impl + that blocked all builds"). Before it: `db2d22f` (glass theme added), + `a01f258` (system theme removed), `c080a8d` (icon), `90d699c` (neon). +- Working tree: clean apart from this checkpoint update. Note: `concept3.svg` + still shows as deleted and `KeynectrAppIconPossibility02.jpeg` is untracked — + left exactly as the user had them, not committed. + +## What was completed + +1. **Root cause found: the repo did not compile since `a01f258`.** That commit + removed `Theme::System` from the enum but missed `Settings::default()` + (`src/settings.rs` still said `theme: Theme::System`). Every rebuild failed, + so the desktop app kept spawning an old `target/release/keynectr` binary + from before glass existed — which rejected `'glass'` on save ("nothing + happens") and explains the flaky "Rust backend exited unexpectedly" + launches. +2. **Fixes in `bfe14f0`:** + - `src/settings.rs`: default theme is now `Theme::Light`. + - `src/main.rs`: CLI error message lists `light, dark, glass, neon` + (no stale "system"). + - `frontend/src/styles.css`: removed ~600 lines of garbage — 24 broken + `:root[data-theme='glass']` blocks nested *inside* the neon rule (a bad + edit had interleaved them). One clean neon block + one clean glass block + remain, values preserved. + - `frontend/src/state/AppProvider.tsx`: theme now applies reactively — a + `useEffect` watches `state.settings.theme` and calls `applyTheme()`, + replacing the mount-only `useThemeSync()` call, so picking Glass/Neon + updates `data-theme` on `` immediately. +3. Rebuilt both artifacts the app actually runs: `cargo build --release` + (Electron spawns `target/release/keynectr`, see `electron/main.ts:82`) and + `npm run build` (renderer loads `dist/`). + +## Commits added most recently + +- `bfe14f0` Fix glass theme: repair broken Default impl that blocked all builds + +## Verification commands run (all green) + +Rust (repo root): + +``` +cargo test # 113 passed; 0 failed +cargo clippy --all-targets # only pre-existing warnings in src/profiles.rs +cargo fmt --check # clean +cargo build --release # success +``` + +Frontend (`frontend/`): + +``` +npm test # 14 files, 92 tests passed +npm run typecheck # clean +npm run lint # 0 errors +npm run format:check # clean +npm run electron:build # tsc electron main success +npm run build # vite build success +``` + +## How to use / reproduce + +```bash +cd ~/Projects/Nostr_Keynctr/frontend && npm start +``` + +Settings → Appearance → Theme → **Glass** applies immediately (dark panels, +cyan `#7fdbff` accents) and persists to settings.json. Neon/Light/Dark behave +the same way. + +CLI: + +```bash +B=~/Projects/Nostr_Keynctr/target/release/keynectr +$B settings set theme glass +``` + +## Notes & next steps + +- If themes ever seem ignored again, first check `cargo check` passes — a + broken build silently leaves the stale release binary in place. +- Pre-existing clippy warnings in `src/profiles.rs` remain untouched (user + asked for theme-only changes). +- Electron GPU/Wayland messages (`--ozone-platform=wayland ... Vulkan`) are + environmental noise on this laptop, not app errors. + +--- + +# Older checkpoint — Neon theme (2026-08-24) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From ecb666bc1dbf8cd87995a2d645bfb985a0c2e1df Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 19:44:11 -0500 Subject: [PATCH 36/51] Glass theme: real frosted-glass look MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Translucent surfaces with backdrop blur over an aurora gradient backdrop, hairline light borders, softened accent tints, frosted modal scrim, inverted sidebar logo. Scoped entirely to html[data-theme='glass'] — other themes untouched. --- frontend/src/styles.css | 89 ++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 60604c0..c2c7acb 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -87,31 +87,73 @@ --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.85), 0 0 32px rgba(255, 20, 147, 0.12); } -/* "Glass" — muted frosted aesthetic inspired by the Meridian Obsidian theme - (MIT): dark panels, cyan accents, and glass-like transparency simulation. */ +/* "Glass" — frosted-glass aesthetic inspired by the Meridian Obsidian theme + (MIT): translucent blurred panels floating over an aurora-lit deep + blue-black field, cyan accents, hairline light borders. */ :root[data-theme='glass'] { - --bg: #0d0d0d; - --surface: #1a1a1f; - --surface-2: #24242a; - --border: #3a3a44; - --border-strong: #4a4a5a; - --text: #e0e0e7; - --text-muted: #888899; + --bg: #0a0d13; + --surface: rgba(23, 29, 41, 0.58); + --surface-2: rgba(255, 255, 255, 0.05); + --surface-hover: rgba(255, 255, 255, 0.09); + --border: rgba(255, 255, 255, 0.1); + --border-strong: rgba(255, 255, 255, 0.2); + --text: #e8ecf4; + --text-muted: #96a2b6; --primary: #7fdbff; - --primary-hover: #5bc0de; - --primary-soft: #1e2a3a; - --on-primary: #050512; - --danger: #ff6b6b; - --danger-soft: #332a2a; - --warning: #f0ad4f; - --warning-soft: #332a2a; - --success: #5cb85c; - --success-soft: #2ecc71; - --info: #5bc0de; - --info-soft: #1e2c3c; + --primary-hover: #a3e5ff; + --primary-soft: rgba(127, 219, 255, 0.13); + --on-primary: #06121c; + --danger: #ff8080; + --danger-soft: rgba(255, 107, 107, 0.14); + --warning: #ffc46b; + --warning-soft: rgba(240, 173, 79, 0.15); + --success: #7ed99a; + --success-soft: rgba(92, 184, 92, 0.16); + --info: #82c9ea; + --info-soft: rgba(91, 192, 222, 0.14); --focus: #7fdbff; - --shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 8px 24px rgba(0, 0, 0, 0.4); - --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.5); + --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 36px rgba(0, 0, 0, 0.38); + --shadow-modal: 0 18px 56px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.07) inset; +} + +/* Glass scene: the aurora backdrop lives on ; panels above it are + translucent and blurred so the glow reads through them. */ +html[data-theme='glass'] body { + background: + radial-gradient(1000px 640px at 10% -10%, rgba(127, 219, 255, 0.15), transparent 60%), + radial-gradient(880px 560px at 92% 6%, rgba(150, 130, 255, 0.12), transparent 58%), + radial-gradient(760px 720px at 50% 120%, rgba(91, 192, 222, 0.1), transparent 62%), #0a0d13; +} + +html[data-theme='glass'] .app-shell, +html[data-theme='glass'] .main { + background: transparent; +} + +/* Frosted panes: every major surface blurs whatever sits behind it. */ +html[data-theme='glass'] .card, +html[data-theme='glass'] .sidebar, +html[data-theme='glass'] .modal, +html[data-theme='glass'] .empty-state, +html[data-theme='glass'] .profile-card, +html[data-theme='glass'] .compose-preview { + -webkit-backdrop-filter: blur(18px) saturate(160%); + backdrop-filter: blur(18px) saturate(160%); +} + +html[data-theme='glass'] input[type='text'], +html[data-theme='glass'] input[type='search'], +html[data-theme='glass'] input[type='password'], +html[data-theme='glass'] select, +html[data-theme='glass'] textarea { + background: rgba(255, 255, 255, 0.045); + border-color: rgba(255, 255, 255, 0.16); +} + +html[data-theme='glass'] .modal-backdrop { + background: rgba(4, 7, 12, 0.5); + -webkit-backdrop-filter: blur(6px); + backdrop-filter: blur(6px); } * { @@ -369,7 +411,8 @@ a { /* The artwork is black-on-white; flip it in dark themes so it stays black bird on dark tile instead of a glaring white square. */ html[data-theme='dark'] .sidebar-logo img, -html[data-theme='neon'] .sidebar-logo img { +html[data-theme='neon'] .sidebar-logo img, +html[data-theme='glass'] .sidebar-logo img { filter: invert(1); } From 0cab88364be8ab45530985817147860b74ae6beb Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 19:44:31 -0500 Subject: [PATCH 37/51] Checkpoint: glass theme look --- CHECKPOINT-encryption.md | 61 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 0722ccd..1995c76 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,63 @@ -# Checkpoint — Glass theme fix (2026-08-24) +# Checkpoint — Glass theme look (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `ecb666b` ("Glass theme: real frosted-glass look"). + Before it: `bfe14f0` (glass fix — broken Default impl), `1aae81c` + (checkpoint), `db2d22f` (glass added), `a01f258` (system removed). +- Working tree: clean apart from this checkpoint update. `concept3.svg` deleted + and `KeynectrAppIconPossibility02.jpeg` untracked remain as the user had them. + +## What was completed + +1. **Glass now looks like glass (`ecb666b`).** The first pass was only a flat + dark palette; the user said "it looks nothing like glass". Redone as real + glassmorphism, scoped 100% to `html[data-theme='glass']`: + - Aurora backdrop on `` (cyan/violet/blue radial glows over + `#0a0d13`); `.app-shell`/`.main` made transparent so it shows through. + - Translucent surfaces (`--surface` etc. as rgba) + `backdrop-filter: + blur(18px) saturate(160%)` on cards, sidebar, modals, empty states, + profile cards, compose preview. + - Hairline light borders, softened translucent accent tints, frosted modal + scrim (blur 6px), inputs at low-alpha white, sidebar logo inverted like + dark/neon. +2. Earlier this session (`bfe14f0`, see next checkpoint): root cause of "glass + does nothing" was a repo that hadn't compiled since `a01f258`. + +## Commits added most recently + +- `ecb666b` Glass theme: real frosted-glass look + +## Verification commands run (all green) + +Rust (repo root): `cargo test` 113 passed; `cargo fmt --check` clean. +Frontend (`frontend/`): `npm test` 92 passed; typecheck, lint, +format:check clean; `npm run build` rebuilt `dist/` (so plain `npm start` +serves the new look). + +## How to use / reproduce + +```bash +cd ~/Projects/Nostr_Keynctr/frontend && npm start +``` + +Settings → Appearance → Theme → **Glass**: frosted translucent panels over a +glowing deep-blue backdrop with cyan accents. + +## Notes & next steps + +- Blur strength lives in the `backdrop-filter` rules and glow colors in the + `html[data-theme='glass'] body` gradient — both near the top of + `frontend/src/styles.css`. +- Electron GPU/Wayland warnings on this laptop are environmental noise. + +--- + +# Older checkpoint — Glass theme fix (2026-08-24) A stopping point you can return to if this session is closed. Everything below was verified green at the moment this file was written. From dd50b24ec186261a21cfea057a868f65e08690d4 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 19:49:30 -0500 Subject: [PATCH 38/51] Rename glass theme display to Aurora; fix unreadable select options The theme dropdown popup inherited the near-transparent select background and rendered white-on-white under the glass theme. Options now get an explicit dark surface, plus color-scheme: dark so native controls render correctly. User-facing name is Aurora; the internal 'glass' id is unchanged so saved settings keep working. --- frontend/src/screens/SettingsScreen.tsx | 4 ++-- frontend/src/styles.css | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/frontend/src/screens/SettingsScreen.tsx b/frontend/src/screens/SettingsScreen.tsx index a107a98..0db240f 100644 --- a/frontend/src/screens/SettingsScreen.tsx +++ b/frontend/src/screens/SettingsScreen.tsx @@ -104,11 +104,11 @@ export function SettingsScreen() { > - +

- “Light” and “Dark” follow your manual selection. “Glass” and “Neon” are aesthetic + “Light” and “Dark” follow your manual selection. “Aurora” and “Neon” are aesthetic themes with distinctive color palettes.

diff --git a/frontend/src/styles.css b/frontend/src/styles.css index c2c7acb..947ee5e 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -87,9 +87,10 @@ --shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.85), 0 0 32px rgba(255, 20, 147, 0.12); } -/* "Glass" — frosted-glass aesthetic inspired by the Meridian Obsidian theme - (MIT): translucent blurred panels floating over an aurora-lit deep - blue-black field, cyan accents, hairline light borders. */ +/* "Aurora" theme (internal id "glass") — frosted-glass aesthetic inspired + by the Meridian Obsidian theme (MIT): translucent blurred panels floating + over an aurora-lit deep blue-black field, cyan accents, hairline light + borders. */ :root[data-theme='glass'] { --bg: #0a0d13; --surface: rgba(23, 29, 41, 0.58); @@ -114,8 +115,8 @@ --focus: #7fdbff; --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 36px rgba(0, 0, 0, 0.38); --shadow-modal: 0 18px 56px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.07) inset; + color-scheme: dark; } - /* Glass scene: the aurora backdrop lives on ; panels above it are translucent and blurred so the glow reads through them. */ html[data-theme='glass'] body { @@ -150,6 +151,14 @@ html[data-theme='glass'] textarea { border-color: rgba(255, 255, 255, 0.16); } +/* The native select popup inherits the select's near-transparent + background, which renders white with light text — give options an + explicit dark surface so entries stay readable. */ +html[data-theme='glass'] select option { + background-color: #141a26; + color: var(--text); +} + html[data-theme='glass'] .modal-backdrop { background: rgba(4, 7, 12, 0.5); -webkit-backdrop-filter: blur(6px); From ae8452602fb5fbb779da72433aa08696bcb8e837 Mon Sep 17 00:00:00 2001 From: Avi Date: Mon, 24 Aug 2026 19:49:52 -0500 Subject: [PATCH 39/51] Checkpoint: Aurora rename + dropdown fix --- CHECKPOINT-encryption.md | 56 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 1995c76..a87f763 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,58 @@ -# Checkpoint — Glass theme look (2026-08-24) +# Checkpoint — Aurora rename + dropdown fix (2026-08-24) + +A stopping point you can return to if this session is closed. Everything below was +verified green at the moment this file was written. + +## Where things are + +- Project: `/home/avi/Projects/Nostr_Keynctr` +- Git repo: `master` @ `dd50b24` ("Rename glass theme display to Aurora; fix + unreadable select options"). Before it: `ecb666b` (frosted-glass look), + `0cab883`/`1aae81c` (checkpoints), `bfe14f0` (glass fix). +- Working tree: clean apart from this checkpoint update. `concept3.svg` deleted + and `KeynectrAppIconPossibility02.jpeg` untracked remain as the user had them. + +## What was completed + +1. **Theme renamed to Aurora (`dd50b24`).** User picked "Aurora" from options + (Aurora/Frost/Glacier/Ice/keep). Display label only — internal id stays + `glass`, so existing settings.json values need no migration. +2. **Fixed unreadable theme dropdown (`dd50b24`).** Under the glass theme the + native `