merge: beta into master
This commit is contained in:
@@ -975,11 +975,15 @@ a { color: var(--primary); text-decoration: none; }
|
|||||||
}
|
}
|
||||||
.ctx-item:hover { background: var(--bg-hover); }
|
.ctx-item:hover { background: var(--bg-hover); }
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
/* ── Event Popup ──────────────────────────────────────────
|
/* ── Event Popup ──────────────────────────────────────────
|
||||||
Layout: Color-Dot + Title links, kleine Icon-Toolbar rechts oben.
|
Layout: Color-Dot + Title links, kleine Icon-Toolbar rechts oben.
|
||||||
Icons sind im Ruhezustand transparent (nur das SVG selbst sichtbar),
|
Icons sind im Ruhezustand transparent (nur das SVG selbst sichtbar),
|
||||||
bekommen erst beim Hover einen runden farbigen Hintergrund. Wirkt
|
bekommen erst beim Hover einen runden farbigen Hintergrund. Wirkt
|
||||||
modern und lässt dem Titel die meiste Breite. */
|
modern und lässt dem Titel die meiste Breite. */
|
||||||
|
=======
|
||||||
|
/* ── Event Popup ────────────────────────────────────────── */
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
.event-popup {
|
.event-popup {
|
||||||
position: fixed; z-index: 600;
|
position: fixed; z-index: 600;
|
||||||
background: var(--bg-surface);
|
background: var(--bg-surface);
|
||||||
@@ -1697,12 +1701,24 @@ a { color: var(--primary); text-decoration: none; }
|
|||||||
.topbar-left { gap: 0; }
|
.topbar-left { gap: 0; }
|
||||||
.topbar-right { gap: 0; }
|
.topbar-right { gap: 0; }
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
/* Event-Popup auf Mobile: an Viewport-Breite anpassen */
|
/* Event-Popup auf Mobile: an Viewport-Breite anpassen */
|
||||||
.event-popup { width: min(94vw, 380px); max-width: 94vw; }
|
.event-popup { width: min(94vw, 380px); max-width: 94vw; }
|
||||||
.popup-header { padding: 10px 8px 10px 14px; }
|
.popup-header { padding: 10px 8px 10px 14px; }
|
||||||
.popup-header h4 { font-size: 13.5px; }
|
.popup-header h4 { font-size: 13.5px; }
|
||||||
.popup-icon-btn { width: 32px; height: 32px; }
|
.popup-icon-btn { width: 32px; height: 32px; }
|
||||||
.popup-icon-btn svg { width: 16px; height: 16px; }
|
.popup-icon-btn svg { width: 16px; height: 16px; }
|
||||||
|
=======
|
||||||
|
/* Event-Popup: Buttons kompakt halten, kein 44px-Override ───── */
|
||||||
|
.event-popup .icon-btn {
|
||||||
|
min-width: 32px !important;
|
||||||
|
min-height: 32px !important;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.event-popup .popup-header { gap: 2px; padding: 10px 12px; }
|
||||||
|
.event-popup { width: min(92vw, 340px); max-width: 92vw; }
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
|
|
||||||
/* Monatsansicht: Startzeit ausblenden — nur Titel anzeigen ──── */
|
/* Monatsansicht: Startzeit ausblenden — nur Titel anzeigen ──── */
|
||||||
.month-event-time { display: none; }
|
.month-event-time { display: none; }
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||||
<!-- APP_VERSION: update here + version.js on every release -->
|
<!-- APP_VERSION: update here + version.js on every release -->
|
||||||
|
<<<<<<< HEAD
|
||||||
<title>Calendarr v17</title>
|
<title>Calendarr v17</title>
|
||||||
|
=======
|
||||||
|
<title>Calendarr v11</title>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<meta name="theme-color" content="#4285f4" />
|
<meta name="theme-color" content="#4285f4" />
|
||||||
@@ -80,7 +84,11 @@
|
|||||||
<button type="submit" class="btn btn-primary btn-full">Anmelden</button>
|
<button type="submit" class="btn btn-primary btn-full">Anmelden</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<<<<<<< HEAD
|
||||||
<button class="impressum-link" onclick="openImpressum()">© 2026 Scarriffleservices · v17</button>
|
<button class="impressum-link" onclick="openImpressum()">© 2026 Scarriffleservices · v17</button>
|
||||||
|
=======
|
||||||
|
<button class="impressum-link" onclick="openImpressum()">© 2026 Scarriffleservices · v11</button>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ─── MAIN APP ──────────────────────────────────────────── -->
|
<!-- ─── MAIN APP ──────────────────────────────────────────── -->
|
||||||
@@ -185,7 +193,7 @@
|
|||||||
<span data-i18n="my_calendars">Meine Kalender</span>
|
<span data-i18n="my_calendars">Meine Kalender</span>
|
||||||
<div class="add-cal-dropdown-wrap">
|
<div class="add-cal-dropdown-wrap">
|
||||||
<button class="icon-btn mini-btn" id="btn-add-cal" title="Kalender hinzufügen">
|
<button class="icon-btn mini-btn" id="btn-add-cal" title="Kalender hinzufügen">
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v11h-2v-6H5v-2h6V5h2v11h6v2z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="add-cal-dropdown hidden" id="add-cal-dropdown">
|
<div class="add-cal-dropdown hidden" id="add-cal-dropdown">
|
||||||
<button data-action="local">Lokaler Kalender</button>
|
<button data-action="local">Lokaler Kalender</button>
|
||||||
@@ -199,7 +207,11 @@
|
|||||||
<div id="cal-list-items"></div>
|
<div id="cal-list-items"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<<<<<<< HEAD
|
||||||
<button class="sidebar-copyright" onclick="openImpressum()">© 2026 Scarriffleservices · v17</button>
|
<button class="sidebar-copyright" onclick="openImpressum()">© 2026 Scarriffleservices · v17</button>
|
||||||
|
=======
|
||||||
|
<button class="sidebar-copyright" onclick="openImpressum()">© 2026 Scarriffleservices · v11</button>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</aside>
|
</aside>
|
||||||
<div id="sidebar-backdrop" class="sidebar-backdrop"></div>
|
<div id="sidebar-backdrop" class="sidebar-backdrop"></div>
|
||||||
|
|
||||||
@@ -235,7 +247,11 @@
|
|||||||
<input type="hidden" id="ev-start" />
|
<input type="hidden" id="ev-start" />
|
||||||
<div class="dt-display" id="ev-start-display" tabindex="0" role="button">
|
<div class="dt-display" id="ev-start-display" tabindex="0" role="button">
|
||||||
<span class="dt-display-text">—</span>
|
<span class="dt-display-text">—</span>
|
||||||
|
<<<<<<< HEAD
|
||||||
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
||||||
|
=======
|
||||||
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v11H7z"/></svg>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group half">
|
<div class="form-group half">
|
||||||
@@ -243,7 +259,11 @@
|
|||||||
<input type="hidden" id="ev-end" />
|
<input type="hidden" id="ev-end" />
|
||||||
<div class="dt-display" id="ev-end-display" tabindex="0" role="button">
|
<div class="dt-display" id="ev-end-display" tabindex="0" role="button">
|
||||||
<span class="dt-display-text">—</span>
|
<span class="dt-display-text">—</span>
|
||||||
|
<<<<<<< HEAD
|
||||||
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
||||||
|
=======
|
||||||
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v11H7z"/></svg>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -253,7 +273,11 @@
|
|||||||
<input type="hidden" id="ev-start-date" />
|
<input type="hidden" id="ev-start-date" />
|
||||||
<div class="dt-display" id="ev-start-date-display" tabindex="0" role="button">
|
<div class="dt-display" id="ev-start-date-display" tabindex="0" role="button">
|
||||||
<span class="dt-display-text">—</span>
|
<span class="dt-display-text">—</span>
|
||||||
|
<<<<<<< HEAD
|
||||||
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
||||||
|
=======
|
||||||
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v11H7z"/></svg>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group half">
|
<div class="form-group half">
|
||||||
@@ -261,7 +285,11 @@
|
|||||||
<input type="hidden" id="ev-end-date" />
|
<input type="hidden" id="ev-end-date" />
|
||||||
<div class="dt-display" id="ev-end-date-display" tabindex="0" role="button">
|
<div class="dt-display" id="ev-end-date-display" tabindex="0" role="button">
|
||||||
<span class="dt-display-text">—</span>
|
<span class="dt-display-text">—</span>
|
||||||
|
<<<<<<< HEAD
|
||||||
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
||||||
|
=======
|
||||||
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v11H7z"/></svg>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -311,7 +339,11 @@
|
|||||||
<input type="hidden" id="ev-rec-until" />
|
<input type="hidden" id="ev-rec-until" />
|
||||||
<div class="dt-display" id="ev-rec-until-display" tabindex="0" role="button">
|
<div class="dt-display" id="ev-rec-until-display" tabindex="0" role="button">
|
||||||
<span class="dt-display-text">—</span>
|
<span class="dt-display-text">—</span>
|
||||||
|
<<<<<<< HEAD
|
||||||
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v17H7z"/></svg>
|
||||||
|
=======
|
||||||
|
<svg class="dt-display-icon" viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.1 0-2 .9-2 2v24a2 2 0 002 2h14a2 2 0 002-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v21zM7 10h5v11H7z"/></svg>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -375,6 +407,7 @@
|
|||||||
<div class="popup-header">
|
<div class="popup-header">
|
||||||
<div class="popup-color-dot" id="popup-color-dot"></div>
|
<div class="popup-color-dot" id="popup-color-dot"></div>
|
||||||
<h4 id="popup-title"></h4>
|
<h4 id="popup-title"></h4>
|
||||||
|
<<<<<<< HEAD
|
||||||
<div class="popup-toolbar">
|
<div class="popup-toolbar">
|
||||||
<button class="popup-icon-btn" id="popup-edit" title="Bearbeiten" aria-label="Bearbeiten">
|
<button class="popup-icon-btn" id="popup-edit" title="Bearbeiten" aria-label="Bearbeiten">
|
||||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
|
||||||
@@ -389,6 +422,18 @@
|
|||||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
=======
|
||||||
|
<button class="icon-btn popup-action" id="popup-edit" title="Bearbeiten">
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
|
||||||
|
</button>
|
||||||
|
<button class="icon-btn popup-action" id="popup-copy" title="Kopieren nach…">
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v24h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v24c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v24z"/></svg>
|
||||||
|
</button>
|
||||||
|
<button class="icon-btn popup-action" id="popup-delete" title="Löschen">
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v22zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
||||||
|
</button>
|
||||||
|
<button class="icon-btn popup-close" id="popup-close">×</button>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
</div>
|
</div>
|
||||||
<div class="popup-body">
|
<div class="popup-body">
|
||||||
<div class="popup-time" id="popup-time"></div>
|
<div class="popup-time" id="popup-time"></div>
|
||||||
@@ -888,7 +933,11 @@
|
|||||||
<a href="mailto:scarriffleservices@gmail.com">scarriffleservices@gmail.com</a></p>
|
<a href="mailto:scarriffleservices@gmail.com">scarriffleservices@gmail.com</a></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer" style="justify-content:space-between;align-items:center">
|
<div class="modal-footer" style="justify-content:space-between;align-items:center">
|
||||||
|
<<<<<<< HEAD
|
||||||
<span style="font-size:12px;color:var(--text-3)">Calendarr v17</span>
|
<span style="font-size:12px;color:var(--text-3)">Calendarr v17</span>
|
||||||
|
=======
|
||||||
|
<span style="font-size:12px;color:var(--text-3)">Calendarr v11</span>
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
<button class="btn btn-ghost" onclick="closeImpressum()">Schliessen</button>
|
<button class="btn btn-ghost" onclick="closeImpressum()">Schliessen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export async function initCalendar() {
|
|||||||
bindProfileModal();
|
bindProfileModal();
|
||||||
bindSwipeNavigation();
|
bindSwipeNavigation();
|
||||||
handleHAOAuthReturn();
|
handleHAOAuthReturn();
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
// Browser-Back/Forward: URL-Hash → State synchronisieren
|
// Browser-Back/Forward: URL-Hash → State synchronisieren
|
||||||
window.addEventListener('hashchange', () => {
|
window.addEventListener('hashchange', () => {
|
||||||
@@ -131,6 +132,8 @@ export async function initCalendar() {
|
|||||||
}
|
}
|
||||||
if (changed) fetchAndRender();
|
if (changed) fetchAndRender();
|
||||||
});
|
});
|
||||||
|
=======
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleHAOAuthReturn() {
|
function handleHAOAuthReturn() {
|
||||||
@@ -144,7 +147,11 @@ function handleHAOAuthReturn() {
|
|||||||
};
|
};
|
||||||
if (params.has('ha_connected')) {
|
if (params.has('ha_connected')) {
|
||||||
showToast('Home Assistant verbunden');
|
showToast('Home Assistant verbunden');
|
||||||
|
<<<<<<< HEAD
|
||||||
window.history.replaceState({}, '', window.location.pathname + window.location.hash);
|
window.history.replaceState({}, '', window.location.pathname + window.location.hash);
|
||||||
|
=======
|
||||||
|
window.history.replaceState({}, '', window.location.pathname);
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
fetchAndRender(true);
|
fetchAndRender(true);
|
||||||
api.get('/homeassistant/accounts').then(accs => {
|
api.get('/homeassistant/accounts').then(accs => {
|
||||||
state.haAccounts = accs || [];
|
state.haAccounts = accs || [];
|
||||||
@@ -154,7 +161,11 @@ function handleHAOAuthReturn() {
|
|||||||
} else if (params.has('ha_error')) {
|
} else if (params.has('ha_error')) {
|
||||||
const code = params.get('ha_error');
|
const code = params.get('ha_error');
|
||||||
showToast(errMap[code] || `HA-Anmeldung fehlgeschlagen: ${code}`, true);
|
showToast(errMap[code] || `HA-Anmeldung fehlgeschlagen: ${code}`, true);
|
||||||
|
<<<<<<< HEAD
|
||||||
window.history.replaceState({}, '', window.location.pathname + window.location.hash);
|
window.history.replaceState({}, '', window.location.pathname + window.location.hash);
|
||||||
|
=======
|
||||||
|
window.history.replaceState({}, '', window.location.pathname);
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +260,10 @@ async function fetchAndRender(force = false, silent = false) {
|
|||||||
renderView();
|
renderView();
|
||||||
updateTitle();
|
updateTitle();
|
||||||
renderMiniCal();
|
renderMiniCal();
|
||||||
|
<<<<<<< HEAD
|
||||||
writeUrlState();
|
writeUrlState();
|
||||||
|
=======
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
prefetchIfNeeded(start, end); // extend cache in background if approaching an edge
|
prefetchIfNeeded(start, end); // extend cache in background if approaching an edge
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,11 @@ const translations = {
|
|||||||
rec_every: 'Alle', rec_days: 'Tage', rec_weeks: 'Wochen', rec_months: 'Monate',
|
rec_every: 'Alle', rec_days: 'Tage', rec_weeks: 'Wochen', rec_months: 'Monate',
|
||||||
rec_ends: 'Endet', rec_never: 'Nie', rec_after_count: 'Nach Anzahl',
|
rec_ends: 'Endet', rec_never: 'Nie', rec_after_count: 'Nach Anzahl',
|
||||||
rec_on_date: 'Am Datum', rec_occurrences: 'Termine',
|
rec_on_date: 'Am Datum', rec_occurrences: 'Termine',
|
||||||
|
<<<<<<< HEAD
|
||||||
copy_to_calendar: 'Kopieren nach…', event_copied: 'Termin kopiert', copy: 'Kopieren',
|
copy_to_calendar: 'Kopieren nach…', event_copied: 'Termin kopiert', copy: 'Kopieren',
|
||||||
|
=======
|
||||||
|
copy_to_calendar: 'Kopieren nach…', event_copied: 'Termin kopiert',
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
edit_before_copy: 'Vor dem Kopieren bearbeiten',
|
edit_before_copy: 'Vor dem Kopieren bearbeiten',
|
||||||
event_updated: 'Termin aktualisiert', event_created: 'Termin erstellt',
|
event_updated: 'Termin aktualisiert', event_created: 'Termin erstellt',
|
||||||
confirm_delete_event: '"{title}" wirklich löschen?',
|
confirm_delete_event: '"{title}" wirklich löschen?',
|
||||||
@@ -365,7 +369,11 @@ const translations = {
|
|||||||
rec_every: 'Every', rec_days: 'days', rec_weeks: 'weeks', rec_months: 'months',
|
rec_every: 'Every', rec_days: 'days', rec_weeks: 'weeks', rec_months: 'months',
|
||||||
rec_ends: 'Ends', rec_never: 'Never', rec_after_count: 'After count',
|
rec_ends: 'Ends', rec_never: 'Never', rec_after_count: 'After count',
|
||||||
rec_on_date: 'On date', rec_occurrences: 'occurrences',
|
rec_on_date: 'On date', rec_occurrences: 'occurrences',
|
||||||
|
<<<<<<< HEAD
|
||||||
copy_to_calendar: 'Copy to…', event_copied: 'Event copied', copy: 'Copy',
|
copy_to_calendar: 'Copy to…', event_copied: 'Event copied', copy: 'Copy',
|
||||||
|
=======
|
||||||
|
copy_to_calendar: 'Copy to…', event_copied: 'Event copied',
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
edit_before_copy: 'Edit before copying',
|
edit_before_copy: 'Edit before copying',
|
||||||
event_updated: 'Event updated', event_created: 'Event created',
|
event_updated: 'Event updated', event_created: 'Event created',
|
||||||
confirm_delete_event: 'Really delete "{title}"?',
|
confirm_delete_event: 'Really delete "{title}"?',
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
// Increment APP_VERSION with every code change
|
// Increment APP_VERSION with every code change
|
||||||
|
<<<<<<< HEAD
|
||||||
export const APP_VERSION = 'v17';
|
export const APP_VERSION = 'v17';
|
||||||
|
=======
|
||||||
|
export const APP_VERSION = 'v11';
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export function renderWeek(container, currentDate, events, onSlotClick, onEventC
|
|||||||
const color = ev.color || ev.calendarColor || '#4285f4';
|
const color = ev.color || ev.calendarColor || '#4285f4';
|
||||||
const pastCls = isPast(ev) ? 'past' : '';
|
const pastCls = isPast(ev) ? 'past' : '';
|
||||||
const multiCls = isMultiTimed ? 'multiday-timed' : '';
|
const multiCls = isMultiTimed ? 'multiday-timed' : '';
|
||||||
|
<<<<<<< HEAD
|
||||||
// continues-left/right: compute on date-only basis for all-day events
|
// continues-left/right: compute on date-only basis for all-day events
|
||||||
let evStart = new Date(ev.start);
|
let evStart = new Date(ev.start);
|
||||||
let evEnd = new Date(ev.end);
|
let evEnd = new Date(ev.end);
|
||||||
@@ -76,6 +77,10 @@ export function renderWeek(container, currentDate, events, onSlotClick, onEventC
|
|||||||
const lastDay = new Date(days[n-1]); lastDay.setHours(0, 0, 0, 0);
|
const lastDay = new Date(days[n-1]); lastDay.setHours(0, 0, 0, 0);
|
||||||
const cL = evStart < firstDay ? 'continues-left' : '';
|
const cL = evStart < firstDay ? 'continues-left' : '';
|
||||||
const cR = (ev.allDay ? evEnd > lastDay : evEnd > lastDayMidnight) ? 'continues-right' : '';
|
const cR = (ev.allDay ? evEnd > lastDay : evEnd > lastDayMidnight) ? 'continues-right' : '';
|
||||||
|
=======
|
||||||
|
const cL = new Date(ev.start) < new Date(days[0]) ? 'continues-left' : '';
|
||||||
|
const cR = new Date(ev.end) > (() => { const d = new Date(days[n-1]); d.setHours(24,0,0,0); return d; })() ? 'continues-right' : '';
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
const label = isMultiTimed && isSameDay(new Date(ev.start), days[colStart])
|
const label = isMultiTimed && isSameDay(new Date(ev.start), days[colStart])
|
||||||
? `${fmtTime(new Date(ev.start))} ${ev.title}`
|
? `${fmtTime(new Date(ev.start))} ${ev.title}`
|
||||||
: ev.title;
|
: ev.title;
|
||||||
@@ -247,6 +252,7 @@ function renderNowLine(container, days, hourH = 60) {
|
|||||||
function layoutWeekAllDay(evs, days) {
|
function layoutWeekAllDay(evs, days) {
|
||||||
const items = [];
|
const items = [];
|
||||||
evs.forEach(ev => {
|
evs.forEach(ev => {
|
||||||
|
<<<<<<< HEAD
|
||||||
// For all-day events, normalize to date-only with inclusive end-day
|
// For all-day events, normalize to date-only with inclusive end-day
|
||||||
// (iCal stores exclusive end → subtract 1). For timed events, keep
|
// (iCal stores exclusive end → subtract 1). For timed events, keep
|
||||||
// the original strict-overlap logic so events ending exactly at
|
// the original strict-overlap logic so events ending exactly at
|
||||||
@@ -269,6 +275,13 @@ function layoutWeekAllDay(evs, days) {
|
|||||||
matches = new Date(ev.start) < de && new Date(ev.end) > ds;
|
matches = new Date(ev.start) < de && new Date(ev.end) > ds;
|
||||||
}
|
}
|
||||||
if (matches) {
|
if (matches) {
|
||||||
|
=======
|
||||||
|
let colStart = -1, colEnd = -1;
|
||||||
|
days.forEach((day, i) => {
|
||||||
|
const ds = new Date(day); ds.setHours(0, 0, 0, 0);
|
||||||
|
const de = new Date(day); de.setHours(24, 0, 0, 0);
|
||||||
|
if (new Date(ev.start) < de && new Date(ev.end) > ds) {
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
if (colStart === -1) colStart = i;
|
if (colStart === -1) colStart = i;
|
||||||
colEnd = i;
|
colEnd = i;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
// Calendarr Service Worker — minimal-cache strategy
|
// Calendarr Service Worker — minimal-cache strategy
|
||||||
//
|
//
|
||||||
// Strategy: network-first for everything. The cache is only used as a
|
// Strategy: network-first for everything. The cache is only used as a
|
||||||
@@ -9,13 +10,50 @@
|
|||||||
|
|
||||||
const CACHE_VERSION = 'calendarr-v17';
|
const CACHE_VERSION = 'calendarr-v17';
|
||||||
const OFFLINE_SHELL = ['/', '/index.html'];
|
const OFFLINE_SHELL = ['/', '/index.html'];
|
||||||
|
=======
|
||||||
|
// Calendarr Service Worker
|
||||||
|
// Cache-first for static assets, network-first for /api/* (graceful offline)
|
||||||
|
|
||||||
|
const CACHE_VERSION = 'calendarr-v11';
|
||||||
|
const STATIC_ASSETS = [
|
||||||
|
'/',
|
||||||
|
'/index.html',
|
||||||
|
'/manifest.json',
|
||||||
|
'/static/css/app.css',
|
||||||
|
'/static/favicon.svg',
|
||||||
|
'/static/js/app.js',
|
||||||
|
'/static/js/api.js',
|
||||||
|
'/static/js/calendar.js',
|
||||||
|
'/static/js/color-picker.js',
|
||||||
|
'/static/js/date-picker.js',
|
||||||
|
'/static/js/i18n.js',
|
||||||
|
'/static/js/utils.js',
|
||||||
|
'/static/js/version.js',
|
||||||
|
'/static/js/views/agenda.js',
|
||||||
|
'/static/js/views/month.js',
|
||||||
|
'/static/js/views/quarter.js',
|
||||||
|
'/static/js/views/week.js',
|
||||||
|
'/icons/icon-192.png',
|
||||||
|
'/icons/icon-512.png',
|
||||||
|
'/icons/icon.svg',
|
||||||
|
];
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
|
|
||||||
self.addEventListener('install', event => {
|
self.addEventListener('install', event => {
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
caches.open(CACHE_VERSION).then(cache =>
|
caches.open(CACHE_VERSION).then(cache =>
|
||||||
|
<<<<<<< HEAD
|
||||||
Promise.all(OFFLINE_SHELL.map(url =>
|
Promise.all(OFFLINE_SHELL.map(url =>
|
||||||
cache.add(url).catch(err => console.warn('[SW] skip', url, err))
|
cache.add(url).catch(err => console.warn('[SW] skip', url, err))
|
||||||
))
|
))
|
||||||
|
=======
|
||||||
|
// Use addAll with a fallback so a single missing file doesn't abort install
|
||||||
|
Promise.all(
|
||||||
|
STATIC_ASSETS.map(url =>
|
||||||
|
cache.add(url).catch(err => console.warn('[SW] skip', url, err))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
).then(() => self.skipWaiting())
|
).then(() => self.skipWaiting())
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -34,8 +72,12 @@ self.addEventListener('fetch', event => {
|
|||||||
|
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
// API routes: always go to the network, no offline fallback (we'd just
|
// API routes: always go to the network, no offline fallback (we'd just
|
||||||
// be returning stale account/event data otherwise).
|
// be returning stale account/event data otherwise).
|
||||||
|
=======
|
||||||
|
// Network-first for API routes — fail silently if offline
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
if (url.pathname.startsWith('/api/')) {
|
if (url.pathname.startsWith('/api/')) {
|
||||||
event.respondWith(
|
event.respondWith(
|
||||||
fetch(req).catch(() =>
|
fetch(req).catch(() =>
|
||||||
@@ -48,6 +90,7 @@ self.addEventListener('fetch', event => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
// Everything else: network-first. The browser's HTTP cache (driven by
|
// Everything else: network-first. The browser's HTTP cache (driven by
|
||||||
// the server's Cache-Control headers) already throttles re-fetches —
|
// the server's Cache-Control headers) already throttles re-fetches —
|
||||||
// the SW just makes sure offline still works for the entry HTML.
|
// the SW just makes sure offline still works for the entry HTML.
|
||||||
@@ -71,6 +114,47 @@ self.addEventListener('fetch', event => {
|
|||||||
return caches.match(req).then(c => c || caches.match('/index.html'));
|
return caches.match(req).then(c => c || caches.match('/index.html'));
|
||||||
}
|
}
|
||||||
return new Response('', { status: 503 });
|
return new Response('', { status: 503 });
|
||||||
|
=======
|
||||||
|
// Network-first for navigation (HTML) and the version-defining files —
|
||||||
|
// ensures users always get the freshest entry point so new releases
|
||||||
|
// take effect on the next reload without a manual SW unregister.
|
||||||
|
const isHtml = req.mode === 'navigate'
|
||||||
|
|| url.pathname === '/'
|
||||||
|
|| url.pathname === '/index.html';
|
||||||
|
const isVersionFile = url.pathname === '/static/js/version.js';
|
||||||
|
|
||||||
|
if (isHtml || isVersionFile) {
|
||||||
|
event.respondWith(
|
||||||
|
fetch(req).then(resp => {
|
||||||
|
if (resp && resp.status === 200) {
|
||||||
|
const clone = resp.clone();
|
||||||
|
caches.open(CACHE_VERSION).then(c => c.put(req, clone)).catch(() => {});
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}).catch(() =>
|
||||||
|
caches.match(req).then(c => c || caches.match('/index.html'))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache-first for everything else (static)
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(req).then(cached => {
|
||||||
|
if (cached) return cached;
|
||||||
|
return fetch(req).then(resp => {
|
||||||
|
// Only cache successful, basic-origin responses
|
||||||
|
if (resp && resp.status === 200 && resp.type === 'basic') {
|
||||||
|
const clone = resp.clone();
|
||||||
|
caches.open(CACHE_VERSION).then(c => c.put(req, clone)).catch(() => {});
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}).catch(() => {
|
||||||
|
// Offline fallback for navigation requests
|
||||||
|
if (req.mode === 'navigate') return caches.match('/index.html');
|
||||||
|
return new Response('', { status: 503 });
|
||||||
|
});
|
||||||
|
>>>>>>> e744b1829e99db6b80922f75542ced329138e474
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user