chore: release v0.33.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas Kalayci
2026-04-29 19:03:04 +02:00
parent 7e61a83db9
commit a872ac52a9
5 changed files with 73 additions and 63 deletions
+7
View File
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.33.1] - 2026-04-29
### Changed
- Navigation: removed the dedicated Search button from the bottom bar; the bottom bar now shows three primary module links plus the More button
- Navigation: the More sheet now opens with a full-width pill-shaped search trigger at the top, replacing the grid-cell search item
- Search: the search overlay input field is now positioned at the bottom of the screen (thumb zone) instead of the top
## [0.33.0] - 2026-04-29
### Added
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "oikos",
"version": "0.33.0",
"version": "0.33.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "oikos",
"version": "0.33.0",
"version": "0.33.1",
"license": "MIT",
"dependencies": {
"bcrypt": "^6.0.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "oikos",
"version": "0.33.0",
"version": "0.33.1",
"description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.",
"main": "server/index.js",
"type": "module",
+21 -56
View File
@@ -478,20 +478,6 @@ function renderAppShell(container) {
const bottomItems = document.createElement('div');
bottomItems.className = 'nav-bottom__items';
navItems().slice(0, PRIMARY_NAV).forEach((item) => bottomItems.appendChild(navItemEl(item)));
const searchNavBtn = document.createElement('button');
searchNavBtn.className = 'nav-item nav-item--search';
searchNavBtn.id = 'search-nav-btn';
searchNavBtn.setAttribute('aria-label', t('search.title'));
const searchNavIcon = document.createElement('i');
searchNavIcon.dataset.lucide = 'search';
searchNavIcon.className = 'nav-item__icon';
searchNavIcon.setAttribute('aria-hidden', 'true');
const searchNavLabel = document.createElement('span');
searchNavLabel.className = 'nav-item__label';
searchNavLabel.textContent = t('search.title');
searchNavBtn.appendChild(searchNavIcon);
searchNavBtn.appendChild(searchNavLabel);
bottomItems.appendChild(searchNavBtn);
const moreBtn = document.createElement('button');
moreBtn.className = 'nav-item nav-item--more';
moreBtn.id = 'more-btn';
@@ -520,19 +506,20 @@ function renderAppShell(container) {
moreSheet.setAttribute('role', 'dialog');
moreSheet.setAttribute('aria-label', t('nav.more'));
moreSheet.setAttribute('aria-hidden', 'true');
const searchBtn = document.createElement('button');
searchBtn.className = 'more-item';
searchBtn.id = 'search-btn';
const searchIcon = document.createElement('i');
searchIcon.dataset.lucide = 'search';
searchIcon.className = 'more-item__icon';
searchIcon.setAttribute('aria-hidden', 'true');
const searchLabel = document.createElement('span');
searchLabel.className = 'more-item__label';
searchLabel.textContent = t('search.title');
searchBtn.appendChild(searchIcon);
searchBtn.appendChild(searchLabel);
moreSheet.appendChild(searchBtn);
const searchTrigger = document.createElement('button');
searchTrigger.className = 'more-sheet__search-trigger';
searchTrigger.id = 'search-btn';
searchTrigger.setAttribute('aria-label', t('search.title'));
const searchTriggerIcon = document.createElement('i');
searchTriggerIcon.dataset.lucide = 'search';
searchTriggerIcon.className = 'more-sheet__search-trigger-icon';
searchTriggerIcon.setAttribute('aria-hidden', 'true');
const searchTriggerText = document.createElement('span');
searchTriggerText.className = 'more-sheet__search-trigger-placeholder';
searchTriggerText.textContent = t('search.placeholder');
searchTrigger.appendChild(searchTriggerIcon);
searchTrigger.appendChild(searchTriggerText);
moreSheet.appendChild(searchTrigger);
navItems().slice(PRIMARY_NAV).forEach((item) => moreSheet.appendChild(moreItemEl(item)));
const searchOverlay = document.createElement('div');
@@ -763,9 +750,8 @@ function initMoreSheet(container) {
* Initialisiert die Suchfunktion (Overlay + API-Calls).
*/
function initSearch(container) {
const searchBtn = container.querySelector('#search-btn');
const searchNavBtn = container.querySelector('#search-nav-btn');
const searchClose = container.querySelector('#search-close');
const searchBtn = container.querySelector('#search-btn');
const searchClose = container.querySelector('#search-close');
const overlay = container.querySelector('#search-overlay');
const input = container.querySelector('#search-input');
const results = container.querySelector('#search-results');
@@ -812,7 +798,6 @@ function initSearch(container) {
}
searchBtn.addEventListener('click', openSearch);
if (searchNavBtn) searchNavBtn.addEventListener('click', openSearch);
searchClose.addEventListener('click', closeSearch);
document.addEventListener('keydown', (e) => {
@@ -1137,34 +1122,14 @@ window.addEventListener('locale-changed', () => {
if (bottomItems) {
const moreBtn = bottomItems.querySelector('#more-btn');
const newItems = navItems().slice(0, PRIMARY_NAV).map(navItemEl);
// Such-Button neu erstellen (wird durch replaceChildren entfernt)
const newSearchBtn = document.createElement('button');
newSearchBtn.className = 'nav-item nav-item--search';
newSearchBtn.id = 'search-nav-btn';
newSearchBtn.setAttribute('aria-label', t('search.title'));
const newSearchIcon = document.createElement('i');
newSearchIcon.dataset.lucide = 'search';
newSearchIcon.className = 'nav-item__icon';
newSearchIcon.setAttribute('aria-hidden', 'true');
const newSearchLbl = document.createElement('span');
newSearchLbl.className = 'nav-item__label';
newSearchLbl.textContent = t('search.title');
newSearchBtn.appendChild(newSearchIcon);
newSearchBtn.appendChild(newSearchLbl);
bottomItems.replaceChildren(...newItems, newSearchBtn, moreBtn);
// Event-Listener auf neuen Such-Button
if (newSearchBtn) {
newSearchBtn.addEventListener('click', () => {
if (window._openSearch) window._openSearch();
});
}
bottomItems.replaceChildren(...newItems, moreBtn);
}
if (moreSheet) {
const searchBtn = moreSheet.querySelector('#search-btn');
const searchLbl = searchBtn?.querySelector('.more-item__label');
if (searchLbl) searchLbl.textContent = t('search.title');
const searchTrig = moreSheet.querySelector('#search-btn');
const searchTrigPlaceholder = searchTrig?.querySelector('.more-sheet__search-trigger-placeholder');
if (searchTrigPlaceholder) searchTrigPlaceholder.textContent = t('search.placeholder');
const newMoreItems = navItems().slice(PRIMARY_NAV).map(moreItemEl);
moreSheet.replaceChildren(searchBtn, ...newMoreItems);
moreSheet.replaceChildren(searchTrig, ...newMoreItems);
}
document.querySelectorAll('[data-route]').forEach((el) => {
+42 -4
View File
@@ -268,6 +268,43 @@
line-height: 1.2;
}
/* ── More-Sheet Suchtrigger ── */
.more-sheet__search-trigger {
grid-column: 1 / -1;
display: flex;
align-items: center;
gap: var(--space-2);
padding: 0 var(--space-4);
height: var(--target-lg);
border-radius: var(--radius-full);
border: 1.5px solid var(--color-border);
background-color: var(--color-surface-elevated);
color: var(--color-text-tertiary);
cursor: pointer;
font-family: inherit;
font-size: var(--text-sm);
text-align: left;
width: 100%;
-webkit-tap-highlight-color: transparent;
transition: background-color var(--transition-fast), border-color var(--transition-fast);
}
.more-sheet__search-trigger:active {
background-color: var(--color-surface-hover);
border-color: var(--color-accent);
transform: scale(0.98);
}
.more-sheet__search-trigger-icon {
width: var(--space-4);
height: var(--space-4);
flex-shrink: 0;
}
.more-sheet__search-trigger-placeholder {
flex: 1;
}
/* ── Such-Overlay ── */
.search-overlay {
position: fixed;
@@ -275,7 +312,7 @@
background-color: var(--color-surface);
z-index: calc(var(--z-nav) + 3);
display: flex;
flex-direction: column;
flex-direction: column-reverse;
transform: translateY(100%);
transition: transform 0.25s var(--ease-out);
}
@@ -288,8 +325,8 @@
display: flex;
align-items: center;
gap: var(--space-3);
padding: calc(var(--space-4) + var(--safe-area-inset-top)) var(--space-4) var(--space-3);
border-bottom: 1px solid var(--color-border-subtle);
padding: var(--space-3) var(--space-4) calc(var(--space-4) + var(--safe-area-inset-bottom));
border-top: 1px solid var(--color-border-subtle);
}
.search-overlay__input {
@@ -331,7 +368,8 @@
.search-overlay__results {
flex: 1;
overflow-y: auto;
padding: var(--space-4);
padding: var(--space-4) var(--space-4) var(--space-2);
padding-top: calc(var(--space-4) + var(--safe-area-inset-top));
}
.search-overlay__empty {