diff --git a/bun.lock b/bun.lock index 75529ea..1e4450a 100644 --- a/bun.lock +++ b/bun.lock @@ -3,11 +3,18 @@ "workspaces": { "": { "name": "st-randomness-helpers", + "dependencies": { + "react": "^19.1.1", + "react-dom": "^19.1.1", + "zod": "^4.0.17", + "zustand": "^5.0.7", + }, "devDependencies": { "@biomejs/biome": "2.1.4", "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", "@types/bun": "latest", + "@types/react-dom": "^19.1.7", "husky": "^9.1.7", "lint-staged": "^16.1.5", }, @@ -81,6 +88,8 @@ "@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="], + "@types/react-dom": ["@types/react-dom@19.1.7", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw=="], + "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], @@ -255,6 +264,10 @@ "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], + + "react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="], + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], @@ -265,6 +278,8 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], @@ -305,6 +320,10 @@ "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], + "zod": ["zod@4.0.17", "", {}, "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ=="], + + "zustand": ["zustand@5.0.7", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg=="], + "cli-truncate/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], diff --git a/dist/index.css b/dist/index.css index 7ab28a7..72882be 100644 --- a/dist/index.css +++ b/dist/index.css @@ -1 +1 @@ -.st-rnd-details{border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px}.st-rnd-details[open] summary{background-color:var(--color-neutral-5)}.st-rnd-details>summary{cursor:pointer;padding:4px 8px;font-size:16px;font-weight:600;line-height:20px}.st-rnd-details>summary:hover{background-color:var(--color-neutral-4)}.st-rnd-details>div{padding:8px}.st-rnd-button{--btn-background-color:var(--color-primary-3);--btn-background-color-hover:var(--color-primary-4);--btn-background-color-active:var(--color-primary-5);--btn-border-color:var(--color-primary-6);--btn-color:var(--color-primary-12)}.st-rnd-button.button-neutral{--btn-background-color:var(--color-neutral-3);--btn-background-color-hover:var(--color-neutral-4);--btn-background-color-active:var(--color-neutral-5);--btn-border-color:var(--color-neutral-6);--btn-color:var(--color-neutral-12)}.st-rnd-button{display:inline-block;border:1px solid var(--btn-border-color);background-color:var(--btn-background-color);color:var(--btn-color);text-align:center;cursor:pointer;border-radius:4px;padding:4px 8px;font-weight:600}.st-rnd-button:hover{background-color:var(--btn-background-color-hover)}.st-rnd-button:active{background-color:var(--btn-background-color-active)}.st-rnd-button:disabled{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-background-color)40%);border-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-border-color)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-color)40%);cursor:not-allowed}.st-rnd-button:disabled:hover{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-background-color)40%);border-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-border-color)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-color)40%)}.st-rnd-button.button-icon{aspect-ratio:1;width:auto;height:auto;padding:4px;font-size:.8em}#st-rnd-upload-wordlist-container{display:flex;flex-direction:column}#st-rnd-wordlist-controls{display:flex;flex-direction:row;gap:8px}#st-rnd-wordlist-controls>select{flex-grow:1}#st-rnd-wordlist-controls>button{flex-shrink:0}.st-rnd-small-text{color:var(--color-neutral-11);font-size:.8em}.st-rnd-dialog{border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-1);color:var(--color-neutral-12);border-radius:4px;padding:8px}.st-rnd-dialog::backdrop{background-color:color-mix(in srgb,var(--color-neutral-2)80%,transparent 20%)}#st-rnd-wordlist-edit-dialog{position:relative;width:clamp(400px,100%,1200px)}#st-rnd-wordlist-edit-dialog>div{display:flex;flex-direction:column;gap:8px}#st-rnd-wordlist-edit-dialog #st-rnd-wordlist-edit-dialog-title{text-align:center;width:100%}#st-rnd-wordlist-edit-dialog #st-rnd-wordlist-edit-dialog-title>span{color:var(--color-primary-9)}#st-rnd-wordlist-edit-dialog #st-rnd-wordlist-actions{display:flex;justify-content:flex-end;gap:8px}.st-rnd-input{display:block;border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px;width:100%;padding:4px 8px}.st-rnd-textarea{display:block;border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px;width:100%;padding:4px 8px}.st-rnd-select{border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px;padding:4px 8px}.st-rnd-select:active{background-color:var(--color-neutral-12)}.st-rnd-select:disabled{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-3)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-12)40%);cursor:not-allowed}.st-rnd-select:disabled:active{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-3)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-12)40%)}:root{--color-neutral-1:#111113;--color-neutral-2:#18191b;--color-neutral-3:#212225;--color-neutral-4:#272a2d;--color-neutral-5:#2e3135;--color-neutral-6:#363a3f;--color-neutral-7:#43484e;--color-neutral-8:#5a6169;--color-neutral-9:#696e77;--color-neutral-10:#777b84;--color-neutral-11:#b0b4ba;--color-neutral-12:#edeef0;--color-primary-1:#13131e;--color-primary-2:#171625;--color-primary-3:#202248;--color-primary-4:#262a65;--color-primary-5:#303374;--color-primary-6:#3d3e82;--color-primary-7:#4a4a95;--color-primary-8:#5958b1;--color-primary-9:#5b5bd6;--color-primary-10:#6e6ade;--color-primary-11:#b1a9ff;--color-primary-12:#e0dffe} +.st-rnd-details{border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px}.st-rnd-details[open] summary{background-color:var(--color-neutral-5)}.st-rnd-details>summary{cursor:pointer;padding:4px 8px;font-size:16px;font-weight:600;line-height:20px}.st-rnd-details>summary:hover{background-color:var(--color-neutral-4)}.st-rnd-details>div{padding:8px}.st-rnd-button{--btn-background-color:var(--color-primary-3);--btn-background-color-hover:var(--color-primary-4);--btn-background-color-active:var(--color-primary-5);--btn-border-color:var(--color-primary-6);--btn-color:var(--color-primary-12)}.st-rnd-button.button-neutral{--btn-background-color:var(--color-neutral-3);--btn-background-color-hover:var(--color-neutral-4);--btn-background-color-active:var(--color-neutral-5);--btn-border-color:var(--color-neutral-6);--btn-color:var(--color-neutral-12)}.st-rnd-button{display:inline-block;border:1px solid var(--btn-border-color);background-color:var(--btn-background-color);color:var(--btn-color);text-align:center;cursor:pointer;border-radius:4px;padding:4px 8px;font-weight:600}.st-rnd-button:hover{background-color:var(--btn-background-color-hover)}.st-rnd-button:active{background-color:var(--btn-background-color-active)}.st-rnd-button:disabled{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-background-color)40%);border-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-border-color)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-color)40%);cursor:not-allowed}.st-rnd-button:disabled:hover{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-background-color)40%);border-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-border-color)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--btn-color)40%)}.st-rnd-button.button-icon{aspect-ratio:1;width:auto;height:auto;padding:4px;font-size:.8em}#st-rnd-upload-wordlist-container{display:flex;flex-direction:column}#st-rnd-wordlist-controls{display:flex;flex-direction:row;gap:8px}#st-rnd-wordlist-controls>select{flex-grow:1}#st-rnd-wordlist-controls>button{flex-shrink:0}.st-rnd-small-text{color:var(--color-neutral-11);font-size:.8em}.st-rnd-select{border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px;padding:4px 8px}.st-rnd-select:active{background-color:var(--color-neutral-12)}.st-rnd-select:disabled{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-3)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-12)40%);cursor:not-allowed}.st-rnd-select:disabled:active{background-color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-3)40%);color:color-mix(in srgb,var(--color-neutral-1)60%,var(--color-neutral-12)40%)}.st-rnd-dialog{border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-1);color:var(--color-neutral-12);border-radius:4px;padding:8px}.st-rnd-dialog::backdrop{background-color:color-mix(in srgb,var(--color-neutral-2)80%,transparent 20%)}#st-rnd-wordlist-edit-dialog{position:relative;width:clamp(400px,100%,1200px)}#st-rnd-wordlist-edit-dialog>div{display:flex;flex-direction:column;gap:8px}#st-rnd-wordlist-edit-dialog #st-rnd-wordlist-edit-dialog-title{text-align:center;width:100%}#st-rnd-wordlist-edit-dialog #st-rnd-wordlist-edit-dialog-title>span{color:var(--color-primary-9)}#st-rnd-wordlist-edit-dialog #st-rnd-wordlist-actions{display:flex;justify-content:flex-end;gap:8px}.st-rnd-input{display:block;border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px;width:100%;padding:4px 8px}.st-rnd-textarea{display:block;border:1px solid var(--color-neutral-6);background-color:var(--color-neutral-3);color:var(--color-neutral-12);border-radius:4px;width:100%;padding:4px 8px}:root{--color-neutral-1:#111113;--color-neutral-2:#18191b;--color-neutral-3:#212225;--color-neutral-4:#272a2d;--color-neutral-5:#2e3135;--color-neutral-6:#363a3f;--color-neutral-7:#43484e;--color-neutral-8:#5a6169;--color-neutral-9:#696e77;--color-neutral-10:#777b84;--color-neutral-11:#b0b4ba;--color-neutral-12:#edeef0;--color-primary-1:#13131e;--color-primary-2:#171625;--color-primary-3:#202248;--color-primary-4:#262a65;--color-primary-5:#303374;--color-primary-6:#3d3e82;--color-primary-7:#4a4a95;--color-primary-8:#5958b1;--color-primary-9:#5b5bd6;--color-primary-10:#6e6ade;--color-primary-11:#b1a9ff;--color-primary-12:#e0dffe} diff --git a/dist/index.js b/dist/index.js index a79a349..40d2cc2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -228,8 +228,8 @@ You might need to use a local HTTP server (instead of file://): https://react.de <%s {...props} /> React keys must be passed directly to JSX without using spread: let props = %s; - <%s key={someKey} {...props} />`,s,m1,O4,m1),_1[m1+s]=!0)}if(m1=null,q1!==void 0&&(y(q1),m1=""+q1),t(p)&&(y(p.key),m1=""+p.key),"key"in p){q1={};for(var t0 in p)t0!=="key"&&(q1[t0]=p[t0])}else q1=p;return m1&&a(q1,typeof L==="function"?L.displayName||L.name||"Unknown":L),W1(L,m1,t1,l1,d(),q1,V4,T1)}function P(L){typeof L==="object"&&L!==null&&L.$$typeof===S&&L._store&&(L._store.validated=1)}var S=Symbol.for("react.transitional.element"),F1=Symbol.for("react.portal"),Q0=Symbol.for("react.fragment"),b1=Symbol.for("react.strict_mode"),k1=Symbol.for("react.profiler");Symbol.for("react.provider");var G0=Symbol.for("react.consumer"),i0=Symbol.for("react.context"),E0=Symbol.for("react.forward_ref"),x4=Symbol.for("react.suspense"),r4=Symbol.for("react.suspense_list"),n1=Symbol.for("react.memo"),L1=Symbol.for("react.lazy"),y0=Symbol.for("react.activity"),k=Symbol.for("react.client.reference"),i1=m6.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,A1=Object.prototype.hasOwnProperty,z1=Array.isArray,X0=console.createTask?console.createTask:function(){return null};m6={react_stack_bottom_frame:function(L){return L()}};var f0,v={},l=m6.react_stack_bottom_frame.bind(m6,r)(),o=X0(b(r)),_1={};Eq.Fragment=Q0,Eq.jsxDEV=function(L,p,q1,s,l1,t1){var V4=1e4>i1.recentlyCreatedOwnerStacks++;return E(L,p,q1,s,l1,t1,V4?Error("react-stack-top-frame"):l,V4?X0(b(L)):o)}})()});var pU=K1(F2(),1),cU=K1(_U(),1);var A3=K1(u1(),1),xq=({title:j,className:N,children:y})=>{return A3.jsxDEV("details",{className:`st-rnd-details${N?` ${N}`:""}`,children:[A3.jsxDEV("summary",{children:j},void 0,!1,void 0,this),A3.jsxDEV("div",{children:y},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},IU=xq;var TU=K1(u1(),1),Vq=({children:j,className:N,neutral:y,icon:b,...d})=>{return TU.jsxDEV("button",{className:`st-rnd-button${y===!0?" button-neutral":""}${b===!0?" button-icon":""}${N?` ${N}`:""}`,...d,children:j},void 0,!1,void 0,this)},u6=Vq;var o8=K1(u1(),1),Cq=({id:j,label:N,...y})=>{return o8.jsxDEV(o8.Fragment,{children:[o8.jsxDEV("input",{type:"file",id:j,...y},void 0,!1,void 0,this),o8.jsxDEV("label",{htmlFor:j,className:"st-rnd-button button-neutral",children:N||"Upload"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},WU=Cq;var FU=(j)=>{let N,y=new Set,b=(W1,E)=>{let P=typeof W1==="function"?W1(N):W1;if(!Object.is(P,N)){let S=N;N=(E!=null?E:typeof P!=="object"||P===null)?P:Object.assign({},N,P),y.forEach((F1)=>F1(N,S))}},d=()=>N,a={setState:b,getState:d,getInitialState:()=>C,subscribe:(W1)=>{return y.add(W1),()=>y.delete(W1)}},C=N=j(b,d,a);return a},wU=(j)=>j?FU(j):FU;var NH=K1(F2(),1);var Dq=(j)=>j;function vq(j,N=Dq){let y=NH.default.useSyncExternalStore(j.subscribe,NH.default.useCallback(()=>N(j.getState()),[j,N]),NH.default.useCallback(()=>N(j.getInitialState()),[j,N]));return NH.default.useDebugValue(y),y}var jU=(j)=>{let N=wU(j),y=(b)=>vq(N,b);return Object.assign(y,N),y},LU=(j)=>j?jU(j):jU;var Sq=(j)=>(N,y,b)=>{let d=b.subscribe;return b.subscribe=(t,a,C)=>{let W1=t;if(a){let E=(C==null?void 0:C.equalityFn)||Object.is,P=t(b.getState());if(W1=(S)=>{let F1=t(S);if(!E(P,F1)){let Q0=P;a(P=F1,Q0)}},C==null?void 0:C.fireImmediately)a(P,P)}return d(W1)},j(N,y,b)},AU=Sq;var r8="st-randomness-helpers";var NU=(j)=>{let y=SillyTavern.getContext().extensionSettings;if(y[r8]===void 0)y[r8]={wordLists:{}};let b=y[r8];return{wordLists:b.wordLists,addWordList:(d,r)=>j({wordLists:{...b.wordLists,[d]:r}}),removeWordList:(d)=>{let r={...b.wordLists};delete r[d],j({wordLists:r})}}};var o4=LU()(AU(NU));o4.subscribe((j)=>j.wordLists,(j)=>{let N=SillyTavern.getContext(),y=N.extensionSettings;y[r8].wordLists=j,N.saveSettingsDebounced()});var n8=K1(F2(),1);var DU=K1(F2(),1);var EU=(j)=>(Symbol.iterator in j),xU=(j)=>("entries"in j),VU=(j,N)=>{let y=j instanceof Map?j:new Map(j.entries()),b=N instanceof Map?N:new Map(N.entries());if(y.size!==b.size)return!1;for(let[d,r]of y)if(!Object.is(r,b.get(d)))return!1;return!0},gq=(j,N)=>{let y=j[Symbol.iterator](),b=N[Symbol.iterator](),d=y.next(),r=b.next();while(!d.done&&!r.done){if(!Object.is(d.value,r.value))return!1;d=y.next(),r=b.next()}return!!d.done&&!!r.done};function CU(j,N){if(Object.is(j,N))return!0;if(typeof j!=="object"||j===null||typeof N!=="object"||N===null)return!1;if(Object.getPrototypeOf(j)!==Object.getPrototypeOf(N))return!1;if(EU(j)&&EU(N)){if(xU(j)&&xU(N))return VU(j,N);return gq(j,N)}return VU({entries:()=>Object.entries(j)},{entries:()=>Object.entries(N)})}function vU(j){let N=DU.default.useRef(void 0);return(y)=>{let b=j(y);return CU(N.current,b)?N.current:N.current=b}}var EH=K1(F2(),1);var gU=K1(u1(),1),bq=({children:j,className:N,...y})=>{return gU.jsxDEV("dialog",{className:`st-rnd-dialog${N?` ${N}`:""}`,...y,children:j},void 0,!1,void 0,this)},SU=bq;var kU=K1(u1(),1),kq=({className:j,...N})=>{return kU.jsxDEV("input",{className:`st-rnd-input${j?` ${j}`:""}`,...N},void 0,!1,void 0,this)},bU=kq;var fU=K1(u1(),1),yq=({className:j,...N})=>{return fU.jsxDEV("textarea",{className:`st-rnd-textarea${j?` ${j}`:""}`,...N},void 0,!1,void 0,this)},yU=yq;var $0=K1(u1(),1),fq=({currentWordList:j})=>{let[N,y]=EH.useState(),[b,d]=EH.useState(void 0),r=EH.useRef(null);return $0.jsxDEV($0.Fragment,{children:[$0.jsxDEV(u6,{type:"button",icon:!0,disabled:j==="",onClick:()=>{if(r.current===null||j==="")return;let t=o4.getState().wordLists[j];if(t===void 0)return;d(j),y(t.join(` -`)),r.current.showModal()},children:$0.jsxDEV("i",{className:"fa fa-fw fa-pencil"},void 0,!1,void 0,this)},void 0,!1,void 0,this),$0.jsxDEV(SU,{id:"st-rnd-wordlist-edit-dialog",ref:r,children:$0.jsxDEV("div",{children:[$0.jsxDEV("h2",{id:"st-rnd-wordlist-edit-dialog-title",children:["Edit Word List: ",$0.jsxDEV("span",{children:j},void 0,!1,void 0,this)]},void 0,!0,void 0,this),$0.jsxDEV("label",{htmlFor:"st-rnd-wordlist-name",children:"Name"},void 0,!1,void 0,this),$0.jsxDEV("p",{className:"st-rnd-small-text",children:"Name of the word list, used in placeholder and macros."},void 0,!1,void 0,this),$0.jsxDEV(bU,{id:"st-rnd-wordlist-name",type:"text",value:b,onChange:(t)=>d(t.target.value)},void 0,!1,void 0,this),$0.jsxDEV("label",{htmlFor:"st-rnd-wordlist-words",children:"Words"},void 0,!1,void 0,this),$0.jsxDEV("p",{className:"st-rnd-small-text",children:"Words separated by newlines, words can be more than one word!"},void 0,!1,void 0,this),$0.jsxDEV(yU,{id:"st-rnd-wordlist-words",value:N,onChange:(t)=>y(t.target.value),rows:10},void 0,!1,void 0,this),$0.jsxDEV("div",{id:"st-rnd-wordlist-actions",children:[$0.jsxDEV(u6,{type:"button",neutral:!0,onClick:()=>{if(r.current===null)return;r.current.close(),y(void 0),d(void 0)},children:"Close"},void 0,!1,void 0,this),$0.jsxDEV(u6,{type:"button",onClick:()=>{if(j===""||b===void 0||N===void 0||r.current===null)return;if(b===""){alert("Name cannot be empty");return}let t=o4.getState().addWordList;if(j!==b){let a=o4.getState().removeWordList;a(j)}t(b,N.split(` -`)),r.current.close(),y(void 0),d(void 0)},children:"Save"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},mU=fq;var hU=K1(u1(),1),mq=({children:j,className:N,...y})=>{return hU.jsxDEV("select",{className:`st-rnd-select${N?` ${N}`:""}`,...y,children:j},void 0,!1,void 0,this)},uU=mq;var P0=K1(u1(),1),uq=()=>{let j=o4(vU((t)=>Object.keys(t.wordLists))),N=o4((t)=>t.addWordList),y=o4((t)=>t.removeWordList),[b,d]=n8.useState(""),r=n8.useCallback((t)=>{let a=t.target.files?.[0];if(a!==void 0&&a.type==="text/plain"){let C=new FileReader;C.onload=(W1)=>{let E=a.name.replace(".txt","");if(j.includes(E)){alert(`A word list with the name ${E} already exists`);return}let P=W1.target?.result;if(typeof P==="string"){let S=P.split(` -`);N(E,S)}},C.readAsText(a)}},[N,j]);return n8.useEffect(()=>{if(!j.includes(b))d("")},[j,b]),P0.jsxDEV(P0.Fragment,{children:[P0.jsxDEV("strong",{children:"Word Lists"},void 0,!1,void 0,this),P0.jsxDEV("div",{id:"st-rnd-wordlist-controls",children:[P0.jsxDEV(uU,{id:"st-rnd-wordlist-select",disabled:j.length===0,value:b,onChange:(t)=>{let a=t.target.value;if(a!=="")d(a)},children:[P0.jsxDEV("option",{value:"",disabled:!0,children:"Select a word list"},void 0,!1,void 0,this),j.map((t)=>P0.jsxDEV("option",{children:t},t,!1,void 0,this))]},void 0,!0,void 0,this),P0.jsxDEV(mU,{currentWordList:b},void 0,!1,void 0,this),P0.jsxDEV(u6,{type:"button",icon:!0,disabled:b==="",onClick:()=>{if(b!==""){let t=o4.getState().wordLists[b];if(t===void 0)return;let a=new Blob([t.join(` + <%s key={someKey} {...props} />`,s,m1,O4,m1),_1[m1+s]=!0)}if(m1=null,q1!==void 0&&(y(q1),m1=""+q1),t(p)&&(y(p.key),m1=""+p.key),"key"in p){q1={};for(var t0 in p)t0!=="key"&&(q1[t0]=p[t0])}else q1=p;return m1&&a(q1,typeof L==="function"?L.displayName||L.name||"Unknown":L),W1(L,m1,t1,l1,d(),q1,V4,T1)}function P(L){typeof L==="object"&&L!==null&&L.$$typeof===S&&L._store&&(L._store.validated=1)}var S=Symbol.for("react.transitional.element"),F1=Symbol.for("react.portal"),Q0=Symbol.for("react.fragment"),b1=Symbol.for("react.strict_mode"),k1=Symbol.for("react.profiler");Symbol.for("react.provider");var G0=Symbol.for("react.consumer"),i0=Symbol.for("react.context"),E0=Symbol.for("react.forward_ref"),x4=Symbol.for("react.suspense"),r4=Symbol.for("react.suspense_list"),n1=Symbol.for("react.memo"),L1=Symbol.for("react.lazy"),y0=Symbol.for("react.activity"),k=Symbol.for("react.client.reference"),i1=m6.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,A1=Object.prototype.hasOwnProperty,z1=Array.isArray,X0=console.createTask?console.createTask:function(){return null};m6={react_stack_bottom_frame:function(L){return L()}};var f0,v={},l=m6.react_stack_bottom_frame.bind(m6,r)(),o=X0(b(r)),_1={};Eq.Fragment=Q0,Eq.jsxDEV=function(L,p,q1,s,l1,t1){var V4=1e4>i1.recentlyCreatedOwnerStacks++;return E(L,p,q1,s,l1,t1,V4?Error("react-stack-top-frame"):l,V4?X0(b(L)):o)}})()});var pU=K1(F2(),1),cU=K1(_U(),1);var A3=K1(u1(),1),xq=({title:j,className:N,children:y})=>{return A3.jsxDEV("details",{className:`st-rnd-details${N?` ${N}`:""}`,children:[A3.jsxDEV("summary",{children:j},void 0,!1,void 0,this),A3.jsxDEV("div",{children:y},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},IU=xq;var TU=K1(u1(),1),Vq=({children:j,className:N,neutral:y,icon:b,...d})=>{return TU.jsxDEV("button",{className:`st-rnd-button${y===!0?" button-neutral":""}${b===!0?" button-icon":""}${N?` ${N}`:""}`,...d,children:j},void 0,!1,void 0,this)},u6=Vq;var o8=K1(u1(),1),Cq=({id:j,label:N,...y})=>{return o8.jsxDEV(o8.Fragment,{children:[o8.jsxDEV("input",{type:"file",id:j,...y},void 0,!1,void 0,this),o8.jsxDEV("label",{htmlFor:j,className:"st-rnd-button button-neutral",children:N||"Upload"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},WU=Cq;var n8=K1(F2(),1);var AU=K1(F2(),1);var FU=(j)=>(Symbol.iterator in j),wU=(j)=>("entries"in j),jU=(j,N)=>{let y=j instanceof Map?j:new Map(j.entries()),b=N instanceof Map?N:new Map(N.entries());if(y.size!==b.size)return!1;for(let[d,r]of y)if(!Object.is(r,b.get(d)))return!1;return!0},Dq=(j,N)=>{let y=j[Symbol.iterator](),b=N[Symbol.iterator](),d=y.next(),r=b.next();while(!d.done&&!r.done){if(!Object.is(d.value,r.value))return!1;d=y.next(),r=b.next()}return!!d.done&&!!r.done};function LU(j,N){if(Object.is(j,N))return!0;if(typeof j!=="object"||j===null||typeof N!=="object"||N===null)return!1;if(Object.getPrototypeOf(j)!==Object.getPrototypeOf(N))return!1;if(FU(j)&&FU(N)){if(wU(j)&&wU(N))return jU(j,N);return Dq(j,N)}return jU({entries:()=>Object.entries(j)},{entries:()=>Object.entries(N)})}function NU(j){let N=AU.default.useRef(void 0);return(y)=>{let b=j(y);return LU(N.current,b)?N.current:N.current=b}}var xU=K1(u1(),1),vq=({children:j,className:N,...y})=>{return xU.jsxDEV("select",{className:`st-rnd-select${N?` ${N}`:""}`,...y,children:j},void 0,!1,void 0,this)},EU=vq;var EH=K1(F2(),1);var CU=K1(u1(),1),Sq=({children:j,className:N,...y})=>{return CU.jsxDEV("dialog",{className:`st-rnd-dialog${N?` ${N}`:""}`,...y,children:j},void 0,!1,void 0,this)},VU=Sq;var DU=(j)=>{let N,y=new Set,b=(W1,E)=>{let P=typeof W1==="function"?W1(N):W1;if(!Object.is(P,N)){let S=N;N=(E!=null?E:typeof P!=="object"||P===null)?P:Object.assign({},N,P),y.forEach((F1)=>F1(N,S))}},d=()=>N,a={setState:b,getState:d,getInitialState:()=>C,subscribe:(W1)=>{return y.add(W1),()=>y.delete(W1)}},C=N=j(b,d,a);return a},vU=(j)=>j?DU(j):DU;var NH=K1(F2(),1);var gq=(j)=>j;function bq(j,N=gq){let y=NH.default.useSyncExternalStore(j.subscribe,NH.default.useCallback(()=>N(j.getState()),[j,N]),NH.default.useCallback(()=>N(j.getInitialState()),[j,N]));return NH.default.useDebugValue(y),y}var SU=(j)=>{let N=vU(j),y=(b)=>bq(N,b);return Object.assign(y,N),y},gU=(j)=>j?SU(j):SU;var kq=(j)=>(N,y,b)=>{let d=b.subscribe;return b.subscribe=(t,a,C)=>{let W1=t;if(a){let E=(C==null?void 0:C.equalityFn)||Object.is,P=t(b.getState());if(W1=(S)=>{let F1=t(S);if(!E(P,F1)){let Q0=P;a(P=F1,Q0)}},C==null?void 0:C.fireImmediately)a(P,P)}return d(W1)},j(N,y,b)},bU=kq;var r8="st-randomness-helpers";var kU=(j)=>{let y=SillyTavern.getContext().extensionSettings;if(y[r8]===void 0)y[r8]={wordLists:{}};let b=y[r8];return{wordLists:b.wordLists,addWordList:(d,r)=>j({wordLists:{...b.wordLists,[d]:r}}),removeWordList:(d)=>{let r={...b.wordLists};delete r[d],j({wordLists:r})}}};var o4=gU()(bU(kU));o4.subscribe((j)=>j.wordLists,(j)=>{let N=SillyTavern.getContext(),y=N.extensionSettings;y[r8].wordLists=j,N.saveSettingsDebounced()});var fU=K1(u1(),1),yq=({className:j,...N})=>{return fU.jsxDEV("input",{className:`st-rnd-input${j?` ${j}`:""}`,...N},void 0,!1,void 0,this)},yU=yq;var uU=K1(u1(),1),fq=({className:j,...N})=>{return uU.jsxDEV("textarea",{className:`st-rnd-textarea${j?` ${j}`:""}`,...N},void 0,!1,void 0,this)},mU=fq;var $0=K1(u1(),1),mq=({currentWordList:j})=>{let[N,y]=EH.useState(),[b,d]=EH.useState(void 0),r=EH.useRef(null);return $0.jsxDEV($0.Fragment,{children:[$0.jsxDEV(u6,{type:"button",icon:!0,disabled:j==="",onClick:()=>{if(r.current===null||j==="")return;let t=o4.getState().wordLists[j];if(t===void 0)return;d(j),y(t.join(` +`)),r.current.showModal()},children:$0.jsxDEV("i",{className:"fa fa-fw fa-pencil"},void 0,!1,void 0,this)},void 0,!1,void 0,this),$0.jsxDEV(VU,{id:"st-rnd-wordlist-edit-dialog",ref:r,children:$0.jsxDEV("div",{children:[$0.jsxDEV("h2",{id:"st-rnd-wordlist-edit-dialog-title",children:["Edit Word List: ",$0.jsxDEV("span",{children:j},void 0,!1,void 0,this)]},void 0,!0,void 0,this),$0.jsxDEV("label",{htmlFor:"st-rnd-wordlist-name",children:"Name"},void 0,!1,void 0,this),$0.jsxDEV("p",{className:"st-rnd-small-text",children:"Name of the word list, used in placeholder and macros."},void 0,!1,void 0,this),$0.jsxDEV(yU,{id:"st-rnd-wordlist-name",type:"text",value:b,onChange:(t)=>d(t.target.value)},void 0,!1,void 0,this),$0.jsxDEV("label",{htmlFor:"st-rnd-wordlist-words",children:"Words"},void 0,!1,void 0,this),$0.jsxDEV("p",{className:"st-rnd-small-text",children:"Words separated by newlines, words can be more than one word!"},void 0,!1,void 0,this),$0.jsxDEV(mU,{id:"st-rnd-wordlist-words",value:N,onChange:(t)=>y(t.target.value),rows:10},void 0,!1,void 0,this),$0.jsxDEV("div",{id:"st-rnd-wordlist-actions",children:[$0.jsxDEV(u6,{type:"button",neutral:!0,onClick:()=>{if(r.current===null)return;r.current.close(),y(void 0),d(void 0)},children:"Close"},void 0,!1,void 0,this),$0.jsxDEV(u6,{type:"button",onClick:()=>{if(j===""||b===void 0||N===void 0||r.current===null)return;if(b===""){alert("Name cannot be empty");return}let t=o4.getState().addWordList;if(j!==b){let a=o4.getState().removeWordList;a(j)}t(b,N.split(` +`)),r.current.close(),y(void 0),d(void 0)},children:"Save"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},hU=mq;var P0=K1(u1(),1),uq=()=>{let j=o4(NU((t)=>Object.keys(t.wordLists))),N=o4((t)=>t.addWordList),y=o4((t)=>t.removeWordList),[b,d]=n8.useState(""),r=n8.useCallback((t)=>{let a=t.target.files?.[0];if(a!==void 0&&a.type==="text/plain"){let C=new FileReader;C.onload=(W1)=>{let E=a.name.replace(".txt","");if(j.includes(E)){alert(`A word list with the name ${E} already exists`);return}let P=W1.target?.result;if(typeof P==="string"){let S=P.split(` +`);N(E,S)}},C.readAsText(a)}},[N,j]);return n8.useEffect(()=>{if(!j.includes(b))d("")},[j,b]),P0.jsxDEV(P0.Fragment,{children:[P0.jsxDEV("strong",{children:"Word Lists"},void 0,!1,void 0,this),P0.jsxDEV("div",{id:"st-rnd-wordlist-controls",children:[P0.jsxDEV(EU,{id:"st-rnd-wordlist-select",disabled:j.length===0,value:b,onChange:(t)=>{let a=t.target.value;if(a!=="")d(a)},children:[P0.jsxDEV("option",{value:"",disabled:!0,children:"Select a word list"},void 0,!1,void 0,this),j.map((t)=>P0.jsxDEV("option",{children:t},t,!1,void 0,this))]},void 0,!0,void 0,this),P0.jsxDEV(hU,{currentWordList:b},void 0,!1,void 0,this),P0.jsxDEV(u6,{type:"button",icon:!0,disabled:b==="",onClick:()=>{if(b!==""){let t=o4.getState().wordLists[b];if(t===void 0)return;let a=new Blob([t.join(` `)],{type:"text/plain"}),C=URL.createObjectURL(a),W1=document.createElement("a");W1.href=C,W1.download=`${b}.txt`,W1.click(),URL.revokeObjectURL(C)}},children:P0.jsxDEV("i",{className:"fa fa-fw fa-download"},void 0,!1,void 0,this)},void 0,!1,void 0,this),P0.jsxDEV(u6,{type:"button",icon:!0,disabled:b==="",onClick:()=>{if(b!=="")y(b),d("")},children:P0.jsxDEV("i",{className:"fa fa-fw fa-trash"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),P0.jsxDEV("p",{className:"st-rnd-small-text",children:"Wordlists are txt files with one (or more) word per line, are used in placeholders and macros."},void 0,!1,void 0,this),P0.jsxDEV("div",{id:"st-rnd-upload-wordlist-container",children:P0.jsxDEV(WU,{id:"upload-wordlist",onChange:r,accept:".txt"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},dU=uq;var i$=K1(u1(),1),hq=()=>{return i$.jsxDEV(IU,{title:"Randomness Helpers",children:i$.jsxDEV(dU,{},void 0,!1,void 0,this)},void 0,!1,void 0,this)},lU=hq;var t$=K1(u1(),1),dq=()=>{let j=document.getElementById("extensions_settings");if(j===null)throw new Error("[st-randomness-helpers] root container not found");let N=document.createElement("div");j.appendChild(N),cU.default.createRoot(N).render(t$.jsxDEV(pU.StrictMode,{children:t$.jsxDEV(lU,{},void 0,!1,void 0,this)},void 0,!1,void 0,this))},sU=dq;sU(); diff --git a/src/Settings.tsx b/src/Settings.tsx new file mode 100644 index 0000000..d19aed6 --- /dev/null +++ b/src/Settings.tsx @@ -0,0 +1,12 @@ +import Details from './components/ui/Details' +import WordLists from './components/WordLists' + +const Settings = () => { + return ( + + + + ) +} + +export default Settings diff --git a/src/components/WordLists.tsx b/src/components/WordLists.tsx new file mode 100644 index 0000000..e4f917d --- /dev/null +++ b/src/components/WordLists.tsx @@ -0,0 +1,131 @@ +import Button from '@/components/ui/Button' +import UploadButton from '@/components/ui/UploadButton' +import '@/components/wordLists.css' +import { + type ChangeEventHandler, + useCallback, + useEffect, + useState +} from 'react' +import { useShallow } from 'zustand/react/shallow' +import Select from '@/components/ui/Select' +import EditWordList from '@/components/wordList/EditWordList' +import { useStore } from '@/store' + +const WordLists = () => { + const wordLists = useStore( + useShallow((state) => Object.keys(state.wordLists)) + ) + const addWordList = useStore((state) => state.addWordList) + const removeWordList = useStore((state) => state.removeWordList) + const [selectedWordList, setSelectedWordList] = useState('') + + const handleUpload: ChangeEventHandler = useCallback( + (event) => { + const file = event.target.files?.[0] + if (file !== undefined && file.type === 'text/plain') { + const reader = new FileReader() + reader.onload = (e) => { + const fileName = file.name.replace('.txt', '') + if (wordLists.includes(fileName)) { + alert(`A word list with the name ${fileName} already exists`) + return + } + const text = e.target?.result + if (typeof text === 'string') { + const words = text.split('\n') + addWordList(fileName, words) + } + } + reader.readAsText(file) + } + }, + [addWordList, wordLists] + ) + + useEffect(() => { + const selectedExist = wordLists.includes(selectedWordList) + if (!selectedExist) { + setSelectedWordList('') + } + }, [wordLists, selectedWordList]) + + return ( + <> + Word Lists + + { + const value = event.target.value + if (value !== '') { + setSelectedWordList(value) + } + }} + > + + Select a word list + + {wordLists.map((name) => ( + {name} + ))} + + + + { + if (selectedWordList !== '') { + const wordList = useStore.getState().wordLists[selectedWordList] + if (wordList === undefined) { + return + } + + const blob = new Blob([wordList.join('\n')], { + type: 'text/plain' + }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = `${selectedWordList}.txt` + link.click() + URL.revokeObjectURL(url) + } + }} + > + + + { + if (selectedWordList !== '') { + removeWordList(selectedWordList) + setSelectedWordList('') + } + }} + > + + + + + Wordlists are txt files with one (or more) word per line, are used in + placeholders and macros. + + + + + > + ) +} + +export default WordLists diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 0000000..d229951 --- /dev/null +++ b/src/components/ui/Button.tsx @@ -0,0 +1,32 @@ +import type { ButtonHTMLAttributes, DetailedHTMLProps, FC } from 'react' +import type { MergeOmitting } from '@/types/helpers' +import '@/components/ui/button.css' + +export type ButtonProps = MergeOmitting< + DetailedHTMLProps, HTMLButtonElement>, + { + neutral?: boolean + icon?: boolean + } +> + +const Button: FC = ({ + children, + className, + neutral, + icon, + ...buttonProps +}) => { + return ( + + {children} + + ) +} + +export default Button diff --git a/src/components/ui/Details.tsx b/src/components/ui/Details.tsx new file mode 100644 index 0000000..287d1c2 --- /dev/null +++ b/src/components/ui/Details.tsx @@ -0,0 +1,24 @@ +import '@/components/ui/details.css' +import type { FC, ReactNode } from 'react' +import type { MergeOmitting } from '@/types/helpers' + +export type DetailsProps = MergeOmitting< + React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLDetailsElement + >, + { + title: string + children: ReactNode + } +> +const Details: FC = ({ title, className, children }) => { + return ( + + {title} + {children} + + ) +} + +export default Details diff --git a/src/components/ui/Dialog.tsx b/src/components/ui/Dialog.tsx new file mode 100644 index 0000000..af02245 --- /dev/null +++ b/src/components/ui/Dialog.tsx @@ -0,0 +1,28 @@ +import '@/components/ui/dialog.css' +import type { + DetailedHTMLProps, + DialogHTMLAttributes, + FC, + ReactNode +} from 'react' +import type { MergeOmitting } from '@/types/helpers' + +export type DialogProps = MergeOmitting< + DetailedHTMLProps, HTMLDialogElement>, + { + children: ReactNode + } +> + +const Dialog: FC = ({ children, className, ...dialogProps }) => { + return ( + + {children} + + ) +} + +export default Dialog diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx new file mode 100644 index 0000000..c033185 --- /dev/null +++ b/src/components/ui/Input.tsx @@ -0,0 +1,19 @@ +import type { DetailedHTMLProps, FC, InputHTMLAttributes } from 'react' +import type { MergeOmitting } from '@/types/helpers' +import '@/components/ui/input.css' + +export type InputProps = MergeOmitting< + DetailedHTMLProps, HTMLInputElement>, + { fullWidth?: boolean } +> + +const Input: FC = ({ className, ...inputProps }) => { + return ( + + ) +} + +export default Input diff --git a/src/components/ui/Select.tsx b/src/components/ui/Select.tsx new file mode 100644 index 0000000..5e82acf --- /dev/null +++ b/src/components/ui/Select.tsx @@ -0,0 +1,28 @@ +import type { + DetailedHTMLProps, + FC, + ReactNode, + SelectHTMLAttributes +} from 'react' +import type { MergeOmitting } from '@/types/helpers' +import '@/components/ui/select.css' + +type SelectProps = MergeOmitting< + DetailedHTMLProps, HTMLSelectElement>, + { + children: ReactNode + } +> + +const Select: FC = ({ children, className, ...selectProps }) => { + return ( + + {children} + + ) +} + +export default Select diff --git a/src/components/ui/Textarea.tsx b/src/components/ui/Textarea.tsx new file mode 100644 index 0000000..4155c27 --- /dev/null +++ b/src/components/ui/Textarea.tsx @@ -0,0 +1,24 @@ +import type { DetailedHTMLProps, FC, TextareaHTMLAttributes } from 'react' +import type { MergeOmitting } from '@/types/helpers' +import '@/components/ui/textarea.css' + +export type TextareaProps = MergeOmitting< + DetailedHTMLProps< + TextareaHTMLAttributes, + HTMLTextAreaElement + >, + { + className?: string + } +> + +const Textarea: FC = ({ className, ...textareaProps }) => { + return ( + + ) +} + +export default Textarea diff --git a/src/components/ui/UploadButton.tsx b/src/components/ui/UploadButton.tsx new file mode 100644 index 0000000..99e44f6 --- /dev/null +++ b/src/components/ui/UploadButton.tsx @@ -0,0 +1,23 @@ +import '@/components/ui/button.css' +import type { DetailedHTMLProps, FC, InputHTMLAttributes } from 'react' +import type { MergeOmitting } from '@/types/helpers' + +export type UploadButtonProps = MergeOmitting< + DetailedHTMLProps, HTMLInputElement>, + { + id: string + label?: string + } +> +const UploadButton: FC = ({ id, label, ...inputProps }) => { + return ( + <> + + + {label || 'Upload'} + + > + ) +} + +export default UploadButton diff --git a/src/components/ui/button.css b/src/components/ui/button.css new file mode 100644 index 0000000..5fc9e5a --- /dev/null +++ b/src/components/ui/button.css @@ -0,0 +1,81 @@ +.st-rnd-button { + --btn-background-color: var(--color-primary-3); + --btn-background-color-hover: var(--color-primary-4); + --btn-background-color-active: var(--color-primary-5); + --btn-border-color: var(--color-primary-6); + --btn-color: var(--color-primary-12); + + &.button-neutral { + --btn-background-color: var(--color-neutral-3); + --btn-background-color-hover: var(--color-neutral-4); + --btn-background-color-active: var(--color-neutral-5); + --btn-border-color: var(--color-neutral-6); + --btn-color: var(--color-neutral-12); + } +} + +.st-rnd-button { + display: inline-block; + + padding: 4px 8px; + + border-radius: 4px; + border: 1px solid var(--btn-border-color); + background-color: var(--btn-background-color); + color: var(--btn-color); + + font-weight: 600; + text-align: center; + + cursor: pointer; + + &:hover { + background-color: var(--btn-background-color-hover); + } + + &:active { + background-color: var(--btn-background-color-active); + } + + &:disabled { + background-color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--btn-background-color) 40% + ); + border-color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--btn-border-color) 40% + ); + color: color-mix(in srgb, var(--color-neutral-1) 60%, var(--btn-color) 40%); + + cursor: not-allowed; + &:hover { + background-color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--btn-background-color) 40% + ); + border-color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--btn-border-color) 40% + ); + color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--btn-color) 40% + ); + } + } + + &.button-icon { + padding: 4px; + height: auto; + width: auto ; + aspect-ratio: 1; + + font-size: 0.8em; + } +} diff --git a/src/components/ui/details.css b/src/components/ui/details.css new file mode 100644 index 0000000..fd2ef6b --- /dev/null +++ b/src/components/ui/details.css @@ -0,0 +1,29 @@ +.st-rnd-details { + border-radius: 4px; + border: 1px solid var(--color-neutral-6); + background-color: var(--color-neutral-3); + color: var(--color-neutral-12); + + &[open] { + summary { + background-color: var(--color-neutral-5); + } + } + + > summary { + padding: 4px 8px; + cursor: pointer; + + font-weight: 600; + font-size: 16px; + line-height: 20px; + + &:hover { + background-color: var(--color-neutral-4); + } + } + + > div { + padding: 8px; + } +} diff --git a/src/components/ui/dialog.css b/src/components/ui/dialog.css new file mode 100644 index 0000000..ec3881d --- /dev/null +++ b/src/components/ui/dialog.css @@ -0,0 +1,15 @@ +.st-rnd-dialog { + border-radius: 4px; + border: 1px solid var(--color-neutral-6); + padding: 8px; + background-color: var(--color-neutral-1); + color: var(--color-neutral-12); + + &::backdrop { + background-color: color-mix( + in srgb, + var(--color-neutral-2) 80%, + transparent 20% + ); + } +} diff --git a/src/components/ui/input.css b/src/components/ui/input.css new file mode 100644 index 0000000..8ae5e06 --- /dev/null +++ b/src/components/ui/input.css @@ -0,0 +1,10 @@ +.st-rnd-input { + display: block; + + width: 100%; + border-radius: 4px; + border: 1px solid var(--color-neutral-6); + padding: 4px 8px; + background-color: var(--color-neutral-3); + color: var(--color-neutral-12); +} diff --git a/src/components/ui/select.css b/src/components/ui/select.css new file mode 100644 index 0000000..f285087 --- /dev/null +++ b/src/components/ui/select.css @@ -0,0 +1,38 @@ +.st-rnd-select { + border-radius: 4px; + border: 1px solid var(--color-neutral-6); + padding: 4px 8px; + background-color: var(--color-neutral-3); + color: var(--color-neutral-12); + + &:active { + background-color: var(--color-neutral-12); + } + + &:disabled { + background-color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--color-neutral-3) 40% + ); + color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--color-neutral-12) 40% + ); + cursor: not-allowed; + + &:active { + background-color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--color-neutral-3) 40% + ); + color: color-mix( + in srgb, + var(--color-neutral-1) 60%, + var(--color-neutral-12) 40% + ); + } + } +} diff --git a/src/components/ui/textarea.css b/src/components/ui/textarea.css new file mode 100644 index 0000000..7ee9b7e --- /dev/null +++ b/src/components/ui/textarea.css @@ -0,0 +1,9 @@ +.st-rnd-textarea { + display: block; + width: 100%; + padding: 4px 8px; + border-radius: 4px; + border: 1px solid var(--color-neutral-6); + background-color: var(--color-neutral-3); + color: var(--color-neutral-12); +} diff --git a/src/components/wordList/EditWordList.tsx b/src/components/wordList/EditWordList.tsx new file mode 100644 index 0000000..a3e296e --- /dev/null +++ b/src/components/wordList/EditWordList.tsx @@ -0,0 +1,114 @@ +import { type FC, useRef, useState } from 'react' +import Button from '@/components/ui/Button' +import Dialog from '@/components/ui/Dialog' +import { useStore } from '@/store' +import '@/components/wordList/editWordlList.css' +import Input from '../ui/Input' +import Textarea from '../ui/Textarea' + +export interface RenameWordListProps { + currentWordList: string +} + +const EditWordList: FC = ({ currentWordList }) => { + const [internalStatus, setInternalStatus] = useState() + const [newName, setNewName] = useState(undefined) + const dialogRef = useRef(null) + + return ( + <> + { + if (dialogRef.current === null || currentWordList === '') { + return + } + const wordList = useStore.getState().wordLists[currentWordList] + if (wordList === undefined) { + return + } + setNewName(currentWordList) + setInternalStatus(wordList.join('\n')) + dialogRef.current.showModal() + }} + > + + + + + + Edit Word List: {currentWordList} + + Name + + Name of the word list, used in placeholder and macros. + + setNewName(e.target.value)} + /> + Words + + Words separated by newlines, words can be more than one word! + + setInternalStatus(e.target.value)} + rows={10} + /> + + { + if (dialogRef.current === null) { + return + } + dialogRef.current.close() + setInternalStatus(undefined) + setNewName(undefined) + }} + > + Close + + { + if ( + currentWordList === '' || + newName === undefined || + internalStatus === undefined || + dialogRef.current === null + ) { + return + } + if (newName === '') { + alert('Name cannot be empty') + return + } + const addWordList = useStore.getState().addWordList + if (currentWordList !== newName) { + const removeWordList = useStore.getState().removeWordList + removeWordList(currentWordList) + } + addWordList(newName, internalStatus.split('\n')) + dialogRef.current.close() + setInternalStatus(undefined) + setNewName(undefined) + }} + > + Save + + + + + > + ) +} + +export default EditWordList diff --git a/src/components/wordList/editWordlList.css b/src/components/wordList/editWordlList.css new file mode 100644 index 0000000..725f026 --- /dev/null +++ b/src/components/wordList/editWordlList.css @@ -0,0 +1,24 @@ +#st-rnd-wordlist-edit-dialog { + position: relative; + + width: clamp(400px, 100%, 1200px); + & > div { + display: flex; + flex-direction: column; + gap: 8px; + } + #st-rnd-wordlist-edit-dialog-title { + width: 100%; + + text-align: center; + + & > span { + color: var(--color-primary-9); + } + } + #st-rnd-wordlist-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + } +} diff --git a/src/components/wordLists.css b/src/components/wordLists.css new file mode 100644 index 0000000..1feb4b5 --- /dev/null +++ b/src/components/wordLists.css @@ -0,0 +1,21 @@ +#st-rnd-upload-wordlist-container { + display: flex; + flex-direction: column; +} + +#st-rnd-wordlist-controls { + display: flex; + flex-direction: row; + gap: 8px; + & > select { + flex-grow: 1; + } + & > button { + flex-shrink: 0; + } +} + +.st-rnd-small-text { + font-size: 0.8em; + color: var(--color-neutral-11); +} diff --git a/src/gui.css b/src/gui.css new file mode 100644 index 0000000..196fa11 --- /dev/null +++ b/src/gui.css @@ -0,0 +1,27 @@ +:root { + --color-neutral-1: #111113; + --color-neutral-2: #18191b; + --color-neutral-3: #212225; + --color-neutral-4: #272a2d; + --color-neutral-5: #2e3135; + --color-neutral-6: #363a3f; + --color-neutral-7: #43484e; + --color-neutral-8: #5a6169; + --color-neutral-9: #696e77; + --color-neutral-10: #777b84; + --color-neutral-11: #b0b4ba; + --color-neutral-12: #edeef0; + + --color-primary-1: #13131e; + --color-primary-2: #171625; + --color-primary-3: #202248; + --color-primary-4: #262a65; + --color-primary-5: #303374; + --color-primary-6: #3d3e82; + --color-primary-7: #4a4a95; + --color-primary-8: #5958b1; + --color-primary-9: #5b5bd6; + --color-primary-10: #6e6ade; + --color-primary-11: #b1a9ff; + --color-primary-12: #e0dffe; +} diff --git a/src/gui.tsx b/src/gui.tsx new file mode 100644 index 0000000..79b2bb8 --- /dev/null +++ b/src/gui.tsx @@ -0,0 +1,24 @@ +import { StrictMode } from 'react' +import ReactDOM from 'react-dom/client' +import Settings from '@/Settings' +import '@/gui.css' + +const renderSettingsGui = () => { + const rootContainer = document.getElementById('extensions_settings') + + if (rootContainer === null) { + throw new Error('[st-randomness-helpers] root container not found') + } + + const rootElement = document.createElement('div') + + rootContainer.appendChild(rootElement) + + ReactDOM.createRoot(rootElement).render( + + + + ) +} + +export default renderSettingsGui diff --git a/src/index.ts b/src/index.ts index 0e8c1a2..e6d62c5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,3 @@ -console.log('Hello via Bun!') +import renderSettingsGui from '@/gui' -const context = SillyTavern.getContext() -console.log(context) +renderSettingsGui() diff --git a/src/types/helpers.ts b/src/types/helpers.ts new file mode 100644 index 0000000..0ac1a95 --- /dev/null +++ b/src/types/helpers.ts @@ -0,0 +1,5 @@ +export type MergeOmitting = Omit< + ReplaceableType, + keyof ReplacerType +> & + ReplacerType
+ Wordlists are txt files with one (or more) word per line, are used in + placeholders and macros. +
+ Name of the word list, used in placeholder and macros. +
+ Words separated by newlines, words can be more than one word! +