Files
pictureFrame-webApp/public/build/assets/HomeView-AbgIw1T0.js
T
football2801 d31698e7b3
CI / test (push) Has been cancelled
fix: thread cropOrientation into StickerCanvas (was using device orientation)
StickerCanvas was being passed contextOrientation (the target device's
orientation), so the final composited.jpg was always sized to the device's
aspect — even when the user toggled the crop tool to a different orientation.
A landscape crop on a portrait device would produce a 1600x960 cropped
blob, then the StickerCanvas would re-render it into a 960x1600 frame,
visibly stretching the image into portrait dimensions and saving it that
way.

UploadView now derives an effectiveOrientation that prefers the user's
chosen crop orientation (uploadStore.cropOrientation) and falls back to
the device's orientation only before the crop step has run. The
StickerCanvas honors that.

Also adds a temporary debug log in the upload controller to verify the
cropOrientation form field is arriving and being persisted — recent
uploads have NULL cropOrientation despite the frontend sending it, and
this log will make the next upload's payload visible. Will remove once
diagnosed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:05:31 -04:00

1 line
12 KiB
JavaScript

import{B as e,C as t,E as n,H as r,K as i,_ as a,d as o,dt as s,f as c,ft as l,g as u,h as d,j as f,k as p,l as m,o as h,p as g,pt as _,t as v,u as y,v as b,z as x}from"./_plugin-vue_export-helper-DVo1OUMD.js";import{l as S,s as C}from"./index-DuvRERmn.js";import{i as w,n as T,r as E,t as D}from"./BaseBottomSheet-CPiyXFZV.js";var O={key:0,class:`frame-card__status-badge`,"aria-live":`polite`},k=[`src`,`alt`],A={key:1,class:`frame-card__empty-preview`,"aria-hidden":`true`},j={class:`frame-card__body`},M={class:`frame-card__name`},N={key:0,class:`frame-card__count`},P={key:0},F={key:1,"aria-hidden":`true`},I=v(b({__name:`FrameCard`,props:{deviceId:{},name:{},size:{},status:{},orientation:{},thumbnailUrl:{},photoCount:{}},emits:[`add-photo`,`edit`],setup(e){let t=e,n=m(()=>t.size===`large`?{aspectRatio:t.orientation===`portrait`?`3/5`:`5/3`}:{});return(t,r)=>(p(),g(`div`,{class:s([`frame-card`,`frame-card--${e.size}`,e.status!==`ok`&&`frame-card--${e.status}`])},[e.status===`ok`?c(``,!0):(p(),g(`div`,O,[r[2]||=y(`span`,{class:`frame-card__status-dot`,"aria-hidden":`true`},null,-1),u(` `+_(e.status===`offline`?`Offline`:`Sync issue`),1)])),e.size===`large`?(p(),g(`button`,{key:1,class:`frame-card__settings-btn`,type:`button`,"aria-label":`Frame settings`,onClick:r[0]||=n=>t.$emit(`edit`,e.deviceId)},[...r[3]||=[y(`svg`,{width:`18`,height:`18`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`,"aria-hidden":`true`},[y(`circle`,{cx:`12`,cy:`12`,r:`3`}),y(`path`,{d:`M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z`})],-1)]])):c(``,!0),y(`div`,{class:`frame-card__preview`,style:l(n.value)},[e.thumbnailUrl?(p(),g(`img`,{key:0,src:e.thumbnailUrl,alt:`Current photo on ${e.name}`,class:`frame-card__img`},null,8,k)):(p(),g(`div`,A,[...r[4]||=[y(`svg`,{width:`32`,height:`32`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`},[y(`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`}),y(`circle`,{cx:`8.5`,cy:`8.5`,r:`1.5`}),y(`polyline`,{points:`21,15 16,10 5,21`})],-1)]]))],4),y(`div`,j,[y(`p`,M,_(e.name),1),e.size===`compact`?(p(),g(`p`,N,_(e.photoCount)+` `+_(e.photoCount===1?`photo`:`photos`),1)):c(``,!0),a(T,{variant:e.size===`large`?`primary`:`icon-pill`,"aria-label":(e.size,`Add photo to ${e.name}`),class:`frame-card__add-btn`,onClick:r[1]||=n=>t.$emit(`add-photo`,e.deviceId)},{default:x(()=>[e.size===`large`?(p(),g(`span`,P,`+ Add Photo`)):(p(),g(`span`,F,`+`))]),_:1},8,[`variant`,`aria-label`])])],2))}}),[[`__scopeId`,`data-v-6b5d66c8`]]),L=[`id`,`value`,`type`],R=[`for`],z=[`id`],B=v(b({__name:`BaseInput`,props:{modelValue:{default:``},label:{},type:{default:`text`},error:{},id:{}},emits:[`update:modelValue`,`blur`],setup(e,{emit:n}){let r=e,i=n,a=m(()=>r.id??`input-${Math.random().toString(36).slice(2)}`);return(n,r)=>(p(),g(`div`,{class:s([`input-wrap`,{"input-wrap--error":!!e.error,"input-wrap--filled":!!e.modelValue}])},[y(`input`,t({id:a.value},n.$attrs,{value:e.modelValue,type:e.type,class:`input-wrap__field`,placeholder:` `,onInput:r[0]||=e=>i(`update:modelValue`,e.target.value),onBlur:r[1]||=e=>i(`blur`,e)}),null,16,L),y(`label`,{for:a.value,class:`input-wrap__label`},_(e.label),9,R),e.error?(p(),g(`p`,{key:0,id:`${a.value}-error`,class:`input-wrap__error`,role:`alert`},_(e.error),9,z)):c(``,!0)],2))}}),[[`__scopeId`,`data-v-28da7c7d`]]),V={class:`orientation-picker`,role:`radiogroup`,"aria-label":`Display orientation`},H=[`aria-checked`,`aria-label`,`onClick`],U=[`viewBox`],W=[`stroke`,`fill`],G=[`fill`],K={class:`orientation-opt__label`},q={class:`orientation-opt__sub`},J=v(b({__name:`OrientationPicker`,props:{modelValue:{}},emits:[`update:modelValue`],setup(e){let n=[{value:`landscape`,label:`Landscape`,sub:`Ribbon at bottom`,viewBox:`0 0 48 48`,rect:{x:4,y:12,width:40,height:24},ribbon:{x:20,y:36,width:8,height:5}},{value:`portrait`,label:`Portrait`,sub:`Ribbon on left`,viewBox:`0 0 48 48`,rect:{x:12,y:4,width:24,height:40},ribbon:{x:7,y:20,width:5,height:8}}];return(r,i)=>(p(),g(`div`,V,[(p(),g(h,null,f(n,n=>y(`button`,{key:n.value,type:`button`,role:`radio`,"aria-checked":e.modelValue===n.value,"aria-label":n.label,class:s([`orientation-opt`,{"orientation-opt--active":e.modelValue===n.value}]),onClick:e=>r.$emit(`update:modelValue`,n.value)},[(p(),g(`svg`,{class:`orientation-opt__diagram`,viewBox:n.viewBox,fill:`none`,"aria-hidden":`true`},[y(`rect`,t({ref_for:!0},n.rect,{rx:`2`,stroke:e.modelValue===n.value?`var(--color-primary)`:`currentColor`,"stroke-width":`1.5`,fill:e.modelValue===n.value?`color-mix(in srgb, var(--color-primary) 12%, transparent)`:`var(--color-surface-2)`}),null,16,W),y(`rect`,t({ref_for:!0},n.ribbon,{fill:e.modelValue===n.value?`var(--color-primary)`:`var(--color-text-muted)`,rx:`1`}),null,16,G)],8,U)),y(`span`,K,_(n.label),1),y(`span`,q,_(n.sub),1)],10,H)),64))]))}}),[[`__scopeId`,`data-v-679dae08`]]),Y={class:`home-view`},X={key:0,class:`home-view__loading`,"aria-live":`polite`},Z={key:1,class:`home-view__empty`},Q={key:2,class:`home-view__single`},$={key:3,class:`home-view__list`},ee={class:`home-view__sheet-field`},te={class:`home-view__sheet-field`},ne={class:`home-view__sheet-field`},re={class:`home-view__interval-grid`},ie=[`onClick`],ae=[`label`],oe=[`value`],se=v(b({__name:`HomeView`,setup(t){function c(e){return e.lastSeenAt&&Date.now()-new Date(e.lastSeenAt).getTime()<=Math.max(e.rotationIntervalMinutes*2*6e4,30*6e4)?`ok`:`offline`}function l(e){let t=e.lockedImageId??e.currentImageId;return t?`/api/devices/${e.id}/preview?v=${t}`:void 0}let m=C(),v=w(),b=E();n(()=>{v.fetchDevices()});function O(e){let t=document.createElement(`input`);t.type=`file`,t.accept=`image/jpeg,image/png,image/webp,image/gif`,t.onchange=()=>{let n=t.files?.[0];n&&(b.init(n,e),m.push(`/upload`))},t.click()}let k=[{hour:0,label:`12 AM`},{hour:2,label:`2 AM`},{hour:4,label:`4 AM`},{hour:6,label:`6 AM`},{hour:8,label:`8 AM`},{hour:10,label:`10 AM`},{hour:12,label:`12 PM`},{hour:18,label:`6 PM`},{hour:20,label:`8 PM`},{hour:22,label:`10 PM`}],A=[{label:`Americas`,zones:[{value:`America/New_York`,label:`Eastern — New York, Toronto`},{value:`America/Chicago`,label:`Central — Chicago, Mexico City`},{value:`America/Denver`,label:`Mountain — Denver, Calgary`},{value:`America/Phoenix`,label:`Mountain (no DST) — Phoenix`},{value:`America/Los_Angeles`,label:`Pacific — Los Angeles, Vancouver`},{value:`America/Anchorage`,label:`Alaska — Anchorage`},{value:`Pacific/Honolulu`,label:`Hawaii — Honolulu`},{value:`America/Sao_Paulo`,label:`Brasília — São Paulo`},{value:`America/Argentina/Buenos_Aires`,label:`Argentina — Buenos Aires`},{value:`America/Bogota`,label:`Colombia — Bogotá`}]},{label:`Europe`,zones:[{value:`Europe/London`,label:`GMT/BST — London, Dublin`},{value:`Europe/Lisbon`,label:`WET/WEST — Lisbon`},{value:`Europe/Paris`,label:`CET/CEST — Paris, Brussels, Amsterdam`},{value:`Europe/Berlin`,label:`CET/CEST — Berlin, Vienna, Zurich`},{value:`Europe/Stockholm`,label:`CET/CEST — Stockholm, Oslo, Copenhagen`},{value:`Europe/Helsinki`,label:`EET/EEST — Helsinki, Tallinn, Riga`},{value:`Europe/Warsaw`,label:`CET/CEST — Warsaw, Prague, Budapest`},{value:`Europe/Rome`,label:`CET/CEST — Rome, Madrid`},{value:`Europe/Athens`,label:`EET/EEST — Athens, Bucharest`},{value:`Europe/Istanbul`,label:`TRT — Istanbul`},{value:`Europe/Moscow`,label:`MSK — Moscow`}]},{label:`Asia & Pacific`,zones:[{value:`Asia/Dubai`,label:`GST — Dubai, Abu Dhabi`},{value:`Asia/Karachi`,label:`PKT — Karachi, Islamabad`},{value:`Asia/Kolkata`,label:`IST — India`},{value:`Asia/Dhaka`,label:`BST — Dhaka, Bangladesh`},{value:`Asia/Bangkok`,label:`ICT — Bangkok, Jakarta, Hanoi`},{value:`Asia/Singapore`,label:`SGT — Singapore, Kuala Lumpur`},{value:`Asia/Shanghai`,label:`CST — Beijing, Shanghai, Taipei`},{value:`Asia/Seoul`,label:`KST — Seoul`},{value:`Asia/Tokyo`,label:`JST — Tokyo`},{value:`Australia/Sydney`,label:`AEDT/AEST — Sydney, Melbourne`},{value:`Australia/Brisbane`,label:`AEST (no DST) — Brisbane`},{value:`Australia/Perth`,label:`AWST — Perth`},{value:`Pacific/Auckland`,label:`NZDT/NZST — Auckland`}]},{label:`Africa & Middle East`,zones:[{value:`Africa/Cairo`,label:`EET — Cairo`},{value:`Africa/Nairobi`,label:`EAT — Nairobi, East Africa`},{value:`Africa/Johannesburg`,label:`SAST — Johannesburg`},{value:`Africa/Lagos`,label:`WAT — Lagos, West Africa`}]},{label:`UTC`,zones:[{value:`UTC`,label:`UTC — Coordinated Universal Time`}]}],j=r(!1),M=r(!1),N=r(null),P=r(``),F=r(`landscape`),L=r(4),R=r(`UTC`);function z(e){let t=v.devices.find(t=>t.id===e);t&&(N.value=t,P.value=t.name,F.value=t.orientation,L.value=t.wakeHour??4,R.value=t.timezone??`UTC`,j.value=!0)}async function V(){if(N.value){M.value=!0;try{await v.updateDevice(N.value.id,{name:P.value.trim()||N.value.name,orientation:F.value,wakeHour:L.value,timezone:R.value}),j.value=!1}finally{M.value=!1}}}return(t,n)=>(p(),g(h,null,[y(`main`,Y,[i(v).loading?(p(),g(`div`,X,` Loading… `)):i(v).devices.length===0?(p(),g(`div`,Z,[...n[4]||=[d(`<div class="home-view__empty-card" data-v-6b74675a><svg class="home-view__empty-icon" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true" data-v-6b74675a><rect x="3" y="3" width="18" height="18" rx="2" data-v-6b74675a></rect><circle cx="8.5" cy="8.5" r="1.5" data-v-6b74675a></circle><polyline points="21,15 16,10 5,21" data-v-6b74675a></polyline></svg><p class="home-view__empty-title" data-v-6b74675a>Set up your first frame</p><p class="home-view__empty-sub" data-v-6b74675a> Power on your pictureFrame device and scan the QR code it displays to get started. </p></div>`,1)]])):i(v).devices.length===1?(p(),g(`div`,Q,[a(I,{deviceId:i(v).devices[0].id,name:i(v).devices[0].name,size:`large`,status:c(i(v).devices[0]),orientation:i(v).devices[0].orientation,thumbnailUrl:l(i(v).devices[0]),onAddPhoto:O,onEdit:z},null,8,[`deviceId`,`name`,`status`,`orientation`,`thumbnailUrl`])])):(p(),g(`div`,$,[(p(!0),g(h,null,f(i(v).devices,e=>(p(),o(I,{key:e.id,deviceId:e.id,name:e.name,size:`compact`,status:c(e),orientation:e.orientation,thumbnailUrl:l(e),onAddPhoto:O,onEdit:z},null,8,[`deviceId`,`name`,`status`,`orientation`,`thumbnailUrl`]))),128))]))]),a(D,{modelValue:j.value,"onUpdate:modelValue":n[3]||=e=>j.value=e,label:`Frame settings`},{default:x(()=>[n[7]||=y(`h2`,{class:`home-view__sheet-title`},`Frame settings`,-1),y(`div`,ee,[a(B,{modelValue:P.value,"onUpdate:modelValue":n[0]||=e=>P.value=e,label:`Frame name`,maxlength:`100`},null,8,[`modelValue`])]),y(`div`,te,[n[5]||=y(`p`,{class:`home-view__sheet-label`},`Orientation`,-1),a(J,{modelValue:F.value,"onUpdate:modelValue":n[1]||=e=>F.value=e},null,8,[`modelValue`])]),y(`div`,ne,[n[6]||=y(`p`,{class:`home-view__sheet-label`},`Update time`,-1),y(`div`,re,[(p(),g(h,null,f(k,e=>y(`button`,{key:e.hour,type:`button`,class:s([`home-view__interval-chip`,{"home-view__interval-chip--on":L.value===e.hour}]),onClick:t=>L.value=e.hour},_(e.label),11,ie)),64))]),e(y(`select`,{class:`home-view__tz-select`,"onUpdate:modelValue":n[2]||=e=>R.value=e},[(p(),g(h,null,f(A,e=>y(`optgroup`,{key:e.label,label:e.label},[(p(!0),g(h,null,f(e.zones,e=>(p(),g(`option`,{key:e.value,value:e.value},_(e.label),9,oe))),128))],8,ae)),64))],512),[[S,R.value]])]),a(T,{variant:`primary`,class:`home-view__sheet-save`,disabled:M.value,onClick:V},{default:x(()=>[u(_(M.value?`Saving…`:`Save`),1)]),_:1},8,[`disabled`])]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-6b74675a`]]);export{se as default};