Merge branch 'dev' of github.com:noctalia-dev/noctalia-shell into dev

This commit is contained in:
quadbyte 2025-08-17 16:04:38 -04:00
commit ba69ccbf82
5 changed files with 72 additions and 179 deletions

View file

@ -27,30 +27,19 @@ jobs:
mv *.tar.gz ${{ github.workspace }}/ mv *.tar.gz ${{ github.workspace }}/
- name: Generate release notes - name: Generate release notes
id: release_notes
run: | run: |
PREV_TAG=$(git describe --tags --abbrev=0 @^ 2>/dev/null || echo "") PREV_TAG=$(git describe --tags --abbrev=0 ${{ github.ref }}^ 2>/dev/null || echo "")
RANGE="${PREV_TAG}..HEAD" if [ -z "$PREV_TAG" ]; then
RANGE=$(git rev-list --max-parents=0 HEAD)..HEAD
PR_NOTES="" else
COMMIT_NOTES="" RANGE=$PREV_TAG..HEAD
fi
git log $RANGE --pretty=format:"%H|%s|%an" | while IFS='|' read -r SHA MSG AUTHOR; do echo "# Release ${{ github.ref_name }}" > release_notes.md
SHORT_MSG=$(echo "$MSG" | cut -c1-80) echo "" >> release_notes.md
if [[ "$MSG" =~ Merge\ pull\ request\ \#([0-9]+) ]]; then echo "## Changes since $PREV_TAG" >> release_notes.md
PR_NUM="${BASH_REMATCH[1]}" echo "" >> release_notes.md
PR_TITLE=$(echo "$SHORT_MSG" | sed -E "s/Merge pull request #$PR_NUM //") git log $RANGE --pretty=format:"- %s ([%h](https://github.com/${{ github.repository }}/commit/%H)) by %an" >> release_notes.md
PR_NOTES+="- [PR #$PR_NUM](https://github.com/${GITHUB_REPOSITORY}/pull/$PR_NUM): $PR_TITLE by $AUTHOR\n" echo "" >> release_notes.md
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
- name: Create GitHub Release - name: Create GitHub Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
@ -58,6 +47,6 @@ jobs:
files: | files: |
noctalia-${{ github.ref_name }}.tar.gz noctalia-${{ github.ref_name }}.tar.gz
noctalia-latest.tar.gz noctalia-latest.tar.gz
body: ${{ env.RELEASE_NOTES }} body_path: release_notes.md
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 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 qs.Widgets
import "../../Helpers/FuzzySort.js" as Fuzzysort import "../../Helpers/FuzzySort.js" as Fuzzysort
import "../../Helpers/MathHelper.js" as MathHelper
NLoader { NLoader {
id: appLauncher id: appLauncher
@ -188,26 +187,54 @@ NLoader {
// Handle calculator // Handle calculator
if (query.startsWith(">calc")) { if (query.startsWith(">calc")) {
var expr = searchText.slice(5).trim() var expr = searchText.slice(5).trim()
if (expr && isMathExpression(expr)) { if (expr && expr !== "") {
var value = safeEval(expr) try {
if (value !== null && value !== undefined && value !== "") { // Simple evaluation - only allow basic math operations
var formattedResult = MathHelper.MathHelper.formatResult(value) 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({ results.push({
"isCalculator": true, "isCalculator": true,
"name": `Calculator: ${expr} = ${formattedResult}`, "name": "Invalid expression",
"result": value, "content": "Please enter a valid mathematical expression",
"expr": expr, "icon": "calculate",
"icon": "calculate", "execute": function () {}
"execute": function () { })
Quickshell.clipboardText = String(formattedResult)
clipboardTextCopyProcess.copyText(String(formattedResult))
Quickshell.execDetached(
["notify-send", "Calculator Result", `${expr} = ${formattedResult} (copied to clipboard)`])
}
})
} }
} 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 return results
} }
@ -240,14 +267,7 @@ NLoader {
updateClipboardHistory() 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 // Main content container
Rectangle { Rectangle {

View file

@ -550,9 +550,8 @@ WlSessionLock {
height: 20 * scaling height: 20 * scaling
color: Color.mPrimary color: Color.mPrimary
visible: passwordInput.activeFocus visible: passwordInput.activeFocus
anchors.left: asterisksText.right Layout.leftMargin: asterisksText.width + Style.marginTiniest * scaling
anchors.leftMargin: Style.marginTiniest * scaling Layout.alignment: Qt.AlignVCenter
anchors.verticalCenter: asterisksText.verticalCenter
SequentialAnimation on opacity { SequentialAnimation on opacity {
loops: Animation.Infinite loops: Animation.Infinite
@ -618,7 +617,7 @@ WlSessionLock {
onClicked: lock.unlockAttempt() onClicked: lock.unlockAttempt()
SequentialAnimation on scale { SequentialAnimation on scale {
running: containsMouse running: executeButtonArea.containsMouse
NumberAnimation { NumberAnimation {
to: 1.05 to: 1.05
duration: Style.animationFast duration: Style.animationFast
@ -627,7 +626,7 @@ WlSessionLock {
} }
SequentialAnimation on scale { SequentialAnimation on scale {
running: !containsMouse running: !executeButtonArea.containsMouse
NumberAnimation { NumberAnimation {
to: 1.0 to: 1.0
duration: Style.animationFast 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 - `matugen` - Material You color scheme generation
- `cava` - Audio visualizer component - `cava` - Audio visualizer component
- `gpu-screen-recorder` - Screen recording functionality - `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 ### 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 ### Application Launcher
@ -218,10 +222,11 @@ Noctalia/
### Contributing ### Contributing
1. Follow the existing code style and patterns 1. All Pull requests should be based on the "dev" branch
2. Use the modular architecture for new features 2. Follow the existing code style and patterns
3. Implement proper error handling and logging 3. Use the modular architecture for new features
4. Test with both Hyprland and Niri compositors (if applicable) 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. 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.