Advertisement
Anonymous_J

quickspell.user.js

Oct 8th, 2022 (edited)
14,328
0
Never
9
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 13.00 KB | Source Code | 0 0
  1. // ==UserScript==
  2. // @name         quickspell
  3. // @namespace    https://pastebin.com/Bv8EP0MF
  4. // @version      0.4.2
  5. // @description  NovelAI ImageGen prompt manager
  6. // @author       彡(゚)(゚)
  7. // @match        https://novelai.net/image
  8. // @match        http://127.0.0.1:7860/
  9. // @icon         https://www.google.com/s2/favicons?sz=64&domain=novelai.net
  10. // @grant        none
  11. // @updateURL    https://pastebin.com/raw/Bv8EP0MF
  12. // ==/UserScript==
  13.  
  14. // Uses hyperapp: MIT License
  15. // Copyright © Jorge Bucaran <https://jorgebucaran.com>
  16. (() => {
  17. 'use strict';
  18. // You can define the query function that specifies how the quickspell UI interacts with page content.
  19. // The UI will determine the parent and the output by the query functions before instantiating itself.
  20. // Please consider defining the queries if the UI will never appeared for unsupported pages.
  21.  
  22. /**
  23.  * The query functions that specifies where to add the quickspell UI.
  24.  * The resulting element must be able to have children.
  25.  */
  26. const APP_ROOT_QUERY_FUNCTIONS = [
  27.     // It's a little greedy to combine it with others.
  28.     // Please consider comment outing that if you will define the query for your own.
  29.     () => document.body,
  30.     // For novelai.net/images
  31.     () => document.getElementById('__next'),
  32. ];
  33.  
  34. /**
  35.  * The query functions that specifies where to write the output from quickspell.
  36.  * The resulting element must be accept the input by "value" property (ordinally must be input or textarea).
  37.  */
  38. const PROMPT_INPUT_QUERY_FUNCTIONS = [
  39.     // For novelai.net/images
  40.     () => document.getElementById('prompt-input-0'),
  41.     // For novelai.net/images New UI
  42.     () => Array.from(document.getElementsByTagName('textarea')).filter(el => el.placeholder.includes('Write your prompt here.') && el.getClientRects().length > 0)[0],
  43.     // For Stable Diffusion WebUI (and the others that using gradio?).
  44.     () => document.getElementsByTagName('gradio-app')[0] && [...document.getElementsByTagName('gradio-app')[0].shadowRoot.querySelectorAll('[placeholder="Prompt"]')].find((el) => el.checkVisibility()),
  45. ];
  46.  
  47. /**
  48.  * The option to specify the position that a new fragment will be added.
  49.  * Set true to add to the beginning of the list, false to the end.
  50.  */
  51. const OPTION_ADD_FRAGMENT_ON_TOP = true;
  52.  
  53. var e={},t=[],n=e=>e,r=t.map,o=Array.isArray,s="undefined"!=typeof requestAnimationFrame?requestAnimationFrame:setTimeout,l=e=>{var t="";if("string"==typeof e)return e;if(o(e))for(var n,r=0;r<e.length;r++)(n=l(e[r]))&&(t+=(t&&" ")+n);else for(var r in e)e[r]&&(t+=(t&&" ")+r);return t},a=(e,t)=>{for(var n in{...e,...t})if("function"==typeof(o(e[n])?e[n][0]:e[n]))t[n]=e[n];else if(e[n]!==t[n])return!0},i=e=>null==e?e:e.key,c=(e,t,n,r,o,s)=>{if("style"===t)for(var l in{...n,...r})n=null==r||null==r[l]?"":r[l],"-"===l[0]?e[t].setProperty(l,n):e[t][l]=n;else"o"===t[0]&&"n"===t[1]?((e.events||(e.events={}))[t=t.slice(2)]=r)?n||e.addEventListener(t,o):e.removeEventListener(t,o):!s&&"list"!==t&&"form"!==t&&t in e?e[t]=null==r?"":r:null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,r)},u=(e,t,n)=>{var r=e.props,o=3===e.type?document.createTextNode(e.tag):(n=n||"svg"===e.tag)?document.createElementNS("http://www.w3.org/2000/svg",e.tag,r.is&&r):document.createElement(e.tag,r.is&&r);for(var s in r)c(o,s,null,r[s],t,n);for(var l=0;l<e.children.length;l++)o.appendChild(u(e.children[l]=d(e.children[l]),t,n));return e.node=o},f=(e,t,n,r,o,s)=>{if(n===r);else if(null!=n&&3===n.type&&3===r.type)n.tag!==r.tag&&(t.nodeValue=r.tag);else if(null==n||n.tag!==r.tag)t=e.insertBefore(u(r=d(r),o,s),t),null!=n&&e.removeChild(n.node);else{var l,a,p,m,g=n.props,h=r.props,v=n.children,b=r.children,y=0,q=0,k=v.length-1,w=b.length-1;for(var x in s=s||"svg"===r.tag,{...g,...h})("value"===x||"selected"===x||"checked"===x?t[x]:g[x])!==h[x]&&c(t,x,g[x],h[x],o,s);for(;q<=w&&y<=k&&null!=(p=i(v[y]))&&p===i(b[q]);)f(t,v[y].node,v[y],b[q]=d(b[q++],v[y++]),o,s);for(;q<=w&&y<=k&&null!=(p=i(v[k]))&&p===i(b[w]);)f(t,v[k].node,v[k],b[w]=d(b[w--],v[k--]),o,s);if(y>k)for(;q<=w;)t.insertBefore(u(b[q]=d(b[q++]),o,s),(a=v[y])&&a.node);else if(q>w)for(;y<=k;)t.removeChild(v[y++].node);else{var A={},N={};for(x=y;x<=k;x++)null!=(p=v[x].key)&&(A[p]=v[x]);for(;q<=w;)p=i(a=v[y]),m=i(b[q]=d(b[q],a)),N[p]||null!=m&&m===i(v[y+1])?(null==p&&t.removeChild(a.node),y++):null==m||1===n.type?(null==p&&(f(t,a&&a.node,a,b[q],o,s),q++),y++):(p===m?(f(t,a.node,a,b[q],o,s),N[m]=!0,y++):null!=(l=A[m])?(f(t,t.insertBefore(l.node,a&&a.node),l,b[q],o,s),N[m]=!0):f(t,a&&a.node,null,b[q],o,s),q++);for(;y<=k;)null==i(a=v[y++])&&t.removeChild(a.node);for(var x in A)null==N[x]&&t.removeChild(A[x].node)}}return r.node=t},d=(e,t)=>!0!==e&&!1!==e&&e?"function"==typeof e.tag?((!t||null==t.memo||((e,t)=>{for(var n in e)if(e[n]!==t[n])return!0;for(var n in t)if(e[n]!==t[n])return!0})(t.memo,e.memo))&&((t=e.tag(e.memo)).memo=e.memo),t):e:g(""),p=t=>3===t.nodeType?g(t.nodeValue,t):m(t.nodeName.toLowerCase(),e,r.call(t.childNodes,p),1,t),m=(e,{key:t,...n},r,o,s)=>({tag:e,props:n,key:t,children:r,type:o,node:s}),g=(n,r)=>m(n,e,t,3,r),h=(n,{class:r,...s},a=t)=>m(n,{...s,...r?{class:l(r)}:e},o(a)?a:[a]),v=({node:r,view:l,subscriptions:i,dispatch:c=n,init:u=e})=>{var d,m,g=r&&p(r),h=[],v=e=>{d!==e&&(null==(d=e)&&(c=i=b=n),i&&(h=((e,n=t,r)=>{for(var o,s,l=[],i=0;i<e.length||i<n.length;i++)o=e[i],s=n[i],l.push(s&&!0!==s?!o||s[0]!==o[0]||a(s[1],o[1])?[s[0],s[1],(o&&o[2](),s[0](r,s[1]))]:o:o&&o[2]());return l})(h,i(d),c)),l&&!m&&s(b,m=!0))},b=()=>r=f(r.parentNode,r,g,g=l(d),y,m=!1),y=function(e){c(this.events[e.type],e)};return(c=c(((e,t)=>"function"==typeof e?c(e(d,t)):o(e)?"function"==typeof e[0]?c(e[0],e[1]):e.slice(1).map((e=>e&&!0!==e&&(e[0]||e)(c,e[1])),v(e[0])):v(e))))(u),c};const b=e=>e.replace(/(^ +| +$)/,""),y=(e,t)=>([...n])=>([n[t],n[e]]=[n[e],n[t]],n),q=e=>()=>{for(let t=0;t<e.length;t++){const n=e[t]();if(n)return n}},k=q(APP_ROOT_QUERY_FUNCTIONS),w=q(PROMPT_INPUT_QUERY_FUNCTIONS),x=e=>{return t=e.target,k().querySelector(".qs").compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY;var t},A=()=>k().querySelector(".qs .pu .ti"),N=(e=!0,t="")=>({on:e,te:t}),S=()=>({on:!1,rd:!1,fq:null,fs:[N()]}),E=e=>e.fs instanceof Array&&e.fs.every((e=>"string"==typeof e.te)),O=()=>{try{return Object.assign(S(),JSON.parse(localStorage["uranus.quickspell.state"]))}catch(e){return console.error(localStorage["uranus.quickspell.state"]),S()}},C=(e,t)=>(e.substring(0,t).match(/[^,]+$/)||"")+(e.substring(t).match(/^[^,]+/)||""),T=(e,t)=>{let n=0;for(let r=0;r<e.length;r++)e[r]===t&&n++;return n},I=e=>e.fs.filter((e=>e.on)).map((e=>e.te)).join(", ").replace(/ *\/\*.*?\*\/ */g,"").replace(/{+}+/g,"").replace(/[+]+/g,"").replace(/(, *){1,}/g,", ").replace(/^, */,""),D=e=>{return e.rd?(t=I(e),Array.from(new Set(t.split(/, */g))).join(", ")):I(e);var t},F=((e,t)=>{let n=null;return(...t)=>{clearTimeout(n),n=setTimeout((()=>{e(...t)}))}})((e=>E(e)&&localStorage.setItem("uranus.quickspell.state",JSON.stringify(e||S())))),_=e=>(F(e),e);let P=null;const R=e=>{const t=k().querySelectorAll(".qs .sh .ti")[e];t&&t.focus()},U=e=>"string"==typeof e.fq?e.fq.split(/ +/):[],K=e=>_({...e,on:!e.on}),j=e=>_(OPTION_ADD_FRAGMENT_ON_TOP?{...e,fs:[N(),...e.fs]}:{...e,fs:[...e.fs,N()]}),L=e=>_({...e,rd:!e.rd}),M=e=>t=>_({...t,fs:[...t.fs.slice(0,e),t.fs[e],...t.fs.slice(e)]}),B=e=>(t,n)=>_({...t,fs:t.fs.map(((t,r)=>r===e?N(t.on,n.target.value):t))}),J=e=>t=>_({...t,fs:t.fs.filter(((t,n)=>n!==e))}),Y=e=>t=>e>0?_({...t,fs:y(e-1,e)(t.fs)}):t,Q=e=>t=>e<t.fs.length-1?_({...t,fs:y(e,e+1)(t.fs)}):t,V=e=>_({...e,fs:e.fs.map((e=>({...e,on:!1})))}),$=e=>t=>_({...t,fq:e}),z=(e,t)=>$(t.target.value)(e),G=e=>{const t=prompt("Importing, please input a json");if(t){const n=JSON.parse(t);if(!E(n))throw"invl";return e.fs.length&&!confirm("Do you want to replace the current data? (cancel to append to the current data)")?_({...e,fs:e.fs.concat(n.fs)}):_(n)}return e},H=e=>(requestAnimationFrame((()=>{const t=JSON.stringify(e),n=document.createElement("a");n.download="quickspell.dump.json",n.href="data:application/json;charset=utf-8,"+encodeURIComponent(t),n.click()})),e),W=e=>t=>(P=e,t),X=e=>t=>n=>r=>{const o=r.fs[n].te,s=t.target.selectionStart,l=b(C(o,s)),a=(e=>{const t=T(e,"{");if(t)return t===T(e,"}")?t:null;const n=T(e,"[");return n?n===T(e,"]")?-n:null:0})(l);return l&&null!==a?(requestAnimationFrame((()=>requestAnimationFrame((()=>{const n=Math.max(0,s+e*Math.sign(a));t.target.setSelectionRange(n,n)})))),_({...r,fs:r.fs.map(((t,r)=>r===n?N(t.on,t.te.replace(l,((e,t)=>t>0?"{".repeat(t)+e.replace(/[{}\[\]]+/g,"")+"}".repeat(t):"[".repeat(-t)+e.replace(/[{}\[\]]+/g,"")+"]".repeat(-t))(l,a+e))):t))})):r},Z=X(1),ee=X(-1),te=e=>t=>n=>r=>{const o=r.fs[n].te,s=t.target.selectionStart,l=b(C(o,s)),a=o.split(/, */g),i=a.indexOf(l),c=i+e;if(l&&0<=Math.min(i,c)&&Math.max(i,c)<a.length&&a[c]){const e=y(i,c)(a).join(", ");return requestAnimationFrame((()=>requestAnimationFrame((()=>{const n=e.indexOf(l);t.target.setSelectionRange(n,n)})))),_({...r,fs:r.fs.map(((t,r)=>r===n?N(t.on,e):t))})}return r},ne=te(-1),re=te(1),oe=e=>(navigator.permissions.query({name:"clipboard-write"}).then((t=>{if("granted"!==t.state&&"prompt"!==t.state)throw"nopm";if(!navigator.clipboard)throw"nocb";return navigator.clipboard.writeText(I(e))})).then((e=>{(()=>{const e=k().querySelector(".qs .pr");e.classList.remove("bl"),requestAnimationFrame((()=>{e.classList.add("bl")}))})(),w()&&w().focus()})).catch((()=>{})),e),se=(e,t)=>{const n=t.indexed?n=>x(n)&&t.filter(n)&&n.target.dataset.qsK&&(n.preventDefault(),n.stopPropagation(),t.dispatcher(e,n,parseInt(n.target.dataset.qsK,10))):n=>x(n)&&t.filter(n)&&(n.preventDefault(),n.stopPropagation(),t.dispatcher(e,n));return addEventListener("keydown",n),()=>removeEventListener("keydown",n)},le=(e,t,n=!1)=>[se,{filter:e,dispatcher:t,indexed:n}],ae=()=>{const e=document.createElement("div");e.className="qs",k().appendChild(e),((e,t)=>{const n=document.createElement("style");n.textContent=t,e.appendChild(n)})(k(),"@keyframes kbl{0%{background-color: #000}20%{background-color: #242}100%{background-color: #000}} .qs{z-index:1000;position:fixed;width:100vw;bottom:0;pointer-events:none} .qs button,.qs input{pointer-events:auto} .qs .pu {overflow-y:scroll} .qs .pr{color:#fff;background-color:#000;user-select:none} .qs .sh{height:0;overflow:hidden} .qs .sh.on{max-height:50vh;height:auto;overflow-y:scroll} .qs .ln{display:flex;height:calc(2rem + 1px);border-bottom:1px solid #222} .qs .ln .cb{width:2rem;height:2rem;margin:0} .qs .ln .bu{width:2rem;color:#000;background-color:#fff} .qs .ln .bu:disabled{color:#888} .qs .ln .ti{flex:1} .qs .bl{animation:kbl 0.5s ease-out 1 normal}"),v({init:O(),node:e,view:e=>{return h("div",{class:"qs"},[h("div",{class:"pu ln"},[h("button",{class:"bu",title:e.on?"Shrink quickspell":"Expand quickspell",onclick:K},e.on?g("▼"):g("▲")),e.on&&h("button",{class:"bu",tabindex:-1,title:"Add a prompt fragment",onclick:j},g("+")),e.on&&h("input",{class:"cb",tabindex:-1,tabindex:-1,title:"Check to remove duplicated words from the combined prompt",type:"checkbox",checked:e.rd,onchange:L}),e.on&&h("button",{class:"bu",tabindex:-1,title:"Disable all fragments",onclick:V},g("🗹")),e.on&&h("input",{class:"ti",type:"text",value:e.fq,oninput:z}),e.on&&h("button",{class:"bu",tabindex:-1,title:"Import the state from json",onclick:G},g("⤓")),e.on&&h("button",{class:"bu",tabindex:-1,title:"Export the state as json",onclick:H},g("↥"))]),e.on&&h("span",{class:"pr",title:"Click to copy the prompt",onclick:oe},g(D(e))),h("div",{class:"sh "+(e.on?"on":"")},[...e.fs.map((n=U(e),t=e=>n.every((t=>e.te.includes(t))),(n,r)=>{return t(n)&&h("div",{class:"ln"},[h("input",{class:"cb",tabindex:-1,title:"Check to include the fragment",type:"checkbox",checked:n.on,onchange:(o=r,(e,t)=>_({...e,fs:e.fs.map(((e,n)=>n===o?N(t.target.checked,e.te):e))}))}),h("button",{class:"bu",tabindex:-1,title:"Replicate the fragment",onclick:M(r)},g("❐")),h("button",{class:"bu",tabindex:-1,title:"Move up",disabled:!!e.fq,onclick:Y(r)},g("↑")),h("button",{class:"bu",tabindex:-1,title:"Move down",disabled:!!e.fq,onclick:Q(r)},g("↓")),h("input",{class:"ti",type:"text",value:n.te,"data-qs-k":r,oninput:B(r),onfocus:W(r)}),h("button",{class:"bu",tabindex:-1,title:"Remove the fragment",onclick:J(r)},g("-"))]);var o}))])]);var t,n},subscriptions:e=>[!e.fq&&le((e=>e.ctrlKey&&/Up|ArrowUp/.test(e.key)),((e,t,n)=>(R(n-1),e(Y(n)))),!0),!e.fq&&le((e=>e.ctrlKey&&/Down|ArrowDown/.test(e.key)),((e,t,n)=>(R(n+1),e(Q(n)))),!0),le((e=>e.ctrlKey&&"Enter"===e.key),(e=>e((e=>((e=>{const t=w();t&&(t.focus(),t.value=D(e))})(e),e))))),le((e=>e.ctrlKey&&"f"===e.key&&e.target!==A()),(e=>(A().focus(),e($(""))))),le((e=>e.altKey&&/Up|ArrowUp/.test(e.key)),((e,t,n)=>e(Z(t)(n))),!0),le((e=>e.altKey&&/Down|ArrowDown/.test(e.key)),((e,t,n)=>e(ee(t)(n))),!0),le((e=>e.altKey&&/Left|ArrowLeft/.test(e.key)),((e,t,n)=>e(ne(t)(n))),!0),le((e=>e.altKey&&/Right|ArrowRight/.test(e.key)),((e,t,n)=>e(re(t)(n))),!0),le((e=>/Esc|Escape/.test(e.key)),((t,n)=>t("string"==typeof e.fq?(requestAnimationFrame((()=>requestAnimationFrame((()=>{R(P)})))),$(null)):(document.activeElement.blur(),K))))]})};(()=>{const e=setInterval((()=>{k()&&w()&&(clearInterval(e),ae())}),500)})();
  54. })();
  55.  
