Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f0a45f487 | ||
|
|
a68b99c807 | ||
|
|
2573f80940 | ||
|
|
0a97ea2fea | ||
|
|
12f499cb98 | ||
|
|
829850aa88 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "kochwas",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kochwas",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.1",
|
||||
"dependencies": {
|
||||
"@google/generative-ai": "^0.24.1",
|
||||
"@types/archiver": "^7.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kochwas",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { onMount, tick, untrack } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { CookingPot, X, ChevronDown } from 'lucide-svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
@@ -235,27 +235,31 @@
|
||||
// profiles, refetch with the new profile_id so the list reflects what
|
||||
// the *current* profile has viewed. Other sorts are profile-agnostic
|
||||
// and don't need this.
|
||||
//
|
||||
// Only `profileStore.active` must be a tracked dep. `allSort` /
|
||||
// `allLoading` are read inside untrack: otherwise the `allLoading = false`
|
||||
// write in the fetch-finally would re-trigger the effect and start the
|
||||
// next fetch → endless loop.
|
||||
$effect(() => {
|
||||
const active = profileStore.active;
|
||||
if (allSort !== 'viewed') return;
|
||||
if (allLoading) return;
|
||||
// Re-fetch the first page; rehydrate would re-load the previous
|
||||
// depth, but a sort-context change should reset to page 1 anyway.
|
||||
void (async () => {
|
||||
allLoading = true;
|
||||
try {
|
||||
const res = await fetch(buildAllUrl('viewed', ALL_PAGE, 0));
|
||||
if (!res.ok) return;
|
||||
const body = await res.json();
|
||||
const hits = body.hits as SearchHit[];
|
||||
allRecipes = hits;
|
||||
allExhausted = hits.length < ALL_PAGE;
|
||||
} finally {
|
||||
allLoading = false;
|
||||
}
|
||||
// 'active' is referenced so $effect tracks it as a dep:
|
||||
void active;
|
||||
})();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
profileStore.active;
|
||||
untrack(() => {
|
||||
if (allSort !== 'viewed') return;
|
||||
if (allLoading) return;
|
||||
void (async () => {
|
||||
allLoading = true;
|
||||
try {
|
||||
const res = await fetch(buildAllUrl('viewed', ALL_PAGE, 0));
|
||||
if (!res.ok) return;
|
||||
const body = await res.json();
|
||||
const hits = body.hits as SearchHit[];
|
||||
allRecipes = hits;
|
||||
allExhausted = hits.length < ALL_PAGE;
|
||||
} finally {
|
||||
allLoading = false;
|
||||
}
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
// Sync current query back into the URL as ?q=... via replaceState,
|
||||
|
||||
@@ -284,6 +284,8 @@
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
line-height: 1.3;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
.meta {
|
||||
display: flex;
|
||||
@@ -340,4 +342,51 @@
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Handy: 2-Spalten-Grid — Bild links ueber alle Rows, rechts stapeln
|
||||
sich Titel, Meta, Actions. `display: contents` auf .body/.text zieht
|
||||
die DOM-Kinder direkt in die Card-Grid, ohne Markup-Umbau. Vermeidet
|
||||
die tote Weissflaeche unter dem Bild bei schmalen Viewports. */
|
||||
@media (max-width: 600px) {
|
||||
.card {
|
||||
display: grid;
|
||||
grid-template-columns: 96px 1fr;
|
||||
grid-template-areas:
|
||||
'img title'
|
||||
'img meta'
|
||||
'img actions';
|
||||
column-gap: 0;
|
||||
}
|
||||
.body {
|
||||
display: contents;
|
||||
}
|
||||
.body img,
|
||||
.placeholder {
|
||||
grid-area: img;
|
||||
width: 96px;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
.text {
|
||||
display: contents;
|
||||
}
|
||||
.title {
|
||||
grid-area: title;
|
||||
padding: 0.7rem 0.75rem 0.15rem;
|
||||
}
|
||||
.meta {
|
||||
grid-area: meta;
|
||||
padding: 0 0.75rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
.actions-top {
|
||||
grid-area: actions;
|
||||
position: static;
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
padding: 0.5rem 0.75rem 0.7rem;
|
||||
justify-content: flex-end;
|
||||
align-self: end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user