From b8c4bcef0c1cead4484696306c669c5d05311412 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Fri, 27 Feb 2026 01:49:04 +0800 Subject: [PATCH] Enhance styles for app layout and components --- src/webclient/script.ts | 909 ++++++++++++++++++++++++++++++++++++++-- src/webclient/styles.ts | 257 +++++++++++- 2 files changed, 1111 insertions(+), 55 deletions(-) diff --git a/src/webclient/script.ts b/src/webclient/script.ts index ae306f7..4eda343 100644 --- a/src/webclient/script.ts +++ b/src/webclient/script.ts @@ -19,6 +19,20 @@ ciphers: [], folders: [], folderFilterId: '', + vaultQuery: '', + vaultType: 'all', + showSelectedPassword: false, + vaultSearchComposing: false, + vaultSearchTimer: 0, + totpTicking: false, + totpTickBusy: false, + detailMode: 'view', + detailDraft: null, + createMenuOpen: false, + fieldModalOpen: false, + fieldModalType: 'text', + fieldModalLabel: '', + fieldModalValue: '', selectedCipherId: '', selectedMap: {}, users: [], @@ -58,11 +72,34 @@ folders: 'Folders', allItems: 'All Items', noFolder: 'No Folder', - refresh: 'Refresh', + searchVault: 'Search vault', + filter: 'Filter', + typeAll: 'All items', + typeLogin: 'Logins', + typeCard: 'Cards', + typeIdentity: 'Identities', + typeNote: 'Secure notes', + typeOther: 'Other', + addWebsite: '+ Add website', + addField: '+ Add field', + fieldType: 'Field type', + fieldLabel: 'Field label', + fieldValue: 'Field value', + fieldText: 'Text', + fieldHidden: 'Hidden', + fieldBoolean: 'Boolean', + fieldLinked: 'Linked', + add: 'Add', + newTypeLogin: 'Login', + newTypeCard: 'Card', + newTypeIdentity: 'Identity', + newTypeNote: 'Note', + newTypeSsh: 'SSH key', + refresh: 'Sync', move: 'Move', delete: 'Delete', selectAll: 'Select All', - clear: 'Clear', + clear: 'Cancel', noItems: 'There are no items to list.', selectItem: 'Select an item to view details.', profile: 'Profile', @@ -71,10 +108,22 @@ currentPwd: 'Current Master Password', newPwd: 'New Master Password', totpSetup: 'Two-Step Login (TOTP)', + totpLiveIn: 'Refresh in', enableTotp: 'Enable TOTP', disableTotp: 'Disable TOTP', secret: 'Authenticator Key', verifyCode: 'Verification Code', + credentials: 'Login credentials', + autofillOptions: 'Autofill', + itemHistory: 'Item history', + website: 'Website', + folder: 'Folder', + createdAt: 'Created', + updatedAt: 'Last edited', + open: 'Open', + copy: 'Copy', + reveal: 'Reveal', + hide: 'Hide', users: 'Users', invites: 'Invites', createInvite: 'Create Invite', @@ -130,11 +179,34 @@ folders: '文件夹', allItems: '所有项目', noFolder: '无文件夹', - refresh: '刷新', + searchVault: '搜索密码库', + filter: '筛选', + typeAll: '所有项目', + typeLogin: '登录', + typeCard: '支付卡', + typeIdentity: '身份', + typeNote: '备注', + typeOther: '其他', + addWebsite: '+ 添加网站', + addField: '+ 添加字段', + fieldType: '字段类型', + fieldLabel: '字段标签', + fieldValue: '字段值', + fieldText: '文本型', + fieldHidden: '隐藏型', + fieldBoolean: '复选框型', + fieldLinked: '链接型', + add: '添加', + newTypeLogin: '登录', + newTypeCard: '支付卡', + newTypeIdentity: '身份', + newTypeNote: '笔记', + newTypeSsh: 'SSH 密钥', + refresh: '同步', move: '移动', delete: '删除', selectAll: '全选', - clear: '清除', + clear: '取消', noItems: '没有可列出的项目。', selectItem: '选择一个项目以查看详细信息。', profile: '个人资料', @@ -143,10 +215,22 @@ currentPwd: '当前主密码', newPwd: '新主密码', totpSetup: '两步登录 (TOTP)', + totpLiveIn: '刷新剩余', enableTotp: '启用 TOTP', disableTotp: '禁用 TOTP', secret: '身份验证器密钥', verifyCode: '验证码', + credentials: '登录凭据', + autofillOptions: '自动填充', + itemHistory: '项目历史记录', + website: '网站', + folder: '文件夹', + createdAt: '创建于', + updatedAt: '最后编辑', + open: '打开', + copy: '复制', + reveal: '显示', + hide: '隐藏', users: '用户', invites: '邀请', createInvite: '创建邀请', @@ -264,6 +348,17 @@ c.identity.decFirstName=await decryptStr(c.identity.firstName,ek,mk); c.identity.decLastName=await decryptStr(c.identity.lastName,ek,mk); c.identity.decEmail=await decryptStr(c.identity.email,ek,mk); c.identity.decPhone=await decryptStr(c.identity.phone,ek,mk); c.identity.decCompany=await decryptStr(c.identity.company,ek,mk); c.identity.decUsername=await decryptStr(c.identity.username,ek,mk); + c.identity.decTitle=await decryptStr(c.identity.title,ek,mk); c.identity.decMiddleName=await decryptStr(c.identity.middleName,ek,mk); + c.identity.decSsn=await decryptStr(c.identity.ssn,ek,mk); c.identity.decPassportNumber=await decryptStr(c.identity.passportNumber,ek,mk); + c.identity.decLicenseNumber=await decryptStr(c.identity.licenseNumber,ek,mk); c.identity.decAddress1=await decryptStr(c.identity.address1,ek,mk); + c.identity.decAddress2=await decryptStr(c.identity.address2,ek,mk); c.identity.decAddress3=await decryptStr(c.identity.address3,ek,mk); + c.identity.decCity=await decryptStr(c.identity.city,ek,mk); c.identity.decState=await decryptStr(c.identity.state,ek,mk); + c.identity.decPostalCode=await decryptStr(c.identity.postalCode,ek,mk); c.identity.decCountry=await decryptStr(c.identity.country,ek,mk); + } + if(c.sshKey){ + c.sshKey.decPrivateKey=await decryptStr(c.sshKey.privateKey,ek,mk); + c.sshKey.decPublicKey=await decryptStr(c.sshKey.publicKey,ek,mk); + c.sshKey.decFingerprint=await decryptStr(c.sshKey.fingerprint,ek,mk); } if(c.fields){ for(var j=0;j>>0, false); + var key=await crypto.subtle.importKey('raw', keyBytes, {name:'HMAC', hash:'SHA-1'}, false, ['sign']); + var sig=new Uint8Array(await crypto.subtle.sign('HMAC', key, buf)); + var offset=sig[sig.length-1]&0x0f; + var bin=((sig[offset]&0x7f)<<24)|((sig[offset+1]&0xff)<<16)|((sig[offset+2]&0xff)<<8)|(sig[offset+3]&0xff); + var token=String(bin%1000000).padStart(6,'0'); + return { token: token.slice(0,3)+' '+token.slice(3), remain: remain }; + } + async function updateLiveTotpDisplay(){ + if(state.phase!=='app'||state.tab!=='vault') return; + if(state.totpTickBusy) return; + var vEl=document.getElementById('totp-live-value'); + var rEl=document.getElementById('totp-live-remain'); + if(!vEl||!rEl) return; + var c=selectedCipher(); if(!c||!c.login) return; + var raw=(c.login.decTotp||c.login.totp||'').trim(); + if(!raw){ vEl.textContent=''; rEl.textContent=''; return; } + state.totpTickBusy=true; + try{ + var x=await calcTotpNow(raw); + if(!x){ vEl.textContent='N/A'; rEl.textContent=''; return; } + vEl.textContent=x.token; + rEl.textContent=t('totpLiveIn')+': '+x.remain+'s'; + }catch(e){ + vEl.textContent='N/A'; + rEl.textContent=''; + }finally{ + state.totpTickBusy=false; + } + } + function ensureTotpTicker(){ + if(state.totpTicking) return; + state.totpTicking=true; + setInterval(function(){ updateLiveTotpDisplay(); }, 1000); + } + function filteredCiphers(){ + var out=[]; var q=String(state.vaultQuery||'').toLowerCase(); + for(var i=0;i=64) return { enc: raw.slice(0,32), mac: raw.slice(32,64), key: cipher.key }; + }catch(e){} + } + return { enc: user.enc, mac: user.mac, key: null }; + } + async function encryptTextValue(v, enc, mac){ + var s=String(v==null?'':v); + if(!s) return null; + return encryptBw(new TextEncoder().encode(s), enc, mac); + } + function openCreateDraft(){ + state.detailMode='create'; + state.showSelectedPassword=true; + state.createMenuOpen=false; + state.detailDraft={ + id: '', + type: 1, + name: '', + folderId: state.folderFilterId&&state.folderFilterId!==NO_FOLDER_FILTER?state.folderFilterId:'', + reprompt: false, + loginUsername: '', + loginPassword: '', + loginTotp: '', + websites: [''], + cardholderName: '', + cardNumber: '', + cardBrand: '', + cardExpMonth: '', + cardExpYear: '', + cardCode: '', + identTitle: '', + identFirstName: '', + identMiddleName: '', + identLastName: '', + identUsername: '', + identCompany: '', + identSsn: '', + identPassportNumber: '', + identLicenseNumber: '', + identEmail: '', + identPhone: '', + identAddress1: '', + identAddress2: '', + identAddress3: '', + identCity: '', + identState: '', + identPostalCode: '', + identCountry: '', + sshPrivateKey: '', + sshPublicKey: '', + sshFingerprint: '', + customFields: [], + notes: '' + }; + } + function openEditDraft(cipher){ + if(!cipher) return; + var login=cipher.login||{}; + var uris=Array.isArray(login.uris)?login.uris:[]; + var ws=[]; for(var i=0;i'+l+''; } + return opt('text',t('fieldText'))+opt('hidden',t('fieldHidden'))+opt('boolean',t('fieldBoolean'))+opt('linked',t('fieldLinked')); + } + function renderCreateMenu(){ + if(!state.createMenuOpen) return ''; + return '
'; + } + function renderFieldModal(){ + if(!state.fieldModalOpen) return ''; + return '' + + '

'+t('addField')+'

' + + '
' + + '
' + + '
' + + '
' + + '
'; + } + function fieldTypeTextByNum(n){ + var x=parseFieldType(n); + if(x===1) return t('fieldHidden'); + if(x===2) return t('fieldBoolean'); + if(x===3) return t('fieldLinked'); + return t('fieldText'); + } + function renderCardBrandOptions(selected){ + var s=String(selected||'').toLowerCase(); + var brands=['','visa','mastercard','amex','discover','jcb','unionpay','dinersclub','maestro']; + var labels={ '':'-- Select --', visa:'Visa', mastercard:'Mastercard', amex:'American Express', discover:'Discover', jcb:'JCB', unionpay:'UnionPay', dinersclub:'Diners Club', maestro:'Maestro' }; + var out=''; + for(var i=0;i'+labels[b]+''; + } + return out; + } + function renderMonthOptions(selected){ + var s=String(selected||''); + var out=''; + for(var m=1;m<=12;m++){ + var mm=m<10?('0'+m):String(m); + out += ''; + } + return out; + } + function renderDraftTypeCards(d){ + var typeNum=Number(d&&d.type||1); + if(typeNum===3){ + return '' + + '
Card details
' + + '
Cardholder name
' + + '
Number
' + + '
Brand
' + + '
Exp month
Exp year
' + + '
Security code (CVV)
' + + '
'; + } + if(typeNum===4){ + return '' + + '
Personal details
' + + '
Title
' + + '
First name
' + + '
Middle name
' + + '
Last name
' + + '
Username
' + + '
Company
' + + '
' + + '
Identity
' + + '
SSN
' + + '
Passport number
' + + '
License number
' + + '
' + + '
Contact information
' + + '
Email
' + + '
Phone
' + + '
' + + '
Address
' + + '
Address 1
' + + '
Address 2
' + + '
Address 3
' + + '
City / Town
' + + '
State / Province
' + + '
ZIP / Postal code
' + + '
Country
' + + '
'; + } + if(typeNum===5){ + return '' + + '
SSH key
' + + '
Private key
' + + '
Public key
' + + '
Fingerprint
' + + '
'; + } + if(typeNum===2){ + return ''; + } + return '' + + '
'+t('credentials')+'
' + + '
Username
' + + '
Password
' + + '
TOTP Secret
' + + '
'; + } + function renderReadOnlyCustomFields(cipher){ + var fs=Array.isArray(cipher&&cipher.fields)?cipher.fields:[]; + if(!fs.length) return ''; + var rows=''; + for(var i=0;i
'+esc(value||'')+'
'; + } + return '
Fields
'+rows+'
'; + } + function renderReadOnlyTypeDetails(c0, folderLabel, created, updated){ + var typeNum=Number(c0&&c0.type||1); + var notes=c0&&((c0.decNotes||c0.notes)||''); + var baseHead='' + + '
' + + '
'+esc(c0.decName||c0.name||'')+'
' + + '
'+t('folder')+': '+esc(folderLabel||t('noFolder'))+'
' + + '
'; + var history='' + + '
'+t('itemHistory')+'
' + + '
'+t('updatedAt')+': '+esc(updated)+'
' + + '
'+t('createdAt')+': '+esc(created)+'
' + + '
'; + if(typeNum===3){ + var c=c0.card||{}; + return baseHead + + '
Card details
' + + '
Cardholder name
'+esc(c.decCardholderName||c.cardholderName||'')+'
' + + '
Number
'+esc(c.decNumber||c.number||'')+'
' + + '
Brand
'+esc(c.decBrand||c.brand||'')+'
' + + '
Exp month/year
'+esc((c.decExpMonth||c.expMonth||'')+' / '+(c.decExpYear||c.expYear||''))+'
' + + '
Security code (CVV)
'+esc(c.decCode||c.code||'')+'
' + + '
' + + '
Additional options
Notes
'+esc(notes)+'
' + + renderReadOnlyCustomFields(c0) + + history; + } + if(typeNum===4){ + var id=c0.identity||{}; + return baseHead + + '
Personal details
' + + '
Title
'+esc(id.decTitle||id.title||'')+'
' + + '
First name
'+esc(id.decFirstName||id.firstName||'')+'
' + + '
Middle name
'+esc(id.decMiddleName||id.middleName||'')+'
' + + '
Last name
'+esc(id.decLastName||id.lastName||'')+'
' + + '
Username
'+esc(id.decUsername||id.username||'')+'
' + + '
Company
'+esc(id.decCompany||id.company||'')+'
' + + '
' + + '
Identity
' + + '
SSN
'+esc(id.decSsn||id.ssn||'')+'
' + + '
Passport number
'+esc(id.decPassportNumber||id.passportNumber||'')+'
' + + '
License number
'+esc(id.decLicenseNumber||id.licenseNumber||'')+'
' + + '
' + + '
Contact information
' + + '
Email
'+esc(id.decEmail||id.email||'')+'
' + + '
Phone
'+esc(id.decPhone||id.phone||'')+'
' + + '
' + + '
Address
' + + '
Address 1
'+esc(id.decAddress1||id.address1||'')+'
' + + '
Address 2
'+esc(id.decAddress2||id.address2||'')+'
' + + '
Address 3
'+esc(id.decAddress3||id.address3||'')+'
' + + '
City / Town
'+esc(id.decCity||id.city||'')+'
' + + '
State / Province
'+esc(id.decState||id.state||'')+'
' + + '
ZIP / Postal code
'+esc(id.decPostalCode||id.postalCode||'')+'
' + + '
Country
'+esc(id.decCountry||id.country||'')+'
' + + '
' + + '
Additional options
Notes
'+esc(notes)+'
' + + renderReadOnlyCustomFields(c0) + + history; + } + if(typeNum===5){ + var ssh=c0.sshKey||{}; + var privateKey=ssh.decPrivateKey||ssh.privateKey||''; + return baseHead + + '
SSH key
' + + '
Private key
'+esc(privateKey?new Array(Math.max(String(privateKey).length,12)+1).join('•'):'')+'
' + + '
Public key
'+esc(ssh.decPublicKey||ssh.publicKey||'')+'
' + + '
Fingerprint
'+esc(ssh.decFingerprint||ssh.fingerprint||'')+'
' + + '
' + + '
Additional options
Notes
'+esc(notes)+'
' + + renderReadOnlyCustomFields(c0) + + history; + } + if(typeNum===2){ + return baseHead + + '
Additional options
Notes
'+esc(notes)+'
' + + renderReadOnlyCustomFields(c0) + + history; + } + var login=c0.login||{}; + var username=login.decUsername||login.username||''; + var rawPwd=login.decPassword||login.password||''; + var masked=rawPwd?new Array(Math.max(rawPwd.length,12)+1).join('•'):''; + var pwdText=state.showSelectedPassword?rawPwd:masked; + var totp=login.decTotp||login.totp||''; + var uri0=firstCipherUri(c0); + return baseHead + + '
'+t('credentials')+'
' + + '
Username
'+esc(username)+'
' + + '
Password
'+esc(pwdText)+'
' + + (totp?('
TOTP
...
'):'') + + '
' + + '
'+t('autofillOptions')+'
' + + '
'+t('website')+'
'+esc(uri0||'')+'
'+(uri0?(''):'')+(uri0?(''):'')+'
' + + '
' + + renderReadOnlyCustomFields(c0) + + history; + } function renderLoginScreen(){ return '' + '
' @@ -375,40 +1080,90 @@ function renderVaultTab(){ var list=filteredCiphers(); + function renderFolderOptions(selectedId){ + var html=''; + for(var fi=0;fi'+esc(ff.decName||ff.name||ff.id)+''; + } + return html; + } var rows=''; for(var i=0;i
'+esc(nameText)+'
'+esc(c.id)+'
'; + var subtitle=listSubtitle(c); + var uri=firstCipherUri(c); + var host=hostFromUri(uri); + var icon=host + ? ('') + : '🌐'; + rows += '' + + '
' + + '' + + '
'+icon+'
'+esc(nameText)+'
'+esc(subtitle||'')+'
' + + '
'; } - if(!rows) rows='
'+t('noItems')+'
'; + if(!rows) rows='
'+t('noItems')+'
'; var c0=selectedCipher(); - var detail='
'+t('selectItem')+'
'; - if(c0){ - var login = c0.login||{}; - var fields=Array.isArray(c0.fields)?c0.fields:[]; - var fh=''; - for(var j=0;j '+esc(fields[j].decValue||fields[j].value||'')+''; - var uriHtml=''; if(login.uris){for(var j=0;j '+esc(u.decUri||u.uri||'')+'';}} - var cardHtml=''; if(c0.card){var cd=c0.card; cardHtml='
Cardholder: '+esc(cd.decCardholderName||cd.cardholderName||'')+'
Number: '+esc(cd.decNumber||cd.number||'')+'
Brand: '+esc(cd.decBrand||cd.brand||'')+'
Exp: '+esc(cd.decExpMonth||cd.expMonth||'')+'/'+esc(cd.decExpYear||cd.expYear||'')+'
CVV: '+esc(cd.decCode||cd.code||'')+'
';} - var identHtml=''; if(c0.identity){var id=c0.identity; identHtml='
Name: '+esc((id.decFirstName||id.firstName||'')+' '+(id.decLastName||id.lastName||''))+'
Email: '+esc(id.decEmail||id.email||'')+'
Phone: '+esc(id.decPhone||id.phone||'')+'
Company: '+esc(id.decCompany||id.company||'')+'
Username: '+esc(id.decUsername||id.username||'')+'
';} + var detail='
'+t('selectItem')+'
'; + if(state.detailMode==='create'){ + var dc=state.detailDraft||{}; + var wsHtml=''; var cws=Array.isArray(dc.websites)?dc.websites:['']; + for(var wci=0;wci'+(cws.length>1?'':'')+''; + } + var cfHtml=''; var cfs=Array.isArray(dc.customFields)?dc.customFields:[]; + for(var cfi=0;cfi
'+esc(cf.value||'')+'
'; + } detail='' - + '
Name: '+esc(c0.decName||c0.name||'')+'
' - + '
Notes: '+esc(c0.decNotes||c0.notes||'')+'
' - + (c0.login?('
Username: '+esc(login.decUsername||login.username||'')+'
' - + '
Password: '+esc(login.decPassword||login.password||'')+'
' - + '
TOTP: '+esc(login.decTotp||login.totp||'')+'
'+uriHtml):'' - ) + cardHtml + identHtml + fh; + + '
'+t('folder')+':
' + + renderDraftTypeCards(dc) + + (Number(dc.type||1)===1?('
'+t('autofillOptions')+'
'+wsHtml+'' + + '
'):'') + + '
Additional options
' + + '' + + '
' + + '
' + + '
Fields
'+cfHtml+'
' + + '
'; + } else if(c0){ + var folderLabel=c0.folderId?folderNameById(c0.folderId):t('noFolder'); + var updated=c0.revisionDate||c0.updatedAt||''; + var created=c0.creationDate||c0.createdAt||''; + if(state.detailMode==='edit'){ + var de=state.detailDraft||{}; + var ewsHtml=''; var ews=Array.isArray(de.websites)?de.websites:['']; + for(var wei=0;wei'+(ews.length>1?'':'')+''; + } + var efsHtml=''; var efs=Array.isArray(de.customFields)?de.customFields:[]; + for(var efi=0;efi
'+esc(ef.value||'')+'
'; + } + detail='' + + '
'+t('folder')+':
' + + renderDraftTypeCards(de) + + (Number(de.type||1)===1?('
'+t('autofillOptions')+'
'+ewsHtml+'' + + '
'):'') + + '
Additional options
' + + '' + + '
' + + '
' + + '
Fields
'+efsHtml+'
' + + '
'; + } else detail=renderReadOnlyTypeDetails(c0, folderLabel, created, updated) + + '
'; } return '' + renderMsg() - + '
' - + '

'+t('vault')+'

' - + '
' - + '
' - + '
'+rows+'
'+detail+'
'; + + '
'+renderCreateMenu()+'
'+rows+'
'+detail+renderFieldModal()+'
'; } function renderSettingsTab(){ @@ -474,9 +1229,17 @@ function renderApp(){ var isAdmin=state.profile&&state.profile.role==='admin'; var showFolders=state.tab==='vault'; - var folders='' - + ''; - for(var i=0;i'+esc(folderName)+''; } + var folders='' + + '' + + ''; + for(var i=0;i📁'+esc(folderName)+''; } + var typeTree='' + + '' + + '' + + '' + + '' + + '' + + ''; var content = state.tab==='vault'?renderVaultTab():state.tab==='settings'?renderSettingsTab():(state.tab==='admin'&&isAdmin)?renderAdminTab():renderHelpTab(); return '' @@ -495,7 +1258,11 @@ + ' ' + '' + '
' - + (showFolders?(' '):'') + + (showFolders?(' '):'') + '
'+content+'
' + '
'+renderTotpDisableModal(); } @@ -505,10 +1272,12 @@ if(state.phase==='register'){ app.innerHTML=renderRegisterScreen(); return; } if(state.phase==='login'){ app.innerHTML=renderLoginScreen(); return; } app.innerHTML=renderApp(); + updateLiveTotpDisplay(); } async function init(){ var url=new URL(window.location.href); state.inviteCode=(url.searchParams.get('invite')||'').trim(); state.session=loadSession(); + ensureTotpTicker(); var st=await fetch('/setup/status'); var setup=await jsonOrNull(st); var registered=!!(setup&&setup.registered); if(state.session){ try{ await loadProfile(); await loadVault(); await loadAdminData(); state.phase='app'; state.tab='vault'; render(); return; } catch(e){ state.session=null; saveSession(); } @@ -654,11 +1423,35 @@ if(a==='totp-cancel'){ state.pendingLogin=null; state.loginTotpToken=''; state.loginTotpError=''; render(); return; } if(a==='totp-disable-cancel'){ state.totpDisableOpen=false; state.totpDisablePassword=''; state.totpDisableError=''; render(); return; } if(a==='tab'){ state.tab=n.getAttribute('data-tab')||'vault'; clearMsg(); render(); return; } - if(a==='folder-filter'){ state.folderFilterId=n.getAttribute('data-folder')||''; var filtered=filteredCiphers(); state.selectedCipherId=filtered.length?filtered[0].id:''; render(); return; } - if(a==='pick-cipher'){ state.selectedCipherId=n.getAttribute('data-id')||''; render(); return; } + if(a==='folder-filter'){ state.folderFilterId=n.getAttribute('data-folder')||''; syncSelectedWithFilter(); state.showSelectedPassword=false; render(); return; } + if(a==='type-filter'){ state.vaultType=n.getAttribute('data-type')||'all'; syncSelectedWithFilter(); state.showSelectedPassword=false; render(); return; } + if(a==='pick-cipher'){ state.selectedCipherId=n.getAttribute('data-id')||''; state.showSelectedPassword=false; render(); return; } + if(a==='detail-create-toggle'){ state.createMenuOpen=!state.createMenuOpen; render(); return; } + if(a==='detail-create-type'){ openCreateDraft(); if(state.detailDraft) state.detailDraft.type=Number(n.getAttribute('data-type')||1); render(); return; } + if(a==='detail-edit'){ openEditDraft(selectedCipher()); render(); return; } + if(a==='detail-cancel'){ closeDetailEdit(); render(); return; } + if(a==='detail-save'){ return void saveDetailDraft(); } + if(a==='detail-delete'){ return void deleteSelectedCipher(); } + if(a==='draft-website-add'){ if(state.detailDraft){ if(!Array.isArray(state.detailDraft.websites)) state.detailDraft.websites=[]; state.detailDraft.websites.push(''); render(); } return; } + if(a==='draft-website-remove'){ if(state.detailDraft&&Array.isArray(state.detailDraft.websites)){ var wi=Number(n.getAttribute('data-index')||-1); if(wi>=0&&wi=0&&fi=0&&wi