Space template controls and prevent cancel buttons submitting forms

This commit is contained in:
Pigway
2026-10-02 14:52:57 +08:00
parent 42ae00268b
commit 68b7b6fc0a
6 changed files with 43 additions and 4 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ const icons={dashboard:'◫',gallery:'▧',upload:'↥',templates:'▦',automati
const t=k=>window.MESSAGES[state.lang][k]||k;
const esc=v=>String(v??'—').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const fmt=v=>v?new Date(v).toLocaleString(state.lang==='zh'?'zh-CN':'en-GB',{timeZone:'Asia/Shanghai'}):'—';
const buttons=(id,text,primary=false)=>`<button id="${id}" class="${primary?'primary':''}">${t(text)}</button>`;
const buttons=(id,text,primary=false)=>`<button type="button" id="${id}" class="${primary?'primary':''}">${t(text)}</button>`;
let toastTimer;
function toast(message){$('#toast').textContent=message;$('#toast').style.display='block';clearTimeout(toastTimer);toastTimer=setTimeout(()=>$('#toast').style.display='none',6000);}
async function api(path,options={}){
+8 -1
View File
@@ -17,9 +17,10 @@ async function enhanceDashboard(){
}
async function enhanceTemplates(){
const panel=document.createElement('section');panel.className='card';$('.template-grid').before(panel);await currentPanel(panel);if(!panel.isConnected)return;
const download=document.createElement('button');download.id='download-template';download.textContent=t('download');$('#display-template').after(download);
const download=document.createElement('button');download.id='download-template';download.textContent=t('download');const previewActions=document.createElement('div');previewActions.className='actions template-preview-actions';$('#display-template').before(previewActions);previewActions.append($('#display-template'),download);
download.onclick=busy(async()=>{if(!state.templatePreview){toast(t('previewHint'));return;}await downloadImage('/api/v1/assets/'+state.templatePreview,'frame-flow-template.jpg');});
await poetryControls();
officialContentInfo();
const photo=$('#field-photo_id');if(photo){const file=document.createElement('input');file.type='file';file.id='template-photo-upload';file.accept='image/jpeg,image/png,image/webp';file.setAttribute('aria-label',t('selectFile'));photo.after(file);file.onchange=busy(async()=>{if(!file.files[0])return;const body=new FormData();body.append('file',file.files[0]);body.append('fit','cover');body.append('profile','color');const asset=await api('/api/v1/preview',{method:'POST',body});const accepted=await api('/api/v1/devices/'+state.device+'/upload',{method:'POST',body:{type:'image',asset_id:asset.asset_id},headers:{'Idempotency-Key':'photo-'+clientUUID()}});await waitOperation(accepted.operation_id);const option=document.createElement('option');option.value=asset.asset_id;option.textContent=asset.name;photo.append(option);photo.value=asset.asset_id;photo.oninput();toast(t('done'));});}
}
async function downloadImage(path,name){const blob=await api(path,{binary:true});const url=URL.createObjectURL(blob);const link=document.createElement('a');link.href=url;link.download=name;document.body.append(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),10000);}
@@ -71,3 +72,9 @@ async function poetryControls(){
description();$('#poem-choice').onchange=()=>{const data=JSON.parse($('#template-data').value);delete data.poem_title;delete data.poem_author;delete data.poem_lines;delete data.poem_source;delete data.poem_edition;if($('#poem-choice').value)data.poem_id=$('#poem-choice').value;else{delete data.poem_id;delete data.date;}$('#template-data').value=JSON.stringify(data,null,2);$('#template-data').oninput();description();};
bind('daily-poetry-job',()=>{showModal(`<h2>${t('dailyPoetryJob')}</h2><form id="poetry-job-form"><div class="field"><label for="poetry-job-time">${t('time')}</label><input id="poetry-job-time" type="time" required value="07:00"></div><label class="check-row"><input id="poetry-job-enabled" type="checkbox">${t('enabled')}</label><p class="hint">${t('poetryScheduleHint')}</p><div class="modal-actions">${buttons('modal-cancel','cancel')}<button type="submit" class="primary">${t('save')}</button></div></form>`);$('#poetry-job-form').onsubmit=busy(async e=>{e.preventDefault();await api('/api/v1/jobs',{method:'POST',body:{device_id:state.device,name:t('dailyPoetryJob'),enabled:$('#poetry-job-enabled').checked,schedule:{type:'daily',time:$('#poetry-job-time').value},source:{type:'template',template:'daily-poetry',data:{},profile:$('#template-profile').value},display_duration:0,restore_previous:false,priority:50}});$('#modal').close();await navigate('automation');});});
}
function officialContentInfo(){
const info=document.createElement('details');info.className='official-content-info';
info.innerHTML=`<summary>${t('officialContentTitle')}</summary><p>${t('officialContentDescription')}</p><ul>${['officialCalendarWeather','officialWordAlmanacTravel','officialNews','officialCreative'].map(k=>`<li>${t(k)}</li>`).join('')}</ul><p class="notice">${t('officialSubscriptionUnavailable')}</p><div class="actions"><a href="https://kokonna.art/products/kokonna-ai-e-ink-frame-7" target="_blank" rel="noopener noreferrer">${t('officialProductDocs')}</a><a href="https://kokonna.art/pages/openapi" target="_blank" rel="noopener noreferrer">${t('officialAPIDocs')}</a></div>`;
$('.template-grid').before(info);
}
+3
View File
@@ -13,3 +13,6 @@ window.MESSAGES.zh.templateHint='先生成预览,再选择显示方式。天
Object.assign(window.MESSAGES.zh,{allowedHosts:'允许下载图片的域名(每行一个)',urlHint:'只允许 HTTPS 和设置中明确允许的域名,内网地址始终禁止。',uploadURL:'从链接导入',urlSettings:'图片 URL 安全设置'});Object.assign(window.MESSAGES.en,{allowedHosts:'Allowed image hostnames (one per line)',urlHint:'HTTPS and explicitly allowed hosts only. Private network addresses are blocked.',uploadURL:'Import URL',urlSettings:'Image URL security'});
Object.assign(window.MESSAGES.zh,{poetryNotice:'Frame Flow 每日诗词:内置古典诗词,按上海日期每天换一首。独立于官方小程序的订阅,官方订阅设置无法通过公开 API 读取或修改。',poemChoice:'选择诗词',dailyAutomatic:'每日自动选诗',poetryCatalog:'内置篇数:',dailyPoetryJob:'每日一首诗词',poetryScheduleHint:'每天按上海时区生成当天诗词并显示。开启任务后会按时改变画框显示。',poem_title:'诗名',poem_author:'作者',poem_lines:'诗词正文'});Object.assign(window.MESSAGES.en,{poetryNotice:'Frame Flow Daily Poetry selects a classical poem by Shanghai date. It is independent of the official mini-app subscription, whose settings are not exposed by the public API.',poemChoice:'Choose poem',dailyAutomatic:'Automatic daily selection',poetryCatalog:'Catalog size: ',dailyPoetryJob:'A poem every day',poetryScheduleHint:'Generate and display the daily poem in Shanghai time. Enabling this job will change the frame on schedule.',poem_title:'Poem title',poem_author:'Poet',poem_lines:'Poem text'});
Object.assign(MESSAGES.zh,{officialContentTitle:'KoKonna 官方小组件与订阅',officialContentDescription:'官方产品资料列出的内容类型;下列功能与 Frame Flow 自建模板分开管理。',officialCalendarWeather:'日历与当地天气',officialWordAlmanacTravel:'每日一词、传统黄历、全球旅行推荐',officialNews:'每日新闻',officialCreative:'AI 语音 / 文字 / 涂鸦 / 照片转艺术;照片轮播',officialSubscriptionUnavailable:'公开 OpenAPI 尚未列出订阅目录、当前订阅、开通或取消订阅接口。官方订阅请继续在官方小程序中管理;Frame Flow 不会读取或修改这些设置。你使用的每日诗词属于官方小程序内容,当前无法通过公开 API 确认完整类型列表。',officialProductDocs:'官方内容介绍 ↗',officialAPIDocs:'官方 OpenAPI ↗'});
Object.assign(MESSAGES.en,{officialContentTitle:'KoKonna official widgets and subscriptions',officialContentDescription:'Content types listed in official product materials; managed separately from Frame Flow templates.',officialCalendarWeather:'Calendar and local weather',officialWordAlmanacTravel:'Word of the day, traditional almanac and global travel recommendations',officialNews:'Daily news',officialCreative:'AI voice / text / sketch / photo-to-art; photo rotation',officialSubscriptionUnavailable:'Public OpenAPI does not list subscription discovery, current subscription, subscribe or unsubscribe endpoints. Manage official subscriptions in the official mini-program. Frame Flow cannot read or modify these settings. Your daily-poetry subscription is mini-program content; its complete catalog cannot currently be verified through public API.',officialProductDocs:'Official content overview ↗',officialAPIDocs:'Official OpenAPI ↗'});
+14
View File
@@ -24,3 +24,17 @@ details[open]>summary{margin-bottom:16px}
#password-form{margin-top:24px;padding-top:24px;border-top:1px solid var(--border)}
#modal-content>p,#modal-content>pre{margin:16px 0}
@media(max-width:720px){#main{gap:18px}.columns,.gallery,.template-grid{gap:14px}.frame-demo{display:flex;padding:24px}.hero-info{padding:24px}.hero h2{margin-top:16px}.image-card .actions{gap:8px}.modal-actions{flex-wrap:wrap}.check-row{margin:14px 0}#current-frame-image{height:240px!important}}
/* Keep controls separated inside cards, not just between page sections. */
.template-preview-actions{margin-top:20px;gap:12px}
.template-preview-actions>button{margin-top:0!important}
.poetry-controls{display:flex;flex-direction:column;gap:18px;margin-bottom:24px}
.poetry-controls>.field,.poetry-controls>p,.poetry-controls>.notice{margin:0}
.poetry-controls>button{align-self:flex-start}
.actions,.modal-actions{column-gap:12px;row-gap:12px}
.official-content-info{padding:20px 24px}
.official-content-info ul{padding-left:22px;margin:16px 0}
.official-content-info li+li{margin-top:10px}
.official-content-info .notice{margin:20px 0}
.official-content-info .actions a{display:inline-block;padding:8px 0}
.tabs{gap:10px}