soiz1 commited on
Commit
41637e7
·
verified ·
1 Parent(s): 63051d6

Update src/addons/addons/paint-gradient-maker/userscript.js

Browse files
src/addons/addons/paint-gradient-maker/userscript.js CHANGED
@@ -641,649 +641,5 @@ export default async function () {
641
  });
642
  }
643
 
644
- if (typeof scaffolding === "undefined") startListenerWorker();
645
- }// Gradient Maker Addon
646
- // By: SharkPool
647
- export default async function () {
648
- const isPM = true;
649
- const customID = "custom-gradient-btn";
650
- const symbolTag = Symbol("custom-gradient-tag");
651
- const guiIMGS = {
652
- "select": `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><rect stroke="#000" fill="#fff" x=".5" y=".5" width="19" height="19" rx="4" stroke-opacity=".15"/><path fill="red" d="M13.35 8.8h-2.4V6.4a1.2 1.2 90 0 0-2.4 0l.043 2.4H6.15a1.2 1.2 90 0 0 0 2.4l2.443-.043L8.55 13.6a1.2 1.2 90 0 0 2.4 0v-2.443l2.4.043a1.2 1.2 90 0 0 0-2.4"/></svg>`,
653
- "add": `<svg viewBox="2 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill="red" d="M18 10h-4V6a2 2 0 0 0-4 0l.071 4H6a2 2 0 0 0 0 4l4.071-.071L10 18a2 2 0 0 0 4 0v-4.071L18 14a2 2 0 0 0 0-4"></path></svg>`,
654
- "delete": `<svg viewBox="2 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill="red" d="M 18 10 h -4 H 6 a 2 2 0 0 0 0 4 L 18 14 a 2 2 0 0 0 0 -4"></path></svg>`,
655
- };
656
-
657
- const paperLinkModes = new Set([
658
- "TEXT", "OVAL", "RECT",
659
- ...(isPM ? ["ROUNDED_RECT", "TRIANGLE", "SUSSY", "ARROW"] : [])
660
- ]);
661
-
662
- let selectedClassName, unselectedClassName, customBtn;
663
- let observerUsed = false;
664
- let modalStorage = {};
665
-
666
- /* Internal Utils */
667
- function position2Angle(p1, p2) {
668
- const dx = p1.x - p2.x;
669
- const dy = p1.y - p2.y;
670
- const angle = Math.atan2(dy, dx) * (180 / Math.PI);
671
- return angle + 90;
672
- }
673
-
674
- function initGradSelectClasses(gradRow) {
675
- const classes = {};
676
- const children = Array.from(gradRow.children);
677
- for (const child of children) {
678
- const name = child.classList.toString();
679
- if (classes[name] === undefined) classes[name] = 1;
680
- else classes[name] = 0;
681
- }
682
-
683
- for (const [cls, count] of Object.entries(classes)) {
684
- if (count) selectedClassName = cls;
685
- else unselectedClassName = cls
686
- }
687
- }
688
-
689
- function encodeGradHTML(settings) {
690
- const sortedParts = [...settings.parts].sort((a, b) => a.p - b.p);
691
-
692
- let gradString = settings.type === "Linear" ? "linear-gradient(" : "radial-gradient(";
693
- if (settings.type === "Linear") gradString += `${settings.dir}deg, `;
694
- for (const part of sortedParts) gradString += `${part.c} ${part.p}%, `;
695
- return gradString.substring(0, gradString.length - 2) + ")";
696
- }
697
-
698
- function genLinearGradPoints(bounds, angleDeg) {
699
- const center = bounds.center;
700
- const dir = new paper.Point({ angle: angleDeg, length: 1 });
701
- const boundsRect = new paper.Path.Rectangle(bounds);
702
- const gradLine = new paper.Path.Line({
703
- from: center.subtract(dir.multiply(10000)),
704
- to: center.add(dir.multiply(10000))
705
- });
706
-
707
- const intersections = gradLine.getIntersections(boundsRect);
708
- gradLine.remove();
709
- boundsRect.remove();
710
- if (intersections.length < 2) {
711
- return {
712
- origin: center.subtract(dir.multiply(bounds.width / 2)),
713
- destination: center.add(dir.multiply(bounds.width / 2))
714
- };
715
- } else {
716
- return {
717
- origin: intersections[0].point,
718
- destination: intersections[1].point
719
- };
720
- }
721
- }
722
-
723
- function setSelected2Grad(settings) {
724
- // compile SVG-based gradient
725
- const sortedParts = [...settings.parts].sort((a, b) => a.p - b.p);
726
- const gradStops = sortedParts.map(part => new paper.GradientStop(part.c, part.p / 100));
727
- const gradient = new paper.Gradient(gradStops, settings.type === "Radial");
728
- modalStorage._gradCache = { settings, gradient };
729
-
730
- paper.project.getSelectedItems().forEach((item) => {
731
- let origin, destination;
732
- if (settings.type === "Radial") {
733
- origin = item.bounds.center;
734
- destination = item.bounds.center.add([item.bounds.width / 2, 0]);
735
- } else {
736
- const points = genLinearGradPoints(item.bounds, settings.dir - 90);
737
- origin = points.origin;
738
- destination = points.destination;
739
- }
740
-
741
- item[settings.path] = { gradient, origin, destination };
742
- });
743
-
744
- // update drawing & action
745
- if (paper.tool.onUpdateImage) paper.tool.onUpdateImage();
746
-
747
- // set with html otherwise GUI will crash
748
- const swatch = document.querySelectorAll(
749
- `div[class^=color-button_color-button_] div[class^=color-button_color-button-swatch_]`
750
- )[settings.path === "fillColor" ? 0 : 1];
751
- if (swatch) swatch.style.background = encodeGradHTML(settings);
752
- }
753
-
754
- function paperGrad2CSS(paperGrad) {
755
- const { gradient, origin, destination } = paperGrad;
756
- if (!gradient || !origin || !destination) return null;
757
-
758
- const stops = gradient.stops.map(s => `${s.color.toCSS(true)} ${Math.round(s.offset * 100)}%`);
759
- if (gradient.radial) return `radial-gradient(circle, ${stops.join(", ")})`;
760
- else return `linear-gradient(${position2Angle(destination, origin)}deg, ${stops.join(", ")})`;
761
- }
762
-
763
- function extractGradient(color) {
764
- if (!color || !color.gradient) return {};
765
- return {
766
- gradient: color.gradient,
767
- origin: color.origin || "",
768
- destination: color.destination || color.highlight || ""
769
- };
770
- }
771
-
772
- function decodeSelectedGrad(item, draggableDiv, settingsDiv) {
773
- const { gradient, origin, destination } = extractGradient(item[modalStorage.path]);
774
- if (!gradient || !origin || !destination) return draggableDiv.append(createDraggable(), createDraggable());
775
-
776
- // create draggables
777
- const newStops = gradient.stops.map((s, i) => {
778
- // "offset" will be undefined when using Scratch gradients, which dont have set-stops
779
- const alpha = Math.round(s.color.alpha * 255).toString(16).padStart(2, "0");
780
- return createDraggable(s.color.toCSS(true) + alpha, s.offset ? s.offset * 100 : i * 100)
781
- });
782
- draggableDiv.append(...newStops);
783
-
784
- // preset values
785
- const angle = position2Angle(destination, origin);
786
- settingsDiv.querySelector("select").value = gradient.radial ? "Radial" : "Linear";
787
- settingsDiv.querySelector("input").value = angle;
788
- modalStorage.type = gradient.radial ? "Radial" : "Linear";
789
- modalStorage.dir = angle;
790
- }
791
-
792
- function decodeFromCache(settings, draggableDiv, settingsDiv) {
793
- // create draggables
794
- const newStops = settings.parts.map((s, i) => {
795
- // "p" will be NaN when using Scratch gradients, which dont have set-stops
796
- return createDraggable(s.c, isNaN(s.p) ? i * 100 : s.p)
797
- });
798
- draggableDiv.append(...newStops);
799
-
800
- // preset values
801
- settingsDiv.querySelector("select").value = settings.type;
802
- settingsDiv.querySelector("input").value = settings.dir;
803
- modalStorage.type = settings.type;
804
- modalStorage.dir = settings.dir;
805
- }
806
-
807
- function handleFillEvent() {
808
- if (!modalStorage._gradCache) return;
809
-
810
- // set the swatch color in case the GUI resets it
811
- const swatch = document.querySelector(`div[class^=color-button_color-button_] div[class^=color-button_color-button-swatch_]`);
812
- if (swatch) queueMicrotask(() => {
813
- if (!modalStorage._gradCache) return;
814
- swatch.style.background = encodeGradHTML(modalStorage._gradCache.settings);
815
- });
816
-
817
- const tool = paper.tool;
818
- if (typeof tool?._getFillItem !== "function") return;
819
-
820
- const item = tool._getFillItem();
821
- if (!item) return;
822
-
823
- const bounds = item.bounds;
824
- let origin, destination;
825
- if (modalStorage.type === "Radial") {
826
- origin = new paper.Point(tool._point.x, tool._point.y);
827
- destination = origin.add([Math.max(bounds.width, bounds.height) / 2, 0]);
828
- } else {
829
- const points = genLinearGradPoints(bounds, modalStorage.dir - 90);
830
- origin = points.origin;
831
- destination = points.destination;
832
- }
833
-
834
- const path = tool.fillProperty === "fill" ? "fillColor" : "strokeColor";
835
- item[path] = {
836
- gradient: modalStorage._gradCache.gradient,
837
- origin, destination
838
- };
839
- }
840
-
841
- function handleShapeModeEvent(type) {
842
- if (!modalStorage._gradCache && type !== "TEXT") return;
843
-
844
- // set the swatch color in case the GUI resets it
845
- const swatch = document.querySelector(`div[class^=color-button_color-button_] div[class^=color-button_color-button-swatch_]`);
846
- if (swatch) queueMicrotask(() => {
847
- if (!modalStorage._gradCache) return;
848
- swatch.style.background = encodeGradHTML(modalStorage._gradCache.settings);
849
- });
850
-
851
- const tool = paper.tool;
852
- if (typeof tool?._onMouseDrag !== "function") return;
853
- if (tool[symbolTag]) return;
854
- // patch this event, if not already, to run our code
855
-
856
- const funcName = type === "TEXT" ? "onKeyDown" : "onMouseDrag";
857
- const ogOnFunc = tool[funcName];
858
- tool[symbolTag] = true;
859
- tool[funcName] = function (...args) {
860
- ogOnFunc.call(this, ...args);
861
-
862
- // replace the fill with the custom gradient
863
- if (!modalStorage._gradCache) {
864
- if (type === "TEXT") {
865
- tool.element.style.background = "";
866
- tool.element.style.backgroundClip = "";
867
- tool.element.style.color = "";
868
- }
869
- return;
870
- }
871
-
872
- let item;
873
- switch (type) {
874
- case "RECT":
875
- item = this.rect;
876
- break;
877
- case "OVAL":
878
- item = this.oval;
879
- break;
880
- case "TEXT":
881
- item = this.textBox;
882
- break;
883
- /* PenguinMod shapes */
884
- case "ROUNDED_RECT":
885
- item = this.rect;
886
- break;
887
- case "TRIANGLE":
888
- item = this.tri;
889
- break;
890
- case "SUSSY":
891
- item = this.sussy;
892
- break;
893
- case "ARROW":
894
- item = this.tri;
895
- break;
896
- default: return;
897
- }
898
- if (!item) return;
899
- const bounds = item.bounds;
900
- let origin, destination;
901
- if (modalStorage.type === "Radial") {
902
- origin = item.bounds.center;
903
- destination = item.bounds.center.add([item.bounds.width / 2, 0]);
904
- } else {
905
- const points = genLinearGradPoints(bounds, modalStorage.dir - 90);
906
- origin = points.origin;
907
- destination = points.destination;
908
- }
909
-
910
- item.fillColor = {
911
- gradient: modalStorage._gradCache.gradient,
912
- origin, destination
913
- };
914
-
915
- // text uses HTML elements, so we have to handle that too
916
- if (type === "TEXT") {
917
- tool.element.style.background = encodeGradHTML(modalStorage._gradCache.settings);
918
- tool.element.style.backgroundClip = "text";
919
- tool.element.style.color = "transparent";
920
- }
921
- }
922
- }
923
-
924
- /* GUI Utils */
925
- function getButtonURI(name, dontCompile) {
926
- const themeHex = isPM ? "#00c3ff" : document.documentElement.style.getPropertyValue("--looks-secondary") || "#ff4c4c";
927
- const guiSVG = guiIMGS[name].replace("red", themeHex);
928
- if (dontCompile) return guiSVG;
929
- else return "data:image/svg+xml;base64," + btoa(guiSVG);
930
- }
931
-
932
- function showSelectedGrad(item) {
933
- const [fillSwatch, outlineSwatch] = document.querySelectorAll(`div[class^=color-button_color-button_] div[class^=color-button_color-button-swatch_]`);
934
- const outCSSGrad = paperGrad2CSS(extractGradient(item.strokeColor));
935
- if (outlineSwatch) {
936
- if (outCSSGrad) outlineSwatch.style.background = outCSSGrad;
937
- else if (!item.strokeColor || item.strokeWidth === 0) outlineSwatch.style.background = "#fff";
938
- }
939
-
940
- const fillGrad = extractGradient(item.fillColor);
941
- const fillCSSGrad = paperGrad2CSS(fillGrad);
942
- modalStorage._gradCache = undefined;
943
- if (fillSwatch) {
944
- if (fillCSSGrad) {
945
- fillSwatch.style.background = fillCSSGrad;
946
-
947
- // update cache
948
- const { gradient, destination, origin } = fillGrad;
949
- modalStorage._gradCache = {
950
- gradient,
951
- settings: {
952
- type: gradient.radial ? "Radial" : "Linear",
953
- dir: position2Angle(destination, origin),
954
- parts: gradient.stops.map(s => {
955
- const alpha = Math.round(s.color.alpha * 255).toString(16).padStart(2, "0");
956
- return { c: s.color.toCSS(true) + alpha, p: s.offset * 100 };
957
- })
958
- }
959
- };
960
- } else if (!item.fillColor) fillSwatch.style.background = "#fff";
961
- }
962
- }
963
-
964
- function createDraggable(optC, optP) {
965
- const index = modalStorage.parts.length;
966
- const rngPos = optP ?? Math.floor(Math.random() * 100);
967
- const rngHex = optC ?? `#${Math.floor(Math.random() * Math.pow(2, 24)).toString(16).padStart(6, "0")}`;
968
- const opacity = optC ? optC.length === 9 ? parseInt(optC.slice(7, 9), 16) / 255 : 1 : 1;
969
-
970
- const draggable = document.createElement("div");
971
- draggable.id = index;
972
- draggable.classList.add("pointer");
973
- draggable.setAttribute("style", `cursor: pointer; width: 25px; position: absolute; top: -6px; transform: translateX(-50%);`);
974
- draggable.style.left = `${rngPos}%`;
975
-
976
- const nub = document.createElementNS("http://www.w3.org/2000/svg", "svg");
977
- nub.setAttribute("width", "14");
978
- nub.setAttribute("height", "7");
979
- nub.style.transform = "translateX(45%)";
980
-
981
- const polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
982
- polygon.setAttribute("points", "0,7 7,0 14,7");
983
- polygon.setAttribute("stroke", "#fff");
984
- polygon.setAttribute("fill", "#fff");
985
- nub.appendChild(polygon);
986
-
987
- const color = document.createElement("div");
988
- color.setAttribute("style", `width: 25px; height: 25px; border-radius: 4px; background: #fff; display: flex; justify-content: center; align-items: center; flex-direction: column;`);
989
-
990
- const colorContainer = document.createElement("div");
991
- colorContainer.setAttribute("style", `width: 16px; height: 16px; border-radius: 5px; background: ${rngHex}; border: solid 2px rgba(0,0,0,.2); opacity: ${opacity}; margin-bottom: 2px;`);
992
-
993
- const colorInput = document.createElement("input");
994
- colorInput.setAttribute("type", "color");
995
- colorInput.setAttribute("style", `opacity: 0; position: absolute; pointer-events: none;`);
996
-
997
- const opacityInput = document.createElement("input");
998
- opacityInput.setAttribute("type", "number");
999
- opacityInput.setAttribute("min", "0");
1000
- opacityInput.setAttribute("max", "100");
1001
- opacityInput.value = opacity * 100;
1002
- opacityInput.setAttribute("style", `visibility: hidden; background: #fff; border: none; color: #000; text-align: center; position: absolute; pointer-events: auto; width: 45px; height: 25px; padding: 0; margin: 0; border-radius: 0 5px 5px 0; left: 22px;`);
1003
-
1004
- // Color picker handler
1005
- colorContainer.addEventListener("click", (e) => {
1006
- opacityInput.style.visibility = "visible";
1007
- colorInput.click();
1008
- e.stopPropagation();
1009
- });
1010
- draggable.addEventListener("mouseleave", (e) => {
1011
- opacityInput.style.visibility = "hidden";
1012
- e.stopPropagation();
1013
- });
1014
-
1015
- colorInput.addEventListener("input", (e) => {
1016
- modalStorage.parts[index].c = e.target.value;
1017
- colorContainer.style.background = e.target.value;
1018
- updateDisplay();
1019
- });
1020
-
1021
- // Opacity slider handler
1022
- opacityInput.addEventListener("click", (e) => {
1023
- opacityInput.focus();
1024
- e.stopPropagation();
1025
- });
1026
- opacityInput.addEventListener("input", (e) => {
1027
- const newOpacity = Math.min(100, Math.max(0, e.target.value));
1028
- e.target.value = newOpacity;
1029
- colorContainer.style.opacity = newOpacity / 100;
1030
-
1031
- const alpha = Math.round(newOpacity * 2.55).toString(16).padStart(2, "0");
1032
- const hex = modalStorage.parts[index].c;
1033
- modalStorage.parts[index].c = hex.substring(0, 7) + alpha;
1034
- updateDisplay();
1035
- });
1036
-
1037
- draggable.addEventListener("mousedown", (e) => {
1038
- e.preventDefault();
1039
- if (e.target === opacityInput) return;
1040
-
1041
- modalStorage.selectedPointer = draggable;
1042
- const container = draggable.parentElement;
1043
- const containerRect = container.getBoundingClientRect();
1044
-
1045
- const onMouseMove = (moveEvent) => {
1046
- const x = moveEvent.clientX - containerRect.left;
1047
- const percent = Math.min(100, Math.max(0, (x / container.offsetWidth) * 100));
1048
- draggable.style.left = `${percent}%`;
1049
- modalStorage.parts[index].p = percent;
1050
- updateDisplay();
1051
- };
1052
-
1053
- const onMouseUp = () => {
1054
- document.removeEventListener("mousemove", onMouseMove);
1055
- document.removeEventListener("mouseup", onMouseUp);
1056
- };
1057
-
1058
- document.addEventListener("mousemove", onMouseMove);
1059
- document.addEventListener("mouseup", onMouseUp);
1060
- });
1061
-
1062
- color.append(colorContainer, colorInput, opacityInput);
1063
- draggable.append(nub, color);
1064
- modalStorage.parts.push({ c: rngHex, p: rngPos });
1065
- modalStorage.selectedPointer = draggable;
1066
- return draggable;
1067
- }
1068
-
1069
- function genSettingsTable(div) {
1070
- const btnStyle = `width: 35px; height: 35px; border: solid 2px var(--ui-black-transparent, hsla(0, 0%, 0%, 0.15)); border-radius: 5px; background: var(--paint-input-background, --ui-primary, #fff); transition: transform 0.2s;`;
1071
- const selectStlye = `cursor: pointer; height: 30px; margin: 5px; border: solid 2px var(--ui-black-transparent, hsla(0, 0%, 0%, 0.15)); border-radius: 5px; background: var(--ui-secondary, #fff);`;
1072
- const directionStyle = `text-align: center; width: 50px; height: 25px; margin: 5px; border: solid 2px var(--ui-black-transparent, hsla(0, 0%, 0%, 0.15)); border-radius: 5px; background: var(--ui-secondary, #fff);`;
1073
-
1074
- const createBtn = document.createElement("button");
1075
- createBtn.setAttribute("style", btnStyle);
1076
- createBtn.setAttribute("onmouseover", `this.style.transform="scale(1.1)"`);
1077
- createBtn.setAttribute("onmouseout", `this.style.transform="scale(1)"`);
1078
- createBtn.innerHTML = getButtonURI("add", true);
1079
- createBtn.addEventListener("click", (e) => {
1080
- const draggableSpace = modalStorage.modal.querySelector(`div[class="draggables"]`);
1081
- draggableSpace.appendChild(createDraggable());
1082
- updateDisplay();
1083
- e.stopPropagation();
1084
- });
1085
-
1086
- const deleteBtn = document.createElement("button");
1087
- deleteBtn.setAttribute("style", btnStyle);
1088
- deleteBtn.setAttribute("onmouseover", `this.style.transform="scale(1.1)"`);
1089
- deleteBtn.setAttribute("onmouseout", `this.style.transform="scale(1)"`);
1090
- deleteBtn.style.margin = "0px 8px";
1091
- deleteBtn.innerHTML = getButtonURI("delete", true);
1092
- deleteBtn.addEventListener("click", (e) => {
1093
- const pointer = modalStorage.selectedPointer;
1094
- if (pointer) {
1095
- modalStorage.parts.splice(pointer.id, 1);
1096
- pointer.remove();
1097
- updateDisplay();
1098
- delete modalStorage.selectedPointer;
1099
- }
1100
- e.stopPropagation();
1101
- });
1102
-
1103
- const title1 = document.createElement("span");
1104
- title1.textContent = "Gradient Type:";
1105
-
1106
- const select = document.createElement("select");
1107
- select.setAttribute("style", selectStlye);
1108
-
1109
- const option1 = document.createElement("option");
1110
- const option2 = document.createElement("option");
1111
- option1.text = "Linear"; option1.value = "Linear";
1112
- option2.text = "Radial"; option2.value = "Radial";
1113
- select.append(option1, option2);
1114
- select.addEventListener("change", (e) => {
1115
- modalStorage.type = e.target.value;
1116
- updateDisplay();
1117
- e.stopPropagation();
1118
- });
1119
-
1120
- const title2 = document.createElement("span");
1121
- title2.textContent = "Direction:";
1122
-
1123
- const dirBtn = document.createElement("input");
1124
- dirBtn.setAttribute("style", directionStyle);
1125
- dirBtn.setAttribute("type", "number");
1126
- dirBtn.setAttribute("max", 360);
1127
- dirBtn.setAttribute("min", 0);
1128
- dirBtn.setAttribute("value", 90);
1129
- dirBtn.addEventListener("input", (e) => {
1130
- modalStorage.dir = e.target.value;
1131
- updateDisplay();
1132
- e.stopPropagation();
1133
- });
1134
-
1135
- div.append(createBtn, deleteBtn, title1, select, title2, dirBtn);
1136
- }
1137
-
1138
- function genButtonTable(div) {
1139
- const themeHex = isPM ? "#00c3ff" : document.documentElement.style.getPropertyValue("--looks-secondary") || "#ff4c4c";
1140
- const btnStyle = `color: #fff; font-weight: 600; text-align: center; padding: 10px; margin: 10px 5px; border: solid 2px var(--ui-black-transparent, hsla(0, 0%, 0%, 0.15)); border-radius: 5px; background: ${themeHex}; transition: transform 0.2s;`;
1141
-
1142
- const enterBtn = document.createElement("button");
1143
- enterBtn.id = "enter";
1144
- enterBtn.setAttribute("style", btnStyle);
1145
- enterBtn.setAttribute("onmouseover", `this.style.transform="scale(1.1)"`);
1146
- enterBtn.setAttribute("onmouseout", `this.style.transform="scale(1)"`);
1147
- enterBtn.textContent = "Okay";
1148
-
1149
- const cancelBtn = document.createElement("button");
1150
- cancelBtn.id = "cancel";
1151
- cancelBtn.setAttribute("style", btnStyle);
1152
- cancelBtn.setAttribute("onmouseover", `this.style.transform="scale(1.1)"`);
1153
- cancelBtn.setAttribute("onmouseout", `this.style.transform="scale(1)"`);
1154
- cancelBtn.textContent = "Cancel";
1155
-
1156
- div.append(cancelBtn, enterBtn);
1157
- }
1158
-
1159
- function updateDisplay() {
1160
- const display = modalStorage.modal.querySelector(`div[class="color-display"]`);
1161
- display.style.background = encodeGradHTML(modalStorage);
1162
- }
1163
-
1164
- /* Main GUI */
1165
- function openGradientMaker() {
1166
- const paint = ReduxStore.getState().scratchPaint;
1167
- const oldCache = modalStorage._gradCache;
1168
- modalStorage = {
1169
- parts: [], type: "Linear", dir: 90,
1170
- selectedPointer: undefined, modal: undefined,
1171
- path: paint.modals.fillColor ? "fillColor" : "strokeColor"
1172
- };
1173
-
1174
- const container = document.createElement("div");
1175
- container.classList.add("SP-gradient-maker");
1176
- container.setAttribute("style", `position: absolute; z-index: 9999; pointer-events: auto; background-color: rgba(0,0,0,.1); width: 100%; height: 100vh;`);
1177
-
1178
- const modal = document.createElement("div");
1179
- modal.classList.add("gradient-modal");
1180
- modal.setAttribute("style", `color: var(--paint-text-primary, #575e75); width: 450px; height: 260px; display: block; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); background: var(--ui-secondary, hsla(215, 75%, 95%, 1)); border: solid 2px var(--ui-black-transparent, hsla(0, 0%, 0%, 0.15)); border-radius: 5px; padding: 15px;`);
1181
- modalStorage.modal = modal;
1182
-
1183
- const title = document.createElement("span");
1184
- title.setAttribute("style", `display: block; text-align: center; justify-content: center; border-bottom: solid 2px var(--ui-black-transparent, hsla(0, 0%, 0%, 0.15)); padding-bottom: 10px; margin: 0 25px 0 25px; font-weight: 600;"`);
1185
- title.textContent = "Gradient Maker";
1186
-
1187
- const display = document.createElement("div");
1188
- display.classList.add("color-display");
1189
- display.setAttribute("style", `width: 420px; height: 40px; display: flex; justify-content: center; align-items: center; margin: 15px 15px 0 15px; border: solid 2px grey; border-radius: 5px 5px 0 0;`);
1190
-
1191
- const draggables = document.createElement("div");
1192
- draggables.classList.add("draggables");
1193
- draggables.setAttribute("style", `width: 420px; height: 40px; position: relative; display: flex; justify-content: center; align-items: center; margin: 0 15px 15px 15px; border: solid 2px grey; border-radius: 0 0 5px 5px; background: #111111;`);
1194
-
1195
- const settings = document.createElement("div");
1196
- settings.classList.add("settings");
1197
- settings.setAttribute("style", `border-top: dashed 2px var(--ui-black-transparent, hsla(0, 0%, 0%, 0.15)); padding-top: 10px; display: flex; justify-content: center; align-items: center;`);
1198
- genSettingsTable(settings);
1199
-
1200
- const buttons = document.createElement("div");
1201
- buttons.setAttribute("style", `display: flex; justify-content: center; align-items: center;`);
1202
- genButtonTable(buttons);
1203
- buttons.addEventListener("click", (e) => {
1204
- if (e.target.id) {
1205
- if (e.target.id === "enter") setSelected2Grad(modalStorage);
1206
- container.remove();
1207
- }
1208
- e.stopPropagation();
1209
- });
1210
-
1211
- modal.append(title, display, draggables, settings, buttons);
1212
- container.appendChild(modal);
1213
- document.body.appendChild(container);
1214
-
1215
- if (paint.selectedItems?.length) decodeSelectedGrad(paint.selectedItems[0], draggables, settings);
1216
- else if (oldCache) decodeFromCache(oldCache.settings, draggables, settings);
1217
- else draggables.append(createDraggable(), createDraggable());
1218
- updateDisplay();
1219
- }
1220
-
1221
- function startListenerWorker() {
1222
- let lastMode, lastSelected, lastModals;
1223
- ReduxStore.subscribe(() => {
1224
- const paint = ReduxStore.getState().scratchPaint;
1225
- if (!paint || paint?.format === undefined || paint?.format === null) return;
1226
- const { mode, selectedItems, modals } = paint;
1227
-
1228
- // no bitmap support :(
1229
- if (paint.format.startsWith("BITMAP")) {
1230
- if (customBtn) {
1231
- customBtn.remove();
1232
- customBtn = undefined;
1233
- }
1234
- return;
1235
- }
1236
-
1237
- // run relative tool events
1238
- if (mode === "FILL") handleFillEvent();
1239
- else if (paperLinkModes.has(mode)) handleShapeModeEvent(mode);
1240
-
1241
- const idChain = selectedItems.map((e) => e.id).join(".");
1242
- const modalChain = `${modals.fillColor}${modals.strokeColor}`;
1243
- if (mode === lastMode && idChain === lastSelected && modalChain === lastModals) return;
1244
- lastMode = mode;
1245
- lastSelected = idChain;
1246
- lastModals = modalChain;
1247
-
1248
- // decode potential custom gradients
1249
- if (selectedItems?.length) showSelectedGrad(selectedItems[0]);
1250
- else if (mode === "SELECT" || mode === "RESHAPE") modalStorage._gradCache = undefined;
1251
-
1252
- // add custom modal
1253
- if (!modals.strokeColor && !modals.fillColor) return;
1254
- if (observerUsed) return;
1255
- const observer = new MutationObserver(() => {
1256
- const gradRow = document.querySelector(`div[class^="color-picker_gradient-picker-row_"]`);
1257
- if (!gradRow || gradRow.lastElementChild.id === customID) return;
1258
-
1259
- // get the appropriate class names for selected items
1260
- if (!selectedClassName) initGradSelectClasses(gradRow);
1261
- const children = Array.from(gradRow.children);
1262
-
1263
- customBtn = children[0].cloneNode(true);
1264
- customBtn.src = getButtonURI("select");
1265
- customBtn.id = customID;
1266
- customBtn.setAttribute("class", unselectedClassName);
1267
- gradRow.appendChild(customBtn);
1268
-
1269
- gradRow.addEventListener("click", (e) => {
1270
- if (e.target === customBtn) {
1271
- for (const child of children) child.setAttribute("class", unselectedClassName);
1272
- customBtn.setAttribute("class", selectedClassName);
1273
- openGradientMaker();
1274
- } else if (e.target.nodeName === "IMG") {
1275
- customBtn.setAttribute("class", unselectedClassName);
1276
- }
1277
- });
1278
-
1279
- observerUsed = false;
1280
- observer.disconnect();
1281
- });
1282
-
1283
- observer.observe(document.body, { childList: true, subtree: true });
1284
- observerUsed = true;
1285
- });
1286
- }
1287
-
1288
  if (typeof scaffolding === "undefined") startListenerWorker();
1289
  }
 
641
  });
642
  }
643
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
644
  if (typeof scaffolding === "undefined") startListenerWorker();
645
  }