Advertisement
Comments
  • Anonymous_J
    1 year
    # text 0.06 KB | 0 0
    1. quickspell 導入方法・使い方
    2. https://pastebin.com/1pJfmTm4
  • Anonymous_J
    1 year
    # text 0.50 KB | 0 0
    1. v0.2.0
    2.  
    3. - UIの最大高さに関するバグを修正
    4. - プレビュークリック時のアニメーションを改善
    5. - 入力欄のタブキー押下時の移動先を改善
    6. - 入力欄の複製機能を追加
    7. - セーブデータのインポート・エクスポート機能を追加
    8. - ワードの入れ替え機能を追加
    9. - ワード強調の調整機能を追加
    10. - 重複ワードの削除機能を追加
    11. - キーバインドを追加
    12.  
    13. キーバインドはドキュメントを参考のこと
    • Anonymous_J
      1 year
      # text 0.43 KB | 0 0
      1. v0.2.1
      2.  
      3. - Stable Diffusion web UIの試験的サポートを追加
      4. - 対応ページを追加するためのフックを追加
      5. - インポート・エクスポートのダイアログのメッセージを改善
      6. - インポート時の置き換え・追加の選択肢を変更
      7. - ワード入れ替えの軽微なバグを修正
      8.  
      9. 対応ページを追加するためのフックについてはドキュメントを参考のこと
    • Anonymous_J
      1 year
      # text 0.32 KB | 0 0
      1. v0.3.0
      2.  
      3. - パネルを開いていたかどうかを保存するように変更
      4. - プレビューが長くなった際のパフォーマンスを改善
      5. - コメント構文のサポートを追加
      6. - エクスポートのファイル出力に対応
      7.  
      8. コメント構文についてはドキュメントを参考のこと
    • Anonymous_J
      1 year
      # text 0.21 KB | 0 0
      1. v0.3.1
      2.  
      3. - 手動コピーによるexportを廃止
      4.  
      5. ダイアログの文字数制限によりjsonが短縮されていた可能性あり
      6. 新しいバージョンでエクスポートを再度行うことを推奨
    • Anonymous_J
      1 year
      # text 0.09 KB | 0 0
      1. v0.3.2
      2.  
      3. - エクスポートでスペースが失われる問題を修正
      4.  
      5. すまん
    • Anonymous_J
      1 year
      # text 0.33 KB | 0 0
      1. v0.4.0
      2.  
      3. - チェック全解除のボタンを追加
      4. - 入力欄の追加位置を一番上に変更
      5. - 入力欄の追加位置を指定するオプションを追加
      6. - promptのフィルタ(検索)機能を追加
      7. - キーバインドを追加
      8.  
      9. オプション・キーバインドについてはドキュメントを参考のこと
    • Anonymous_J
      1 year
      # text 0.04 KB | 0 0
      1. v0.4.1
      2.  
      3. - NovelAIのUI更新に仮対応
    • Anonymous_J
      1 year
      # text 0.09 KB | 0 0
      1. v0.4.2
      2.  
      3. - NovelAIの画面下部に位置するUIのクリック判定を奪う問題を修正
Add Comment
Please, Sign In to add comment
Advertisement