diff --git a/frontend/src/views/KnowledgeView.vue b/frontend/src/views/KnowledgeView.vue
index c4c6cb6..a2e865b 100644
--- a/frontend/src/views/KnowledgeView.vue
+++ b/frontend/src/views/KnowledgeView.vue
@@ -83,13 +83,16 @@ async function fetchItems(reset = false) {
loading.value = false;
}
- // If the sentinel is still in the viewport after appending, keep loading
+ // If the sentinel is still visible in the card-grid after appending, keep loading
if (!reset && items.value.length < total.value) {
await nextTick();
- const rect = sentinelEl.value?.getBoundingClientRect();
- if (rect && rect.top < window.innerHeight + 200) {
- page.value++;
- await fetchItems();
+ if (sentinelEl.value && cardGridEl.value) {
+ const containerBottom = cardGridEl.value.getBoundingClientRect().bottom;
+ const sentinelTop = sentinelEl.value.getBoundingClientRect().top;
+ if (sentinelTop < containerBottom + 200) {
+ page.value++;
+ await fetchItems();
+ }
}
}
}
@@ -390,8 +393,13 @@ watchEffect(() => {
Start by creating a note, saving a person or place, or making a list.
-
+
-
-
-
- Loading…
-
@@ -888,11 +891,12 @@ watchEffect(() => {
/* ── Infinite scroll sentinel ────────────────────────────── */
.scroll-sentinel {
+ grid-column: 1 / -1; /* span all grid columns */
+ order: 9999; /* push to end of grid items */
height: 40px;
display: flex;
align-items: center;
justify-content: center;
- flex-shrink: 0;
}
.sentinel-loading {
font-size: 0.8rem;
diff --git a/src/fabledassistant/services/tools.py b/src/fabledassistant/services/tools.py
index bf7f718..a351676 100644
--- a/src/fabledassistant/services/tools.py
+++ b/src/fabledassistant/services/tools.py
@@ -666,17 +666,17 @@ _CORE_TOOLS = [
"type": "function",
"function": {
"name": "list_events",
- "description": "List calendar events in a date range. Use this when the user asks what events or meetings they have.",
+ "description": "List calendar events in a date range. Use this when the user asks what events or meetings they have. Always use full-day UTC ranges: date_from at T00:00:00Z and date_to at T23:59:59Z for the days of interest so events stored in UTC are not missed.",
"parameters": {
"type": "object",
"properties": {
"date_from": {
"type": "string",
- "description": "Start of range in ISO 8601 format (e.g. 2025-01-15T00:00:00)",
+ "description": "Start of range in ISO 8601 UTC format (e.g. 2025-01-15T00:00:00Z). Use T00:00:00Z for the start of the day.",
},
"date_to": {
"type": "string",
- "description": "End of range in ISO 8601 format (e.g. 2025-01-22T23:59:59)",
+ "description": "End of range in ISO 8601 UTC format (e.g. 2025-01-22T23:59:59Z). Use T23:59:59Z for the end of the day.",
},
},
"required": ["date_from", "date_to"],
@@ -1453,10 +1453,14 @@ async def execute_tool(
elif tool_name == "list_events":
try:
- date_from = datetime.fromisoformat(arguments["date_from"])
- date_to = datetime.fromisoformat(arguments["date_to"])
+ date_from = datetime.fromisoformat(arguments["date_from"].replace("Z", "+00:00"))
+ date_to = datetime.fromisoformat(arguments["date_to"].replace("Z", "+00:00"))
except (ValueError, TypeError, KeyError) as exc:
return {"success": False, "error": f"Invalid date range: {exc}"}
+ if date_from.tzinfo is None:
+ date_from = date_from.replace(tzinfo=timezone.utc)
+ if date_to.tzinfo is None:
+ date_to = date_to.replace(tzinfo=timezone.utc)
events = await events_list_events(
user_id=user_id,
date_from=date_from,