@shhhum/xftp-web 0.11.0 → 0.13.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.
- package/dist-web/assets/index.css +1 -1
- package/dist-web/assets/index.js +1 -1
- package/package.json +1 -1
- package/web/main.ts +3 -2
- package/web/style.css +8 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
#app{font-family:system-ui,-apple-system,sans-serif;color:#333;width:100%;max-width:480px;padding:16px;box-sizing:border-box}#app .card{background:#fff;border-radius:12px;padding:32px 24px;box-shadow:0 1px 3px #0000001a;text-align:center}#app h1{font-size:1.25rem;font-weight:600;margin-bottom:24px}#app .stage{margin-top:16px}#app .drop-zone{border:2px dashed #ccc;border-radius:8px;padding:32px 16px;transition:border-color .15s,background .15s}#app .drop-zone.drag-over{border-color:#3b82f6;background:#eff6ff}#app .btn{display:inline-block;padding:10px 24px;border:none;border-radius:6px;background:#3b82f6;color:#fff;font-size:.9rem;font-weight:500;cursor:pointer;transition:background .15s}#app .btn:hover{background:#2563eb}#app .btn-secondary{background:#6b7280}#app .btn-secondary:hover{background:#4b5563}#app .hint{color:#999;font-size:.85rem;margin-top:8px}#app .expiry{margin-top:12px}#app .progress-ring{display:block;margin:0 auto 12px}#app #upload-status,#app #dl-status{font-size:.9rem;color:#666;margin-bottom:12px}#app .link-row{display:flex;gap:8px;margin-top:12px}#app .link-row input{flex:1;padding:8px 10px;border:1px solid #ccc;border-radius:6px;font-size:.85rem;background:#f9fafb}#app .success{color:#16a34a;font-weight:600}#app .error{color:#dc2626;font-weight:500;margin-bottom:12px}#app .security-note{margin-top:20px;padding:12px;background:#f0fdf4;border-radius:6px;font-size:.8rem;color:#555;text-align:left}#app .security-note p+p{margin-top:6px}#app .security-note a{color:#3b82f6;text-decoration:none}#app .security-note a:hover{text-decoration:underline}.dark #app{color:#e5e7eb;--xftp-ring-bg: #374151;--xftp-ring-fg: #60a5fa;--xftp-ring-text: #e5e7eb}.dark #app .card{background:#1f2937;box-shadow:0 1px 3px #0006}.dark #app .drop-zone{border-color:#4b5563}.dark #app .drop-zone.drag-over{border-color:#60a5fa;background:#3b82f626}.dark #app .btn-secondary{background:#4b5563}.dark #app .btn-secondary:hover{background:#374151}.dark #app .hint,.dark #app #upload-status,.dark #app #dl-status{color:#9ca3af}.dark #app .link-row input{background:#374151;border-color:#4b5563;color:#e5e7eb}.dark #app .success{color:#4ade80}.dark #app .error{color:#f87171}.dark #app .security-note{background:#22c55e1a;color:#d1d5db}.dark #app .security-note a{color:#60a5fa}
|
|
1
|
+
#app,[data-xftp-app]{font-family:system-ui,-apple-system,sans-serif;color:#333;width:100%;max-width:480px;padding:16px;box-sizing:border-box}#app:empty,[data-xftp-app]:empty{min-height:var(--xftp-reserve-height, 22rem)}#app .card{background:#fff;border-radius:12px;padding:32px 24px;box-shadow:0 1px 3px #0000001a;text-align:center}#app h1{font-size:1.25rem;font-weight:600;margin-bottom:24px}#app .stage{margin-top:16px}#app .drop-zone{border:2px dashed #ccc;border-radius:8px;padding:32px 16px;transition:border-color .15s,background .15s}#app .drop-zone.drag-over{border-color:#3b82f6;background:#eff6ff}#app .btn{display:inline-block;padding:10px 24px;border:none;border-radius:6px;background:#3b82f6;color:#fff;font-size:.9rem;font-weight:500;cursor:pointer;transition:background .15s}#app .btn:hover{background:#2563eb}#app .btn-secondary{background:#6b7280}#app .btn-secondary:hover{background:#4b5563}#app .hint{color:#999;font-size:.85rem;margin-top:8px}#app .expiry{margin-top:12px}#app .progress-ring{display:block;margin:0 auto 12px}#app #upload-status,#app #dl-status{font-size:.9rem;color:#666;margin-bottom:12px}#app .link-row{display:flex;gap:8px;margin-top:12px}#app .link-row input{flex:1;padding:8px 10px;border:1px solid #ccc;border-radius:6px;font-size:.85rem;background:#f9fafb}#app .success{color:#16a34a;font-weight:600}#app .error{color:#dc2626;font-weight:500;margin-bottom:12px}#app .security-note{margin-top:20px;padding:12px;background:#f0fdf4;border-radius:6px;font-size:.8rem;color:#555;text-align:left}#app .security-note p+p{margin-top:6px}#app .security-note a{color:#3b82f6;text-decoration:none}#app .security-note a:hover{text-decoration:underline}.dark #app{color:#e5e7eb;--xftp-ring-bg: #374151;--xftp-ring-fg: #60a5fa;--xftp-ring-text: #e5e7eb}.dark #app .card{background:#1f2937;box-shadow:0 1px 3px #0006}.dark #app .drop-zone{border-color:#4b5563}.dark #app .drop-zone.drag-over{border-color:#60a5fa;background:#3b82f626}.dark #app .btn-secondary{background:#4b5563}.dark #app .btn-secondary:hover{background:#374151}.dark #app .hint,.dark #app #upload-status,.dark #app #dl-status{color:#9ca3af}.dark #app .link-row input{background:#374151;border-color:#4b5563;color:#e5e7eb}.dark #app .success{color:#4ade80}.dark #app .error{color:#f87171}.dark #app .security-note{background:#22c55e1a;color:#d1d5db}.dark #app .security-note a{color:#60a5fa}
|
package/dist-web/assets/index.js
CHANGED
|
@@ -1465,4 +1465,4 @@ jv.ÉÂ
,r¡è¿¢Kf¨pK£QlÇèÑ$Ö
5ôp jÁ¤\bl7LwH'
|
|
|
1465
1465
|
<p class="error" id="dl-error-msg"></p>
|
|
1466
1466
|
<button id="dl-retry-btn" class="btn">${F2("retry","Retry")}</button>
|
|
1467
1467
|
</div>
|
|
1468
|
-
</div>`;const j=document.getElementById("dl-ready"),w=document.getElementById("dl-progress"),q=document.getElementById("dl-error"),P=document.getElementById("dl-progress-container"),R=document.getElementById("dl-status"),Y=document.getElementById("dl-btn"),X=document.getElementById("dl-error-msg"),n0=document.getElementById("dl-retry-btn");function Z(k0){for(const T0 of[j,w,q])T0.hidden=!0;k0.hidden=!1}function r0(k0){X.innerHTML=k0,Z(q)}Y.addEventListener("click",o0),n0.addEventListener("click",()=>Z(j));async function o0(){Z(w);const k0=ws();P.innerHTML="",P.appendChild(k0.canvas),R.textContent=F2("downloading","Downloading…");const T0=ks(),B0=Q8();try{const q0=await Fk(B0,y,async U0=>{await T0.decryptAndStoreChunk(U0.dhSecret,U0.nonce,U0.body,U0.digest,U0.chunkNo)},{onProgress:(U0,G0)=>{k0.update(U0/G0*.8)}});R.textContent=F2("decrypting","Decrypting…"),k0.update(.85);const{header:D0,content:R0}=await T0.verifyAndDecrypt({size:q0.size,digest:q0.digest,key:q0.key,nonce:q0.nonce});k0.update(.95);const i2=Kk(D0.fileName),c2=new Blob([R0.buffer]),b0=URL.createObjectURL(c2),f2=document.createElement("a");f2.href=b0,f2.download=encodeURIComponent(i2),f2.style.display="none",document.body.appendChild(f2),f2.click(),document.body.removeChild(f2),setTimeout(()=>URL.revokeObjectURL(b0),1e3),k0.update(1),R.textContent=F2("downloadComplete","Download complete"),l.dispatchEvent(new CustomEvent("xftp:download-complete",{detail:{fileName:i2},bubbles:!0}))}catch(q0){const D0=q0?.message??String(q0);r0(D0),q0 instanceof tn?n0.hidden=!0:n0.hidden=!1}finally{await T0.cleanup().catch(()=>{}),GA(B0)}}}function Kk(l){let p=l;return p=p.replace(/[/\\]/g,""),p=p.replace(/[\x00-\x1f\x7f]/g,"_"),p=p.replace(/[\u202a-\u202e\u2066-\u2069]/g,""),p.length>255&&(p=p.slice(0,255)),p||"download"}function zk(l){return l<1024?l+" B":l<1024*1024?(l/1024).toFixed(1)+" KB":(l/(1024*1024)).toFixed(1)+" MB"}function ci(){return document.querySelector("[data-xftp-app]")??document.getElementById("app")}const z8=T2.ready;async function Zk(){
|
|
1468
|
+
</div>`;const j=document.getElementById("dl-ready"),w=document.getElementById("dl-progress"),q=document.getElementById("dl-error"),P=document.getElementById("dl-progress-container"),R=document.getElementById("dl-status"),Y=document.getElementById("dl-btn"),X=document.getElementById("dl-error-msg"),n0=document.getElementById("dl-retry-btn");function Z(k0){for(const T0 of[j,w,q])T0.hidden=!0;k0.hidden=!1}function r0(k0){X.innerHTML=k0,Z(q)}Y.addEventListener("click",o0),n0.addEventListener("click",()=>Z(j));async function o0(){Z(w);const k0=ws();P.innerHTML="",P.appendChild(k0.canvas),R.textContent=F2("downloading","Downloading…");const T0=ks(),B0=Q8();try{const q0=await Fk(B0,y,async U0=>{await T0.decryptAndStoreChunk(U0.dhSecret,U0.nonce,U0.body,U0.digest,U0.chunkNo)},{onProgress:(U0,G0)=>{k0.update(U0/G0*.8)}});R.textContent=F2("decrypting","Decrypting…"),k0.update(.85);const{header:D0,content:R0}=await T0.verifyAndDecrypt({size:q0.size,digest:q0.digest,key:q0.key,nonce:q0.nonce});k0.update(.95);const i2=Kk(D0.fileName),c2=new Blob([R0.buffer]),b0=URL.createObjectURL(c2),f2=document.createElement("a");f2.href=b0,f2.download=encodeURIComponent(i2),f2.style.display="none",document.body.appendChild(f2),f2.click(),document.body.removeChild(f2),setTimeout(()=>URL.revokeObjectURL(b0),1e3),k0.update(1),R.textContent=F2("downloadComplete","Download complete"),l.dispatchEvent(new CustomEvent("xftp:download-complete",{detail:{fileName:i2},bubbles:!0}))}catch(q0){const D0=q0?.message??String(q0);r0(D0),q0 instanceof tn?n0.hidden=!0:n0.hidden=!1}finally{await T0.cleanup().catch(()=>{}),GA(B0)}}}function Kk(l){let p=l;return p=p.replace(/[/\\]/g,""),p=p.replace(/[\x00-\x1f\x7f]/g,"_"),p=p.replace(/[\u202a-\u202e\u2066-\u2069]/g,""),p.length>255&&(p=p.slice(0,255)),p||"download"}function zk(l){return l<1024?l+" B":l<1024*1024?(l/1024).toFixed(1)+" KB":(l/(1024*1024)).toFixed(1)+" MB"}function ci(){return document.querySelector("[data-xftp-app]")??document.getElementById("app")}const z8=T2.ready;async function Zk(){const l=ci();l?.hasAttribute("data-defer-init")||KA(),l?.hasAttribute("data-no-hashchange")||window.addEventListener("hashchange",()=>{const p=window.location.hash.slice(1);(!p||Z8(p))&&KA()}),await z8,l?.dispatchEvent(new CustomEvent("xftp:ready",{bubbles:!0}))}function Z8(l){try{return G8(l),!0}catch{return!1}}function KA(){const l=ci(),p=window.location.hash.slice(1);p&&Z8(p)?Gk(l,p):Xk(l)}window.__xftp_initApp=async()=>{await z8,KA()};Zk().catch(l=>{const p=ci();p&&(p.innerHTML=`<div class="error"><p>${F2("initError","Failed to initialize: %error%").replace("%error%",l.message)}</p></div>`),console.error(l)});
|
package/package.json
CHANGED
package/web/main.ts
CHANGED
|
@@ -11,8 +11,8 @@ function getAppElement(): HTMLElement | null {
|
|
|
11
11
|
const wasmReady = sodium.ready
|
|
12
12
|
|
|
13
13
|
async function main() {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Render UI immediately — no WASM needed for HTML + event listeners.
|
|
15
|
+
// WASM is only used later when user triggers upload/download.
|
|
16
16
|
const app = getAppElement()
|
|
17
17
|
if (!app?.hasAttribute('data-defer-init')) {
|
|
18
18
|
initApp()
|
|
@@ -23,6 +23,7 @@ async function main() {
|
|
|
23
23
|
if (!hash || isXFTPHash(hash)) initApp()
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
|
+
await wasmReady
|
|
26
27
|
app?.dispatchEvent(new CustomEvent('xftp:ready', {bubbles: true}))
|
|
27
28
|
}
|
|
28
29
|
|
package/web/style.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#app {
|
|
1
|
+
#app, [data-xftp-app] {
|
|
2
2
|
font-family: system-ui, -apple-system, sans-serif;
|
|
3
3
|
color: #333;
|
|
4
4
|
width: 100%;
|
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
box-sizing: border-box;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/* Reserve space before JS renders — prevents layout shift.
|
|
11
|
+
:empty stops matching once initApp() sets innerHTML.
|
|
12
|
+
Override with --xftp-reserve-height on the container. */
|
|
13
|
+
#app:empty, [data-xftp-app]:empty {
|
|
14
|
+
min-height: var(--xftp-reserve-height, 22rem);
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
#app .card {
|
|
11
18
|
background: #fff;
|
|
12
19
|
border-radius: 12px;
|