diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 627c712..bd6db1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main, master, dev ] + branches: [ main ] pull_request: - branches: [ main, master ] + branches: [ main ] jobs: frontend: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed00ef4..3246291 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,21 +5,6 @@ on: tags: - 'v*' workflow_dispatch: - inputs: - release_title: - description: 'Custom Release Title (leave empty for default: "WEave vX.Y.Z")' - required: false - type: string - is_draft: - description: 'Publish as Draft' - required: false - type: boolean - default: false - is_prerelease: - description: 'Publish as Pre-release' - required: false - type: boolean - default: false permissions: contents: write @@ -27,7 +12,7 @@ permissions: attestations: write jobs: - publish-tauri: + release: name: Build & Publish Release runs-on: windows-latest steps: @@ -53,19 +38,6 @@ jobs: - name: Install frontend dependencies run: npm ci - - name: Determine Version & Title - id: version_info - shell: powershell - run: | - $ver = (Get-Content package.json | ConvertFrom-Json).version - echo "VERSION=$ver" >> $env:GITHUB_OUTPUT - - $customTitle = "${{ github.event.inputs.release_title }}" - if ([string]::IsNullOrWhiteSpace($customTitle)) { - $customTitle = "WEave v$ver" - } - echo "TITLE=$customTitle" >> $env:GITHUB_OUTPUT - - name: Build Tauri App run: npm run tauri build @@ -73,7 +45,7 @@ jobs: id: prepare_artifacts shell: powershell run: | - $ver = "${{ steps.version_info.outputs.VERSION }}" + $ver = (Get-Content package.json | ConvertFrom-Json).version $distDir = "dist_artifacts" New-Item -ItemType Directory -Force -Path $distDir @@ -102,47 +74,31 @@ jobs: } - name: Extract Release Notes from CHANGELOG.md - id: changelog + id: extract_notes shell: powershell run: | - $latestTagNotes = "" - if (Test-Path "CHANGELOG.md") { - $lines = Get-Content "CHANGELOG.md" - $extracted = @() - $started = $false - foreach ($line in $lines) { - if ($line -match '^##\s+\[?v?\d+') { - if ($started) { break } - $started = $true - continue - } - if ($started) { - $extracted += $line - } - } - $latestTagNotes = ($extracted -join "`n").Trim() - } - - if (-not $latestTagNotes) { - $latestTagNotes = "Release v${{ steps.version_info.outputs.VERSION }}" + $changelog = Get-Content CHANGELOG.md -Raw + # Match notes from the first `## [` heading until the next `## [` heading or end of file + if ($changelog -match "(?ms)^##\s+\[.*?\]\s*.*?\r?\n(.*?)(?=^##\s+\[|\z)") { + $notes = $matches[1].Trim() + Set-Content -Path "release_notes.md" -Value $notes -Encoding utf8 + Write-Host "Extracted release notes successfully." + } else { + Set-Content -Path "release_notes.md" -Value "Release ${{ github.ref_name }}" -Encoding utf8 + Write-Host "Fallback release notes used." } - [System.IO.File]::WriteAllText("CHANGELOG_GENERATED.md", $latestTagNotes, [System.Text.Encoding]::UTF8) - - name: Attest Build Artifacts uses: actions/attest-build-provenance@v2 with: subject-path: 'dist_artifacts/*' - - name: Create GitHub Release + - name: Publish GitHub Release uses: softprops/action-gh-release@v2 with: - tag_name: v${{ steps.version_info.outputs.VERSION }} - name: "${{ steps.version_info.outputs.TITLE }}" - body_path: CHANGELOG_GENERATED.md - draft: ${{ github.event.inputs.is_draft || false }} - prerelease: ${{ github.event.inputs.is_prerelease || false }} - files: | - dist_artifacts/* + tag_name: ${{ github.ref_name }} + name: WEave ${{ github.ref_name }} + body_path: release_notes.md + files: dist_artifacts/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f45f5..c19fddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,34 +1,49 @@ # Changelog +## [4.11.1](https://github.com/psyattack/WEave/compare/v4.11.0...v4.11.1) - 2026-07-25 + +### Fixed +- Fixed an issue where selecting an option from a dropdown menu in Settings would unexpectedly close the entire dialog. + +
Detailed Changelog + +### Changed +- **release:** Switch release workflow to tag-based GitHub releases (by @psyattack in https://github.com/psyattack/WEave/commit/830e16c) +- **ci:** Integrate semantic-release and refactor bump script (by @psyattack in https://github.com/psyattack/WEave/commit/c4bde98) +- **release:** Extract release notes directly from CHANGELOG.md (by @psyattack in https://github.com/psyattack/WEave/commit/ac0d1ed) + +### Fixed +- **settings:** Prevent select dropdown from closing parent dialog (by @psyattack in https://github.com/psyattack/WEave/commit/62b4c1e) + +
+ ## [4.11.0] - 2026-07-24 ### Added -- Combined "Download & Release Notes" action button in the update dialog. -- Header icons for UpdateDialog and ParserDebugDialog. +- Direct button to download updates and view release notes on GitHub from the update dialog. ### Fixed -- Fixed automatic update modal popup on startup when a new release is available. -- Prevented blank window flash on application launch by holding window visibility until React UI is ready. -- Fixed unexpected focus rings on dialog opening and closing. +- Prevented a brief blank window flash when launching the application. +- Fixed update detection on startup to reliably display the update dialog whenever a new version is available. -### Changed -- Streamlined UpdateDialog by removing release notes details and custom Markdown dependencies. -- Added NOTICE.md for third-party license compliance. +### Improved +- Polished UI elements including cleaner dropdown outlines, improved pagination spacing, and simplified hotkey tooltips. +- Updated Legal and Info dialogs to include comprehensive third-party software licensing details.
Detailed Changelog ### Added -- feat(updater): Improve update dialog, auto-check flow and focus handling ([edc286c](https://github.com/psyattack/WEave/commit/edc286c)) +- feat(updater): Improve update dialog, auto-check flow and focus handling ([edc286c](https://github.com/psyattack/WEave/commit/edc286c)) by @psyattack ### Changed -- chore(release): bump version to 4.11.0 ([3ec8041](https://github.com/psyattack/WEave/commit/3ec8041)) -- style(pagination): Improve spacing around page slash separator ([1ca2259](https://github.com/psyattack/WEave/commit/1ca2259)) -- style(ui): Remove focus outline from selects and tooltip from hotkeys ([2fb28dd](https://github.com/psyattack/WEave/commit/2fb28dd)) -- refactor(dialogs): Update Legal and Info dialogs and add NOTICE.md ([73607bc](https://github.com/psyattack/WEave/commit/73607bc)) -- chore: remove knip from repository ([1189551](https://github.com/psyattack/WEave/commit/1189551)) +- chore(release): bump version to 4.11.0 ([3ec8041](https://github.com/psyattack/WEave/commit/3ec8041)) by @psyattack +- style(pagination): Improve spacing around page slash separator ([1ca2259](https://github.com/psyattack/WEave/commit/1ca2259)) by @psyattack +- style(ui): Remove focus outline from selects and tooltip from hotkeys ([2fb28dd](https://github.com/psyattack/WEave/commit/2fb28dd)) by @psyattack +- refactor(dialogs): Update Legal and Info dialogs and add NOTICE.md ([73607bc](https://github.com/psyattack/WEave/commit/73607bc)) by @psyattack +- chore: remove knip from repository ([1189551](https://github.com/psyattack/WEave/commit/1189551)) by @psyattack ### Fixed -- fix(window): Prevent blank window flash on app startup ([406ff22](https://github.com/psyattack/WEave/commit/406ff22)) +- fix(window): Prevent blank window flash on app startup ([406ff22](https://github.com/psyattack/WEave/commit/406ff22)) by @psyattack
@@ -44,11 +59,11 @@
Detailed Changelog ### Changed -- chore(release): bump version to 4.10.3 ([78219c8](https://github.com/psyattack/WEave/commit/78219c8)) -- ci(release): skip version header in generated release body ([eac2d0a](https://github.com/psyattack/WEave/commit/eac2d0a)) -- chore(release): bump version to 4.10.2 ([bfa471d](https://github.com/psyattack/WEave/commit/bfa471d)) -- ci: add GitHub Actions workflows for CI and release ([ed6ce39](https://github.com/psyattack/WEave/commit/ed6ce39)) -- docs(readme): Update features description and formatting ([9b68fb1](https://github.com/psyattack/WEave/commit/9b68fb1)) -- chore: Update documentation structure and filter cookie store logs ([dde6819](https://github.com/psyattack/WEave/commit/dde6819)) +- chore(release): bump version to 4.10.3 ([78219c8](https://github.com/psyattack/WEave/commit/78219c8)) by @psyattack +- ci(release): skip version header in generated release body ([eac2d0a](https://github.com/psyattack/WEave/commit/eac2d0a)) by @psyattack +- chore(release): bump version to 4.10.2 ([bfa471d](https://github.com/psyattack/WEave/commit/bfa471d)) by @psyattack +- ci: add GitHub Actions workflows for CI and release ([ed6ce39](https://github.com/psyattack/WEave/commit/ed6ce39)) by @psyattack +- docs(readme): Update features description and formatting ([9b68fb1](https://github.com/psyattack/WEave/commit/9b68fb1)) by @psyattack +- chore: Update documentation structure and filter cookie store logs ([dde6819](https://github.com/psyattack/WEave/commit/dde6819)) by @psyattack
diff --git a/README.md b/README.md index 0d6b9c6..64cd3f5 100644 --- a/README.md +++ b/README.md @@ -142,19 +142,15 @@ npm run tauri build npm run test # Frontend tests cd src-tauri && cargo test # Backend tests -# Checks backend (personal recommendation) +# Checks backend cargo check -cargo machete -cargo clippy -- -W dead_code -cargo +nightly udeps -# Checks frontend (personal recommendation) -npx knip +# Checks frontend npm run typecheck npm run lint -# Bump version -npm run bump -- +# Bump +npm run bump ``` diff --git a/README.ru.md b/README.ru.md index 1e31839..57c07a8 100644 --- a/README.ru.md +++ b/README.ru.md @@ -142,19 +142,15 @@ npm run tauri build npm run test # Тесты frontend cd src-tauri && cargo test # Тесты backend -# Проверки backend (личная рекомендация) +# Проверки backend cargo check -cargo machete -cargo clippy -- -W dead_code -cargo +nightly udeps -# Проверки frontend (личная рекомендация) -npx knip +# Проверки frontend npm run typecheck npm run lint -# Сменить версию -npm run bump -- +# Bump +npm run bump ``` diff --git a/bump.mjs b/bump.mjs new file mode 100644 index 0000000..d28fd89 --- /dev/null +++ b/bump.mjs @@ -0,0 +1,365 @@ +#!/usr/bin/env node + +/** + * Usage: + * node bump.mjs -> Auto-determines next SemVer based on git commit history + * node bump.mjs 4.12.0 -> Manually specifies the target version + * + * Updates version strings across all locations, generates local Detailed Changelog, + * updates CHANGELOG.md, pauses for user confirmation/editing, then stages files, + * creates a git commit, tags the release, and pushes to remote. + */ + +import { readFileSync, writeFileSync, existsSync } from "fs"; +import { execSync } from "child_process"; +import readline from "readline"; + +const REPO_URL = "https://github.com/psyattack/WEave"; +const authorCache = new Map(); + +function waitForEnter(query) { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + return new Promise((resolve) => + rl.question(query, () => { + rl.close(); + resolve(); + }), + ); +} + +async function resolveAuthor(hash, authorName, authorEmail) { + const cacheKey = `${authorName}<${authorEmail}>`; + if (authorCache.has(cacheKey)) { + return authorCache.get(cacheKey); + } + + // 1. Check GitHub noreply email + if (authorEmail && authorEmail.includes("@users.noreply.github.com")) { + const match = authorEmail.match(/^(?:\d+\+)?([^@]+)@users\.noreply\.github\.com$/); + if (match && match[1]) { + const handle = `@${match[1]}`; + authorCache.set(cacheKey, handle); + return handle; + } + } + + // 2. Query GitHub API for commit author login + try { + const repoMatch = REPO_URL.match(/github\.com\/([^/]+\/[^/]+)/); + if (repoMatch && repoMatch[1]) { + const repo = repoMatch[1]; + const res = await fetch(`https://api.github.com/repos/${repo}/commits/${hash}`, { + headers: { "User-Agent": "Node-WEave-Release" }, + }); + if (res.ok) { + const data = await res.json(); + if (data.author && data.author.login) { + const handle = `@${data.author.login}`; + authorCache.set(cacheKey, handle); + return handle; + } + } + } + } catch { + // Offline or API error fallback + } + + // 3. Fallback: plain author name without @ + const fallback = authorName ? authorName.trim() : ""; + authorCache.set(cacheKey, fallback); + return fallback; +} + +async function formatDetailedChangelog(rawEntries) { + // Strict Keep a Changelog categories + const categories = { + feat: "Added", + feature: "Added", + fix: "Fixed", + bugfix: "Fixed", + perf: "Changed", + refactor: "Changed", + style: "Changed", + chore: "Changed", + build: "Changed", + ci: "Changed", + docs: "Changed", + deprecate: "Deprecated", + deprecated: "Deprecated", + remove: "Removed", + revert: "Removed", + sec: "Security", + security: "Security", + }; + + const grouped = {}; + + for (const entry of rawEntries) { + const lines = entry.trim().split(/\r?\n/); + if (lines.length < 4) continue; + + const hash = lines[0].trim(); + const shortHash = hash.slice(0, 7); + const subject = lines[1].trim(); + const authorName = lines[2].trim(); + const authorEmail = lines[3].trim(); + + if (!subject) continue; + + // Parse conventional commit: type(scope): message + const match = subject.match(/^(\w+)(?:\(([^)]+)\))?:\s*(.+)$/); + if (!match) continue; + + const rawType = match[1].toLowerCase(); + const scope = match[2] || null; + let title = match[3]; + + // Filter out release commits + if (rawType === "chore" && title.startsWith("bump version to")) { + continue; + } + + const catName = categories[rawType] || "Changed"; + if (!grouped[catName]) { + grouped[catName] = []; + } + + const authorStr = await resolveAuthor(hash, authorName, authorEmail); + + // Extract PR number if present e.g. (#12) or #12 + const prMatch = title.match(/^(.*?)\s*\((?:#|PR\s*)(\d+)\)$/i) || title.match(/^(.*?)\s*#(\d+)$/i); + let linkStr; + if (prMatch) { + title = prMatch[1].trim(); + const prNum = prMatch[2]; + linkStr = `${REPO_URL}/pull/${prNum}`; + } else { + linkStr = `${REPO_URL}/commit/${shortHash}`; + } + + const scopeStr = scope ? `**${scope}:** ` : ""; + const authorPart = authorStr ? `by ${authorStr} in ` : "in "; + const lineItem = `- ${scopeStr}${title} (${authorPart}${linkStr})`; + + grouped[catName].push(lineItem); + } + + const categoryOrder = ["Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"]; + const categoryKeys = categoryOrder.filter((cat) => grouped[cat] && grouped[cat].length > 0); + + if (categoryKeys.length === 0) { + return ""; + } + + let markdown = "
Detailed Changelog\n\n"; + for (let i = 0; i < categoryKeys.length; i++) { + const cat = categoryKeys[i]; + markdown += `### ${cat}\n`; + for (const item of grouped[cat]) { + markdown += `${item}\n`; + } + if (i < categoryKeys.length - 1) { + markdown += "\n"; + } + } + markdown += "\n
"; + + return markdown; +} + +async function main() { + // 1. Read current version from package.json + const pkg = JSON.parse(readFileSync("package.json", "utf-8")); + const currentVersion = pkg.version; + + let newVersion = process.argv[2]; + + let prevTag = ""; + try { + prevTag = execSync("git describe --tags --abbrev=0").toString().trim(); + } catch { + // No tags found + } + + const range = prevTag ? `${prevTag}..HEAD` : "HEAD"; + let rawLog; + try { + rawLog = execSync( + `git log ${range} --pretty=format:"%H%n%s%n%an%n%ae%n-END-"`, + ).toString(); + } catch { + rawLog = ""; + } + + const entries = rawLog.split("-END-").filter((e) => e.trim()); + + if (!newVersion) { + // Auto-determine version if not provided manually + let bumpType = "patch"; // default + + for (const entry of entries) { + const subject = entry.trim().split(/\r?\n/)[1] || ""; + if (/BREAKING CHANGE|^\w+(\(.*\))?!:/i.test(subject)) { + bumpType = "major"; + break; + } else if (/^feat(\(.*\))?:/i.test(subject)) { + bumpType = "minor"; + } + } + + const parts = currentVersion.split(".").map(Number); + let [major, minor, patch] = parts; + + if (bumpType === "major") { + major += 1; + minor = 0; + patch = 0; + } else if (bumpType === "minor") { + minor += 1; + patch = 0; + } else { + patch += 1; + } + + newVersion = `${major}.${minor}.${patch}`; + console.log( + `Auto-determined next version: ${newVersion} (${bumpType} release based on git history)`, + ); + } else { + if (!/^\d+\.\d+\.\d+/.test(newVersion)) { + console.error( + "ERROR: Invalid semver format provided. Example: node bump.mjs 4.12.0", + ); + process.exit(1); + } + console.log(`Using manually specified version: ${newVersion}`); + } + + // 2. Generate local Detailed Changelog + console.log(`\nGenerating Detailed Changelog...`); + const detailedChangelog = await formatDetailedChangelog(entries); + + // 3. Update version strings across files + const files = [ + { + path: "package.json", + pattern: new RegExp('("version"\\s*:\\s*")\\d+\\.\\d+\\.\\d+(")'), + replace: `$1${newVersion}$2`, + }, + { + path: "src-tauri/Cargo.toml", + pattern: new RegExp('^(version\\s*=\\s*")\\d+\\.\\d+\\.\\d+(")', "m"), + replace: `$1${newVersion}$2`, + }, + { + path: "src-tauri/tauri.conf.json", + pattern: new RegExp('("version"\\s*:\\s*")\\d+\\.\\d+\\.\\d+(")'), + replace: `$1${newVersion}$2`, + }, + { + path: "src-tauri/src/core/constants.rs", + pattern: new RegExp( + '^(pub const APP_VERSION: &str = ")\\d+\\.\\d+\\.\\d+(")', + "m", + ), + replace: `$1${newVersion}$2`, + }, + ]; + + console.log(`\nBumping version from ${currentVersion} to ${newVersion}...\n`); + + for (const file of files) { + const content = readFileSync(file.path, "utf-8"); + + if (!file.pattern.test(content)) { + console.error(`ERROR: Could not find version pattern in ${file.path}`); + process.exit(1); + } + + const updated = content.replace(file.pattern, file.replace); + writeFileSync(file.path, updated, "utf-8"); + + const oldMatch = content.match(file.pattern); + const oldVersion = oldMatch + ? (oldMatch[0].match(/\d+\.\d+\.\d+/)?.[0] ?? "???") + : "???"; + + console.log(` ${file.path}: ${oldVersion} → ${newVersion}`); + } + + // 4. Always update or create CHANGELOG.md with Detailed Changelog + const today = new Date().toISOString().split("T")[0]; + const compareUrl = prevTag ? `${REPO_URL}/compare/${prevTag}...v${newVersion}` : null; + const versionLink = compareUrl ? `[${newVersion}](${compareUrl})` : `[${newVersion}]`; + const header = `## ${versionLink} - ${today}`; + + let changelogContent = existsSync("CHANGELOG.md") + ? readFileSync("CHANGELOG.md", "utf-8") + : "# Changelog\n\n"; + + if (changelogContent.includes(`## [${newVersion}]`)) { + // If version section exists (e.g. user manually wrote human notes) + if (detailedChangelog && !changelogContent.includes("Detailed Changelog")) { + changelogContent = changelogContent.replace( + new RegExp(`(##\\s*\\[${newVersion}\\].*?)(\\r?\\n(?=##\\s*\\[|\\z))`, "s"), + `$1\n\n${detailedChangelog.trim()}\n`, + ); + writeFileSync("CHANGELOG.md", changelogContent, "utf-8"); + console.log(` CHANGELOG.md: Appended Detailed Changelog to ## [${newVersion}]`); + } + } else { + // Create new version section with Detailed Changelog + const newSection = `${header}\n\n${detailedChangelog ? detailedChangelog.trim() + "\n" : ""}`; + if (/# Changelog\r?\n/.test(changelogContent)) { + changelogContent = changelogContent.replace( + /# Changelog\r?\n/, + `# Changelog\n\n${newSection}`, + ); + } else { + changelogContent = `${newSection}${changelogContent}`; + } + writeFileSync("CHANGELOG.md", changelogContent, "utf-8"); + console.log(` CHANGELOG.md: Added section ## [${newVersion}] with Detailed Changelog`); + } + + console.log(`\nSyncing package-lock.json...`); + execSync("npm install --package-lock-only --ignore-scripts", { + stdio: "inherit", + }); + + console.log(`\nSyncing Cargo.lock...`); + execSync("cargo check", { cwd: "src-tauri", stdio: "inherit" }); + + console.log(`\n================================================================`); + console.log(`CHANGELOG.md has been created/updated with version ## [${newVersion}] and Detailed Changelog.`); + console.log(`You can now inspect or edit CHANGELOG.md.`); + console.log(`================================================================\n`); + + await waitForEnter("Press ENTER when ready to commit, tag, and push to remote... "); + + console.log(`\nStaging files...`); + execSync( + "git add package.json package-lock.json src-tauri/Cargo.toml src-tauri/Cargo.lock src-tauri/tauri.conf.json src-tauri/src/core/constants.rs CHANGELOG.md", + { stdio: "inherit" }, + ); + + const commitMsg = `chore(release): bump version to ${newVersion}`; + console.log(`\nCreating commit: "${commitMsg}"...`); + execSync(`git commit -m "${commitMsg}"`, { stdio: "inherit" }); + + const tagName = `v${newVersion}`; + console.log(`\nCreating git tag: ${tagName}...`); + execSync(`git tag ${tagName} -m "Release ${tagName}"`, { stdio: "inherit" }); + + console.log(`\nPushing commit and tag to remote...`); + execSync(`git push origin HEAD ${tagName}`, { stdio: "inherit" }); + + console.log( + `\nSuccessfully bumped to ${newVersion}, tagged ${tagName}, and pushed to remote!`, + ); +} + +main().catch(console.error); diff --git a/package-lock.json b/package-lock.json index 1ecbd80..cff7908 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "weave", - "version": "4.11.0", + "version": "4.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "weave", - "version": "4.11.0", + "version": "4.11.1", "dependencies": { "@radix-ui/react-dialog": "^1.1.17", "@radix-ui/react-dropdown-menu": "^2.1.18", @@ -5100,9 +5100,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "dev": true, "funding": [ { @@ -5312,9 +5312,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "dev": true, "funding": [ { @@ -5332,7 +5332,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/package.json b/package.json index 1d4e750..146a855 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "weave", "private": true, - "version": "4.11.0", + "version": "4.11.1", "type": "module", "scripts": { "dev": "vite", @@ -11,7 +11,7 @@ "lint": "eslint src", "typecheck": "tsc --noEmit", "test": "vitest run", - "bump": "node version.mjs" + "bump": "node bump.mjs" }, "dependencies": { "@radix-ui/react-dialog": "^1.1.17", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0c6af21..5c7a466 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -5429,7 +5429,7 @@ dependencies = [ [[package]] name = "weave" -version = "4.11.0" +version = "4.11.1" dependencies = [ "aes-gcm", "anyhow", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5635b43..08ab576 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "weave" -version = "4.11.0" +version = "4.11.1" description = "WEave" authors = ["psyattack"] edition = "2021" diff --git a/src-tauri/src/core/constants.rs b/src-tauri/src/core/constants.rs index 5d07667..06f0ca4 100644 --- a/src-tauri/src/core/constants.rs +++ b/src-tauri/src/core/constants.rs @@ -1,5 +1,5 @@ pub const APP_NAME: &str = "WEave"; -pub const APP_VERSION: &str = "4.11.0"; +pub const APP_VERSION: &str = "4.11.1"; pub const STEAM_APP_ID: &str = "431960"; pub const UPDATE_REPO_OWNER: &str = "psyattack"; pub const UPDATE_REPO_NAME: &str = "WEave"; diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b6a45a5..71819f4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "WEave", - "version": "4.11.0", + "version": "4.11.1", "identifier": "WEave", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/components/common/Dialog.tsx b/src/components/common/Dialog.tsx index 0d9f6c2..852deab 100644 --- a/src/components/common/Dialog.tsx +++ b/src/components/common/Dialog.tsx @@ -66,7 +66,7 @@ export default function Dialog({ exit={{ opacity: 0, y: 8, scale: 0.97 }} transition={{ duration: 0.18, ease: [0.2, 0.8, 0.2, 1] }} className={cn( - "pointer-events-auto relative flex max-h-[calc(100vh-2rem)] w-full flex-col overflow-hidden rounded-xl border border-white/10 bg-surface/60 shadow-[0_16px_40px_rgba(0,0,0,0.6)] backdrop-blur-2xl outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0", + "pointer-events-auto relative flex max-h-[calc(100vh-2rem)] w-full flex-col overflow-hidden rounded-xl border border-white/10 bg-surface/60 shadow-[0_16px_40px_rgba(0,0,0,0.6)] backdrop-blur-2xl outline-none focus:ring-0 focus:outline-none focus-visible:ring-0 focus-visible:outline-none", SIZES[size], )} > diff --git a/src/components/common/Select.tsx b/src/components/common/Select.tsx index 0e94667..827a696 100644 --- a/src/components/common/Select.tsx +++ b/src/components/common/Select.tsx @@ -1,6 +1,6 @@ import * as RadixSelect from "@radix-ui/react-select"; import { Check, ChevronDown } from "lucide-react"; -import { ReactNode } from "react"; +import { ReactNode, useState } from "react"; import { cn } from "@/lib/utils"; @@ -17,6 +17,7 @@ interface Props { placeholder?: string; className?: string; icon?: ReactNode; + closeOnSelectionOnly?: boolean; } const EMPTY_SENTINEL = "__empty__"; @@ -29,26 +30,32 @@ export default function Select({ placeholder, className, icon, + closeOnSelectionOnly = false, }: Props) { const normalized = value === "" ? EMPTY_SENTINEL : value; + const [open, setOpen] = useState(false); return ( onValueChange(v === EMPTY_SENTINEL ? "" : v)} - onOpenChange={(open) => { - if (!open) { - setTimeout(() => { - if (document.activeElement instanceof HTMLElement) { - document.activeElement.blur(); - } - }, 0); + open={open} + onValueChange={(v) => { + onValueChange(v === EMPTY_SENTINEL ? "" : v); + setOpen(false); + }} + onOpenChange={(newOpen) => { + if (closeOnSelectionOnly) { + if (newOpen) { + setOpen(true); + } + } else { + setOpen(newOpen); } }} > @@ -61,12 +68,21 @@ export default function Select({ position="popper" sideOffset={6} className="z-50 min-w-40 overflow-hidden rounded-md border border-border bg-surface shadow-card-hover" + onPointerDownOutside={(e) => { + if (closeOnSelectionOnly) e.preventDefault(); + }} > {options.map((opt) => ( setOpen(false)} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + setOpen(false); + } + }} className="flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-highlighted:bg-surface-raised" > diff --git a/src/components/common/details/PresetSettings.tsx b/src/components/common/details/PresetSettings.tsx index a4c80b4..c6cbd08 100644 --- a/src/components/common/details/PresetSettings.tsx +++ b/src/components/common/details/PresetSettings.tsx @@ -192,7 +192,7 @@ export default function PresetSettings({ item, open }: PresetSettingsProps) { } e.target.blur(); }} - className="w-full cursor-pointer appearance-none rounded-md border border-white/5 bg-black/40 px-3 py-1.5 text-sm text-foreground shadow-sm backdrop-blur-xl transition-colors hover:bg-black/60 focus:outline-none focus:ring-0" + className="w-full cursor-pointer appearance-none rounded-md border border-white/5 bg-black/40 px-3 py-1.5 text-sm text-foreground shadow-sm backdrop-blur-xl transition-colors hover:bg-black/60 focus:ring-0 focus:outline-none" > {options.map((opt: string, i: number) => { const optVal = optionValues ? optionValues[i] : i; diff --git a/src/components/dialogs/LegalDialog.tsx b/src/components/dialogs/LegalDialog.tsx index a982bdb..09ab8e9 100644 --- a/src/components/dialogs/LegalDialog.tsx +++ b/src/components/dialogs/LegalDialog.tsx @@ -126,7 +126,7 @@ export default function LegalDialog({

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

-

+

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

diff --git a/src/components/dialogs/UpdateDialog.tsx b/src/components/dialogs/UpdateDialog.tsx index 16328e0..38ab534 100644 --- a/src/components/dialogs/UpdateDialog.tsx +++ b/src/components/dialogs/UpdateDialog.tsx @@ -99,7 +99,7 @@ export default function UpdateDialog({ open, onOpenChange }: Props) {