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.
@@ -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).
@@ -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. 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.
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
- 3. 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.
28
- 4. Handle the response:
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
- 5. 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.
34
- 6. 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.
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};