pantrybase 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +8 -2
- package/dist/{chunk-2EMC6TJB.js → chunk-QUWSBZ2V.js} +94 -6
- package/dist/cli.js +1 -1
- package/dist/server.js +79 -61
- package/dist/web/assets/{graph-Mpnwdw0-.js → graph-qSs25SSD.js} +1 -1
- package/dist/web/assets/{index-PLKhwbDz.css → index-Dsc3USAJ.css} +1 -1
- package/dist/web/assets/index-lHc1E7AV.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/providers/generic/profile.yaml +1 -0
- package/providers/ucook/guide.md +1 -1
- package/providers/ucook/profile.yaml +2 -0
- package/skills/ingest/SKILL.md +6 -5
- package/dist/web/assets/index-BQVf10Ok.js +0 -1
package/providers/ucook/guide.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UCOOK card extraction guide
|
|
2
2
|
|
|
3
|
-
UCOOK (South Africa) cards are landscape, two-sided. Read every photo of a card before saving. Save with `provider: {id: "ucook", …}` and `source: {kind: "card"}`.
|
|
3
|
+
UCOOK (South Africa) cards are landscape, two-sided. Read every photo of a card before saving. A card photo that is taller than it is wide, or whose text runs sideways or upside down, needs `pb_inbox_rotate` (clockwise degrees) before you read it. Save with `provider: {id: "ucook", …}` and `source: {kind: "card"}`.
|
|
4
4
|
|
|
5
5
|
## Front (photo left, text panel right)
|
|
6
6
|
|
|
@@ -9,6 +9,8 @@ sides:
|
|
|
9
9
|
back: [Ingredients & Prep, From Your Kitchen, Nutritional Information, Allergens, Eat Within]
|
|
10
10
|
# Photos are usually shot front then back; the back carries no title, so pair by order and content.
|
|
11
11
|
pairing: front-then-back
|
|
12
|
+
# Upright cards are wider than tall; a portrait photo of one is almost certainly sideways.
|
|
13
|
+
orientation: landscape
|
|
12
14
|
nutrition_basis: per_100g
|
|
13
15
|
default_servings: 2
|
|
14
16
|
# Heading of the card section listing what you supply yourself (ingredients get source: kitchen).
|
package/skills/ingest/SKILL.md
CHANGED
|
@@ -21,17 +21,18 @@ You turn photos in `pantrybase/inbox/` into recipes. The queue is on disk, so in
|
|
|
21
21
|
## 3. Loop, one card at a time
|
|
22
22
|
|
|
23
23
|
1. `pb_inbox_next {window: 4}` gives the next photos in capture order.
|
|
24
|
-
2.
|
|
24
|
+
2. If a photo has a `rotation_hint`, or shows up sideways or upside down, turn it upright first with `pb_inbox_rotate {id, degrees}` (clockwise: text reading bottom-to-top → 90, upside down → 180, top-to-bottom → 270) and view it again. Never transcribe a sideways photo.
|
|
25
|
+
3. View them in order with `pb_inbox_image`. Group the photos that belong to one card: usually a front (title, photo) followed by its back (ingredients, steps, nutrition — no title). Match a back to its front by order and by ingredients that echo the front's subtitle and description.
|
|
25
26
|
- `retake_of` means a near-identical photo of the same side: use the clearer one as the main image and attach the other with the same side, or skip it.
|
|
26
27
|
- If small print is unreadable, request `pb_inbox_image {id, max_px: 2400}` before giving up on a field.
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
4. Extract everything the guide asks for and call `pb_save_recipe` with `provider`, `source: {kind: "card"}`, the full structured record, your own `blurb`, and `ingest: [{id, side}]` listing **every** photo of the card.
|
|
29
|
+
5. Handle the response:
|
|
29
30
|
- `saved: true` — note the slug and move on. Glance at `vocabulary.rejected` and `unmatched_uses`; fix only clear mistakes (re-save with `slug`).
|
|
30
31
|
- `duplicate_of` — compare with that recipe (`pb_get_recipe`). Same card → attach the photos to it (`pb_save_recipe` with its `slug`, the same data and `ingest`) or `pb_inbox_resolve {status: "skipped", reason: "duplicate of <slug>"}`. Genuinely different → re-save with `allow_duplicate: true`.
|
|
31
32
|
- `already_ingested` — the photos were saved before; continue.
|
|
32
33
|
- `error` — read it; usually a photo id is wrong or already attached elsewhere.
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
6. Photos that aren't recipe cards → `skipped` with a reason. Photos you cannot read at all → `failed` with what's wrong ("back too blurry to read steps") so the user can re-shoot them.
|
|
35
|
+
7. A front whose back is missing: save what the front gives with `extraction.needs_review: true` and list the missing parts in `extraction.uncertain`. Don't hold up the batch waiting for it.
|
|
35
36
|
|
|
36
37
|
## Rules
|
|
37
38
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e,t,n,r,i,a,o,s,c,l,u,d={},f=[],p=/^m(i|n|o|s|text|space)$/,m=Array.isArray,h=f.slice,g=Object.assign;function _(e){e&&e.parentNode&&e.remove()}function v(e,t,n){var r,i,a,o={},s=arguments.length;for(a in t)a==`key`?r=t[a]:a==`ref`&&typeof e!=`function`?i=t[a]:o[a]=t[a];return s>2&&(o.children=s>3?h.call(arguments,2):n),b(e,o,r,i,null)}function y(e,t,n){var r,i,a,o=g({},e.props),s=arguments.length;for(a in t)a==`key`?r=t[a]:a==`ref`&&typeof e.type!=`function`?i=t[a]:o[a]=t[a];return s>2&&(o.children=s>3?h.call(arguments,2):n),b(e.type,o,r===void 0?e.key:r,i===void 0?e.ref:i,null)}function b(n,r,i,a,o){var s={type:n,props:r,key:i,ref:a,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:o||++t,__i:-1,__u:0};return!o&&e.vnode&&e.vnode(s),s}function x(e){return e.children}function S(e,t){this.props=e,this.context=t,this.__g=0}function C(e,t){if(t==null)return e.__?C(e.__,e.__i+1):null;for(var n;t<e.__k.length;t++)if((n=e.__k[t])&&n.__e)return n.__e;return typeof e.type!=`function`||e.props.__P?null:C(e)}function w(e){if((e=e.__)&&e.__c&&!e.props.__P)return e.__e=null,e.__k.some(function(t){return t&&(e.__e=t.__e)}),w(e)}function T(t){(8&t.__g||!(t.__g|=8)||!n.push(t)||i++)&&r==e.debounceRendering||((r=e.debounceRendering)||queueMicrotask)(E)}function E(){var t,r,o,s,c,l,u,d,f;try{for(r=1;n.length;)n.length>r&&n.sort(a),t=n.shift(),r=n.length,8&t.__g&&(s=void 0,c=void 0,l=(c=(o=t).__v).__e,u=[],d=[],(f=o.__P)&&((s=g({constructor:void 0},c)).__v=c.__v+1,e.vnode&&e.vnode(s),N(f,s,c,o.__n,f.namespaceURI,32&c.__u?[l]:null,u,l||C(c),32&c.__u,d),s.__v=c.__v,s.__.__k[s.__i]=s,P(u,s,d),c.__=c.__e=null,s.__e!=l&&w(s)))}finally{n.length=i=0}}function ee(e,t,n,r,i,a,o,s,c,l,u){var p,m,h,g,_,v,y=r.__k||f,b=t.length;for(c=D(n,t,y,c,b),p=0;p<b;p++)(h=n.__k[p])!=null&&(m=~h.__i&&y[h.__i]||d,h.__i=p,v=N(e,h,m,i,a,o,s,c,l,u),g=h.__e,(m.ref!=h.ref||8&m.__u)&&(m.ref&&F(m.ref,null,h,m),h.ref&&u.push(h.ref,h.__c||g,h)),_||=g,4&h.__u?(c=O(h,c,e,!m.__v),m.__e&&(m.__e=null)):typeof h.type==`function`&&v!==void 0?c=v:g&&(c=g.nextSibling),h.__u&=-7);return n.__e=_,c}function D(e,t,n,r,i){var a,o,s,c,l,u,d,f,p,h,g=n.length,_=g,v=0,y=!1,S=e.__k=Array(i);for(a=0;a<i;a++)(o=t[a])!=null&&typeof o!=`boolean`&&typeof o!=`function`?(typeof o!=`object`||o.constructor==String?o=S[a]=b(null,o):m(o)?o=S[a]=b(x,{children:o}):o.constructor===void 0&&o.__b?o=S[a]=b(o.type,o.props,o.key,o.ref,o.__v):S[a]=o,c=a+v,o.__=e,o.__b=e.__b+1,s=null,~(l=o.__i=A(o,n,c,_))&&(_--,(s=n[l])&&(s.__u|=2)),s&&s.__v?(o.__u|=2,l==c-1?v--:l==c+1?v++:l!=c&&(l>c?v--:v++,y=!0)):(~l||(i>g?v--:i<g&&v++),typeof o.type!=`function`&&(o.__u|=4))):S[a]=null;if(y){for(u=[],d=[],a=0;a<i;a++)if((o=S[a])&&2&o.__u){for(f=0,p=u.length;f<p;)u[h=f+p>>1]<o.__i?f=h+1:p=h;u[f]=o.__i,d[a]=f+1}for(v=u.length;a--;)d[a]&&(d[a]==v?v--:S[a].__u|=4)}if(_)for(a=0;a<g;a++)!(s=n[a])||2&s.__u||(s.__e==r&&(r=C(s)),ie(s,s));return r}function O(e,t,n,r){var i,a,o;if(typeof e.type==`function`){if(e.props.__P)return t;if(i=e.__k)for(a=0;a<i.length;a++)i[a]&&(i[a].__=e,t=O(i[a],t,n,!1));return t}for(t&&!t.parentNode&&(t=C(e))&&!t.parentNode&&(t=null),o=t;o&&o.nodeType==8;)o=o.nextSibling;for(e.__e!=o&&(!r&&n.moveBefore&&e.__e.parentNode?n.moveBefore(e.__e,t):n.insertBefore(e.__e,t||null)),t=e.__e;(t&&=t.nextSibling)&&t.nodeType==8;);return t}function k(e,t){return t||=[],e!=null&&typeof e!=`boolean`&&(m(e)?e.some(function(e){k(e,t)}):t.push(e)),t}function A(e,t,n,r){var i,a,o,s=e.key,c=e.type,l=t[n],u=l&&!(2&l.__u);if(l===null&&s==null||u&&s==l.key&&c==l.type)return n;if(r>+!!u){for(i=n-1,a=n+1;i>=0||a<t.length;)if((l=t[o=i>=0?i--:a++])&&!(2&l.__u)&&s==l.key&&c==l.type)return o}return-1}function j(e,t,n){n??=``,t[0]==`-`?e.setProperty(t,n):e[t]=n}function M(e,t,n,r,i){var a,o;n:if(t==`style`){if(typeof n==`string`)e.style.cssText=n;else{if(typeof r==`string`&&(e.style.cssText=r=``),r)for(t in r)n&&t in n||j(e.style,t,``);if(n)for(t in n)r&&n[t]==r[t]||j(e.style,t,n[t])}}else if(t[0]==`o`&&t[1]==`n`)(e.__e||={})[t]=n,n&&r||(o=l[t]||(l[t]=te(t)),(e.__a||={})[t]=c,a=t!=(t=t.replace(s,`$1`)),(t=t.slice(2))[0]<`a`&&(t=t.toLowerCase()),n?e.addEventListener(t,o,a):e.removeEventListener(t,o,a));else{if(i==`http://www.w3.org/2000/svg`)t=t.replace(/xlink(H|:h)/,`h`).replace(/sName$/,`s`);else if(t!=`width`&&t!=`height`&&t!=`href`&&t!=`list`&&t!=`form`&&t!=`tabIndex`&&t!=`download`&&t!=`rowSpan`&&t!=`colSpan`&&t!=`role`&&t!=`popover`&&t in e)try{e[t]=n??``;break n}catch{}typeof n==`function`||(n==null||!1===n&&t[4]!=`-`?e.removeAttribute(t):e.setAttribute(t,t==`popover`&&n==1?``:n))}}function te(t){return function(n){if(this.__e){var r=this.__e[t];if(n[o]==null)n[o]=c++;else if(n[o]<this.__a[t])return;return r(e.event?e.event(n):n)}}}function N(t,n,r,i,a,o,s,c,l,u){var d,p,h,v,y,b,w,T,E,D,O,k,A,j,M,te,N,P,F,I,L=n.type;if(n.constructor!==void 0)return null;if(128&r.__u&&(l=32&r.__u,d=r.__c.__z)){if(n.__u|=l,p=o=[],d.nodeType==8)for(h=1,v=d.nextSibling;v;v=v.nextSibling){if(v.nodeType==8){if(v.data.startsWith(`$s`))h++;else if(v.data.startsWith(`/$s`)&&!--h)break}o.push(v)}else o.push(d);c=o[0]}(d=e.__b)&&d(n);n:if(typeof L==`function`){y=s.length;try{if(D=n.props,O=(d=L.prototype)&&d.render,k=(d=L.contextType)&&i[d.__c],A=d?k?k.props.value:d.__:i,r.__c?2&(b=n.__c=r.__c).__g&&(b.__g|=1):(O?n.__c=b=new L(D,A):(n.__c=b=new S(D,A),b.constructor=L,b.render=ae),k&&k.sub(b),b.state||(b.state={}),b.__n=i,b.__g|=8,b.__h=[],b.__k=[]),O&&(b.__s||(b.__s=b.state),L.getDerivedStateFromProps&&(b.__s==b.state&&(b.__s=g({},b.__s)),g(b.__s,L.getDerivedStateFromProps(D,b.__s)))),w=b.props,T=b.state,b.__v=n,r.__c){if(O&&!L.getDerivedStateFromProps&&D!==w&&b.componentWillReceiveProps&&b.componentWillReceiveProps(D,A),n.__v==r.__v&&!(8&b.__g)||!(4&b.__g)&&b.shouldComponentUpdate&&!1===b.shouldComponentUpdate(D,b.__s,A)){n.__v!=r.__v&&(b.props=D,b.state=b.__s,b.__g&=-9),n.__e=r.__e,n.__k=r.__k,n.__k.some(function(e){e&&(e.__=n)}),f.push.apply(b.__h,b.__k),b.__k=[],b.__h.length&&s.push(b),c=C(r);break n}b.componentWillUpdate&&b.componentWillUpdate(D,b.__s,A),O&&b.componentDidUpdate&&b.__h.push(function(){b.componentDidUpdate(w,T,E)})}else O&&!L.getDerivedStateFromProps&&b.componentWillMount&&b.componentWillMount(),O&&b.componentDidMount&&b.__h.push(b.componentDidMount);if(b.context=A,b.props=D,b.__P=t,b.__g&=-5,j=e.__r,M=0,O)b.state=b.__s,b.__g&=-9,j&&j(n),d=b.render(b.props,b.state,b.context),f.push.apply(b.__h,b.__k),b.__k=[];else do b.__g&=-9,j&&j(n),d=b.render(b.props,b.state,b.context),b.state=b.__s;while(8&b.__g&&++M<25);b.state=b.__s,b.getChildContext&&(i=g({},i,b.getChildContext())),O&&r.__c&&b.getSnapshotBeforeUpdate&&(E=b.getSnapshotBeforeUpdate(w,T)),te=d&&d.type===x&&d.key==null?d.props.children:d,D.__P&&(d=c,a=(t=D.__P).namespaceURI,l=o=null,r.props&&r.props.__P!=t&&(r.__k.some(function(e){e&&ie(e,e)}),r.__k=null),c=r.__k?C(r,0):null),c=ee(t,m(te)?te:[te],n,r,i,a,o,s,c,l,u),D.__P&&(n.__e=null,c=d),n.__u&=-161,128&r.__u&&(b.__z=null),p&&p.some(_),b.__h.length&&s.push(b),1&b.__g&&(b.__g&=-4)}catch(t){if(s.length=y,n.__v=null,l||o){if(t.then){if(N=0,n.__u|=l?160:128,o){for(~(F=o.indexOf(c||void 0))||(F=o.length);(I=o[F-1])&&I.nodeType==8;)F--;for(;F<o.length;F++)if(I=o[F]){if(o[F]=null,I.nodeType==8){if(I.data.startsWith(`$s`))N++||(P=I);else if(N&&I.data.startsWith(`/$s`)&&!--N){c=I;break}}else if(!N)break}}if(!P){for(;c&&c.nodeType==8&&c.nextSibling;)c=c.nextSibling;P=c}n.__c.__z||(n.__c.__z=P),n.__e=c}else o&&o.some(_)}else n.__e=r.__e;n.__k||=r.__k||[],t.then||ne(n),e.__e(t,n,r)}}else c=n.__e=re(r.__e,n,r,i,a,o,s,l,u,t);return(d=e.diffed)&&d(n),128&n.__u?void 0:c}function ne(e){e&&(e.__c&&(e.__c.__g|=4),e.__k&&e.__k.some(ne))}function P(t,n,r){for(var i=0;i<r.length;)F(r[i++],r[i++],r[i++]);e.__c&&e.__c(n,t),t.some(function(n){try{t=n.__h,n.__h=[],t.some(function(e){e.call(n)})}catch(t){e.__e(t,n.__v)}})}function re(t,n,r,i,a,o,s,c,l,u){var f,g,v,y,b,x,S,w,T,E=r.props||d,D=n.props,O=n.type;if(O==`svg`?a=`http://www.w3.org/2000/svg`:O==`math`?a=`http://www.w3.org/1998/Math/MathML`:a||=`http://www.w3.org/1999/xhtml`,o){for(f=0;f<o.length;f++)if((b=o[f])&&(O?b.localName==O:b.nodeType==3)){t=b,o[f]=null;break}}if(!t){if(w=u.ownerDocument||document,!O)return w.createTextNode(D);t=w.createElementNS(a,O,D.is&&D),c&&=(e.__m&&e.__m(n,o),!1),o=null}if(O){if(u=O==`template`?t.content:t,o=O==`textarea`&&D.defaultValue!=null?null:o&&h.call(u.childNodes),!c&&o)for(E={},f=0;f<t.attributes.length;f++)E[(b=t.attributes[f]).name]=b.value;for(f in E)b=E[f],f==`dangerouslySetInnerHTML`?v=b:f==`children`||f in D||f==`value`&&`defaultValue`in D||f==`checked`&&`defaultChecked`in D||M(t,f,null,b,a);for(f in T=1&r.__u,D)b=D[f],f==`children`?y=b:f==`dangerouslySetInnerHTML`?g=b:f==`value`?x=b:f==`checked`?S=b:c&&typeof b!=`function`||!(E[f]!==b||T&&b!=null)||M(t,f,b,E[f],a);g?(c||v&&(g.__html==v.__html||g.__html==t.innerHTML)||(t.innerHTML=g.__html),n.__k=[]):(v&&(t.textContent=``),(O==`foreignObject`||a==`http://www.w3.org/1998/Math/MathML`&&p.test(O))&&(a=`http://www.w3.org/1999/xhtml`),ee(u,m(y)?y:[y],n,r,i,a,o,s,o?o[0]:r.__k&&C(r,0),c,l),o&&o.some(_)),c&&O!=`textarea`||(f=`value`,O==`progress`&&x==null?t.removeAttribute(f):x==null||x===t[f]&&(O!=`progress`||x)||M(t,f,x,E[f],a),f=`checked`,S!=null&&S!=t[f]&&M(t,f,S,E[f],a))}else E===D||c&&t.data==D||(t.data=D);return t}function F(t,n,r,i){try{typeof t==`function`?n?n.__x=t(n)||1:(i&&=i.__c||i.__e)&&(n=i.__x)&&(i.__x=null,typeof n==`function`?n():t(null)):t.current=n}catch(t){e.__e(t,r)}}function ie(t,n,r){var i,a;if(e.unmount&&e.unmount(t),!(i=t.ref)||i.current&&i.current!=t.__e||F(i,null,n,t),i=t.__c){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(t){e.__e(t,n)}i.__P=i.__n=null}if(i=t.__k)for(a=0;a<i.length;a++)i[a]&&ie(i[a],n,typeof t.type!=`function`||r&&!t.props.__P);(i=t.__e)&&(r||_(i),i.__e&&(i.__e=null)),t.__e=t.__c=t.__=null}function ae(e,t,n){return this.constructor(e,n)}function I(t,n){var r,i,a,o;e.__&&e.__(t,n),n.nodeType==9&&(n=n.documentElement),i=(r=t&&32&t.__u)?null:n.__k,n.__k=b(x,{children:[t]}),a=[],o=[],N(n,n.__k,i||d,d,n.namespaceURI,i?null:n.firstChild?h.call(n.childNodes):null,a,i?i.__e:n.firstChild,r,o),P(a,n.__k,o),n.__k.props.children=null}function L(e){function t(e){var n,r;return this.getChildContext||(n=new Set,(r={})[t.__c]=this,this.getChildContext=function(){return r},this.shouldComponentUpdate=function(e){this.props.value!=e.value&&n.forEach(function(e){e.__g|=4,T(e)})},this.sub=function(e){n.add(e);var t=e.componentWillUnmount;e.componentWillUnmount=function(){n.delete(e),t&&t.call(e)}}),e.children}return t.__c=`__cC`+u++,t.__=e,t.Provider=(t.Consumer=function(e,t){return e.children(t)}).contextType=t,t}e={__e:function(e,t,n,r){for(var a,o,s;t=t.__;)if((a=t.__c)&&!(1&a.__g)){a.__g|=4;try{if((o=a.constructor)&&o.getDerivedStateFromError&&(a.setState(o.getDerivedStateFromError(e)),s=8&a.__g),a.componentDidCatch&&(a.componentDidCatch(e,r||{}),s=8&a.__g),s)return void(a.__g|=2)}catch(t){e=t,s=0}}throw i=0,e}},t=0,S.prototype.setState=function(e,t){var n=this.__s;n&&n!=this.state||(n=this.__s=g({},this.state)),typeof e==`function`&&(e=e(g({},n),this.props)),e&&(g(n,e),this.__v&&(t&&this.__k.push(t),T(this)))},S.prototype.forceUpdate=function(e){this.__v&&(this.__g|=4,e&&this.__h.push(e),T(this))},S.prototype.render=x,n=[],i=0,a=function(e,t){return e.__v.__b-t.__v.__b},o=Symbol(),s=/(PointerCapture)$|Capture$/i,c=0,l={},u=0;var R,z,oe,se,ce=Object.is,le=0,ue=[],de=[],B=e,fe=B.__b,pe=B.__r,me=B.diffed,he=B.__c,ge=B.unmount,_e=B.__;function ve(e,t){B.__h&&B.__h(z,e,le||t),le=0;var n=z.__H||(z.__H={__:[],__h:[]});return e>=n.__.length&&n.__.push({}),n.__[e]}function V(e){return le=1,ye(je,e)}function ye(e,t,n){var r=ve(R++,2);if(r.t=e,!r.__c&&(r.__=[n?n(t):je(void 0,t),function(e){var t=r.__N?r.__N[0]:r.__[0],n=r.t(t,e);ce(t,n)||(r.__N=[n,r.__[1]],r.__c.setState({}))}],r.__c=z,!z.__f)){z.__f=!0;var i=z.shouldComponentUpdate;z.shouldComponentUpdate=function(e,t,n){var r=this.__H;if(!r)return!0;var a=!1,o=this.props!=e;if(r.__.some(function(e){e.__N&&(a=!0,ce(e.__[0],e.__N[0])||(o=!0))}),i){var s=i.call(this,e,t,n);return a?s||o:s}return!a||o}}return r.__}function be(e,t){var n=ve(R++,3);!B.__s&&Ae(n.__H,t)&&(n.__P=!0,n.__=e,n.u=t,z.__H.__h.push(n))}function xe(e,t){var n=ve(R++,4);!B.__s&&Ae(n.__H,t)&&(n.__P=!1,n.__=e,n.u=t,z.__h.push(n))}function H(e){return le=5,Se(function(){return{current:e}},[])}function Se(e,t){var n=ve(R++,7);return Ae(n.__H,t)&&(n.__=e(),n.__H=t),n.__}function Ce(e,t){return le=8,Se(function(){return e},t)}function we(e){var t=z.context[e.__c],n=ve(R++,9);return n.c=e,t?(n.__??(n.__=!0,t.sub(z)),t.props.value):e.__}function Te(){var e;do{for(;e=de.shift();)try{U(e)}catch(t){B.__e(t,{__:(e=e.__P)&&e.__v})}for(;e=ue.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(U),t.__h.some(ke),t.__h=[]}catch(n){t.__h=[],B.__e(n,e.__v)}}}while(de.length)}B.__b=function(e){z=null,fe&&fe(e)},B.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),_e&&_e(e,t)},B.__r=function(e){pe&&pe(e),R=0;var t=(z=e.__c).__H;t&&(oe==z?z.__h=[]:(t.__h.some(U),t.__h.some(ke),R=0),t.__h=[],t.__.some(function(e){e.__N&&(e.__=e.__N),e.u=e.__N=void 0})),oe=z},B.diffed=function(e){me&&me(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&Oe(ue.push(t)),t.__H.__.some(function(e){e.u&&(e.__H=e.u)})),oe=z=null},B.__c=function(e,t){t.some(function(e){try{e.__h.some(U),e.__h=e.__h.filter(function(e){return!e.__||ke(e)})}catch(n){t.some(function(e){e.__h&&=[]}),t=[],B.__e(n,e.__v)}}),he&&he(e,t)},B.unmount=function(e){ge&&ge(e);var t,n,r=e.__c;r&&r.__H&&(r.__H.__.some(function(r){try{if(r.__P&&r.__c){if(n===void 0){for(n=e.__;n&&(!n.__c||!n.__c.__P);)n=n.__;n&&=n.__c}r.__P=n,Oe(de.push(r))}else U(r)}catch(e){t=e}}),r.__H=void 0,t&&B.__e(t,r.__v))};var Ee=typeof requestAnimationFrame==`function`;function De(e){var t,n=function(){clearTimeout(r),Ee&&cancelAnimationFrame(t),setTimeout(e)},r=setTimeout(n,35);Ee&&(t=requestAnimationFrame(n))}function Oe(e){e!=1&&se==B.requestAnimationFrame||((se=B.requestAnimationFrame)||De)(Te)}function U(e){var t=z,n=e.__c;typeof n==`function`&&(e.__c=void 0,n()),z=t}function ke(e){var t=z;e.__c=e.__(),z=t}function Ae(e,t){return!e||e.length!=t.length||t.some(function(t,n){return!ce(t,e[n])})}function je(e,t){return typeof t==`function`?t(e):t}var W,Me;function Ne(e){return!Me||(typeof Me==`string`?e.startsWith(Me):Me.test(e))}function Pe(e,t){let n=``;if(W=void 0,t&&t.type===`click`){if(t.ctrlKey||t.metaKey||t.altKey||t.shiftKey||t.button!==0)return e;let r=t.composedPath().find(e=>e.nodeName==`A`&&e.href),i=r&&r.getAttribute(`href`);if(!r||r.origin!=location.origin||/^#/.test(i)||!/^(_?self)?$/i.test(r.target)||!Ne(i)||r.download)return e;W=!0,t.preventDefault(),n=r.href.replace(location.origin,``)}else t&&t.url?(W=!t.replace,n=t.url):n=location.pathname+location.search;return W===!0?history.pushState(null,``,n):W===!1&&history.replaceState(null,``,n),n}var Fe=(e,t,n={})=>{e=e.split(`/`).filter(Boolean),t=(t||``).split(`/`).filter(Boolean),n.params||={};for(let r=0,i,a;r<Math.max(e.length,t.length);r++){let[,o,s,c]=(t[r]||``).match(/^(:?)(.*?)([+*?]?)$/);if(i=e[r],o||s!=i){if(!o&&i&&c==`*`){n.rest=`/`+e.slice(r).join(`/`);break}if(!o||!i&&c!=`?`&&c!=`*`)return;if(a=c==`+`||c==`*`,a?i=e.slice(r).map(decodeURIComponent).join(`/`)||void 0:i&&=decodeURIComponent(i),n.params[s]=i,s in n||(n[s]=i),a)break}}return n};function Ie(e){let[t,n]=ye(Pe,e.url||location.pathname+location.search);e.scope&&(Me=e.scope);let r=W===!0,i=Se(()=>{let e=new URL(t,location.origin),i=e.pathname.replace(/\/+$/g,``)||`/`;return{url:t,path:i,query:Object.fromEntries(e.searchParams),route:(e,t)=>n({url:e,replace:t}),back:()=>{history.back()},forward:()=>{history.forward()},wasPush:r}},[t]);return xe(()=>(addEventListener(`click`,n),addEventListener(`popstate`,n),()=>{removeEventListener(`click`,n),removeEventListener(`popstate`,n)}),[]),v(Ie.ctx.Provider,{value:i},e.children)}var Le=Promise.resolve();function Re(e){let[t,n]=ye(e=>e+1,0),{url:r,query:i,wasPush:a,path:o}=Ue();if(!r)throw Error(`preact-iso's <Router> must be used within a <LocationProvider>, see: https://github.com/preactjs/preact-iso#locationprovider`);let{rest:s=o,params:c={}}=we(He),l=H(!1),u=H(o),d=H(1),f=H(),p=H(),m=H(),h=H(!1),g=H();g.current=!1;let _,b,x;k(e.children).some(e=>{if(Fe(s,e.props.path,x={...e.props,path:s,query:i,params:Object.assign({},c),rest:``}))return _=y(e,x);e.props.default&&(b=y(e,x))});let S=_||b,C=f.current&&f.current.__u&ze&&f.current.__u&Be,w=f.current&&f.current.__h,T=Se(()=>{p.current=f.current,f.current=v(He.Provider,{value:x},S);let e=p.current&&p.current.props.children;return!e||!S||S.type!==e.type||S.props.component!==e.props.component?(d.current++,!0):!1},[r,JSON.stringify(x)]);C?(f.current.__u|=ze,f.current.__u|=Be):w&&(f.current.__h=!0);let E=p.current;return p.current=null,this.__c=(t,i)=>{g.current=!0,p.current=E,e.onLoadStart&&e.onLoadStart(r),l.current=!0;let a=d.current;t.then(()=>{a===d.current&&(p.current=null,f.current&&(i.__h&&(f.current.__h=i.__h),i.__u&Be&&(f.current.__u|=Be),i.__u&ze&&(f.current.__u|=ze)),Le.then(n))})},xe(()=>{let t=this.__v&&this.__v.__e;g.current?!h.current&&!m.current&&(m.current=t):(!h.current&&m.current&&(m.current!==t&&m.current.remove(),m.current=null),h.current=!0,u.current!==o&&(a&&scrollTo(0,0),e.onRouteChange&&e.onRouteChange(r),u.current=o),e.onLoadEnd&&l.current&&e.onLoadEnd(r),l.current=!1)},[o,a,t]),T?[v(Ve,{r:f,key:d.current}),v(Ve,{r:p,key:d.current-1})]:v(Ve,{r:f,key:d.current})}var ze=32,Be=128,Ve=({r:e})=>e.current;Re.Provider=Ie,Ie.ctx=L({});var He=L({}),G=e=>v(e.component,e),Ue=()=>we(Ie.ctx),We=()=>we(He),Ge=e.__b;e.__b=e=>{e.type&&e.type._forwarded&&e.ref&&(e.props.ref=e.ref,e.ref=null),Ge&&Ge(e)};function Ke(e){let t,n,r=()=>e().then(e=>n=e&&e.default||e),i=e=>{let[,i]=V(0),a=H(n);if(t||=r(),n!==void 0)return v(n,e);throw a.current||=t.then(()=>i(1)),t};return i.preload=()=>(t||=r(),t),i._forwarded=!0,i}var qe=e.__e;e.__e=(e,t,n)=>{if(e&&e.then){let r=t;for(;r=r.__;)if(r.__c&&r.__c.__c)return t.__e??(t.__c.__z=[n.__e],t.__e=n.__e,t.__k=n.__k),t.__k||=[],r.__c.__c(e,t)}qe&&qe(e,t,n)};var Je=`modulepreload`,Ye=function(e){return`/`+e},Xe={},Ze=function(e){return e.pathname.endsWith(`.css`)},Qe=function(e,t,n){if(t in e)return e[t];let r=n();if(!r){e[t]=void 0;return}let i=r.then(()=>{e[t]=void 0},n=>{throw e[t]=void 0,n});return e[t]=i,i},$e=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e,i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?new URL(import.meta.resolve(e)):new URL(e,import.meta.url)}r=o(t.map(t=>{t=Ye(t,n);let r=s(t),i=Ze(r);return Qe(Xe,r.href,()=>{if(e===void 0){e={all:new Set,styles:new Set};let t=document.getElementsByTagName(`link`);for(let n=t.length-1;n>=0;n--){let r=t[n];e.all.add(r.href),r.rel===`stylesheet`&&e.styles.add(r.href)}}if((i?e.styles:e.all).has(r.href))return;let t=document.createElement(`link`);if(t.rel=i?`stylesheet`:Je,i||(t.as=`script`),t.crossOrigin=``,t.href=r.href,a&&t.setAttribute(`nonce`,a),document.head.appendChild(t),i)return new Promise((e,n)=>{t.addEventListener(`load`,e),t.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${r}`)))})})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};async function K(e,t,n,r={}){let i={method:e,headers:{...r}};e!==`GET`&&(i.headers[`X-Pantrybase`]=`1`),n instanceof Blob?i.body=n:n!==void 0&&(i.body=JSON.stringify(n),i.headers[`Content-Type`]=`application/json`);let a=await fetch(t,i),o=await a.json().catch(()=>({}));if(!a.ok)throw Error(o.error??`${a.status} ${a.statusText}`);return o}var et=e=>{let t=new URLSearchParams;for(let[n,r]of Object.entries(e))r!=null&&r!==``&&t.set(n,String(r));let n=t.toString();return n?`?${n}`:``},tt=e=>({tags:e.tags&&Object.keys(e.tags).length?JSON.stringify(e.tags):void 0,exclude_allergens:e.exclude_allergens?.length?e.exclude_allergens.join(`,`):void 0,max_total_min:e.max_total_min,needs_review:e.needs_review?`true`:void 0}),q=(e,t)=>`/api/files/${e.split(`/`).map(encodeURIComponent).join(`/`)}${t?`?w=${t}`:``}`,J={stats:()=>K(`GET`,`/api/stats`),facets:()=>K(`GET`,`/api/facets`),search:(e,t,n=48)=>K(`GET`,`/api/search${et({q:e,limit:n,...tt(t)})}`),recipe:e=>K(`GET`,`/api/recipes/${encodeURIComponent(e)}`),pantry:()=>K(`GET`,`/api/pantry`),pantryAdd:e=>K(`POST`,`/api/pantry`,{items:e.map(e=>({name:e}))}),pantryRemove:e=>K(`DELETE`,`/api/pantry`,{names:e}),pantryClear:()=>K(`DELETE`,`/api/pantry`,{all:!0}),match:e=>K(`GET`,`/api/pantry/match${et({...tt(e),min_coverage:e.min_coverage,ingredients:e.ingredients?.join(`,`)})}`),ingredients:e=>K(`GET`,`/api/ingredients${et({q:e,limit:12})}`),graph:e=>K(`GET`,`/api/graph${et({categories:e.categories.join(`,`),ingredients:e.ingredients?`1`:`0`,min_degree:e.min_degree,cuisine:e.cuisine,similar:e.similar})}`),inbox:e=>K(`GET`,`/api/inbox${et({status:e,limit:300})}`),upload:e=>K(`POST`,`/api/inbox/upload`,e,{"X-Filename":encodeURIComponent(e.name)}),resolve:(e,t,n)=>K(`POST`,`/api/inbox/resolve`,{ids:e,status:t,reason:n})},nt=0;Array.isArray;function Y(t,n,r,i,a,o){n||={};var s,c,l=n;if(`ref`in l&&typeof t!=`function`)for(c in l={},n)c==`ref`?s=n[c]:l[c]=n[c];var u={type:t,props:l,key:r,ref:s,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--nt,__i:-1,__u:0};return(a||o)&&(u.__source=a,u.__self=o),e.vnode&&e.vnode(u),u}function X(e,t){let[n,r]=V({loading:!0}),i=H(0),a=Ce(()=>{let t=++i.current;r(e=>({...e,loading:!0})),e().then(e=>t===i.current&&r({data:e,loading:!1}),e=>t===i.current&&r(t=>({...t,error:String(e instanceof Error?e.message:e),loading:!1})))},t);return be(()=>a(),[a]),{...n,reload:a}}function rt(e,t=250){let[n,r]=V(e);return be(()=>{let n=setTimeout(()=>r(e),t);return()=>clearTimeout(n)},[e,t]),n}function it(e){return e==null?``:e>=60?`${Math.floor(e/60)}h${e%60?` ${e%60}m`:``}`:`${e} min`}function Z(e){return e.replace(/-/g,` `)}function Q(e){let t=`chip${e.tone?` ${e.tone}`:``}`;return e.onClick?Y(`button`,{type:`button`,class:t,onClick:e.onClick,title:e.title,children:e.children}):Y(`span`,{class:t,title:e.title,children:e.children})}function at({r:e}){let t=e.images[0];return Y(`a`,{class:`card`,href:`/recipe/${e.slug}`,children:[t?Y(`div`,{class:`img`,style:{backgroundImage:`url("${q(t,480)}")`}}):Y(`div`,{class:`img none`,"aria-hidden":`true`,children:e.source_kind===`synthesized`?`✦`:`🍽`}),Y(`div`,{class:`body`,children:[Y(`div`,{class:`title`,children:e.title}),e.subtitle&&Y(`div`,{class:`sub`,children:e.subtitle}),Y(`div`,{class:`meta`,children:[e.total_min!=null&&Y(Q,{children:[`⏱ `,it(e.total_min)]}),e.tags.cuisine?.slice(0,1).map(e=>Y(Q,{children:Z(e)},e)),e.tags.diet?.slice(0,2).map(e=>Y(Q,{tone:`ok`,children:Z(e)},e)),e.needs_review&&Y(Q,{tone:`warn`,children:`needs review`})]})]})]})}function $({children:e}){return Y(`div`,{class:`empty`,children:e})}var ot=[`pending`,`done`,`skipped`,`failed`];function st(){let[e,t]=V(`pending`),n=X(()=>J.inbox(e),[e]),[r,i]=V(!1),[a,o]=V(null),[s,c]=V({}),l=async e=>{let r=[...e].filter(e=>/\.(jpe?g|png|webp|heic|heif|tiff?)$/i.test(e.name));if(!r.length)return;let i=[];o({done:0,total:r.length,errors:i});for(let[e,t]of r.entries()){try{await J.upload(t)}catch(e){i.push(`${t.name}: ${String(e)}`)}o({done:e+1,total:r.length,errors:i})}t(`pending`),n.reload()},u=async(e,t)=>{await J.resolve([e.id],t,s[e.id]),n.reload()},d=n.data?.counts;return Y(`div`,{class:`stack`,children:[Y(`label`,{class:`drop${r?` over`:``}`,onDragOver:e=>{e.preventDefault(),i(!0)},onDragLeave:()=>i(!1),onDrop:e=>{e.preventDefault(),i(!1),e.dataTransfer?.files&&l(e.dataTransfer.files)},children:[Y(`input`,{type:`file`,multiple:!0,accept:`image/*,.heic,.heif`,style:{display:`none`},onChange:e=>{let t=e.target.files;t&&l(t)}}),Y(`div`,{style:{fontSize:`1.1rem`,fontWeight:600},children:`Drop recipe card photos here, or click to choose`}),Y(`div`,{class:`small`,children:`JPEG, PNG, WebP or iPhone HEIC — fronts and backs. Exact duplicates are ignored automatically.`}),a&&Y(`div`,{class:`small`,style:{marginTop:`0.5rem`},children:[`Uploaded `,a.done,`/`,a.total,a.errors.map(e=>Y(`div`,{class:`error`,children:e},e))]})]}),d&&d.pending>0&&Y(`div`,{class:`notice`,children:[d.pending,` photo`,d.pending===1?``:`s`,` waiting. In Claude Code, run`,` `,Y(`b`,{children:`/pantrybase:ingest`}),` and Claude will read each card, pair fronts with backs, and save the recipes.`]}),Y(`div`,{class:`tabs`,children:ot.map(n=>Y(`button`,{type:`button`,class:n===e?`on`:``,onClick:()=>t(n),children:[n,` `,d?Y(`span`,{class:`muted`,children:d[n]}):null]},n))}),n.data&&n.data.items.length===0?Y($,{children:[`Nothing `,e,`.`]}):Y(`div`,{class:`thumbgrid`,children:n.data?.items.map(e=>Y(`div`,{class:`thumb`,children:[Y(`a`,{href:q(e.path,1600),target:`_blank`,rel:`noreferrer`,children:Y(`img`,{src:q(e.path,360),alt:e.name,loading:`lazy`})}),Y(`div`,{class:`tb`,children:[Y(`div`,{class:`row`,style:{justifyContent:`space-between`},children:[Y(`span`,{title:e.name,children:[`#`,e.id]}),Y(`span`,{class:`muted`,children:e.captured_at.slice(5,16).replace(`T`,` `)})]}),e.retake_of&&Y(Q,{tone:`warn`,title:`Looks like photo #${e.retake_of.id} (distance ${e.retake_of.distance})`,children:[`retake of #`,e.retake_of.id]}),e.recipe_slug&&Y(`a`,{href:`/recipe/${e.recipe_slug}`,children:[`→ `,e.recipe_slug]}),e.reason&&Y(`span`,{class:`muted`,children:e.reason}),e.status!==`done`&&Y(x,{children:[e.status===`pending`&&Y(`input`,{type:`text`,placeholder:`reason (optional)`,value:s[e.id]??``,onInput:t=>c(n=>({...n,[e.id]:t.target.value}))}),Y(`div`,{class:`row`,children:e.status===`pending`?Y(x,{children:[Y(`button`,{type:`button`,class:`small`,onClick:()=>u(e,`skipped`),children:`Skip`}),Y(`button`,{type:`button`,class:`small`,onClick:()=>u(e,`failed`),children:`Unreadable`})]}):Y(`button`,{type:`button`,class:`small`,onClick:()=>u(e,`pending`),children:`Back to queue`})})]})]})]},e.id))})]})}function ct(){let e=X(()=>J.pantry(),[]),[t,n]=V(``),[r,i]=V(.5),[a,o]=V(null),s=rt(t,150),c=X(()=>s.trim().length>=2?J.ingredients(s.trim()):Promise.resolve([]),[s]),l=X(()=>J.match({min_coverage:r}),[r,e.data?.length]),u=async t=>{let r=t.split(`,`).map(e=>e.trim()).filter(Boolean);if(r.length){o(null);try{await J.pantryAdd(r),n(``),e.reload()}catch(e){o(String(e))}}},d=async t=>{await J.pantryRemove([t]),e.reload()};return Y(`div`,{class:`layout`,style:{gridTemplateColumns:`minmax(280px, 380px) 1fr`},children:[Y(`section`,{class:`panel stack`,children:[Y(`h2`,{children:`Your pantry`}),Y(`form`,{onSubmit:e=>{e.preventDefault(),u(t)},class:`row`,style:{flexWrap:`nowrap`},children:[Y(`input`,{type:`text`,list:`ingredient-suggestions`,placeholder:`Add items, e.g. eggs, baby marrow, rice`,value:t,onInput:e=>n(e.target.value)}),Y(`datalist`,{id:`ingredient-suggestions`,children:c.data?.map(e=>Y(`option`,{value:e.canonical},e.canonical))}),Y(`button`,{type:`submit`,class:`primary`,children:`Add`})]}),a&&Y(`p`,{class:`error small`,children:a}),e.data?.length?Y(`div`,{class:`row`,children:e.data.map(e=>Y(Q,{onClick:()=>d(e.canonical),title:`Remove`,children:[e.canonical,e.qty==null?``:` ×${e.qty}${e.unit?` ${e.unit}`:``}`,` `,Y(`span`,{class:`x`,children:`✕`})]},e.canonical))}):Y(`p`,{class:`muted small`,children:`Nothing yet. Salt, oil, water and other kitchen staples never count against a recipe, so skip those.`}),e.data&&e.data.length>0&&Y(`button`,{type:`button`,class:`ghost small`,onClick:async()=>{await J.pantryClear(),e.reload()},children:`Clear pantry`})]}),Y(`section`,{class:`panel`,children:[Y(`div`,{class:`row`,style:{justifyContent:`space-between`},children:[Y(`h2`,{children:`What can I cook?`}),Y(`label`,{class:`row small`,children:[`at least `,Math.round(r*100),`% covered`,Y(`input`,{type:`range`,min:0,max:1,step:.1,value:r,onInput:e=>i(Number(e.target.value))})]})]}),l.data&&l.data.results.length===0?Y($,{children:`No recipes reach that coverage yet — add more to your pantry or lower the bar.`}):l.data?.results.map(e=>Y(`div`,{class:`list-row`,children:[Y(`div`,{children:[Y(`a`,{href:`/recipe/${e.slug}`,children:Y(`b`,{children:e.title})}),e.near_miss&&Y(Q,{tone:`warn`,title:`1–2 items short`,children:`near miss`}),Y(`div`,{class:`row small`,style:{marginTop:`0.25rem`},children:e.missing.length?Y(x,{children:[Y(`span`,{class:`muted`,children:`missing`}),e.missing.map(e=>Y(Q,{onClick:()=>u(e.canonical),title:`Add to pantry`,children:[`+ `,Z(e.canonical)]},e.canonical))]}):Y(`span`,{class:`muted`,children:`you have everything`})})]}),Y(`div`,{children:[Y(`div`,{class:`bar`,children:Y(`span`,{style:{width:`${Math.round(e.coverage*100)}%`}})}),Y(`div`,{class:`muted small`,style:{textAlign:`right`},children:[e.have,`/`,e.need,` · `,Math.round(e.coverage*100),`%`]})]})]},e.slug))]})]})}var lt=[[`energy_kj`,`Energy`,`kJ`],[`energy_kcal`,`Energy`,`kcal`],[`protein_g`,`Protein`,`g`],[`carbs_g`,`Carbs`,`g`],[`sugars_g`,`of which sugars`,`g`],[`fibre_g`,`Fibre`,`g`],[`fat_g`,`Fat`,`g`],[`saturated_g`,`of which saturated`,`g`],[`sodium_mg`,`Sodium`,`mg`]];function ut(e){return[e.qty,e.unit].filter(e=>e!=null&&e!==``).join(` `)}function dt(){let{params:e}=We(),t=e.slug??``,n=X(()=>J.recipe(t),[t]),[r,i]=V(0),[a,o]=V(!1);if(n.error)return Y($,{children:n.error});let s=n.data;if(!s)return Y($,{children:`Loading…`});let c=s.recipe,l=new Set(s.pantry_have),u=c.ingredients.filter(e=>e.source!==`kitchen`),d=c.ingredients.filter(e=>e.source===`kitchen`),f=u.filter(e=>!e.staple&&!e.optional&&!l.has(e.canonical)),p=async e=>{o(!0);try{await J.pantryAdd(e),n.reload()}finally{o(!1)}},m=e=>{let t=l.has(e.canonical);return Y(`li`,{children:[Y(`span`,{class:`tick`,title:t?`in your pantry`:e.staple?`staple`:`not in pantry`,children:t?`✓`:e.staple?`·`:`○`}),Y(`span`,{children:[Y(`span`,{title:ut(e)?`${ut(e)} ${e.canonical}`:e.canonical,children:e.raw}),e.prep&&Y(`span`,{class:`prep`,children:[` — `,e.prep]}),e.optional&&!/optional/i.test(e.raw)&&Y(`span`,{class:`muted small`,children:` (optional)`}),e.alternatives?.length?Y(`span`,{class:`muted small`,children:[` · or `,e.alternatives.join(`, `)]}):null,e.components?.length?Y(`div`,{class:`muted small`,children:e.components.map(e=>e.raw).join(` + `)}):null]})]},`${e.raw}-${e.canonical}`)},h=c.nutrition,g=c.provider?.wine;return Y(`article`,{children:[Y(`div`,{class:`hero`,children:[Y(`div`,{class:`gallery`,children:c.images.length?Y(x,{children:[Y(`img`,{class:`main`,src:q(c.images[r]??c.images[0]??``,1400),alt:c.title}),c.images.length>1&&Y(`div`,{class:`thumbs`,children:c.images.map((e,t)=>Y(`button`,{type:`button`,class:`ghost`,style:{padding:0},onClick:()=>i(t),"aria-label":`Photo ${t+1}`,children:Y(`img`,{src:q(e,200),class:t===r?`sel`:``,alt:``})},e))})]}):Y(`div`,{class:`panel empty`,children:`No photo`})}),Y(`div`,{class:`stack`,children:[Y(`div`,{children:[Y(`h1`,{children:c.title}),c.subtitle&&Y(`div`,{class:`muted`,children:c.subtitle})]}),Y(`div`,{class:`row`,children:[`cuisine`,`dish`,`diet`,`protein`,`flavor`,`texture`,`season`].flatMap(e=>(c.tags[e]??[]).map(t=>Y(`a`,{href:`/?tags=${encodeURIComponent(JSON.stringify({[e]:[t]}))}`,children:Y(Q,{tone:e===`diet`?`ok`:void 0,children:Z(t)})},`${e}${t}`)))}),c.extraction?.needs_review&&Y(`div`,{class:`notice`,children:[`Flagged for review`,c.extraction.uncertain?.length?`: couldn't read ${c.extraction.uncertain.join(`, `)}`:``,`.`,c.extraction.notes?` ${c.extraction.notes}`:``]}),c.blurb&&Y(`p`,{children:c.blurb}),Y(`div`,{class:`facts`,children:[c.time.active!=null&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`Hands-on`}),it(c.time.active)]}),c.time.total!=null&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`Total`}),it(c.time.total)]}),c.servings!=null&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`Serves`}),c.servings]}),c.provider?.chef&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`Chef`}),c.provider.chef]}),c.provider&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`From`}),c.provider.name??c.provider.id,c.provider.plan?` · ${c.provider.plan}`:``]}),c.provider?.eat_within_days!=null&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`Eat within`}),c.provider.eat_within_days,` days`]}),g&&(g.name||g.producer)&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`Wine`}),[g.producer,g.name,g.vintage].filter(Boolean).join(` · `)]}),c.source.kind===`synthesized`&&Y(`div`,{class:`fact`,children:[Y(`b`,{children:`Source`}),`Synthesized`]})]}),c.description&&Y(`p`,{class:`muted small`,children:c.description})]})]}),Y(`div`,{class:`cols`,children:[Y(`section`,{class:`panel`,children:[Y(`div`,{class:`row`,style:{justifyContent:`space-between`},children:[Y(`h2`,{children:`Ingredients`}),f.length>0&&Y(`button`,{type:`button`,class:`small`,disabled:a,onClick:()=>p(f.map(e=>e.canonical)),children:`I have these`})]}),Y(`ul`,{class:`ings`,children:u.map(m)}),d.length>0&&Y(x,{children:[Y(`h3`,{style:{marginTop:`1rem`},children:`From your kitchen`}),Y(`ul`,{class:`ings`,children:d.map(m)})]}),Y(`p`,{class:`muted small`,children:`✓ in your pantry · ○ missing · · staple`})]}),Y(`section`,{class:`panel`,children:[Y(`h2`,{children:`Method`}),Y(`ol`,{class:`steps`,children:c.steps.map((e,t)=>Y(`li`,{children:[e.title&&Y(`div`,{class:`stitle`,children:e.title}),Y(`div`,{children:e.text}),Y(`div`,{class:`row`,style:{marginTop:`0.3rem`},children:[e.techniques?.map(e=>Y(Q,{children:Z(e)},e)),e.temp_c!=null&&Y(Q,{children:[`🌡 `,e.temp_c,`°C`]}),e.minutes?.length?Y(Q,{children:[`⏱ `,e.minutes.join(`–`),` min`]}):null]})]},t))}),c.tips.length>0&&Y(x,{children:[Y(`h3`,{children:`Tips`}),c.tips.map(e=>Y(`p`,{class:`small`,children:e},e))]}),c.notes&&Y(x,{children:[Y(`h3`,{children:`Notes`}),Y(`p`,{class:`small`,children:c.notes})]})]}),Y(`aside`,{class:`stack`,children:[h&&Y(`section`,{class:`panel`,children:[Y(`h2`,{children:`Nutrition`}),Y(`div`,{class:`muted small`,children:h.basis===`per_100g`?`per 100 g`:`per serving`}),Y(`table`,{class:`nut`,children:Y(`tbody`,{children:lt.filter(([e])=>h[e]!=null).map(([e,t,n])=>Y(`tr`,{children:[Y(`td`,{children:t}),Y(`td`,{children:[h[e],` `,n]})]},e))})})]}),(c.allergens.length>0||c.equipment.length>0)&&Y(`section`,{class:`panel stack`,children:[c.allergens.length>0&&Y(`div`,{children:[Y(`h3`,{children:`Allergens`}),Y(`div`,{class:`row`,children:c.allergens.map(e=>Y(Q,{tone:`bad`,children:Z(e)},e))})]}),c.equipment.length>0&&Y(`div`,{children:[Y(`h3`,{children:`Equipment`}),Y(`div`,{class:`row`,children:c.equipment.map(e=>Y(Q,{children:Z(e)},e))})]})]}),s.sources.length>0&&Y(`section`,{class:`panel`,children:[Y(`h3`,{children:`Inspired by`}),s.sources.map(e=>Y(`div`,{children:Y(`a`,{href:`/recipe/${e.slug}`,children:e.title})},e.slug))]}),s.derived.length>0&&Y(`section`,{class:`panel`,children:[Y(`h3`,{children:`Recipes built on this`}),s.derived.map(e=>Y(`div`,{children:Y(`a`,{href:`/recipe/${e.slug}`,children:e.title})},e.slug))]})]})]}),s.similar.length>0&&Y(`section`,{style:{marginTop:`2rem`},children:[Y(`h2`,{children:`Similar recipes`}),Y(`div`,{class:`grid`,children:s.similar.map(e=>Y(at,{r:e},e.slug))})]})]})}var ft=[`diet`,`cuisine`,`dish`,`protein`,`starch`,`flavor`,`texture`,`method`,`meal`,`time`,`season`,`technique`,`equipment`,`chef`,`plan`,`provider`],pt=[0,20,30,45,60];function mt(e){let t={};try{t=e.tags?JSON.parse(e.tags):{}}catch{t={}}return{q:e.q??``,tags:t,exclude:e.exclude?e.exclude.split(`,`):[],max:e.max?Number(e.max):0,review:e.review===`1`}}function ht(){let e=Ue(),t=mt(e.query),[n,r]=V(t.q),[i,a]=V(t.tags),[o,s]=V(t.exclude),[c,l]=V(t.max),[u,d]=V(t.review),[f,p]=V({}),m=rt(n);be(()=>{let t=new URLSearchParams;m&&t.set(`q`,m),Object.keys(i).length&&t.set(`tags`,JSON.stringify(i)),o.length&&t.set(`exclude`,o.join(`,`)),c&&t.set(`max`,String(c)),u&&t.set(`review`,`1`);let n=`/${t.toString()?`?${t}`:``}`;n!==`${e.path}${e.url.includes(`?`)?e.url.slice(e.url.indexOf(`?`)):``}`&&e.route(n,!0)},[m,i,o,c,u]);let h={tags:i,exclude_allergens:o,max_total_min:c||void 0,needs_review:u||void 0},g=X(()=>J.search(m,h),[m,JSON.stringify(h)]),_=X(()=>J.facets(),[]),v=(e,t)=>a(n=>{let r=new Set(n[e]??[]);r.has(t)?r.delete(t):r.add(t);let i={...n,[e]:[...r]};return i[e]?.length||delete i[e],i}),y=e=>s(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e]),b=Object.entries(i).flatMap(([e,t])=>t.map(t=>[e,t])),x=_.data?.used??{};return Y(`div`,{class:`layout`,children:[Y(`aside`,{class:`panel`,children:[Y(`div`,{class:`facet`,children:[Y(`h3`,{children:`Total time`}),Y(`select`,{value:c,onChange:e=>l(Number(e.target.value)),children:pt.map(e=>Y(`option`,{value:e,children:e?`≤ ${e} min`:`Any`},e))})]}),x.allergen?.length?Y(`div`,{class:`facet`,children:[Y(`h3`,{children:`Leave out allergens`}),Y(`div`,{class:`chips`,children:x.allergen.map(e=>Y(Q,{tone:o.includes(e.name)?`bad`:void 0,onClick:()=>y(e.name),children:[o.includes(e.name)?`✕ `:``,Z(e.name)]},e.name))})]}):null,ft.filter(e=>x[e]?.length).map(e=>{let t=x[e]??[],n=f[e]?t:t.slice(0,8);return Y(`div`,{class:`facet`,children:[Y(`h3`,{children:e}),Y(`div`,{class:`chips`,children:n.map(t=>Y(Q,{tone:i[e]?.includes(t.name)?`on`:void 0,onClick:()=>v(e,t.name),children:[Z(t.name),` `,Y(`span`,{class:`muted`,children:t.count})]},t.name))}),t.length>8&&Y(`button`,{type:`button`,class:`ghost more`,onClick:()=>p(t=>({...t,[e]:!t[e]})),children:f[e]?`fewer`:`+${t.length-8} more`})]},e)}),Y(`label`,{class:`row small`,children:[Y(`input`,{type:`checkbox`,checked:u,onChange:e=>d(e.target.checked)}),`Only recipes needing review`]})]}),Y(`section`,{class:`stack`,children:[Y(`input`,{type:`search`,placeholder:`Search recipes — a dish, an ingredient, a mood (“cosy weeknight curry”)…`,value:n,onInput:e=>r(e.target.value)}),(b.length>0||o.length>0||c>0)&&Y(`div`,{class:`row`,children:[b.map(([e,t])=>Y(Q,{tone:`on`,onClick:()=>v(e,t),children:[e,`: `,Z(t),` `,Y(`span`,{class:`x`,children:`✕`})]},`${e}:${t}`)),o.map(e=>Y(Q,{tone:`bad`,onClick:()=>y(e),children:[`no `,Z(e),` `,Y(`span`,{class:`x`,children:`✕`})]},e)),Y(`button`,{type:`button`,class:`ghost small`,onClick:()=>{a({}),s([]),l(0),d(!1)},children:`Clear all`})]}),g.error&&Y(`p`,{class:`error`,children:g.error}),Y(`div`,{class:`muted small`,children:[g.data?`${g.data.results.length} recipes`:`Loading…`,g.data&&m?g.data.vector_used?` · semantic + keyword`:` · keyword (semantic index warming up)`:``]}),g.data&&g.data.results.length===0?Y($,{children:[`No recipes match.`,` `,m||b.length?`Try fewer filters.`:`Add some — drop card photos on the Inbox page, then ask Claude to /pantrybase:ingest them.`]}):Y(`div`,{class:`grid`,children:g.data?.results.map(e=>Y(at,{r:e},e.slug))})]})]})}var gt=[`cuisine`,`dish`,`diet`,`protein`,`starch`,`flavor`,`texture`,`method`,`meal`,`time`,`season`,`allergen`,`technique`,`equipment`,`chef`,`plan`,`provider`,`other`];function _t(){let e=X(()=>J.facets(),[]),t=e.data?.used??{},n=gt.filter(e=>t[e]?.length);return e.data&&!n.length?Y($,{children:`No tags yet — they appear as recipes are saved.`}):Y(`div`,{class:`grid`,style:{gridTemplateColumns:`repeat(auto-fill, minmax(300px, 1fr))`},children:n.map(n=>Y(`section`,{class:`panel`,children:[Y(`h3`,{children:[n,` `,Y(`span`,{class:`muted`,children:[`· `,t[n]?.length]})]}),Y(`div`,{class:`row`,children:t[n]?.map(e=>Y(`a`,{href:n===`allergen`?`/?exclude=${encodeURIComponent(e.name)}`:`/?tags=${encodeURIComponent(JSON.stringify({[n]:[e.name]}))}`,children:Y(Q,{children:[Z(e.name),` `,Y(`span`,{class:`muted`,children:e.count})]})},e.name))}),e.data?.vocabulary[n]?.mode===`closed`&&Y(`div`,{class:`muted small`,style:{marginTop:`0.5rem`},children:`Controlled vocabulary`})]},n))})}var vt=Ke(()=>$e(()=>import(`./graph-Mpnwdw0-.js`).then(e=>e.GraphPage),[]));function yt(){let{path:e}=Ue(),t=X(()=>J.stats(),[e]),n=(t,n,r)=>Y(`a`,{class:`link${(t===`/`?e===`/`:e.startsWith(t))?` active`:``}`,href:t,children:[n,r?Y(`span`,{class:`badge`,children:r}):null]}),r=t.data;return Y(`nav`,{class:`nav`,children:[Y(`a`,{class:`brand`,href:`/`,children:[Y(`i`,{}),` pantrybase`]}),n(`/`,`Recipes`),n(`/pantry`,`Pantry`),n(`/graph`,`Graph`),n(`/inbox`,`Inbox`,r?.inbox.pending),n(`/tags`,`Tags`),Y(`span`,{class:`spacer`}),r&&Y(`span`,{class:`stat`,children:[r.recipes,` recipes · `,r.ingredients,` ingredients`,r.needs_review?` · ${r.needs_review} to review`:``]})]})}function bt(){return Y(Ie,{children:[Y(yt,{}),Y(`main`,{children:Y(Re,{children:[Y(G,{path:`/`,component:ht}),Y(G,{path:`/recipe/:slug`,component:dt}),Y(G,{path:`/pantry`,component:ct}),Y(G,{path:`/graph`,component:vt}),Y(G,{path:`/inbox`,component:st}),Y(G,{path:`/tags`,component:_t}),Y(G,{default:!0,component:ht})]})})]})}var xt=document.getElementById(`app`);xt&&I(Y(bt,{}),xt);export{J as a,be as c,V as d,Y as i,H as l,Z as n,q as o,X as r,Ue as s,Q as t,Se as u};
|