78405b644d
CI / test (push) Has been cancelled
Two complaints, one root cause: the FrameCard was floating in the slide with a min-height-padded preview, so (1) photos got top/bottom gray bars instead of fitting their container, and (2) there was a fat empty gap between the card body and the bottom nav. Restructured the large card to flex-fill its slide: - preview hugs the photo's intrinsic aspect ratio (img with width:100% height:auto); no min-height, no aspect-ratio override → no letterbox - card body has flex:1, info pinned at top, Add Photo button pinned at bottom via margin-top:auto and width:100% - HomeView main / single-card / carousel all flex:1 down through the layout so the slide gets the full available height - empty-state placeholder still reserves the device's aspect so the card doesn't jump while images load Result: the photo fills its container left/right with no bars; the body absorbs all remaining space below, with the action button always sitting just above the bottom nav. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 line
14 KiB
JavaScript
1 line
14 KiB
JavaScript
import{B as e,C as t,E as n,H as r,K as i,_ as a,dt as o,f as s,ft as c,g as l,h as u,j as d,k as f,l as p,o as m,p as h,pt as g,t as _,u as v,v as y,w as b,z as x}from"./_plugin-vue_export-helper-DVo1OUMD.js";import{l as S,s as C}from"./index-aP_uBWCi.js";import{i as w,n as T,r as E,t as D}from"./BaseBottomSheet-CXcLrct9.js";var O=[`src`,`alt`],k={class:`frame-card__body`},A={class:`frame-card__info`},j={class:`frame-card__name`},M={class:`frame-card__status-line`,"aria-live":`polite`},N={class:`frame-card__status-text`},P={key:0,class:`frame-card__sync-line`},F={key:0},I={key:1,class:`frame-card__sync-sep`,"aria-hidden":`true`},L={key:2},R={key:1,class:`frame-card__count`},z={key:0},B={key:1,"aria-hidden":`true`},V=_(y({__name:`FrameCard`,props:{deviceId:{},name:{},size:{},status:{},orientation:{},thumbnailUrl:{},photoCount:{},lastSync:{},nextSync:{}},emits:[`add-photo`,`edit`],setup(e){let t=e,n=p(()=>({})),r=p(()=>t.size===`large`?{aspectRatio:t.orientation===`portrait`?`3 / 5`:`5 / 3`}:{}),i=p(()=>{switch(t.status){case`ok`:return`Online`;case`sync-fail`:return`Sync issue`;case`offline`:return`Offline`}});return(t,l)=>(f(),h(`div`,{class:o([`frame-card`,`frame-card--${e.size}`,`frame-card--${e.status}`])},[e.size===`large`?(f(),h(`button`,{key:0,class:`frame-card__settings-btn`,type:`button`,"aria-label":`Frame settings`,onClick:l[0]||=n=>t.$emit(`edit`,e.deviceId)},[...l[2]||=[v(`svg`,{width:`18`,height:`18`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`,"aria-hidden":`true`},[v(`circle`,{cx:`12`,cy:`12`,r:`3`}),v(`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)]])):s(``,!0),v(`div`,{class:`frame-card__preview`,style:c(n.value)},[e.thumbnailUrl?(f(),h(`img`,{key:0,src:e.thumbnailUrl,alt:`Current photo on ${e.name}`,class:`frame-card__img`},null,8,O)):(f(),h(`div`,{key:1,class:`frame-card__empty-preview`,style:c(r.value),"aria-hidden":`true`},[...l[3]||=[v(`svg`,{width:`32`,height:`32`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`},[v(`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`}),v(`circle`,{cx:`8.5`,cy:`8.5`,r:`1.5`}),v(`polyline`,{points:`21,15 16,10 5,21`})],-1)]],4))],4),v(`div`,k,[v(`div`,A,[v(`p`,j,g(e.name),1),v(`p`,M,[l[4]||=v(`span`,{class:`frame-card__status-dot`,"aria-hidden":`true`},null,-1),v(`span`,N,g(i.value),1)]),e.size===`large`&&(e.lastSync||e.nextSync)?(f(),h(`p`,P,[e.lastSync?(f(),h(`span`,F,`synced `+g(e.lastSync),1)):s(``,!0),e.lastSync&&e.nextSync?(f(),h(`span`,I,`·`)):s(``,!0),e.nextSync?(f(),h(`span`,L,g(e.nextSync),1)):s(``,!0)])):e.size===`compact`&&e.photoCount!==void 0?(f(),h(`p`,R,g(e.photoCount)+` `+g(e.photoCount===1?`photo`:`photos`),1)):s(``,!0)]),a(T,{variant:e.size===`large`?`primary`:`icon-pill`,"aria-label":`Add photo to ${e.name}`,class:`frame-card__add-btn`,onClick:l[1]||=n=>t.$emit(`add-photo`,e.deviceId)},{default:x(()=>[e.size===`large`?(f(),h(`span`,z,`+ Add Photo`)):(f(),h(`span`,B,`+`))]),_:1},8,[`variant`,`aria-label`])])],2))}}),[[`__scopeId`,`data-v-2643e2f7`]]),H=[`id`,`value`,`type`],U=[`for`],W=[`id`],G=_(y({__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=p(()=>r.id??`input-${Math.random().toString(36).slice(2)}`);return(n,r)=>(f(),h(`div`,{class:o([`input-wrap`,{"input-wrap--error":!!e.error,"input-wrap--filled":!!e.modelValue}])},[v(`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,H),v(`label`,{for:a.value,class:`input-wrap__label`},g(e.label),9,U),e.error?(f(),h(`p`,{key:0,id:`${a.value}-error`,class:`input-wrap__error`,role:`alert`},g(e.error),9,W)):s(``,!0)],2))}}),[[`__scopeId`,`data-v-c8235ab2`]]),K={class:`orientation-picker`,role:`radiogroup`,"aria-label":`Display orientation`},q=[`aria-checked`,`aria-label`,`onClick`],J=[`viewBox`],Y=[`stroke`,`fill`],X=[`fill`],Z={class:`orientation-opt__label`},Q={class:`orientation-opt__sub`},$=_(y({__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)=>(f(),h(`div`,K,[(f(),h(m,null,d(n,n=>v(`button`,{key:n.value,type:`button`,role:`radio`,"aria-checked":e.modelValue===n.value,"aria-label":n.label,class:o([`orientation-opt`,{"orientation-opt--active":e.modelValue===n.value}]),onClick:e=>r.$emit(`update:modelValue`,n.value)},[(f(),h(`svg`,{class:`orientation-opt__diagram`,viewBox:n.viewBox,fill:`none`,"aria-hidden":`true`},[v(`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,Y),v(`rect`,t({ref_for:!0},n.ribbon,{fill:e.modelValue===n.value?`var(--color-primary)`:`var(--color-text-muted)`,rx:`1`}),null,16,X)],8,J)),v(`span`,Z,g(n.label),1),v(`span`,Q,g(n.sub),1)],10,q)),64))]))}}),[[`__scopeId`,`data-v-679dae08`]]),ee={class:`home-view`},te={key:0,class:`home-view__loading`,"aria-live":`polite`},ne={key:1,class:`home-view__empty`},re={key:2,class:`home-view__single`},ie=[`aria-label`],ae={class:`home-view__dots`,role:`tablist`,"aria-label":`Frame selector`},oe=[`aria-label`,`aria-selected`,`onClick`],se={class:`home-view__sheet-field`},ce={class:`home-view__sheet-field`},le={class:`home-view__sheet-field`},ue={class:`home-view__interval-grid`},de=[`onClick`],fe=[`label`],pe=[`value`],me=_(y({__name:`HomeView`,setup(t){function s(e){return e.wakeHour===null?e.rotationIntervalMinutes*6e4:1440*60*1e3}function c(e){if(!e.lastSeenAt)return`offline`;let t=Date.now()-new Date(e.lastSeenAt).getTime(),n=s(e);return t<=n?`ok`:t<=2*n?`sync-fail`:`offline`}function p(e){if(!e.lastSeenAt)return null;let t=Date.now()-new Date(e.lastSeenAt).getTime();if(t<6e4)return`just now`;if(t<36e5)return`${Math.round(t/6e4)}m ago`;if(t<864e5)return`${Math.round(t/36e5)}h ago`;let n=Math.round(t/864e5);return n===1?`yesterday`:`${n} days ago`}function _(e){return e===0?`12 AM`:e<12?`${e} AM`:e===12?`12 PM`:`${e-12} PM`}function y(e){if(e.wakeHour!==null){let t=new Intl.DateTimeFormat(`en-US`,{timeZone:e.timezone||`UTC`,hour:`numeric`,hour12:!1}),n=parseInt(t.format(new Date),10)<e.wakeHour?`today`:`tomorrow`;return`next sync ~${_(e.wakeHour)} ${n}`}if(!e.lastSeenAt)return null;let t=new Date(e.lastSeenAt).getTime()+e.rotationIntervalMinutes*6e4-Date.now();return t<=0?null:t<6e4?`next sync in <1m`:t<36e5?`next sync in ${Math.round(t/6e4)}m`:`next sync in ${Math.round(t/36e5)}h`}function O(e){let t=e.lockedImageId??e.currentImageId;return t?`/api/devices/${e.id}/preview?v=${t}`:void 0}let k=C(),A=w(),j=E();n(()=>{A.fetchDevices()});let M=r(null),N=r(0);function P(){let e=M.value;e&&(N.value=Math.round(e.scrollLeft/e.clientWidth))}async function F(e){await b();let t=M.value;t&&t.scrollTo({left:e*t.clientWidth,behavior:`smooth`})}function I(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&&(j.init(n,e),k.push(`/upload`))},t.click()}let L=[{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`}],R=[{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`}]}],z=r(!1),B=r(!1),H=r(null),U=r(``),W=r(`landscape`),K=r(4),q=r(`UTC`);function J(e){let t=A.devices.find(t=>t.id===e);t&&(H.value=t,U.value=t.name,W.value=t.orientation,K.value=t.wakeHour??4,q.value=t.timezone??`UTC`,z.value=!0)}async function Y(){if(H.value){B.value=!0;try{await A.updateDevice(H.value.id,{name:U.value.trim()||H.value.name,orientation:W.value,wakeHour:K.value,timezone:q.value}),z.value=!1}finally{B.value=!1}}}return(t,n)=>(f(),h(m,null,[v(`main`,ee,[i(A).loading?(f(),h(`div`,te,` Loading… `)):i(A).devices.length===0?(f(),h(`div`,ne,[...n[4]||=[u(`<div class="home-view__empty-card" data-v-f2cc382f><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-f2cc382f><rect x="3" y="3" width="18" height="18" rx="2" data-v-f2cc382f></rect><circle cx="8.5" cy="8.5" r="1.5" data-v-f2cc382f></circle><polyline points="21,15 16,10 5,21" data-v-f2cc382f></polyline></svg><p class="home-view__empty-title" data-v-f2cc382f>Set up your first frame</p><p class="home-view__empty-sub" data-v-f2cc382f> Power on your pictureFrame device and scan the QR code it displays to get started. </p></div>`,1)]])):i(A).devices.length===1?(f(),h(`div`,re,[a(V,{deviceId:i(A).devices[0].id,name:i(A).devices[0].name,size:`large`,status:c(i(A).devices[0]),orientation:i(A).devices[0].orientation,thumbnailUrl:O(i(A).devices[0]),lastSync:p(i(A).devices[0]),nextSync:y(i(A).devices[0]),onAddPhoto:I,onEdit:J},null,8,[`deviceId`,`name`,`status`,`orientation`,`thumbnailUrl`,`lastSync`,`nextSync`])])):(f(),h(m,{key:3},[v(`div`,{ref_key:`carouselEl`,ref:M,class:`home-view__carousel`,role:`region`,"aria-roledescription":`carousel`,"aria-label":`Frames`,onScrollPassive:P},[(f(!0),h(m,null,d(i(A).devices,e=>(f(),h(`div`,{key:e.id,class:`home-view__slide`,role:`group`,"aria-roledescription":`Slide`,"aria-label":e.name},[a(V,{deviceId:e.id,name:e.name,size:`large`,status:c(e),orientation:e.orientation,thumbnailUrl:O(e),lastSync:p(e),nextSync:y(e),onAddPhoto:I,onEdit:J},null,8,[`deviceId`,`name`,`status`,`orientation`,`thumbnailUrl`,`lastSync`,`nextSync`])],8,ie))),128))],544),v(`div`,ae,[(f(!0),h(m,null,d(i(A).devices,(e,t)=>(f(),h(`button`,{key:e.id,type:`button`,role:`tab`,class:o([`home-view__dot`,{"home-view__dot--active":t===N.value}]),"aria-label":`Go to ${e.name}`,"aria-selected":t===N.value,onClick:e=>F(t)},null,10,oe))),128))])],64))]),a(D,{modelValue:z.value,"onUpdate:modelValue":n[3]||=e=>z.value=e,label:`Frame settings`},{default:x(()=>[n[7]||=v(`h2`,{class:`home-view__sheet-title`},`Frame settings`,-1),v(`div`,se,[a(G,{modelValue:U.value,"onUpdate:modelValue":n[0]||=e=>U.value=e,label:`Frame name`,maxlength:`100`},null,8,[`modelValue`])]),v(`div`,ce,[n[5]||=v(`p`,{class:`home-view__sheet-label`},`Orientation`,-1),a($,{modelValue:W.value,"onUpdate:modelValue":n[1]||=e=>W.value=e},null,8,[`modelValue`])]),v(`div`,le,[n[6]||=v(`p`,{class:`home-view__sheet-label`},`Update time`,-1),v(`div`,ue,[(f(),h(m,null,d(L,e=>v(`button`,{key:e.hour,type:`button`,class:o([`home-view__interval-chip`,{"home-view__interval-chip--on":K.value===e.hour}]),onClick:t=>K.value=e.hour},g(e.label),11,de)),64))]),e(v(`select`,{class:`home-view__tz-select`,"onUpdate:modelValue":n[2]||=e=>q.value=e},[(f(),h(m,null,d(R,e=>v(`optgroup`,{key:e.label,label:e.label},[(f(!0),h(m,null,d(e.zones,e=>(f(),h(`option`,{key:e.value,value:e.value},g(e.label),9,pe))),128))],8,fe)),64))],512),[[S,q.value]])]),a(T,{variant:`primary`,class:`home-view__sheet-save`,disabled:B.value,onClick:Y},{default:x(()=>[l(g(B.value?`Saving…`:`Save`),1)]),_:1},8,[`disabled`])]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-f2cc382f`]]);export{me as default}; |