personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

pairing: add desktop pairing UI

+498
+18
convey/static/pairing-qr.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + /* 4 + * QR Code Generator for JavaScript 5 + * Copyright (c) 2009 Kazuhiko Arase 6 + * URL: http://www.d-project.com/ 7 + * Licensed under the MIT license: 8 + * http://www.opensource.org/licenses/mit-license.php 9 + * Source: https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js 10 + */ 11 + /** 12 + * Minified by jsDelivr using Terser v5.37.0. 13 + * Original file: /npm/qrcode-generator@1.4.4/qrcode.js 14 + * 15 + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files 16 + */ 17 + var qrcode=function(){var t=function(t,r){var e=t,n=g[r],o=null,i=0,a=null,u=[],f={},c=function(t,r){o=function(t){for(var r=new Array(t),e=0;e<t;e+=1){r[e]=new Array(t);for(var n=0;n<t;n+=1)r[e][n]=null}return r}(i=4*e+17),l(0,0),l(i-7,0),l(0,i-7),s(),h(),d(t,r),e>=7&&v(t),null==a&&(a=p(e,n,u)),w(a,r)},l=function(t,r){for(var e=-1;e<=7;e+=1)if(!(t+e<=-1||i<=t+e))for(var n=-1;n<=7;n+=1)r+n<=-1||i<=r+n||(o[t+e][r+n]=0<=e&&e<=6&&(0==n||6==n)||0<=n&&n<=6&&(0==e||6==e)||2<=e&&e<=4&&2<=n&&n<=4)},h=function(){for(var t=8;t<i-8;t+=1)null==o[t][6]&&(o[t][6]=t%2==0);for(var r=8;r<i-8;r+=1)null==o[6][r]&&(o[6][r]=r%2==0)},s=function(){for(var t=B.getPatternPosition(e),r=0;r<t.length;r+=1)for(var n=0;n<t.length;n+=1){var i=t[r],a=t[n];if(null==o[i][a])for(var u=-2;u<=2;u+=1)for(var f=-2;f<=2;f+=1)o[i+u][a+f]=-2==u||2==u||-2==f||2==f||0==u&&0==f}},v=function(t){for(var r=B.getBCHTypeNumber(e),n=0;n<18;n+=1){var a=!t&&1==(r>>n&1);o[Math.floor(n/3)][n%3+i-8-3]=a}for(n=0;n<18;n+=1){a=!t&&1==(r>>n&1);o[n%3+i-8-3][Math.floor(n/3)]=a}},d=function(t,r){for(var e=n<<3|r,a=B.getBCHTypeInfo(e),u=0;u<15;u+=1){var f=!t&&1==(a>>u&1);u<6?o[u][8]=f:u<8?o[u+1][8]=f:o[i-15+u][8]=f}for(u=0;u<15;u+=1){f=!t&&1==(a>>u&1);u<8?o[8][i-u-1]=f:u<9?o[8][15-u-1+1]=f:o[8][15-u-1]=f}o[i-8][8]=!t},w=function(t,r){for(var e=-1,n=i-1,a=7,u=0,f=B.getMaskFunction(r),c=i-1;c>0;c-=2)for(6==c&&(c-=1);;){for(var g=0;g<2;g+=1)if(null==o[n][c-g]){var l=!1;u<t.length&&(l=1==(t[u]>>>a&1)),f(n,c-g)&&(l=!l),o[n][c-g]=l,-1==(a-=1)&&(u+=1,a=7)}if((n+=e)<0||i<=n){n-=e,e=-e;break}}},p=function(t,r,e){for(var n=A.getRSBlocks(t,r),o=b(),i=0;i<e.length;i+=1){var a=e[i];o.put(a.getMode(),4),o.put(a.getLength(),B.getLengthInBits(a.getMode(),t)),a.write(o)}var u=0;for(i=0;i<n.length;i+=1)u+=n[i].dataCount;if(o.getLengthInBits()>8*u)throw"code length overflow. ("+o.getLengthInBits()+">"+8*u+")";for(o.getLengthInBits()+4<=8*u&&o.put(0,4);o.getLengthInBits()%8!=0;)o.putBit(!1);for(;!(o.getLengthInBits()>=8*u||(o.put(236,8),o.getLengthInBits()>=8*u));)o.put(17,8);return function(t,r){for(var e=0,n=0,o=0,i=new Array(r.length),a=new Array(r.length),u=0;u<r.length;u+=1){var f=r[u].dataCount,c=r[u].totalCount-f;n=Math.max(n,f),o=Math.max(o,c),i[u]=new Array(f);for(var g=0;g<i[u].length;g+=1)i[u][g]=255&t.getBuffer()[g+e];e+=f;var l=B.getErrorCorrectPolynomial(c),h=k(i[u],l.getLength()-1).mod(l);for(a[u]=new Array(l.getLength()-1),g=0;g<a[u].length;g+=1){var s=g+h.getLength()-a[u].length;a[u][g]=s>=0?h.getAt(s):0}}var v=0;for(g=0;g<r.length;g+=1)v+=r[g].totalCount;var d=new Array(v),w=0;for(g=0;g<n;g+=1)for(u=0;u<r.length;u+=1)g<i[u].length&&(d[w]=i[u][g],w+=1);for(g=0;g<o;g+=1)for(u=0;u<r.length;u+=1)g<a[u].length&&(d[w]=a[u][g],w+=1);return d}(o,n)};f.addData=function(t,r){var e=null;switch(r=r||"Byte"){case"Numeric":e=M(t);break;case"Alphanumeric":e=x(t);break;case"Byte":e=m(t);break;case"Kanji":e=L(t);break;default:throw"mode:"+r}u.push(e),a=null},f.isDark=function(t,r){if(t<0||i<=t||r<0||i<=r)throw t+","+r;return o[t][r]},f.getModuleCount=function(){return i},f.make=function(){if(e<1){for(var t=1;t<40;t++){for(var r=A.getRSBlocks(t,n),o=b(),i=0;i<u.length;i++){var a=u[i];o.put(a.getMode(),4),o.put(a.getLength(),B.getLengthInBits(a.getMode(),t)),a.write(o)}var g=0;for(i=0;i<r.length;i++)g+=r[i].dataCount;if(o.getLengthInBits()<=8*g)break}e=t}c(!1,function(){for(var t=0,r=0,e=0;e<8;e+=1){c(!0,e);var n=B.getLostPoint(f);(0==e||t>n)&&(t=n,r=e)}return r}())},f.createTableTag=function(t,r){t=t||2;var e="";e+='<table style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: "+(r=void 0===r?4*t:r)+"px;",e+='">',e+="<tbody>";for(var n=0;n<f.getModuleCount();n+=1){e+="<tr>";for(var o=0;o<f.getModuleCount();o+=1)e+='<td style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: 0px;",e+=" width: "+t+"px;",e+=" height: "+t+"px;",e+=" background-color: ",e+=f.isDark(n,o)?"#000000":"#ffffff",e+=";",e+='"/>';e+="</tr>"}return e+="</tbody>",e+="</table>"},f.createSvgTag=function(t,r,e,n){var o={};"object"==typeof arguments[0]&&(t=(o=arguments[0]).cellSize,r=o.margin,e=o.alt,n=o.title),t=t||2,r=void 0===r?4*t:r,(e="string"==typeof e?{text:e}:e||{}).text=e.text||null,e.id=e.text?e.id||"qrcode-description":null,(n="string"==typeof n?{text:n}:n||{}).text=n.text||null,n.id=n.text?n.id||"qrcode-title":null;var i,a,u,c,g=f.getModuleCount()*t+2*r,l="";for(c="l"+t+",0 0,"+t+" -"+t+",0 0,-"+t+"z ",l+='<svg version="1.1" xmlns="http://www.w3.org/2000/svg"',l+=o.scalable?"":' width="'+g+'px" height="'+g+'px"',l+=' viewBox="0 0 '+g+" "+g+'" ',l+=' preserveAspectRatio="xMinYMin meet"',l+=n.text||e.text?' role="img" aria-labelledby="'+y([n.id,e.id].join(" ").trim())+'"':"",l+=">",l+=n.text?'<title id="'+y(n.id)+'">'+y(n.text)+"</title>":"",l+=e.text?'<description id="'+y(e.id)+'">'+y(e.text)+"</description>":"",l+='<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>',l+='<path d="',a=0;a<f.getModuleCount();a+=1)for(u=a*t+r,i=0;i<f.getModuleCount();i+=1)f.isDark(a,i)&&(l+="M"+(i*t+r)+","+u+c);return l+='" stroke="transparent" fill="black"/>',l+="</svg>"},f.createDataURL=function(t,r){t=t||2,r=void 0===r?4*t:r;var e=f.getModuleCount()*t+2*r,n=r,o=e-r;return I(e,e,(function(r,e){if(n<=r&&r<o&&n<=e&&e<o){var i=Math.floor((r-n)/t),a=Math.floor((e-n)/t);return f.isDark(a,i)?0:1}return 1}))},f.createImgTag=function(t,r,e){t=t||2,r=void 0===r?4*t:r;var n=f.getModuleCount()*t+2*r,o="";return o+="<img",o+=' src="',o+=f.createDataURL(t,r),o+='"',o+=' width="',o+=n,o+='"',o+=' height="',o+=n,o+='"',e&&(o+=' alt="',o+=y(e),o+='"'),o+="/>"};var y=function(t){for(var r="",e=0;e<t.length;e+=1){var n=t.charAt(e);switch(n){case"<":r+="&lt;";break;case">":r+="&gt;";break;case"&":r+="&amp;";break;case'"':r+="&quot;";break;default:r+=n}}return r};return f.createASCII=function(t,r){if((t=t||1)<2)return function(t){t=void 0===t?2:t;var r,e,n,o,i,a=1*f.getModuleCount()+2*t,u=t,c=a-t,g={"██":"█","█ ":"▀"," █":"▄"," ":" "},l={"██":"▀","█ ":"▀"," █":" "," ":" "},h="";for(r=0;r<a;r+=2){for(n=Math.floor((r-u)/1),o=Math.floor((r+1-u)/1),e=0;e<a;e+=1)i="█",u<=e&&e<c&&u<=r&&r<c&&f.isDark(n,Math.floor((e-u)/1))&&(i=" "),u<=e&&e<c&&u<=r+1&&r+1<c&&f.isDark(o,Math.floor((e-u)/1))?i+=" ":i+="█",h+=t<1&&r+1>=c?l[i]:g[i];h+="\n"}return a%2&&t>0?h.substring(0,h.length-a-1)+Array(a+1).join("▀"):h.substring(0,h.length-1)}(r);t-=1,r=void 0===r?2*t:r;var e,n,o,i,a=f.getModuleCount()*t+2*r,u=r,c=a-r,g=Array(t+1).join("██"),l=Array(t+1).join(" "),h="",s="";for(e=0;e<a;e+=1){for(o=Math.floor((e-u)/t),s="",n=0;n<a;n+=1)i=1,u<=n&&n<c&&u<=e&&e<c&&f.isDark(o,Math.floor((n-u)/t))&&(i=0),s+=i?g:l;for(o=0;o<t;o+=1)h+=s+"\n"}return h.substring(0,h.length-1)},f.renderTo2dContext=function(t,r){r=r||2;for(var e=f.getModuleCount(),n=0;n<e;n++)for(var o=0;o<e;o++)t.fillStyle=f.isDark(n,o)?"black":"white",t.fillRect(n*r,o*r,r,r)},f};t.stringToBytes=(t.stringToBytesFuncs={default:function(t){for(var r=[],e=0;e<t.length;e+=1){var n=t.charCodeAt(e);r.push(255&n)}return r}}).default,t.createStringToBytes=function(t,r){var e=function(){for(var e=S(t),n=function(){var t=e.read();if(-1==t)throw"eof";return t},o=0,i={};;){var a=e.read();if(-1==a)break;var u=n(),f=n()<<8|n();i[String.fromCharCode(a<<8|u)]=f,o+=1}if(o!=r)throw o+" != "+r;return i}(),n="?".charCodeAt(0);return function(t){for(var r=[],o=0;o<t.length;o+=1){var i=t.charCodeAt(o);if(i<128)r.push(i);else{var a=e[t.charAt(o)];"number"==typeof a?(255&a)==a?r.push(a):(r.push(a>>>8),r.push(255&a)):r.push(n)}}return r}};var r,e,n,o,i,a=1,u=2,f=4,c=8,g={L:1,M:0,Q:3,H:2},l=0,h=1,s=2,v=3,d=4,w=5,p=6,y=7,B=(r=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],e=1335,n=7973,i=function(t){for(var r=0;0!=t;)r+=1,t>>>=1;return r},(o={}).getBCHTypeInfo=function(t){for(var r=t<<10;i(r)-i(e)>=0;)r^=e<<i(r)-i(e);return 21522^(t<<10|r)},o.getBCHTypeNumber=function(t){for(var r=t<<12;i(r)-i(n)>=0;)r^=n<<i(r)-i(n);return t<<12|r},o.getPatternPosition=function(t){return r[t-1]},o.getMaskFunction=function(t){switch(t){case l:return function(t,r){return(t+r)%2==0};case h:return function(t,r){return t%2==0};case s:return function(t,r){return r%3==0};case v:return function(t,r){return(t+r)%3==0};case d:return function(t,r){return(Math.floor(t/2)+Math.floor(r/3))%2==0};case w:return function(t,r){return t*r%2+t*r%3==0};case p:return function(t,r){return(t*r%2+t*r%3)%2==0};case y:return function(t,r){return(t*r%3+(t+r)%2)%2==0};default:throw"bad maskPattern:"+t}},o.getErrorCorrectPolynomial=function(t){for(var r=k([1],0),e=0;e<t;e+=1)r=r.multiply(k([1,C.gexp(e)],0));return r},o.getLengthInBits=function(t,r){if(1<=r&&r<10)switch(t){case a:return 10;case u:return 9;case f:case c:return 8;default:throw"mode:"+t}else if(r<27)switch(t){case a:return 12;case u:return 11;case f:return 16;case c:return 10;default:throw"mode:"+t}else{if(!(r<41))throw"type:"+r;switch(t){case a:return 14;case u:return 13;case f:return 16;case c:return 12;default:throw"mode:"+t}}},o.getLostPoint=function(t){for(var r=t.getModuleCount(),e=0,n=0;n<r;n+=1)for(var o=0;o<r;o+=1){for(var i=0,a=t.isDark(n,o),u=-1;u<=1;u+=1)if(!(n+u<0||r<=n+u))for(var f=-1;f<=1;f+=1)o+f<0||r<=o+f||0==u&&0==f||a==t.isDark(n+u,o+f)&&(i+=1);i>5&&(e+=3+i-5)}for(n=0;n<r-1;n+=1)for(o=0;o<r-1;o+=1){var c=0;t.isDark(n,o)&&(c+=1),t.isDark(n+1,o)&&(c+=1),t.isDark(n,o+1)&&(c+=1),t.isDark(n+1,o+1)&&(c+=1),0!=c&&4!=c||(e+=3)}for(n=0;n<r;n+=1)for(o=0;o<r-6;o+=1)t.isDark(n,o)&&!t.isDark(n,o+1)&&t.isDark(n,o+2)&&t.isDark(n,o+3)&&t.isDark(n,o+4)&&!t.isDark(n,o+5)&&t.isDark(n,o+6)&&(e+=40);for(o=0;o<r;o+=1)for(n=0;n<r-6;n+=1)t.isDark(n,o)&&!t.isDark(n+1,o)&&t.isDark(n+2,o)&&t.isDark(n+3,o)&&t.isDark(n+4,o)&&!t.isDark(n+5,o)&&t.isDark(n+6,o)&&(e+=40);var g=0;for(o=0;o<r;o+=1)for(n=0;n<r;n+=1)t.isDark(n,o)&&(g+=1);return e+=Math.abs(100*g/r/r-50)/5*10},o),C=function(){for(var t=new Array(256),r=new Array(256),e=0;e<8;e+=1)t[e]=1<<e;for(e=8;e<256;e+=1)t[e]=t[e-4]^t[e-5]^t[e-6]^t[e-8];for(e=0;e<255;e+=1)r[t[e]]=e;var n={glog:function(t){if(t<1)throw"glog("+t+")";return r[t]},gexp:function(r){for(;r<0;)r+=255;for(;r>=256;)r-=255;return t[r]}};return n}();function k(t,r){if(void 0===t.length)throw t.length+"/"+r;var e=function(){for(var e=0;e<t.length&&0==t[e];)e+=1;for(var n=new Array(t.length-e+r),o=0;o<t.length-e;o+=1)n[o]=t[o+e];return n}(),n={getAt:function(t){return e[t]},getLength:function(){return e.length},multiply:function(t){for(var r=new Array(n.getLength()+t.getLength()-1),e=0;e<n.getLength();e+=1)for(var o=0;o<t.getLength();o+=1)r[e+o]^=C.gexp(C.glog(n.getAt(e))+C.glog(t.getAt(o)));return k(r,0)},mod:function(t){if(n.getLength()-t.getLength()<0)return n;for(var r=C.glog(n.getAt(0))-C.glog(t.getAt(0)),e=new Array(n.getLength()),o=0;o<n.getLength();o+=1)e[o]=n.getAt(o);for(o=0;o<t.getLength();o+=1)e[o]^=C.gexp(C.glog(t.getAt(o))+r);return k(e,0).mod(t)}};return n}var A=function(){var t=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],r=function(t,r){var e={};return e.totalCount=t,e.dataCount=r,e},e={};return e.getRSBlocks=function(e,n){var o=function(r,e){switch(e){case g.L:return t[4*(r-1)+0];case g.M:return t[4*(r-1)+1];case g.Q:return t[4*(r-1)+2];case g.H:return t[4*(r-1)+3];default:return}}(e,n);if(void 0===o)throw"bad rs block @ typeNumber:"+e+"/errorCorrectionLevel:"+n;for(var i=o.length/3,a=[],u=0;u<i;u+=1)for(var f=o[3*u+0],c=o[3*u+1],l=o[3*u+2],h=0;h<f;h+=1)a.push(r(c,l));return a},e}(),b=function(){var t=[],r=0,e={getBuffer:function(){return t},getAt:function(r){var e=Math.floor(r/8);return 1==(t[e]>>>7-r%8&1)},put:function(t,r){for(var n=0;n<r;n+=1)e.putBit(1==(t>>>r-n-1&1))},getLengthInBits:function(){return r},putBit:function(e){var n=Math.floor(r/8);t.length<=n&&t.push(0),e&&(t[n]|=128>>>r%8),r+=1}};return e},M=function(t){var r=a,e=t,n={getMode:function(){return r},getLength:function(t){return e.length},write:function(t){for(var r=e,n=0;n+2<r.length;)t.put(o(r.substring(n,n+3)),10),n+=3;n<r.length&&(r.length-n==1?t.put(o(r.substring(n,n+1)),4):r.length-n==2&&t.put(o(r.substring(n,n+2)),7))}},o=function(t){for(var r=0,e=0;e<t.length;e+=1)r=10*r+i(t.charAt(e));return r},i=function(t){if("0"<=t&&t<="9")return t.charCodeAt(0)-"0".charCodeAt(0);throw"illegal char :"+t};return n},x=function(t){var r=u,e=t,n={getMode:function(){return r},getLength:function(t){return e.length},write:function(t){for(var r=e,n=0;n+1<r.length;)t.put(45*o(r.charAt(n))+o(r.charAt(n+1)),11),n+=2;n<r.length&&t.put(o(r.charAt(n)),6)}},o=function(t){if("0"<=t&&t<="9")return t.charCodeAt(0)-"0".charCodeAt(0);if("A"<=t&&t<="Z")return t.charCodeAt(0)-"A".charCodeAt(0)+10;switch(t){case" ":return 36;case"$":return 37;case"%":return 38;case"*":return 39;case"+":return 40;case"-":return 41;case".":return 42;case"/":return 43;case":":return 44;default:throw"illegal char :"+t}};return n},m=function(r){var e=f,n=t.stringToBytes(r),o={getMode:function(){return e},getLength:function(t){return n.length},write:function(t){for(var r=0;r<n.length;r+=1)t.put(n[r],8)}};return o},L=function(r){var e=c,n=t.stringToBytesFuncs.SJIS;if(!n)throw"sjis not supported.";!function(){var t=n("友");if(2!=t.length||38726!=(t[0]<<8|t[1]))throw"sjis not supported."}();var o=n(r),i={getMode:function(){return e},getLength:function(t){return~~(o.length/2)},write:function(t){for(var r=o,e=0;e+1<r.length;){var n=(255&r[e])<<8|255&r[e+1];if(33088<=n&&n<=40956)n-=33088;else{if(!(57408<=n&&n<=60351))throw"illegal char at "+(e+1)+"/"+n;n-=49472}n=192*(n>>>8&255)+(255&n),t.put(n,13),e+=2}if(e<r.length)throw"illegal char at "+(e+1)}};return i},D=function(){var t=[],r={writeByte:function(r){t.push(255&r)},writeShort:function(t){r.writeByte(t),r.writeByte(t>>>8)},writeBytes:function(t,e,n){e=e||0,n=n||t.length;for(var o=0;o<n;o+=1)r.writeByte(t[o+e])},writeString:function(t){for(var e=0;e<t.length;e+=1)r.writeByte(t.charCodeAt(e))},toByteArray:function(){return t},toString:function(){var r="";r+="[";for(var e=0;e<t.length;e+=1)e>0&&(r+=","),r+=t[e];return r+="]"}};return r},S=function(t){var r=t,e=0,n=0,o=0,i={read:function(){for(;o<8;){if(e>=r.length){if(0==o)return-1;throw"unexpected end of file./"+o}var t=r.charAt(e);if(e+=1,"="==t)return o=0,-1;t.match(/^\s$/)||(n=n<<6|a(t.charCodeAt(0)),o+=6)}var i=n>>>o-8&255;return o-=8,i}},a=function(t){if(65<=t&&t<=90)return t-65;if(97<=t&&t<=122)return t-97+26;if(48<=t&&t<=57)return t-48+52;if(43==t)return 62;if(47==t)return 63;throw"c:"+t};return i},I=function(t,r,e){for(var n=function(t,r){var e=t,n=r,o=new Array(t*r),i={setPixel:function(t,r,n){o[r*e+t]=n},write:function(t){t.writeString("GIF87a"),t.writeShort(e),t.writeShort(n),t.writeByte(128),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(0),t.writeByte(255),t.writeByte(255),t.writeByte(255),t.writeString(","),t.writeShort(0),t.writeShort(0),t.writeShort(e),t.writeShort(n),t.writeByte(0);var r=a(2);t.writeByte(2);for(var o=0;r.length-o>255;)t.writeByte(255),t.writeBytes(r,o,255),o+=255;t.writeByte(r.length-o),t.writeBytes(r,o,r.length-o),t.writeByte(0),t.writeString(";")}},a=function(t){for(var r=1<<t,e=1+(1<<t),n=t+1,i=u(),a=0;a<r;a+=1)i.add(String.fromCharCode(a));i.add(String.fromCharCode(r)),i.add(String.fromCharCode(e));var f,c,g,l=D(),h=(f=l,c=0,g=0,{write:function(t,r){if(t>>>r!=0)throw"length over";for(;c+r>=8;)f.writeByte(255&(t<<c|g)),r-=8-c,t>>>=8-c,g=0,c=0;g|=t<<c,c+=r},flush:function(){c>0&&f.writeByte(g)}});h.write(r,n);var s=0,v=String.fromCharCode(o[s]);for(s+=1;s<o.length;){var d=String.fromCharCode(o[s]);s+=1,i.contains(v+d)?v+=d:(h.write(i.indexOf(v),n),i.size()<4095&&(i.size()==1<<n&&(n+=1),i.add(v+d)),v=d)}return h.write(i.indexOf(v),n),h.write(e,n),h.flush(),l.toByteArray()},u=function(){var t={},r=0,e={add:function(n){if(e.contains(n))throw"dup key:"+n;t[n]=r,r+=1},size:function(){return r},indexOf:function(r){return t[r]},contains:function(r){return void 0!==t[r]}};return e};return i}(t,r),o=0;o<r;o+=1)for(var i=0;i<t;i+=1)n.setPixel(i,o,e(i,o));var a=D();n.write(a);for(var u=function(){var t=0,r=0,e=0,n="",o={},i=function(t){n+=String.fromCharCode(a(63&t))},a=function(t){if(t<0);else{if(t<26)return 65+t;if(t<52)return t-26+97;if(t<62)return t-52+48;if(62==t)return 43;if(63==t)return 47}throw"n:"+t};return o.writeByte=function(n){for(t=t<<8|255&n,r+=8,e+=1;r>=6;)i(t>>>r-6),r-=6},o.flush=function(){if(r>0&&(i(t<<6-r),t=0,r=0),e%3!=0)for(var o=3-e%3,a=0;a<o;a+=1)n+="="},o.toString=function(){return n},o}(),f=a.toByteArray(),c=0;c<f.length;c+=1)u.writeByte(f[c]);return u.flush(),"data:image/gif;base64,"+u};return t}();qrcode.stringToBytesFuncs["UTF-8"]=function(t){return function(t){for(var r=[],e=0;e<t.length;e++){var n=t.charCodeAt(e);n<128?r.push(n):n<2048?r.push(192|n>>6,128|63&n):n<55296||n>=57344?r.push(224|n>>12,128|n>>6&63,128|63&n):(e++,n=65536+((1023&n)<<10|1023&t.charCodeAt(e)),r.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|63&n))}return r}(t)},function(t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports&&(module.exports=t())}((function(){return qrcode})); 18 + //# sourceMappingURL=/sm/26b4b0d0b1e283d6b3ec9857ac597d7a60c76ac17be1ef4c965f03086de426bb.map
+191
convey/static/pairing.css
··· 1 + .pairing-page { 2 + margin: 0; 3 + min-height: 100vh; 4 + background: 5 + radial-gradient(circle at top left, rgba(245, 188, 94, 0.18), transparent 28rem), 6 + radial-gradient(circle at bottom right, rgba(85, 144, 111, 0.18), transparent 30rem), 7 + #f8f3e8; 8 + color: #1f1f1a; 9 + font-family: "Comfortaa Variable", "Avenir Next", sans-serif; 10 + } 11 + 12 + .pairing-shell { 13 + box-sizing: border-box; 14 + max-width: 72rem; 15 + margin: 0 auto; 16 + padding: 2rem 1.25rem 3rem; 17 + display: grid; 18 + gap: 1.5rem; 19 + } 20 + 21 + .pairing-panel { 22 + padding: 1.5rem; 23 + border: 1px solid rgba(31, 31, 26, 0.1); 24 + border-radius: 1.25rem; 25 + background: rgba(255, 252, 245, 0.92); 26 + box-shadow: 0 1rem 2.5rem rgba(51, 45, 35, 0.08); 27 + } 28 + 29 + .pairing-kicker { 30 + margin: 0 0 0.5rem; 31 + font-size: 0.8rem; 32 + letter-spacing: 0.14em; 33 + text-transform: uppercase; 34 + color: #7f5b2a; 35 + } 36 + 37 + .pairing-panel h1, 38 + .pairing-panel h2 { 39 + margin: 0; 40 + font-size: clamp(1.8rem, 3vw, 2.6rem); 41 + } 42 + 43 + .pairing-panel h2 { 44 + font-size: clamp(1.35rem, 2.3vw, 1.8rem); 45 + } 46 + 47 + .pairing-intro { 48 + max-width: 44rem; 49 + margin: 1rem 0 0; 50 + line-height: 1.6; 51 + } 52 + 53 + .pairing-actions { 54 + margin-top: 1.5rem; 55 + display: grid; 56 + gap: 0.75rem; 57 + } 58 + 59 + .pairing-button, 60 + .pairing-unpair { 61 + appearance: none; 62 + border: 0; 63 + border-radius: 999px; 64 + padding: 0.9rem 1.3rem; 65 + font: inherit; 66 + font-weight: 700; 67 + cursor: pointer; 68 + } 69 + 70 + .pairing-button { 71 + width: fit-content; 72 + background: #1f1f1a; 73 + color: #fff7ec; 74 + } 75 + 76 + .pairing-button:disabled, 77 + .pairing-unpair:disabled { 78 + opacity: 0.6; 79 + cursor: default; 80 + } 81 + 82 + .pairing-feedback { 83 + min-height: 1.2rem; 84 + margin: 0; 85 + color: #5c4d38; 86 + } 87 + 88 + .pairing-feedback[data-state="error"] { 89 + color: #8c2d2d; 90 + } 91 + 92 + .pairing-minted { 93 + margin-top: 1.5rem; 94 + display: grid; 95 + gap: 1rem; 96 + grid-template-columns: minmax(14rem, 18rem) 1fr; 97 + align-items: start; 98 + } 99 + 100 + .pairing-qr-wrap { 101 + padding: 1rem; 102 + border-radius: 1rem; 103 + background: #fff; 104 + border: 1px solid rgba(31, 31, 26, 0.08); 105 + } 106 + 107 + .pairing-qr { 108 + width: 100%; 109 + aspect-ratio: 1 / 1; 110 + display: grid; 111 + place-items: center; 112 + } 113 + 114 + .pairing-qr svg { 115 + width: 100%; 116 + height: auto; 117 + } 118 + 119 + .pairing-label { 120 + margin: 0; 121 + font-size: 0.85rem; 122 + color: #7f5b2a; 123 + text-transform: uppercase; 124 + letter-spacing: 0.1em; 125 + } 126 + 127 + .pairing-url { 128 + display: block; 129 + margin-top: 0.5rem; 130 + padding: 0.85rem 1rem; 131 + border-radius: 0.85rem; 132 + background: #f2ebdc; 133 + word-break: break-all; 134 + line-height: 1.5; 135 + } 136 + 137 + .pairing-countdown { 138 + margin: 0.75rem 0 0; 139 + font-weight: 700; 140 + } 141 + 142 + .pairing-devices-list { 143 + display: grid; 144 + gap: 0.9rem; 145 + } 146 + 147 + .pairing-empty { 148 + color: #5c4d38; 149 + } 150 + 151 + .pairing-device { 152 + display: flex; 153 + justify-content: space-between; 154 + gap: 1rem; 155 + align-items: center; 156 + padding: 1rem 1.1rem; 157 + border-radius: 1rem; 158 + background: #fff; 159 + border: 1px solid rgba(31, 31, 26, 0.08); 160 + } 161 + 162 + .pairing-device h3 { 163 + margin: 0; 164 + font-size: 1rem; 165 + } 166 + 167 + .pairing-device-details { 168 + margin: 0.35rem 0 0; 169 + color: #5c4d38; 170 + line-height: 1.45; 171 + } 172 + 173 + .pairing-unpair { 174 + background: #ebe1d2; 175 + color: #4a3721; 176 + } 177 + 178 + @media (max-width: 720px) { 179 + .pairing-shell { 180 + padding: 1rem 0.9rem 2rem; 181 + } 182 + 183 + .pairing-minted { 184 + grid-template-columns: 1fr; 185 + } 186 + 187 + .pairing-device { 188 + flex-direction: column; 189 + align-items: stretch; 190 + } 191 + }
+200
convey/static/pairing.js
··· 1 + // SPDX-License-Identifier: AGPL-3.0-only 2 + // Copyright (c) 2026 sol pbc 3 + 4 + (function () { 5 + function $(id) { 6 + return document.getElementById(id); 7 + } 8 + 9 + const generateButton = $("pairing-generate"); 10 + const feedback = $("pairing-feedback"); 11 + const mintedSection = $("pairing-minted"); 12 + const qrContainer = $("pairing-qr"); 13 + const pairingUrl = $("pairing-url"); 14 + const countdown = $("pairing-countdown"); 15 + const devicesEmpty = $("pairing-devices-empty"); 16 + const devicesList = $("pairing-devices-list"); 17 + 18 + let countdownTimer = null; 19 + let pollTimer = null; 20 + 21 + async function fetchJson(url, options) { 22 + const response = await fetch( 23 + url, 24 + Object.assign({ credentials: "same-origin" }, options || {}), 25 + ); 26 + 27 + let payload = null; 28 + try { 29 + payload = await response.json(); 30 + } catch (error) { 31 + payload = null; 32 + } 33 + 34 + if (!response.ok) { 35 + const message = payload && payload.error ? payload.error : "Request failed"; 36 + throw new Error(message); 37 + } 38 + 39 + return payload || {}; 40 + } 41 + 42 + function setFeedback(message, isError) { 43 + if (!feedback) { 44 + return; 45 + } 46 + feedback.textContent = message || ""; 47 + feedback.dataset.state = isError ? "error" : "info"; 48 + } 49 + 50 + function renderQr(data) { 51 + if (!qrContainer) { 52 + return; 53 + } 54 + qrContainer.innerHTML = ""; 55 + 56 + if (typeof window.qrcode !== "function") { 57 + qrContainer.textContent = "QR unavailable"; 58 + return; 59 + } 60 + 61 + const qr = window.qrcode(0, "M"); 62 + qr.addData(data); 63 + qr.make(); 64 + qrContainer.innerHTML = qr.createSvgTag({ 65 + cellSize: 6, 66 + margin: 0, 67 + scalable: true, 68 + title: "Phone pairing code", 69 + alt: "Phone pairing code", 70 + }); 71 + } 72 + 73 + function renderCountdown(expiresAt) { 74 + if (!countdown) { 75 + return; 76 + } 77 + 78 + function update() { 79 + const remaining = Math.max(0, expiresAt - Math.floor(Date.now() / 1000)); 80 + countdown.textContent = 81 + remaining > 0 ? `Expires in ${remaining}s` : "Expired"; 82 + if (remaining === 0 && countdownTimer !== null) { 83 + window.clearInterval(countdownTimer); 84 + countdownTimer = null; 85 + } 86 + } 87 + 88 + if (countdownTimer !== null) { 89 + window.clearInterval(countdownTimer); 90 + } 91 + update(); 92 + countdownTimer = window.setInterval(update, 1000); 93 + } 94 + 95 + function renderDevices(devices) { 96 + if (!devicesList || !devicesEmpty) { 97 + return; 98 + } 99 + 100 + devicesList.innerHTML = ""; 101 + const rows = Array.isArray(devices) ? devices : []; 102 + devicesEmpty.hidden = rows.length > 0; 103 + 104 + rows.forEach((device) => { 105 + const row = document.createElement("article"); 106 + row.className = "pairing-device"; 107 + 108 + const meta = document.createElement("div"); 109 + meta.className = "pairing-device-meta"; 110 + 111 + const name = document.createElement("h3"); 112 + name.textContent = device.name || "Unnamed phone"; 113 + meta.appendChild(name); 114 + 115 + const details = document.createElement("p"); 116 + details.className = "pairing-device-details"; 117 + details.textContent = [ 118 + device.platform || "unknown", 119 + `paired ${device.paired_at || "unknown"}`, 120 + `last seen ${device.last_seen_at || "never"}`, 121 + ].join(" · "); 122 + meta.appendChild(details); 123 + 124 + const button = document.createElement("button"); 125 + button.type = "button"; 126 + button.className = "pairing-unpair"; 127 + button.textContent = "Unpair"; 128 + button.addEventListener("click", async function () { 129 + button.disabled = true; 130 + try { 131 + await fetchJson(`/api/pairing/devices/${encodeURIComponent(device.id)}`, { 132 + method: "DELETE", 133 + }); 134 + setFeedback("Phone removed.", false); 135 + await refreshDevices(); 136 + } catch (error) { 137 + setFeedback(error.message, true); 138 + button.disabled = false; 139 + } 140 + }); 141 + 142 + row.appendChild(meta); 143 + row.appendChild(button); 144 + devicesList.appendChild(row); 145 + }); 146 + } 147 + 148 + async function refreshDevices() { 149 + try { 150 + const payload = await fetchJson("/api/pairing/devices"); 151 + renderDevices(payload.devices); 152 + } catch (error) { 153 + setFeedback(error.message, true); 154 + } 155 + } 156 + 157 + async function mintPairingCode() { 158 + if (!generateButton) { 159 + return; 160 + } 161 + generateButton.disabled = true; 162 + setFeedback("", false); 163 + 164 + try { 165 + const payload = await fetchJson("/api/pairing/create", { 166 + method: "POST", 167 + headers: { "Content-Type": "application/json" }, 168 + body: "{}", 169 + }); 170 + 171 + if (mintedSection) { 172 + mintedSection.hidden = false; 173 + } 174 + if (pairingUrl) { 175 + pairingUrl.textContent = payload.pairing_url || ""; 176 + } 177 + renderQr(payload.qr_data || ""); 178 + renderCountdown(payload.expires_at || 0); 179 + } catch (error) { 180 + setFeedback(error.message, true); 181 + } finally { 182 + generateButton.disabled = false; 183 + } 184 + } 185 + 186 + if (generateButton) { 187 + generateButton.addEventListener("click", mintPairingCode); 188 + } 189 + 190 + refreshDevices(); 191 + pollTimer = window.setInterval(refreshDevices, 5000); 192 + window.addEventListener("beforeunload", function () { 193 + if (countdownTimer !== null) { 194 + window.clearInterval(countdownTimer); 195 + } 196 + if (pollTimer !== null) { 197 + window.clearInterval(pollTimer); 198 + } 199 + }); 200 + })();
+57
convey/templates/pairing.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="utf-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1"> 6 + <title>Pair a phone - solstone</title> 7 + <link rel="stylesheet" href="{{ url_for('root.static', filename='app.css') }}"> 8 + <link rel="stylesheet" href="{{ url_for('root.static', filename='pairing.css') }}"> 9 + </head> 10 + <body class="pairing-page"> 11 + <main class="pairing-shell"> 12 + <section class="pairing-panel"> 13 + <p class="pairing-kicker">Phone pairing</p> 14 + <h1>Pair a phone</h1> 15 + <p class="pairing-intro"> 16 + Generate a short-lived code to pair your phone with this solstone journal. 17 + Paired phones show up below as listening devices, and you can remove them from here. 18 + </p> 19 + 20 + <div class="pairing-actions"> 21 + <button id="pairing-generate" class="pairing-button" type="button"> 22 + Generate pairing code 23 + </button> 24 + <p id="pairing-feedback" class="pairing-feedback" aria-live="polite"></p> 25 + </div> 26 + 27 + <section id="pairing-minted" class="pairing-minted" hidden> 28 + <div class="pairing-qr-wrap"> 29 + <div id="pairing-qr" class="pairing-qr" aria-live="polite"></div> 30 + </div> 31 + <div class="pairing-code"> 32 + <p class="pairing-label">Phone link</p> 33 + <code id="pairing-url" class="pairing-url"></code> 34 + <p id="pairing-countdown" class="pairing-countdown">Expires in 0s</p> 35 + </div> 36 + </section> 37 + </section> 38 + 39 + <section class="pairing-panel pairing-devices-panel"> 40 + <div class="pairing-panel-head"> 41 + <div> 42 + <p class="pairing-kicker">Listening devices</p> 43 + <h2>Paired phones</h2> 44 + </div> 45 + </div> 46 + 47 + <div id="pairing-devices-empty" class="pairing-empty"> 48 + No phones are paired yet. 49 + </div> 50 + <div id="pairing-devices-list" class="pairing-devices-list"></div> 51 + </section> 52 + </main> 53 + 54 + <script src="{{ url_for('root.static', filename='pairing-qr.js') }}"></script> 55 + <script src="{{ url_for('root.static', filename='pairing.js') }}"></script> 56 + </body> 57 + </html>
+32
tests/test_pairing_ui.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2026 sol pbc 3 + 4 + from __future__ import annotations 5 + 6 + import pytest 7 + 8 + from convey import create_app 9 + 10 + 11 + @pytest.fixture 12 + def pairing_ui_client(journal_copy): 13 + app = create_app(str(journal_copy)) 14 + app.config["TESTING"] = True 15 + client = app.test_client() 16 + with client.session_transaction() as session: 17 + session["logged_in"] = True 18 + session.permanent = True 19 + return client 20 + 21 + 22 + def test_pairing_ui_smoke(pairing_ui_client): 23 + response = pairing_ui_client.get("/app/pairing/") 24 + 25 + assert response.status_code == 200 26 + body = response.get_data(as_text=True) 27 + assert "Pair a phone" in body 28 + assert "/static/pairing-qr.js" in body 29 + assert "/static/pairing.js" in body 30 + assert "/static/pairing.css" in body 31 + for forbidden in ("keeper", "assistant", "record", "capture", "Capture"): 32 + assert forbidden not in body