Merge branch 'main' into dev

This commit is contained in:
Ly-sec 2025-08-17 21:56:08 +02:00
commit ba0ea45c80
5 changed files with 72 additions and 179 deletions

View file

@ -27,30 +27,19 @@ jobs:
mv *.tar.gz ${{ github.workspace }}/
- name: Generate release notes
id: release_notes
run: |
PREV_TAG=$(git describe --tags --abbrev=0 @^ 2>/dev/null || echo "")
RANGE="${PREV_TAG}..HEAD"
PR_NOTES=""
COMMIT_NOTES=""
git log $RANGE --pretty=format:"%H|%s|%an" | while IFS='|' read -r SHA MSG AUTHOR; do
SHORT_MSG=$(echo "$MSG" | cut -c1-80)
if [[ "$MSG" =~ Merge\ pull\ request\ \#([0-9]+) ]]; then
PR_NUM="${BASH_REMATCH[1]}"
PR_TITLE=$(echo "$SHORT_MSG" | sed -E "s/Merge pull request #$PR_NUM //")
PR_NOTES+="- [PR #$PR_NUM](https://github.com/${GITHUB_REPOSITORY}/pull/$PR_NUM): $PR_TITLE by $AUTHOR\n"
else
COMMIT_NOTES+="- [$SHA](https://github.com/${GITHUB_REPOSITORY}/commit/$SHA): $SHORT_MSG by $AUTHOR\n"
fi
done
NOTES="### Merged PRs\n$PR_NOTES\n### Direct commits\n$COMMIT_NOTES"
echo "RELEASE_NOTES<<EOF" >> $GITHUB_ENV
echo -e "$NOTES" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
PREV_TAG=$(git describe --tags --abbrev=0 ${{ github.ref }}^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
RANGE=$(git rev-list --max-parents=0 HEAD)..HEAD
else
RANGE=$PREV_TAG..HEAD
fi
echo "# Release ${{ github.ref_name }}" > release_notes.md
echo "" >> release_notes.md
echo "## Changes since $PREV_TAG" >> release_notes.md
echo "" >> release_notes.md
git log $RANGE --pretty=format:"- %s ([%h](https://github.com/${{ github.repository }}/commit/%H)) by %an" >> release_notes.md
echo "" >> release_notes.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
@ -58,6 +47,6 @@ jobs:
files: |
noctalia-${{ github.ref_name }}.tar.gz
noctalia-latest.tar.gz
body: ${{ env.RELEASE_NOTES }}
body_path: release_notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,120 +0,0 @@
// Math helper functions for calculator functionality
var MathHelper = {
// Basic arithmetic operations
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => b !== 0 ? a / b : NaN,
// Power and roots
pow: (base, exponent) => Math.pow(base, exponent),
sqrt: (x) => x >= 0 ? Math.sqrt(x) : NaN,
cbrt: (x) => Math.cbrt(x),
// Trigonometric functions (in radians)
sin: (x) => Math.sin(x),
cos: (x) => Math.cos(x),
tan: (x) => Math.tan(x),
asin: (x) => Math.asin(x),
acos: (x) => Math.acos(x),
atan: (x) => Math.atan(x),
// Logarithmic functions
log: (x) => x > 0 ? Math.log(x) : NaN,
log10: (x) => x > 0 ? Math.log10(x) : NaN,
log2: (x) => x > 0 ? Math.log2(x) : NaN,
// Other mathematical functions
abs: (x) => Math.abs(x),
floor: (x) => Math.floor(x),
ceil: (x) => Math.ceil(x),
round: (x) => Math.round(x),
min: (...args) => Math.min(...args),
max: (...args) => Math.max(...args),
// Constants
PI: Math.PI,
E: Math.E,
// Factorial
factorial: (n) => {
if (n < 0 || n !== Math.floor(n)) return NaN;
if (n === 0 || n === 1) return 1;
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
},
// Percentage
percent: (value, total) => (value / total) * 100,
// Degrees to radians and vice versa
toRadians: (degrees) => degrees * (Math.PI / 180),
toDegrees: (radians) => radians * (180 / Math.PI),
// Safe evaluation with math functions
evaluate: (expression) => {
try {
// Replace common math functions with MathHelper equivalents
let processedExpr = expression
.replace(/\bpi\b/gi, 'MathHelper.PI')
.replace(/\be\b/gi, 'MathHelper.E')
.replace(/\bsin\b/gi, 'MathHelper.sin')
.replace(/\bcos\b/gi, 'MathHelper.cos')
.replace(/\btan\b/gi, 'MathHelper.tan')
.replace(/\basin\b/gi, 'MathHelper.asin')
.replace(/\bacos\b/gi, 'MathHelper.acos')
.replace(/\batan\b/gi, 'MathHelper.atan')
.replace(/\blog\b/gi, 'MathHelper.log')
.replace(/\blog10\b/gi, 'MathHelper.log10')
.replace(/\blog2\b/gi, 'MathHelper.log2')
.replace(/\bsqrt\b/gi, 'MathHelper.sqrt')
.replace(/\bcbrt\b/gi, 'MathHelper.cbrt')
.replace(/\bpow\b/gi, 'MathHelper.pow')
.replace(/\babs\b/gi, 'MathHelper.abs')
.replace(/\bfloor\b/gi, 'MathHelper.floor')
.replace(/\bceil\b/gi, 'MathHelper.ceil')
.replace(/\bround\b/gi, 'MathHelper.round')
.replace(/\bmin\b/gi, 'MathHelper.min')
.replace(/\bmax\b/gi, 'MathHelper.max')
.replace(/\bfactorial\b/gi, 'MathHelper.factorial')
.replace(/\bpercent\b/gi, 'MathHelper.percent')
.replace(/\btoRadians\b/gi, 'MathHelper.toRadians')
.replace(/\btoDegrees\b/gi, 'MathHelper.toDegrees');
// Evaluate the expression
const result = Function('MathHelper', 'return ' + processedExpr)(MathHelper);
// Check if result is valid
if (isNaN(result) || !isFinite(result)) {
return null;
}
return result;
} catch (error) {
return null;
}
},
// Format result for display
formatResult: (result) => {
if (result === null || isNaN(result) || !isFinite(result)) {
return "Error";
}
// For very large or small numbers, use scientific notation
if (Math.abs(result) >= 1e10 || (Math.abs(result) < 1e-10 && result !== 0)) {
return result.toExponential(6);
}
// For integers, don't show decimal places
if (Number.isInteger(result)) {
return result.toString();
}
// For decimals, limit to 8 significant digits
return parseFloat(result.toPrecision(8)).toString();
}
};

View file

@ -11,7 +11,6 @@ import qs.Services
import qs.Widgets
import "../../Helpers/FuzzySort.js" as Fuzzysort
import "../../Helpers/MathHelper.js" as MathHelper
NLoader {
id: appLauncher
@ -188,26 +187,54 @@ NLoader {
// Handle calculator
if (query.startsWith(">calc")) {
var expr = searchText.slice(5).trim()
if (expr && isMathExpression(expr)) {
var value = safeEval(expr)
if (value !== null && value !== undefined && value !== "") {
var formattedResult = MathHelper.MathHelper.formatResult(value)
if (expr && expr !== "") {
try {
// Simple evaluation - only allow basic math operations
var sanitizedExpr = expr.replace(/[^0-9+\-*/().\s]/g, '')
var result = eval(sanitizedExpr)
if (isFinite(result) && !isNaN(result)) {
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, '')
results.push({
"isCalculator": true,
"name": `${expr} = ${displayResult}`,
"result": result,
"expr": expr,
"icon": "calculate",
"execute": function () {
Quickshell.clipboardText = displayResult
copyText(displayResult)
Quickshell.execDetached(["notify-send", "Calculator", `${expr} = ${displayResult} (copied to clipboard)`])
}
})
} else {
results.push({
"isCalculator": true,
"name": "Invalid expression",
"content": "Please enter a valid mathematical expression",
"icon": "calculate",
"execute": function () {}
})
}
} catch (error) {
results.push({
"isCalculator": true,
"name": `Calculator: ${expr} = ${formattedResult}`,
"result": value,
"expr": expr,
"icon": "calculate",
"execute": function () {
Quickshell.clipboardText = String(formattedResult)
clipboardTextCopyProcess.copyText(String(formattedResult))
Quickshell.execDetached(
["notify-send", "Calculator Result", `${expr} = ${formattedResult} (copied to clipboard)`])
}
})
"isCalculator": true,
"name": "Invalid expression",
"content": "Please enter a valid mathematical expression",
"icon": "calculate",
"execute": function () {}
})
}
} else {
// Show placeholder when just ">calc" is entered
results.push({
"isCalculator": true,
"name": "Calculator",
"content": "Enter a mathematical expression (e.g., 5+5, 2*3, 10/2)",
"icon": "calculate",
"execute": function () {}
})
}
return results
}
@ -240,14 +267,7 @@ NLoader {
updateClipboardHistory()
}
function isMathExpression(str) {
// Allow more characters for enhanced math functions
return /^[-+*/().0-9\s\w]+$/.test(str)
}
function safeEval(expr) {
return MathHelper.MathHelper.evaluate(expr)
}
// Main content container
Rectangle {

View file

@ -551,9 +551,8 @@ WlSessionLock {
height: 20 * scaling
color: Color.mPrimary
visible: passwordInput.activeFocus
anchors.left: asterisksText.right
anchors.leftMargin: Style.marginTiniest * scaling
anchors.verticalCenter: asterisksText.verticalCenter
Layout.leftMargin: asterisksText.width + Style.marginTiniest * scaling
Layout.alignment: Qt.AlignVCenter
SequentialAnimation on opacity {
loops: Animation.Infinite
@ -619,7 +618,7 @@ WlSessionLock {
onClicked: lock.unlockAttempt()
SequentialAnimation on scale {
running: containsMouse
running: executeButtonArea.containsMouse
NumberAnimation {
to: 1.05
duration: Style.animationFast
@ -628,7 +627,7 @@ WlSessionLock {
}
SequentialAnimation on scale {
running: !containsMouse
running: !executeButtonArea.containsMouse
NumberAnimation {
to: 1.0
duration: Style.animationFast

View file

@ -78,6 +78,8 @@ A sleek, minimal, and thoughtfully crafted desktop shell for Wayland using **Qui
- `matugen` - Material You color scheme generation
- `cava` - Audio visualizer component
- `gpu-screen-recorder` - Screen recording functionality
- `brightnessctl` - For internal/laptop monitor brightness
- `ddcutil` - For desktop monitor brightness
---
@ -119,7 +121,9 @@ qs ipc call lockScreen toggle
### Configuration
Access settings through the side panel (top right button) to configure weather, wallpapers, screen recording, audio, network, and theme options.
Access settings through the side panel (top right button) to configure weather, wallpapers, screen recording, audio, network, and theme options.
Configuration is usually stored in ~/.config/noctalia
If you upgrade from v1, you can delete the old configuration folder at ~/.config/Noctalia (with capital N)
### Application Launcher
@ -218,10 +222,11 @@ Noctalia/
### Contributing
1. Follow the existing code style and patterns
2. Use the modular architecture for new features
3. Implement proper error handling and logging
4. Test with both Hyprland and Niri compositors (if applicable)
1. All Pull requests should be based on the "dev" branch
2. Follow the existing code style and patterns
3. Use the modular architecture for new features
4. Implement proper error handling and logging
5. Test with both Hyprland and Niri compositors (if applicable)
Contributions are welcome! Don't worry about being perfect - every contribution helps! Whether it's fixing a small bug, adding a new feature, or improving documentation, we welcome all contributions. Feel free to open an issue to discuss ideas or ask questions before diving in. For feature requests and ideas, you can also use our discussions page.