-
+
-
+
@@ -13,7 +13,7 @@
-
+
diff --git a/web/core/css/main.css b/web/core/css/main.css
index a7c39ed..f6953b2 100644
--- a/web/core/css/main.css
+++ b/web/core/css/main.css
@@ -1,4 +1,3 @@
-
* {
box-sizing: border-box;
margin: 0;
@@ -7,26 +6,48 @@
}
:root {
- --background-color: #121213;
- --border-color: #320848;
- --text-color: #cba3e5;
- --hover-background-color: #8621b7;
- --hover-box-shadow: 0 0 8px rgba(134, 33, 183, 0.6);
- --focus-border-color: #570d7b;
- --focus-box-shadow: 0 0 8px rgba(134, 33, 183, 0.6);
- --focus-outline-color: #8621b7;
- --scrollbar-thumb-color: #570d7b;
- --scrollbar-track-color: #1e1e1f;
+ --control-height: 36px;
+ --color-primary: #430474;
+ --color-secondary: #f4b6ff;
+ --color-accent: #8621b7;
+ --color-highlight: #7d0ab6;
+ --color-primary-text: #cba3e5;
+ --color-border: #570d7b;
+ --color-background: #181b1d;
+ --color-background-secondary: #131312;
+ --color-background-pattern: #4d0f7c;
+ --color-header-background: #131312;
+ --color-header-logo-text: #570d7b;
+ --color-header-text:#570d7b;
+ --color-social-icons: #570d7b;
+ --color-button-primary: #570d7b;
+ --color-button-primary-hover: #8621b7;
+ --color-button-primary-text: #f4b6ff;
+ --color-button-primary-text-hover: #131312;
+ --color-button-primary-active: #8621b7;
+ --color-button-primary-text-active: #8621b7;
+ --color-button-secondary: #131312;
+ --color-button-secondary-hover: #8621b7;
+ --color-button-secondary-text: #f4b6ff;
+ --color-button-secondary-text-hover: #131312;
+ --color-button-secondary-active: #8621b7;
+ --color-button-secondary-text-active: #f4b6ff;
+ --color-progress-background: #2c063f;
+ --color-progress-value:#7d0ab6;
+ --color-scrollbar-track: #1e1e1f;
+ --color-scrollbar-thumb: #570d7b;
+ --color-input-range-thumb: #f4b6ff;
+ --color-input-range-background: #570d7b;
+ --color-spinner:#570d7b;
+ --color-spinner-highlight:#7d0ab6;
}
html, body {
height: 100%;
- margin: 0;
- padding: 0;
- background-color: #181b1d;
+ background-color: var(--color-background);
background-size: 32px 32px;
- background-image: radial-gradient(circle, #430474 1px, transparent 1px);
- color: #cba3e5;
+ background-image: radial-gradient(circle, var(--color-background-pattern) 1px, transparent 1px);
+ color: var(--color-primary-text);
overflow-x: hidden;
}
@@ -40,16 +61,14 @@ header {
display: flex;
justify-content: space-between;
align-items: center;
- background-color: #121213;
- color: #fff;
+ background-color: var(--color-header-background);
padding-left: 50px;
padding-right: 50px;
padding-top: 10px;
text-align: left;
- color: #570d7b;
- border-bottom: 1px dashed;
- border-color: #570d7b #570d7b;
+ border-bottom: 1px dashed var(--color-border);
}
+
#right-header {
display: flex;
justify-content: center;
@@ -58,49 +77,40 @@ header {
padding: 5px;
}
-#Support{
- /* padding: 10px; */
- color: #cba3e5;
+#Support {
+ color: var(--color-header-text);
margin: 10px;
font-size: 12px;
}
+
a {
text-decoration: none;
}
-.appName{
- /* background-color: #570d7b; */
+
+.appName {
padding: 10px;
- color: #cba3e5;
- /* height: 100%; */
- /* border-radius: 10%; */
- border-top: 1px dashed;
- border-left: 1px dashed;
- border-right: 1px dashed;
-
- border-color: #570d7b #570d7b;
+ color: var(--color-header-text);
+ border-top: 1px dashed var(--color-border);
+ border-left: 1px dashed var(--color-border);
+ border-right: 1px dashed var(--color-border);
font-size: 12px;
}
-#github{
- /* padding: 5px; */
- /* color: #570d7b; */
- /* border-radius: 5px; */
- /* margin-right: 10px; */
-}
+/* Animations */
@keyframes animation-text {
- 0% {opacity:0;}
- 40% {opacity:0;}
- 100% { opacity:1;}
+ 0% { opacity: 0; }
+ 40% { opacity: 0; }
+ 100% { opacity: 1; }
}
@keyframes animation-left {
- from {left: 30px;}
- to { left: -30px;}
+ from { left: 30px; }
+ to { left: -30px; }
}
@keyframes animation-right {
- from {right: 30px;}
- to { right: -30px;}
+ from { right: 30px; }
+ to { right: -30px; }
}
header #logo {
@@ -115,10 +125,10 @@ header #img-logo {
header .logo-text {
display: inline-block;
- position: relative;
- font-family: Arial;
+ position: relative;
+ font-family: Arial;
cursor: pointer;
- color: #570d7b;
+ color: var(--color-header-text);
}
header .logo-text .text {
@@ -154,11 +164,10 @@ header .right {
display: flex;
flex-direction: column;
flex: 1;
- /* background-color: #121213; */
- color: #cba3e5;
- /* padding: 10px; */
+ color: var(--color-primary-text);
overflow: hidden;
}
+
.content {
display: flex;
flex: 1;
@@ -171,7 +180,9 @@ header .right {
padding: 4px 6px;
}
-.content .mid-col, .content .left-col, .content .right-col {
+.content .mid-col,
+.content .left-col,
+.content .right-col {
display: flex;
flex-direction: column;
align-items: left;
@@ -179,61 +190,56 @@ header .right {
.content .left-col {
flex: 1.1;
- background-color: #181b1d;
+ background-color: var(--color-background);
}
.content .mid-col {
flex: 2.5;
- border-left: 1px dashed #570d7b;
- border-right: 1px dashed #570d7b;
- /* align-items: center; */
+ border-left: 1px dashed var(--color-border);
+ border-right: 1px dashed var(--color-border);
justify-content: space-around;
min-height: 100%;
}
+
.content .right-col {
flex: 0.5;
- background-color: #181b1d;
+ background-color: var(--color-background);
}
#control button {
- padding: 5px;
- background-color: #570d7b;
- user-select: none;
-}
-
-#control button:hover {
- box-shadow: 0px 0px 20px var(--box-shadow-color);
- background-color: #8621b7;
-}
-
-#dropdownIdSearch {
- padding: 8px;
- border: 1px solid #ccc;
- border-radius: 4px;
- box-sizing: border-box;
+ padding: 5px;
+ background-color: var(--color-primary);
+ user-select: none;
}
-#dropdownIdSearch:focus {
- outline: none;
- border-color: #0056b3;
+#control button:hover {
+ box-shadow: var(--shadow-hover);
+ background-color: var(--color-accent);
}
-input[type="text"], input[type="number"], textarea {
- border: 1px solid #320848;
- transition: border-color 0.3s ease-in-out;
+input[type="text"],
+input[type="number"],
+textarea {
+ border:none;
+ border-bottom: 1px solid var(--color-border);
+ transition: border-color 0.3s ease-in-out;
padding: 8px;
- background-color: #121213;
- color: #cba3e5;
+ background-color: var(--color-background-secondary);
+ color: var(--color-primary-text);
+ margin:0 10px;
}
-input[type="text"]:hover, input[type="number"]:hover, textarea:hover {
- border-color: #570d7b;
+input[type="text"]:hover,
+input[type="number"]:hover,
+textarea:hover {
+ border-color: var(--color-border);
}
-input[type="text"]:focus, input[type="number"]:focus, textarea:focus {
- border-color: #8621b7;
- outline: none;
- box-shadow: 0 0 8px rgba(134, 33, 183, 0.6);
+input[type="text"]:focus,
+input[type="number"]:focus,
+textarea:focus {
+ border-color: var(--color-primary-text);
+ outline: none;
}
#title {
@@ -241,7 +247,7 @@ input[type="text"]:focus, input[type="number"]:focus, textarea:focus {
flex-direction: row;
align-items: center;
margin: 3px 0;
- background-color: #121213;
+ background-color: var(--color-background-secondary);
width: 100%;
text-align: left;
}
@@ -251,7 +257,8 @@ input[type="text"]:focus, input[type="number"]:focus, textarea:focus {
user-select: none;
}
-#title input[type="text"], #title textarea {
+#title input[type="text"],
+#title textarea {
flex: 3;
}
@@ -260,7 +267,7 @@ input[type="range"]::-webkit-slider-thumb {
width: 10px;
height: 10px;
border-radius: 100%;
- background: #ccb1d9;
+ background: var(--color-input-range-thumb);
cursor: pointer;
margin: 2px;
}
@@ -268,10 +275,31 @@ input[type="range"]::-webkit-slider-thumb {
input[type="range"] {
-webkit-appearance: none;
width: 100%;
- background-color: #570d7b;
+ background-color: var(--color-input-range-background);
padding: 4px;
}
+#buttonsgen {
+ display: flex;
+ justify-content: right;
+}
+
+#buttonsgen button {
+ display: flex;
+ justify-content: right;
+ background-color: var(--color-button-primary);
+ padding: 10px;
+ margin: 0px 10px;
+ color: var(--color-button-primary-text);
+ transition: background-color 0.3s ease, color 0.3s ease;
+}
+
+#buttonsgen button:hover {
+ background-color: var(--color-button-primary-hover);
+ color: var(--color-button-primary-text-hover);
+
+}
+
.stepper-container {
width: 100%;
display: flex;
@@ -279,7 +307,7 @@ input[type="range"] {
flex-direction: row;
justify-content: space-around;
margin-bottom: 5px;
- background-color: #121213;
+ background-color: var(--color-background-secondary);
font-size: 15px;
font-weight: bold;
}
@@ -292,57 +320,147 @@ input[type="range"] {
width: 60px;
text-align: center;
border: none;
- color: #cba3e5;
-}
-
-.stepper-container #seed1Input {
- flex: 2;
+ color: var(--color-primary-text);
}
.stepper-container span {
- color: #cba3e5;
- /* font-size: 1.1em; */
+ color: var(--color-primary-text);
flex: 1.8;
}
.stepper-container button {
width: 30px;
height: 30px;
+ font-weight: bold;
+ color: var(--color-button-secondary-text);
+ background-color: var(--color-button-secondary);
+
}
+
+
button {
- padding: 5px;
+ padding: 5px;
border: none;
- background-color: #121213;
+ background-color: var(--color-background-secondary);
font-size: 1.2em;
- color: #f4b6ff;
- font-weight: bold;
+ color: var(--color-secondary);
+ font-weight: bold;
cursor: pointer;
- transition: transform 0.3s, box-shadow 0.3s, background-color 0.3s;
- margin-left: 1px;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ margin-left: 1px;
user-select: none;
-}
+}
button:last-child {
- margin-right: 0;
+ margin-right: 0;
+}
+
+button.active {
+ background-color: var(--color-button-secondary-active);
+ color: var(--color-button-secondary-text-active);
+ /* box-shadow: var(--shadow-hover); */
}
button:hover {
- box-shadow: 0px 0px 20px var(--box-shadow-color);
- background-color: #8621b7;
+ background-color: var(--color-button-secondary-hover);
+ color: var(--color-button-secondary-text-hover);
}
-button:active {
- transform: translateY(1px);
- box-shadow: 0px 0px 10px var(--box-shadow-color);
+input[type='number']::-webkit-inner-spin-button,
+input[type='number']::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
}
-
-#canvas {
- border: 1px dashed #570d7b;
+
+input[type='number'] {
+ -moz-appearance: textfield;
+}
+
+.dimension-selector-container {
display: flex;
flex-direction: column;
- align-items: center;
- justify-content: center;
- background-color: #121213;
+ background-color: var(--color-background-secondary);
+ color: var(--color-primary-text);
+ width: 100%;
+ margin-bottom: 4px;
+ padding: 6px 0;
+}
+
+#dimension-selector {
+ display: flex;
+ width: 100%;
+}
+
+.dimension-stepper {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+}
+
+.dimension-stepper label {
+ margin-bottom: 5px;
+ text-align: center;
+ font-size: 0.9em;
+}
+
+.stepper {
+ display: flex;
+}
+
+.dimension-stepper .stepper__button {
+ background-color: var(--color-background-secondary);
+ color: var(--color-primary-text);
+ padding: 5px 10px;
+ cursor: pointer;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ width: 100%;
+ border: none;
+}
+
+.stepper__input {
+ border: 1px solid var(--color-background-secondary);
+ background-color: var(--color-background-secondary);
+ color: var(--color-primary-text);
+ padding: 5px;
+ width: 80px;
+ text-align: center;
+ transition: background-color 0.3s ease, color 0.3s ease;
+}
+
+.swap-btn {
+ background-color: var(--color-background-secondary);
+ border: none;
+ color: var(--color-primary-text);
+ padding: 5px 10px;
+ cursor: pointer;
+ transition: background-color 0.3s ease, color 0.3s ease;
+}
+
+.aspect-ratio-selector__select {
+ border-top: 1px solid var(--color-background-secondary);
+ background-color: var(--color-background-secondary);
+ color: var(--color-primary-text);
+ padding: 5px;
+ width: 100%;
+ outline: none;
+
+}
+
+.stepper__button:hover,
+.swap-btn:hover {
+ /* background-color: var(--color-accent); */
+ /* box-shadow: var(--shadow-hover); */
+ background-color: var(--color-button-secondary-hover);
+ color: var(--color-button-secondary-text-hover);
+ border: none;
+
+}
+
+.dropdown-stepper-container,
+.multi-stepper-container {
+ background-color: var(--color-background-secondary);
+ margin-bottom: 6px;
+ padding: 10px;
}
#main-progress {
@@ -353,12 +471,12 @@ button:active {
z-index: 3;
border-radius: 0;
height: 20px;
- background-color: #2c063f;
+ background-color: var(--color-progress-background);
border: 0;
}
#main-progress::-moz-progress-bar {
- background: #7d0ab6;
+ background: var(--color-highlight);
}
#main-progress::-webkit-progress-bar {
@@ -366,14 +484,14 @@ button:active {
}
#main-progress::-webkit-progress-value {
- background: #7d0ab6;
+ background: var(--color-progress-value);
}
#control {
display: flex;
justify-content: center;
- background-color: #121213;
- border-top: 1px dashed #570d7b;
+ background-color: var(--color-background-secondary);
+ border-top: 1px dashed var(--color-primary);
padding: 6px;
}
@@ -381,16 +499,16 @@ button:active {
height: 20px;
width: 20px;
border-radius: 10%;
- border: 4px dashed;
- border-color: #570d7b #570d7b;
+ border: 4px dashed var(--color-spinner);
margin-right: 10px;
}
-
+
+
.spin {
animation: spin 1s infinite ease-out;
- border-color: #570d7b #7d0ab6 !important;
+ border-color: var(--color-spinner) var(--color-spinner-highlight) !important;
}
-
+
@keyframes spin {
0% {
transform: rotate(0deg);
@@ -402,43 +520,21 @@ button:active {
#loading-area {
display: none;
- background-color: rgba(0, 0, 0, 0.5);
+ background-color: var(--color-background-secondary);
z-index: 100;
display: flex;
justify-content: center;
align-items: center;
}
-@media (max-width: 768px) {
- .content {
- flex-direction: column;
- }
-}
-
-#buttonsgen {
- display: flex;
- justify-content: right;
-}
-
-#buttonsgen button {
- display: flex;
- justify-content: right;
- background-color: #570d7b;
- padding: 10px;
- margin: 0px 10px;
-}
-
-#buttonsgen button:hover {
- background-color: #8621b7;
-}
footer {
- background-color: #121213;
- color: #570d7b;
+ background-color: var(--color-background-secondary);
+ color: var(--color-primary-text);
padding: 15px 50px;
text-align: center;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
- border-top: 1px dashed #570d7b;
+ border-top: 1px dashed var(--color-border);
margin-top: auto;
}
@@ -448,13 +544,13 @@ footer {
align-items: center;
margin-bottom: 4px;
justify-content: space-around;
- background-color: #121213;
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+ background-color: var(--color-background-secondary);
+ box-shadow: 0 4px 8px var(--color-background-secondary);
}
.loader label {
- width: 50%;
- color: #cba3e5;
+ width: 50%;
+ color: var(--color-primary-text);
font-size: 15px;
font-weight: bold;
}
@@ -462,370 +558,679 @@ footer {
select {
width: 100%;
padding: 8px;
- background-color: #121213;
- color: #cba3e5;
+ background-color: var(--color-background-secondary);
+ color: var(--color-primary-text);
border: none;
}
-select::-webkit-scrollbar {
- width: 12px;
- background-color: #121213;
+select:focus {
+ border-color: var(--color-accent);
}
-select::-webkit-scrollbar-thumb {
- background-color: #cba3e5;
- border: 3px solid #121213;
+select option:hover {
+ background-color: var(--color-primary-text);
+ color: var(--color-background-secondary);
}
-select::-webkit-scrollbar-track {
- box-shadow: inset 0 0 5px grey;
-}
-select:focus {
- border-color: #ff85c0;
+#display-media-main {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+ align-items: stretch;
+ width: 100%;
+ height: 550px;
+ overflow: hidden;
}
-select option:hover {
- background-color: #cba3e5;
- color: #121213;
+#load-image-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ gap: 10px;
+ width: 300px;
+ height: 100%;
+ overflow-y: auto;
}
-select option:checked,
-select option:hover {
- background-color: #9b4db4;
- color: white;
+.image-loader {
+ flex: 0 1 auto;
+ width: 100%;
+ min-height: 250px;
+ max-height: 50%;
+ position: relative;
+ border-radius: 5px;
+ border: 2px dashed var(--color-border);
+ /* background: var(--color-background-secondary); */
}
-input[type='number']::-webkit-inner-spin-button,
-input[type='number']::-webkit-outer-spin-button {
- -webkit-appearance: none;
- margin: 0;
-}
+#load-image-container img {
+ max-width: 100%;
+ max-height: 100%;
+ width: auto;
+ height: auto;
+ position: absolute;
+ object-fit: contain;
+ border-radius: 5px;
+ padding: 5px;
+ object-fit: cover;
-input[type='number'] {
- -moz-appearance: textfield;
}
-button.active {
- background-color: #8621b7;
+#image-container {
+ flex: 1;
+ height: 100%;
+ overflow: hidden;
+ position: relative;
+ align-items: center;
+ margin: 4px;
}
-.dimension-selector-container {
- display: flex;
- flex-direction: column;
- background-color: var(--background-color);
- color: var(--text-color);
+#image-container img,
+#image-container video {
width: 100%;
- margin-bottom: 4px;
- padding: 6px 0;
+ height: 100%;
+ position: relative;
+ object-fit: contain;
+ border: 2px dashed var(--color-border);
+ border-radius: 5px;
}
-#dimension-selector {
- display: flex;
+#batch-images-container {
width: 100%;
- border-bottom: 2px solid var(--border-color);
+ height: 100px;
+ border: 1px solid var(--color-primary);
+ position: relative;
}
-.dimension-stepper {
+#batch-images {
+ padding: 10px;
+ background-color: var(--color-background-secondary);
+ border: 1px solid var(--color-border);
+ border-radius: 5px;
+ height: 100%;
+ scrollbar-width: thin;
+ scrollbar-color: var(--color-primary) var(--color-scrollbar-track);
display: flex;
- flex-direction: column;
- width: 100%;
+ flex-direction: row;
}
-.dimension-stepper label {
- margin-bottom: 5px;
+#history h2 {
+ color: var(--color-primary-text);
text-align: center;
- font-size: 0.9em;
+ margin-bottom: 10px;
+ font-size: 1.2em;
}
-.stepper {
- display: flex;
+.history-thumbnail {
+ border: 1px dashed var(--color-border);
+ /* border-radius: 5px; */
+ overflow: hidden;
+ cursor: pointer;
+ position: relative;
}
-.stepper__button {
- background-color: var(--background-color);
- color: var(--text-color);
- padding: 5px 10px;
+.history-thumbnail img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ transition: transform 0.3s;
+}
+
+#history,
+#side-workflow-controls,
+#load-image-container,
+#custom-theme-modal,
+.aspect-ratio-selector,
+.content {
+ scrollbar-width: thin;
+ scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
+ overflow-y: auto;
+}
+
+.custom-scrollbar::-webkit-scrollbar {
+ width: 2px;
+}
+
+.custom-scrollbar::-webkit-scrollbar-track {
+ background: var(--color-scrollbar-track);
+ border-radius: 2px;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb {
+ background-color: var(--color-scrollbar-thumb);
+ border-radius: 2px;
+ border: 2px solid var(--color-scrollbar-track);
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb:hover {
+ background-color: var(--color-accent);
+}
+
+.images-thumbnail {
+ border: 2px solid var(--color-border);
+ /* border-radius: 5px; */
+ overflow: hidden;
cursor: pointer;
- transition: background-color 0.3s, box-shadow 0.3s;
+ position: relative;
+}
+
+.images-thumbnail img {
width: 100%;
- border: none;
+ height: 100%;
+ object-fit: cover;
+ transition: transform 0.3s;
}
-.stepper__input {
- border: 1px solid var(--background-color);
- background-color: var(--background-color);
- color: var(--text-color);
- padding: 5px;
- width: 80px;
- text-align: center;
- transition: border-color 0.3s ease-in-out;
+@media (max-width: 768px) {
+ .content {
+ flex-direction: column;
+ }
}
-.swap-btn {
- background-color: var(--background-color);
+#theme-selector {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border: none;
+ scrollbar-width: thin;
+ scrollbar-color: #570d7b #1e1e1f;
+ overflow-y: auto;
+
+}
+
+#theme-selector-dropdown {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ background-color: var(--color-header-background);
+ color: var(--color-header-text);
border: none;
- color: var(--text-color);
- padding: 5px 10px;
cursor: pointer;
- transition: background-color 0.3s, box-shadow 0.3s;
+ background-repeat: no-repeat;
+ background-position: right 1rem center;
+ background-size: 12px 7px;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ padding-right: 5rem;
}
-.aspect-ratio-selector__select {
- border: 1px solid var(--background-color);
- background-color: var(--background-color);
- color: var(--text-color);
- padding: 5px;
- width: 100%;
+#theme-selector-dropdown optgroup {
+ color: var(--color-header-text);
+
+ font-weight: bold;
+ /* font-style: italic; */
+ /* background-color: #1e1e1f; */
+
}
-.stepper__button:hover, .swap-btn:hover {
- background-color: var(--hover-background-color);
- box-shadow: var(--hover-box-shadow);
+#theme-selector-dropdown:focus {
+ outline: none;
+
}
-input[type="number"]::-webkit-inner-spin-button,
-input[type="number"]::-webkit-outer-spin-button {
- -webkit-appearance: none;
+
+#theme-selector-dropdown:hover {
+ border-color: var(--color-accent);
}
-input[type="number"] {
- -moz-appearance: textfield;
+#theme-selector-dropdown option {
+ color: var(--color-primary-text);
+
+ font-style: none;
+ background-color: var(--color-background);
+
}
-.dropdown-stepper-container, .multi-stepper-container {
- background-color: #121213;
- margin-bottom: 6px;
- padding: 10px;
+#theme-selector-dropdown option:disabled {
+ color: var(--color-primary-text);
+ background-color: var(--color-background);
+ font-weight: bold;
+ font-size: 14px;
+
+
}
-input:hover, input:focus, select:hover, select:focus {
+
+#theme-option-create-custom {
+ color: var(--color-primary-text) !important;
+ font-weight:bolder !important;
+ font-size: 15px;
+ background-color: var(--color-background-secondary) !important;
+
}
-input:focus, select:focus {
- outline: none;
- border: 1px solid var(--focus-outline-color);
+.custom-theme-modal {
+ display: none;
+ position: fixed;
+ z-index: 1000;
+ left: 40%;
+ top: 8%;
+ max-height: 80%;
+ background-color: #1e1e1f !important;
+ padding: 0;
+
+ color: #bababa;
+ border: 1px dashed #320848;
+
}
-*:focus {
- outline: none;
+.custom-theme-modal button {
+ background-color: #1e1e1f;
+ color: #bababa ;
+ font-size: 14px;
+ font-weight: normal;
+
}
-#display-media-main {
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
- align-items: stretch;
- width: 100%;
- height: 550px;
- overflow: hidden;
+
+.custom-theme-modal button:hover{
+ background-color: #7d0ab6;
+ /* color: #1e1e1f; */
}
-#load-image-container {
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- gap: 10px;
- width: 300px;
- height: 100%;
- overflow-y: auto;
+.modal-content {
+ background-color: #fff;
+ /* width: 98%; */
+ /* max-width: 900px; */
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ background-color: #1e1e1f !important;
+
}
-.image-loader {
- flex: 0 1 auto;
- width: 100%;
- min-height: 250px;
- max-height: 50%;
- position: relative;
- border-radius: 5px;
- border: 2px dashed #570d7b;
- background: #121213;
+.modal-header {
+ padding: 16px 24px;
+ background-color: #121212;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
}
-#load-image-container img {
- max-width: 100%;
- max-height: 100%;
- width: auto;
- height: auto;
- position: absolute;
- object-fit: contain;
- border-radius: 5px;
- padding: 5px
- /* border: 2px dashed #570d7b; */
+.modal-header h2 {
+ margin: 0;
+ font-size: 1.5rem;
}
-#image-container {
- flex: 1;
- height: 100%;
- overflow: hidden;
- position: relative;
- /* border: 1px dashed #570d7b; */
- align-items: center;
+.close-button {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+}
- margin: 4px;
+.modal-body {
+ /* padding: 24px; */
+ display: flex;
+ flex-direction: column;
+ padding: 10px;
}
-#image-container img , #image-container video {
- width: 100%;
- height: 100%;
- position: relative;
- object-fit: contain;
- border: 2px dashed #570d7b;
- border-radius: 5px;
- /* margin: 10px; */
+.notification {
+ /* margin-bottom: 16px; */
+ /* padding: 12px; */
+ border-radius: 4px;
+ /* background-color: #e0f7fa; */
+ /* color: #006064; */
+}
+.modal-main {
+ display: flex;
+ flex-direction: row;
+ gap: 24px;
}
-#load-image-container::-webkit-scrollbar {
- width: 8px;
+.left-panel {
+ flex: 2;
+ display: flex;
+ flex-direction: column;
}
-#load-image-container::-webkit-scrollbar-track {
- background: var(--scrollbar-track-color);
- border-radius: 4px;
+
+ .theme-form fieldset {
+ border: none;
+ margin-bottom: 16px;
}
-#load-image-container::-webkit-scrollbar-thumb {
- background-color: var(--scrollbar-thumb-color);
- border-radius: 4px;
- border: 2px solid var(--scrollbar-track-color);
+
+.theme-form legend {
+ font-weight: bold;
+ margin-bottom: 8px;
}
-#load-image-container::-webkit-scrollbar-thumb:hover {
- background-color: var(--hover-background-color);
+
+.form-group {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 12px;
}
-#batch-images-container{
- width: 100%;
- height: 100px;
- border: 1px solid #9303d6;
- position: relative;
- /* display: flex; */
- /* flex-direction: row; */
- gap: 10px;
- /* overflow: hidden; */
- /* padding: 10px;
- background-color: var(--background-color);
- border: 1px solid var(--border-color);
- border-radius: 5px;
- height: 100%;
- overflow-y: auto;
- scrollbar-width: thin;
- scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color); */
+.form-group label {
+ margin-bottom: 4px;
+ font-size: 0.9rem;
}
-#batch-images {
- padding: 10px;
- background-color: var(--background-color);
- border: 1px solid var(--border-color);
- border-radius: 5px;
- height: 100%;
- /* overflow-y: auto; */
- scrollbar-width: thin;
- scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);
+.form-group input[type="text"],
+.form-group select {
+ padding: 8px;
+ font-size: 1rem;
+ border: none;
+ /* border-radius: 4px; */
+ color: #bababa !important;
+ background-color: #131312;
+ border: none;
+ border-bottom: 1px solid #7a7a7a;
+ margin-bottom: 10px;
+}
+
+.form-group input[type="text"]:focus,
+.form-group select:focus {
+ border: none;
+ border-bottom: 1px solid #acabab;
+}
+
+.new-theme-set {
display: flex;
- flex-direction: row;
+ /* flex-direction: row; */
+ /* gap: 10px; */
+ padding: 10px;
+ background-color: #131312;
+ /* border-radius: 5px; */
+ /* margin: 10px; */
+ color: #bababa !important;
}
-#history {
- padding: 10px;
- background-color: var(--background-color);
- border: 1px solid var(--border-color);
- border-radius: 5px;
- height: 100%;
- overflow-y: auto;
- scrollbar-width: thin;
- scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);
+
+
+.variables-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 16px;
+ padding: 8px 0;
+ height: 200px;
+ overflow: auto;
+ margin: 10px;
+
}
-#history::-webkit-scrollbar {
- width: 8px;
+.color-field {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background-color: #131312;
+ padding: 8px;
+
}
-#history::-webkit-scrollbar-track {
- background: var(--scrollbar-track-color);
- border-radius: 4px;
+.color-field label {
+ margin-bottom: 8px;
+ font-size: 0.85rem;
+ text-align: center;
}
-#history::-webkit-scrollbar-thumb {
- background-color: var(--scrollbar-thumb-color);
- border-radius: 4px;
- border: 2px solid var(--scrollbar-track-color);
+.color-field input[type="color"] {
+ width: 50px;
+ height: 50px;
+ padding: 0;
+ border: none;
+ background: none;
+ cursor: pointer;
+ border-radius: 0px;
+ transition: transform 0.2s ease;
}
-#history::-webkit-scrollbar-thumb:hover {
- background-color: var(--hover-background-color);
+.color-field input[type="color"]:hover {
+ transform: scale(1.1);
}
-#side-workflow-controls::-webkit-scrollbar {
- width: 8px;
+.form-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
}
-#side-workflow-controls::-webkit-scrollbar-track {
- background: var(--scrollbar-track-color);
- border-radius: 4px;
+
+.right-panel {
+ flex: 1;
+ border-left: 1px solid #ddd;
+ padding-left: 16px;
}
-#side-workflow-controls::-webkit-scrollbar-thumb {
- background-color: var(--scrollbar-thumb-color);
- border-radius: 4px;
- border: 2px solid var(--scrollbar-track-color);
+.right-panel h3 {
+ margin-top: 0;
+ font-size: 1.2rem;
+ margin-bottom: 12px;
}
-#side-workflow-controls::-webkit-scrollbar-thumb:hover {
- background-color: var(--hover-background-color);
+#custom-themes-listing{
+ display: flex;
+ justify-content: space-between;
+ flex-direction: row;
+
+ gap: 16px;
+ width: 900px;
+ /* height: 200px; */
+ overflow: auto;
+
+
}
+.form-section.css-variables {
+ padding: 16px;
+}
+
+.custom-themes-section {
+ margin-bottom: 24px;
+ /* height: 250px; */
+ overflow: auto;
+ flex: 4;
+ /* background-color: #1976d2; */
-#history h2 {
- color: var(--text-color);
- text-align: center;
- margin-bottom: 10px;
- font-size: 1.2em;
}
-/* #images-container {
- display: flex;
+#custom-themes-input {
+ padding: 8px;
+ font-size: 1rem;
+ /* border: 1px solid #ccc; */
+ border-radius: 4px;
+ /* width: 100px; */
+ flex: 1;
+
+}
+
+#custom-themes-list-container {
+ height: 250px;
+ overflow: auto;
+
+}
+
+#custom-themes-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ font-size: 14px;
+
+}
+
+.custom-themes-list li {
+ padding: 8px 0;
+ /* border-bottom: 1px solid #f0f0f0; */
+ background-color: #222222;
+ margin: 4px 0;
+
+}
+
+@media (max-width: 768px) {
+ .modal-main {
flex-direction: column;
- gap: 10px;
- overflow: hidden;
-} */
+ }
+
+ .right-panel {
+ border-left: none;
+ padding-left: 0;
+ margin-top: 24px;
+ }
+
+ .variables-grid {
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+ }
+}
-.images-thumbnail {
- /* width: 100%; */
- /* max-width: 400px; */
- /* height: 200px; */
- border: 2px solid var(--border-color);
- border-radius: 5px;
- overflow: hidden;
+
+.new-theme-set input[type="text"] {
+ flex: 1;
+ color: #bababa !important;
+
+ }
+
+#custom-theme-modal {
+ scrollbar-width: thin;
+ scrollbar-color: #570d7b #1e1e1f;
+ overflow-y: auto;
+}
+
+#custom-theme-modal::-webkit-scrollbar {
+ width: 1px;
+}
+
+#custom-theme-modal::-webkit-scrollbar-track {
+ background: #1e1e1f;
+ border-radius: 1px ;
+}
+
+#custom-theme-modal::-webkit-scrollbar-thumb {
+ background-color: #1e1e1f;
+ border-radius: 1px ;
+ border: 1px solid 570d7b;
+}
+
+#custom-theme-modal::-webkit-scrollbar-thumb:hover {
+ background-color: #1e1e1f;
+}
+
+#save-theme-button {
+ position: absolute;
+ background-color: #570d7b;
+ color: #bababa;
+ padding: 10px;
+ margin: 0 10px;
+ border: none;
cursor: pointer;
- position: relative;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ align-self: flex-end;
}
-.images-thumbnail img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- transition: transform 0.3s;
+#save-and-continue-theme-button {
+ position: absolute;
+ right: 200px;
+ background-color: #570d7b;
+ color: #bababa;
+ padding: 10px;
+ margin: 0 10px;
+ border: none;
+ cursor: pointer;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ align-self: flex-end;
}
-.history-thumbnail {
- /* width: 100%; */
- /* max-width: 400px; */
- /* height: 200px; */
- border: 2px solid var(--border-color);
- border-radius: 5px;
- overflow: hidden;
+
+#save-theme-set-button {
+ background-color: #570d7b;
+ color: #bababa;
+ padding: 10px;
+ margin: 0 10px;
+ border: none;
cursor: pointer;
- position: relative;
+ transition: background-color 0.3s ease, color 0.3s ease;
}
-.history-thumbnail img {
+#save-theme-button:hover, #save-and-continue-theme-button:hover ,#save-theme-set-button:hover {
+ background-color: #8621b7;
+ color: #bababa;
+}
+
+#theme-name {
+ flex: 1;
+ transition: border-color 0.3s ease-in-out;
+ padding: 8px;
+ background-color: #131312;
+ margin: 0;
width: 100%;
- height: 100%;
- object-fit: cover;
- transition: transform 0.3s;
}
-.history-thumbnail:hover img {
- /* transform: scale(1.05); */
+#theme-set-select {
+ border: none;
+ outline: none;
+
+}
+
+@keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+.fade-out {
+ animation: fadeOut 0.3s forwards;
+}
+
+.fade-in {
+ animation: fadeIn 0.3s forwards;
}
+@keyframes fadeSlideDown {
+ from {
+ opacity: 0;
+ transform: translateY(-30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+body {
+ transition: transform 0.3s ease-out, opacity 0.3s ease-out;
+ will-change: transform, opacity;
+}
+html.css-loading body {
+ opacity: 0;
+ transform: translateY(-30px);
+}
+
+html:not(.css-loading) body {
+ opacity: 1;
+ transform: translateY(0);
+ animation: fadeSlideDown 0.3s forwards;
+}
+
+#css-loading-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #ffffff;
+ opacity: 1;
+ transition: opacity 0.3s ease-out;
+ will-change: opacity;
+ z-index: 9999;
+}
+
+#css-loading-overlay.fade-out {
+ opacity: 0;
+ pointer-events: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ body {
+ opacity: 1;
+ transform: translateY(0);
+ transition: none;
+ }
+ #css-loading-overlay {
+ opacity: 0;
+ transition: none;
+ }
+}
diff --git a/web/core/css/themes.css b/web/core/css/themes.css
new file mode 100644
index 0000000..d1251b8
--- /dev/null
+++ b/web/core/css/themes.css
@@ -0,0 +1,301 @@
+
+[data-theme="flow-dark"] {
+ --color-primary: #430474;
+ --color-secondary: #f4b6ff;
+ --color-accent: #8621b7;
+ --color-highlight: #7d0ab6;
+ --color-primary-text: #cba3e5;
+ --color-border: #570d7b;
+ --color-background: #181b1d;
+ --color-background-secondary: #131312;
+ --color-background-pattern: #4d0f7c;
+ --color-header-background: #131312;
+ --color-header-logo-text: #570d7b;
+ --color-header-text:#570d7b;
+ --color-social-icons: #570d7b;
+ --color-button-primary: #570d7b;
+ --color-button-primary-hover: #8621b7;
+ --color-button-primary-text: #f4b6ff;
+ --color-button-primary-text-hover: #131312;
+ --color-button-primary-active: #8621b7;
+ --color-button-primary-text-active: #8621b7;
+ --color-button-secondary: #131312;
+ --color-button-secondary-hover: #8621b7;
+ --color-button-secondary-text: #f4b6ff;
+ --color-button-secondary-text-hover: #131312;
+ --color-button-secondary-active: #8621b7;
+ --color-button-secondary-text-active: #f4b6ff;
+ --color-progress-background: #2c063f;
+ --color-progress-value:#7d0ab6;
+ --color-scrollbar-track: #1e1e1f;
+ --color-scrollbar-thumb: #570d7b;
+ --color-input-range-thumb: #f4b6ff;
+ --color-input-range-background: #570d7b;
+ --color-spinner:#570d7b;
+ --color-spinner-highlight:#7d0ab6;
+}
+[data-theme="monochrome-noir"] {
+ --color-background: #000000;
+ --color-primary-text: #888888;
+ --color-background-pattern: #424242;
+ --color-border: #7a7a7a;
+ --color-header-background: #131312;
+ --color-header-text: #888888;
+ --color-background-secondary: #0d0d0d;
+ --color-button-primary: #000000;
+ --color-button-primary-text: #888888;
+ --color-button-primary-hover: #707070;
+ --color-button-primary-text-hover: #000000;
+ --color-button-primary-active: #d9d8d8;
+ --color-button-primary-text-active: #707070;
+ --color-button-secondary: #0d0d0d;
+ --color-button-secondary-text: #888888;
+ --color-button-secondary-hover: #707070;
+ --color-button-secondary-text-hover: #000000;
+ --color-button-secondary-active: #707070;
+ --color-button-secondary-text-active: #000000;
+ --color-progress-background: #000000;
+ --color-progress-value: #908989;
+ --color-scrollbar-track: #1e1e1f;
+ --color-scrollbar-thumb: #7a7a7a;
+ --color-input-range-thumb: #0d0d0d;
+ --color-input-range-background: #707070;
+ --color-spinner: #908989;
+ --color-spinner-highlight: #908989;
+}
+
+[data-theme="monochrome-blanc"] {
+ --color-background: #ffffff;
+ --color-primary-text: #5a5a5a;
+ --color-background-pattern: #e5e5e5;
+ --color-border: #d0d0d0;
+ --color-header-background: #f7f7f7;
+ --color-header-text: #5a5a5a;
+ --color-background-secondary: #f9f9f9;
+ --color-button-primary: #c0c0c0;
+ --color-button-primary-text: #5a5a5a;
+ --color-button-primary-hover: #5a5a5a;
+ --color-button-primary-text-hover: #ffffff;
+ --color-button-primary-active: #f9f9f9;
+ --color-button-primary-text-active: #c0c0c0;
+ --color-button-secondary: #f9f9f9;
+ --color-button-secondary-text: #5a5a5a;
+ --color-button-secondary-hover: #c0c0c0;
+ --color-button-secondary-text-hover: #ffffff;
+ --color-button-secondary-active: #c0c0c0;
+ --color-button-secondary-text-active: #5a5a5a;
+ --color-progress-background: #f9f9f9;
+ --color-progress-value: #b5b5b5;
+ --color-scrollbar-track: #f2f2f2;
+ --color-scrollbar-thumb: #c0c0c0;
+ --color-input-range-thumb: #5a5a5a;
+ --color-input-range-background: #c0c0c0;
+ --color-spinner: #b5b5b5;
+ --color-spinner-highlight: #ffffff;
+}
+[data-theme="shadow-realm"] {
+ --color-header-background: #131312;
+ --color-header-text: #888888;
+ --color-background: #212121;
+ --color-background-pattern: #424242;
+ --color-primary-text: #bababa;
+ --color-border: #454545;
+ --color-background-secondary: #171717;
+ --color-button-primary: #707070;
+ --color-button-primary-hover: #1f1f1f;
+ --color-button-primary-text: #171717;
+ --color-button-primary-text-hover: #707070;
+ --color-button-primary-active: #000000;
+ --color-button-primary-text-active: #707070;
+ --color-button-secondary: #171717;
+ --color-button-secondary-hover: #707070;
+ --color-button-secondary-text: #888888;
+ --color-button-secondary-text-hover: #000000;
+ --color-button-secondary-active: #707070;
+ --color-button-secondary-text-active: #000000;
+ --color-progress-background: #1f1f1f;
+ --color-progress-value: #707070;
+ --color-scrollbar-track: #1e1e1f;
+ --color-scrollbar-thumb: #7a7a7a;
+ --color-input-range-thumb: #000000;
+ --color-input-range-background: #707070;
+ --color-spinner: #908989;
+ --color-spinner-highlight: #908989;
+}
+[data-theme="dawn-realm"] {
+ --color-background: #707070;
+ --color-primary-text: #131312;
+ --color-background-pattern: #424242;
+ --color-border: #454545;
+ --color-header-background: #707070;
+ --color-header-text: #131312;
+ --color-background-secondary: #888888;
+ --color-button-primary: #707070;
+ --color-button-primary-text: #171717;
+ --color-button-primary-hover: #1f1f1f;
+ --color-button-primary-text-hover: #707070;
+ --color-button-primary-active: #000000;
+ --color-button-primary-text-active: #707070;
+ --color-button-secondary: #888888;
+ --color-button-secondary-text: #1f1f1f;
+ --color-button-secondary-hover: #707070;
+ --color-button-secondary-text-hover: #000000;
+ --color-button-secondary-active: #707070;
+ --color-button-secondary-text-active: #000000;
+ --color-progress-background: #1f1f1f;
+ --color-progress-value: #707070;
+ --color-scrollbar-track: #1e1e1f;
+ --color-scrollbar-thumb: #7a7a7a;
+ --color-input-range-thumb: #000000;
+ --color-input-range-background: #707070;
+ --color-spinner: #171717;
+ --color-spinner-highlight: #171717;
+}
+[data-theme="terminal-dark"] {
+ --color-background: #000000;
+ --color-primary-text: #00ff00;
+ --color-background-pattern: #003300;
+ --color-border: #00ff00;
+ --color-header-background: #000000;
+ --color-header-text: #00ff00;
+ --color-background-secondary: #0a0b0a;
+ --color-button-primary: #00ff00;
+ --color-button-primary-text: #000000;
+ --color-button-primary-hover: #003300;
+ --color-button-primary-text-hover: #00ff00;
+ --color-button-primary-active: #003300;
+ --color-button-primary-text-active: #00ff00;
+ --color-button-secondary: #000000;
+ --color-button-secondary-text: #00ff00;
+ --color-button-secondary-hover: #00ff00;
+ --color-button-secondary-text-hover: #000000;
+ --color-button-secondary-active: #00ff00;
+ --color-button-secondary-text-active: #000000;
+ --color-progress-background: #001a00;
+ --color-progress-value: #00ff00;
+ --color-scrollbar-track: #001a00;
+ --color-scrollbar-thumb: #00ff00;
+ --color-input-range-thumb: #00ff00;
+ --color-input-range-background: #001a00;
+ --color-spinner: #00ff00;
+ --color-spinner-highlight: #00ff00;
+}
+[data-theme="terminal-soft-dark"] {
+ --color-background: #0a0a0a;
+ --color-primary-text: #82e082;
+ --color-background-pattern: #0f1e0f;
+ --color-border: #369136;
+ --color-header-background: #0a0a0a;
+ --color-header-text: #82e082;
+ --color-background-secondary: #1a1a1a;
+ --color-button-primary: #4ec94e;
+ --color-button-primary-text: #0a0a0a;
+ --color-button-primary-hover: #5aff5a;
+ --color-button-primary-text-hover: #101010;
+ --color-button-primary-active: #7aff7a;
+ --color-button-primary-text-active: #0a0a0a;
+ --color-button-secondary: #1a1a1a;
+ --color-button-secondary-text: #82e082;
+ --color-button-secondary-hover: #4ec94e;
+ --color-button-secondary-text-hover: #0a0a0a;
+ --color-button-secondary-active: #4ec94e;
+ --color-button-secondary-text-active: #1a1a1a;
+ --color-progress-background: #1a2b1a;
+ --color-progress-value: #7aff7a;
+ --color-scrollbar-track: #151515;
+ --color-scrollbar-thumb: #4ec94e;
+ --color-input-range-thumb: #1a1a1a;
+ --color-input-range-background: #4ec94e;
+ --color-spinner: #1a2b1a;
+ --color-spinner-highlight: #7aff7a;
+}
+[data-theme="ink-dark"] {
+ --color-background: #000000;
+ --color-primary-text: #ffffff;
+ --color-background-pattern: #727272;
+ --color-border: #ffffff;
+ --color-header-background: #000000;
+ --color-header-text: #ffffff;
+ --color-background-secondary: #000000;
+ --color-button-primary: #ffffff;
+ --color-button-primary-text: #000000;
+ --color-button-primary-hover: #000000;
+ --color-button-primary-text-hover: #ffffff;
+ --color-button-primary-active: #ffffff;
+ --color-button-primary-text-active: #000000;
+ --color-button-secondary: #000000;
+ --color-button-secondary-text: #ffffff;
+ --color-button-secondary-hover: #ffffff;
+ --color-button-secondary-text-hover: #000000;
+ --color-button-secondary-active: #ffffff;
+ --color-button-secondary-text-active: #000000;
+ --color-progress-background: #0f0f0f;
+ --color-progress-value: #ffffff;
+ --color-scrollbar-track: #000000;
+ --color-scrollbar-thumb: #ffffff;
+ --color-input-range-thumb: #141414;
+ --color-input-range-background: #ffffff;
+ --color-spinner: #0f0f0f;
+ --color-spinner-highlight: #0f0f0f;
+}
+[data-theme="paper-light"] {
+ --color-background: #ffffff;
+ --color-primary-text: #0d0d0d;
+ --color-background-pattern: #808080;
+ --color-border: #d0d0d0;
+ --color-header-background: #ffffff;
+ --color-header-text: #5a5a5a;
+ --color-background-secondary: #ffffff;
+ --color-button-primary: #171717;
+ --color-button-primary-text: #f9f9f9;
+ --color-button-primary-hover: #dbdbdb;
+ --color-button-primary-text-hover: #5a5a5a;
+ --color-button-primary-active: #fcfcfc;
+ --color-button-primary-text-active: #fcfcfc;
+ --color-button-secondary: #ffffff;
+ --color-button-secondary-text: #5a5a5a;
+ --color-button-secondary-hover: #121212;
+ --color-button-secondary-text-hover: #ffffff;
+ --color-button-secondary-active: #121212;
+ --color-button-secondary-text-active: #ffffff;
+ --color-progress-background: #f9f9f9;
+ --color-progress-value: #b5b5b5;
+ --color-scrollbar-track: #f2f2f2;
+ --color-scrollbar-thumb: #c0c0c0;
+ --color-input-range-thumb: #ffffff;
+ --color-input-range-background: #121212;
+ --color-spinner: #121212;
+ --color-spinner-highlight: #121212;
+}
+
+[data-theme="glimpse-of-life"] {
+ --color-primary-text: #bababa;
+ --color-border: #454545;
+ --color-background: #212121;
+ --color-background-secondary: #171717;
+ --color-background-pattern: #424242;
+ --color-header-background: #131312;
+ --color-header-logo-text: #888888;
+ --color-header-text: #888888;
+ --color-social-icons: #888888;
+ --color-button-primary: #457434;
+ --color-button-primary-hover: #1f1f1f;
+ --color-button-primary-text: #171717;
+ --color-button-primary-text-hover: #707070;
+ --color-button-primary-active: #000000;
+ --color-button-primary-text-active: #707070;
+ --color-button-secondary: #171717;
+ --color-button-secondary-hover: #707070;
+ --color-button-secondary-text: #888888;
+ --color-button-secondary-text-hover: #000000;
+ --color-button-secondary-active: #707070;
+ --color-button-secondary-text-active: #000000;
+ --color-progress-background: #1f1f1f;
+ --color-progress-value: #707070;
+ --color-scrollbar-track: #1e1e1f;
+ --color-scrollbar-thumb: #7a7a7a;
+ --color-input-range-thumb: #000000;
+ --color-input-range-background: #457434;
+ --color-spinner: #908989;
+ --color-spinner-highlight: #908989;
+}
\ No newline at end of file
diff --git a/web/core/js/common/components/ImageLoader.js b/web/core/js/common/components/ImageLoader.js
index 90dfd17..4d32e94 100644
--- a/web/core/js/common/components/ImageLoader.js
+++ b/web/core/js/common/components/ImageLoader.js
@@ -3,7 +3,7 @@ import { showSpinner, hideSpinner } from './utils.js';
export default class ImageLoader {
static DEFAULT_CONFIG = {
allowedFileType: 'video',
- defaultImageSrc: '../core/media/ui/drop_image_rect_no_border.png',
+ defaultImageSrc: '../core/media/ui/drop_image_rect_no_border_trans.png',
showIndicator: false,
};
diff --git a/web/core/js/common/components/header.js b/web/core/js/common/components/header.js
index 43bf776..5a2a5d5 100644
--- a/web/core/js/common/components/header.js
+++ b/web/core/js/common/components/header.js
@@ -15,12 +15,14 @@ const headerHTML = `
Recent
${appName}s
+
+
Support in keeping the flow.
-
@@ -28,13 +30,13 @@ const headerHTML = `
-
+
diff --git a/web/core/js/common/components/imageLoaderComp.js b/web/core/js/common/components/imageLoaderComp.js
index ab0bf82..32b84db 100644
--- a/web/core/js/common/components/imageLoaderComp.js
+++ b/web/core/js/common/components/imageLoaderComp.js
@@ -35,7 +35,7 @@ export default function imageLoaderComp(flowConfig, workflow) {
const containerId = loadImageContainer.id;
const imageLoader = new ImageLoader(containerId, {
allowedFileType: 'image',
- defaultImageSrc: '../core/media/ui/drop_image_rect_no_border.png',
+ defaultImageSrc: '../core/media/ui/drop_image_rect_no_border_trans.png',
showIndicator: true,
}, (localSrc, serverResult) => {
console.log(`Image ${index + 1} loaded:`, serverResult);
diff --git a/web/core/js/common/scripts/ThemeManager.js b/web/core/js/common/scripts/ThemeManager.js
new file mode 100644
index 0000000..c0a5623
--- /dev/null
+++ b/web/core/js/common/scripts/ThemeManager.js
@@ -0,0 +1,1157 @@
+
+class ThemeManager {
+ constructor(preferencesManager, cssPath = '/core/css/themes.css') {
+ this.preferencesManager = preferencesManager;
+ this.themes = [];
+ this.customThemes = [];
+ this.externalCustomThemes = [];
+ this.targetElementId = 'theme-selector';
+ this.cssPath = cssPath;
+ this.customThemeModalId = 'custom-theme-modal';
+ this.isDragging = false;
+ this.dragOffset = { x: 0, y: 0 };
+ this.animationFrame = null;
+ this.currentEditingThemeId = null;
+ this.themeCache = new Map();
+ this.themeSets = [];
+ this.customLabels = {
+ '--color-background': 'Background',
+ '--color-primary-text': 'Text',
+ '--color-background-pattern': 'Background Pattern',
+ '--color-border': 'Border',
+ '--color-header-background': 'Header Background',
+ '--color-header-text': 'Header Text',
+ '--color-background-secondary': 'Widget Background',
+ '--color-button-primary': 'Button',
+ '--color-button-primary-text': 'Button Text',
+ '--color-button-primary-hover': 'Button Hover',
+ '--color-button-primary-text-hover': 'Button Text Hover',
+ '--color-button-primary-active': 'Button Active',
+ '--color-button-primary-text-active': 'Button Text Active',
+ '--color-button-secondary': 'Widget Button',
+ '--color-button-secondary-text': 'Widget Button Text',
+ '--color-button-secondary-hover': 'Widget Button Hover',
+ '--color-button-secondary-text-hover': 'Widget Button Text Hover',
+ '--color-button-secondary-active': 'Widget Button Active',
+ '--color-button-secondary-text-active': 'Widget Button Text Active',
+ '--color-progress-background': 'Progress Background',
+ '--color-progress-value': 'Progress Value',
+ '--color-scrollbar-track': 'Scrollbar Track',
+ '--color-scrollbar-thumb': 'Scrollbar Thumb',
+ '--color-input-range-thumb': 'Widget Range Thumb',
+ '--color-input-range-background': 'Widget Range Background',
+ '--color-spinner': 'Spinner',
+ '--color-spinner-highlight': 'Spinner Highlight',
+ };
+ this.cssVariables = Object.keys(this.customLabels);
+ this.onMouseMove = this.onMouseMove.bind(this);
+ this.onMouseUp = this.onMouseUp.bind(this);
+ }
+
+ static applyInitialTheme(preferencesManager) {
+ let themeToApply = preferencesManager.get('selectedTheme');
+ if (!themeToApply) {
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ themeToApply = prefersDark ? 'flow-dark' : 'flow-light';
+ }
+ document.documentElement.setAttribute('data-theme', themeToApply);
+ }
+
+ async init() {
+ try {
+ document.documentElement.classList.add('css-loading');
+ this.loadCustomThemesFromStorage();
+ this.applySavedTheme();
+ await this.loadThemesFromCSS();
+ await this.loadExternalCustomThemes();
+ this.loadThemeSetsFromStorage();
+ this.addMenu();
+ this.setupCustomThemeModal();
+ } catch (error) {
+ console.error('Failed to initialize ThemeManager:', error);
+ } finally {
+ document.documentElement.classList.remove('css-loading');
+
+ const overlay = document.getElementById('css-loading-overlay');
+ if (overlay) {
+ overlay.addEventListener('transitionend', () => {
+ if (overlay.parentNode) {
+ overlay.parentNode.removeChild(overlay);
+ }
+ });
+ setTimeout(() => {
+ if (overlay.parentNode) {
+ overlay.parentNode.removeChild(overlay);
+ }
+ }, 50);
+ }
+ }}
+
+ getTheme(themeValue) {
+ if (!this.themeCache.has(themeValue)) {
+ const theme = this.themes.find(t => t.value === themeValue) ||
+ this.customThemes.find(t => t.value === themeValue) ||
+ this.externalCustomThemes.flatMap(s => s.themes).find(t => t.value === themeValue);
+ if (theme) {
+ this.themeCache.set(themeValue, theme);
+ }
+ }
+ return this.themeCache.get(themeValue);
+ }
+
+ async loadThemesFromCSS() {
+ try {
+ const response = await fetch(this.cssPath);
+ const cssText = await response.text();
+ this.parseThemesFromCSS(cssText);
+ } catch (error) {
+ console.error('Failed to load themes:', error);
+ }
+ }
+
+ parseThemesFromCSS(cssText) {
+ const themeRegex = /\[data-theme="([^"]+)"\]\s*\{([^}]+)\}/g;
+ let match;
+
+ while ((match = themeRegex.exec(cssText)) !== null) {
+ const themeValue = match[1];
+ const themeName = this.formatThemeName(themeValue);
+ const variablesBlock = match[2];
+ const variables = this.parseCSSVariables(variablesBlock);
+
+ if (!this.themes.some(theme => theme.value === themeValue)) {
+ this.themes.push({ name: themeName, value: themeValue, variables: variables });
+ }
+ }
+
+ if (this.themes.length === 0) {
+ console.warn('No themes found in the CSS file.');
+ } else {
+ console.info(`Loaded ${this.themes.length} themes from CSS.`);
+ }
+
+ if (!this.themes.some(t => t.value === 'create-custom')) {
+ this.themes.push({ name: 'Create Custom Theme', value: 'create-custom', variables: {} });
+ }
+ }
+
+ parseCSSVariables(cssBlock) {
+ const variableRegex = /(--[\w-]+)\s*:\s*([^;]+);/g;
+ let match;
+ const variables = {};
+
+ while ((match = variableRegex.exec(cssBlock)) !== null) {
+ const variable = match[1];
+ const value = match[2].trim();
+ variables[variable] = value;
+ }
+
+ return variables;
+ }
+
+ async loadExternalCustomThemes() {
+ try {
+ const response = await fetch('/core/css/themes/list');
+ if (!response.ok) {
+ throw new Error('Failed to fetch the list of custom theme CSS files.');
+ }
+ const cssFiles = await response.json();
+
+ for (const fileName of cssFiles) {
+ if (!fileName.endsWith('.css')) continue;
+ const styleName = this.formatStyleName(fileName);
+
+ try {
+ const cssResponse = await fetch(`/core/css/themes/${fileName}`);
+ if (!cssResponse.ok) {
+ console.warn(`Failed to load CSS file: ${fileName}`);
+ continue;
+ }
+ const cssText = await cssResponse.text();
+
+ const themes = this.extractThemesFromCSS(cssText).map(theme => ({
+ ...theme,
+ themesSetName: styleName,
+ }));
+ if (themes.length === 0) {
+ console.warn(`No valid themes found in CSS file: ${fileName}`);
+ continue;
+ }
+
+ this.externalCustomThemes.push({
+ styleName: styleName,
+ themes: themes
+ });
+
+ this.appendExternalCSS(fileName, cssText);
+ } catch (error) {
+ console.error(`Error loading custom theme from file ${fileName}:`, error);
+ }
+ }
+
+ if (this.externalCustomThemes.length === 0) {
+ console.info('No external custom themes loaded.');
+ } else {
+ console.info(`Loaded ${this.externalCustomThemes.length} external custom theme styles.`);
+ }
+ } catch (error) {
+ console.error('Error loading external custom themes:', error);
+ }
+ }
+
+ extractThemesFromCSS(cssText) {
+ const themeRegex = /\[data-theme="([^"]+)"\]\s*\{([^}]+)\}/g;
+ let match;
+ const themes = [];
+
+ while ((match = themeRegex.exec(cssText)) !== null) {
+ const themeValue = match[1];
+ const themeName = this.formatThemeName(themeValue);
+ const variablesBlock = match[2];
+ const variables = this.parseCSSVariables(variablesBlock);
+
+ if (!themes.some(theme => theme.value === themeValue)) {
+ themes.push({ name: themeName, value: themeValue, variables: variables });
+ }
+ }
+
+ return themes;
+ }
+
+ formatStyleName(fileName) {
+ const nameWithoutExt = fileName.replace('.css', '');
+ const nameWithSpaces = nameWithoutExt.replace(/[_-]+/g, ' ');
+ return nameWithSpaces
+ .split(' ')
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(' ');
+ }
+
+ formatThemeName(themeValue) {
+ if (this.customLabels[themeValue]) {
+ return this.customLabels[themeValue];
+ }
+ return themeValue
+ .split('-')
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(' ');
+ }
+
+ applySavedTheme() {
+ const savedTheme = this.preferencesManager.get('selectedTheme');
+ if (savedTheme) {
+ document.documentElement.setAttribute('data-theme', savedTheme);
+ }
+ }
+
+ applyTheme(theme) {
+ document.documentElement.setAttribute('data-theme', theme);
+ this.preferencesManager.set('selectedTheme', theme);
+ }
+
+ addMenu() {
+ const targetElement = document.getElementById(this.targetElementId);
+ console.debug(`Attempting to add/populate theme selector in element: ${this.targetElementId}`);
+
+ if (!targetElement) {
+ console.error(`Element with ID "${this.targetElementId}" not found. Theme selector will not be injected.`);
+ return;
+ }
+
+ let selector = targetElement.querySelector('#theme-selector-dropdown');
+
+ if (!selector) {
+ selector = document.createElement('select');
+ selector.id = 'theme-selector-dropdown';
+ selector.style.cursor = 'pointer';
+ selector.setAttribute('aria-label', 'Select Theme');
+
+ targetElement.appendChild(selector);
+ console.info('Theme selector dropdown created and injected.');
+ } else {
+ console.info('Theme selector dropdown already exists. Populating with themes.');
+ selector.innerHTML = '';
+ }
+
+ const defaultOption = document.createElement('option');
+ defaultOption.value = '';
+ defaultOption.textContent = 'Set Theme';
+ defaultOption.disabled = true;
+ defaultOption.selected = true;
+ selector.appendChild(defaultOption);
+
+ this.themes.forEach(theme => {
+ if (theme.value !== 'create-custom') {
+ const option = document.createElement('option');
+ option.value = theme.value;
+ option.textContent = theme.name;
+ selector.appendChild(option);
+ }
+ });
+
+ const createCustomOption = document.createElement('option');
+ createCustomOption.value = 'create-custom';
+ createCustomOption.textContent = 'Create Custom Theme';
+ createCustomOption.id = `theme-option-create-custom`;
+
+ selector.appendChild(createCustomOption);
+
+ if (this.externalCustomThemes.length + this.customThemes.length > 0) {
+ const customThemesHeader = document.createElement('option');
+ customThemesHeader.textContent = 'Custom Themes';
+ customThemesHeader.disabled = true;
+ selector.appendChild(customThemesHeader);
+
+ const allCustomThemes = [
+ ...this.externalCustomThemes.flatMap(style => style.themes),
+ ...this.customThemes
+ ];
+
+ const groupedThemes = this.groupThemesBySet(allCustomThemes);
+
+ for (const [setName, themes] of groupedThemes) {
+ const optgroup = document.createElement('optgroup');
+ optgroup.label = setName;
+
+ themes.forEach(theme => {
+ const option = document.createElement('option');
+ option.value = theme.value;
+ option.textContent = theme.name;
+ optgroup.appendChild(option);
+ });
+
+ selector.appendChild(optgroup);
+ }
+ }
+
+ const currentTheme = this.preferencesManager.get('selectedTheme') || this.getDefaultTheme();
+ selector.value = currentTheme;
+
+ selector.removeEventListener('change', this.handleThemeChange);
+ selector.addEventListener('change', this.handleThemeChange.bind(this));
+
+ console.info('Theme selector dropdown populated with themes.');
+ }
+
+ groupThemesBySet(themes) {
+ const map = new Map();
+ themes.forEach(theme => {
+ const setName = theme.themesSetName || 'Default Set';
+ if (!map.has(setName)) {
+ map.set(setName, []);
+ }
+ map.get(setName).push(theme);
+ });
+ return map;
+ }
+
+ getDefaultTheme() {
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ const preferredTheme = this.themes.find(theme =>
+ theme.value.toLowerCase().includes(prefersDark ? 'dark' : 'light')
+ );
+ return preferredTheme ? preferredTheme.value : this.themes[0]?.value || 'flow-dark';
+ }
+
+ handleThemeChange(e) {
+ const selectedTheme = e.target.value;
+ if (selectedTheme === 'create-custom') {
+ this.openCustomThemeModal();
+ } else {
+ this.applyTheme(selectedTheme);
+ }
+ }
+
+ setupCustomThemeModal() {
+ if (document.getElementById(this.customThemeModalId)) {
+ console.warn('Custom Theme Modal already exists.');
+ return;
+ }
+
+ const modalHTML = `
+ Flow
-
+
+}
+
+.mid-col::-webkit-scrollbar {
+ width: 8px;
+}
+
+.mid-col::-webkit-scrollbar-track {
+ background: var(--color-scrollbar-track);
+ border-radius: var(--border-radius);
+}
+
+.mid-col::-webkit-scrollbar-thumb {
+ background-color: var(--color-primary);
+ border-radius: var(--border-radius);
+ border: 2px solid var(--color-scrollbar-track);
+}
+
+.mid-col::-webkit-scrollbar-thumb:hover {
+ background-color: var(--color-highlight);
+}
+
+@keyframes animation-text {
+ 0% { opacity: 0; }
+ 40% { opacity: 0; }
+ 100% { opacity: 1; }
+}
+
+@keyframes animation-left {
+ from { left: 30px; }
+ to { left: -30px; }
+}
+
+@keyframes animation-right {
+ from { right: 30px; }
+ to { right: -30px; }
+}
+
+.appName {
+ padding: 10px;
+ color: var(--color-header-logo-text);
+ border-top: 1px dashed var(--color-border);
+ border-left: 1px dashed var(--color-border);
+ border-right: 1px dashed var(--color-border);
+ font-size: 12px;
+}
+
+
+
+ `;
+
+ document.body.insertAdjacentHTML('beforeend', modalHTML);
+ const style = document.createElement('style');
+ style.textContent = `
+ CSS
+ `;
+
+ // document.head.appendChild(style);
+ this.attachModalEventListeners();
+ }
+
+ attachModalEventListeners() {
+ const modal = document.getElementById(this.customThemeModalId);
+ if (!modal) return;
+
+ const closeButton = modal.querySelector('.close-button');
+ if (closeButton) {
+ closeButton.addEventListener('click', this.closeCustomThemeModal.bind(this));
+ }
+
+ const modalHeader = modal.querySelector('.modal-header');
+ if (modalHeader) {
+ modalHeader.addEventListener('mousedown', this.onMouseDown.bind(this));
+ }
+
+ const saveThemeSetButton = modal.querySelector('#save-theme-set-button');
+ if (saveThemeSetButton) {
+ saveThemeSetButton.addEventListener('click', () => this.addThemeSet());
+ }
+
+ const form = modal.querySelector('#custom-theme-form');
+ if (form) {
+ form.addEventListener('submit', (event) => {
+ event.preventDefault();
+ this.saveCustomTheme(true);
+ });
+
+ const saveAndContinueButton = modal.querySelector('#save-and-continue-theme-button');
+ if (saveAndContinueButton) {
+ saveAndContinueButton.addEventListener('click', () => this.saveCustomTheme(false));
+ }
+ }
+
+ this.cssVariables.forEach(variable => {
+ const colorInput = modal.querySelector(`#${this.escapeHTML(variable)}`);
+ if (colorInput) {
+ colorInput.addEventListener('input', (e) => {
+ const colorValue = e.target.value;
+ this.applyInlineCustomCSS(variable, colorValue);
+ });
+ }
+ });
+
+ this.populateCustomThemesList();
+ }
+
+ escapeHTML(str) {
+ return str.replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ }
+
+ addThemeSet() {
+ const input = document.getElementById('new-theme-set-name');
+ if (!input) {
+ this.displayNotification('Theme Set input field not found.', 'error');
+ return;
+ }
+
+ const setName = input.value.trim();
+ if (!setName) {
+ this.displayNotification('Theme Set name cannot be empty.', 'error');
+ return;
+ }
+
+ if (this.themeSets.includes(setName)) {
+ this.displayNotification('A Theme Set with this name already exists.', 'error');
+ return;
+ }
+
+ this.themeSets.push(setName);
+ this.saveThemeSetsToStorage();
+ this.updateThemeSetDropdown();
+ this.updateCustomThemesListUI();
+ input.value = '';
+ this.displayNotification(`Theme Set "${setName}" has been added.`, 'success');
+ }
+
+ deleteThemeSet(setName) {
+ const confirmDelete = confirm(`Are you sure you want to delete the Theme Set "${setName}" and all its themes?`);
+ if (!confirmDelete) return;
+
+ this.themeSets = this.themeSets.filter(set => set !== setName);
+ this.saveThemeSetsToStorage();
+
+ const themesToRemove = this.customThemes.filter(theme => theme.themesSetName === setName);
+ themesToRemove.forEach(theme => this.deleteCustomTheme(theme.id, false)); // false to prevent confirmation
+
+ const externalThemesToRemove = this.externalCustomThemes.filter(style => style.styleName === setName);
+ externalThemesToRemove.forEach(style => {
+ const styleElement = document.getElementById(`external-${style.styleName}.css`);
+ if (styleElement) {
+ styleElement.parentNode.removeChild(styleElement);
+ }
+ this.externalCustomThemes = this.externalCustomThemes.filter(s => s.styleName !== setName);
+ });
+
+ this.addMenu();
+ this.populateCustomThemesList();
+
+ this.displayNotification(`Theme Set "${setName}" and all its themes have been deleted.`, 'success');
+ }
+
+ updateThemeSetDropdown() {
+ const select = document.getElementById('theme-set-select');
+ if (!select) return;
+
+ select.innerHTML = '';
+
+ if (this.themeSets.length === 0) {
+ const noSetOption = document.createElement('option');
+ noSetOption.value = '';
+ noSetOption.textContent = 'No Theme Sets Available';
+ noSetOption.disabled = true;
+ noSetOption.selected = true;
+ select.appendChild(noSetOption);
+ select.disabled = true;
+ return;
+ }
+
+ this.themeSets.forEach(setName => {
+ const option = document.createElement('option');
+ option.value = setName;
+ option.textContent = setName;
+ select.appendChild(option);
+ });
+
+ select.disabled = false;
+ }
+
+ formatLabel(variable) {
+ return this.customLabels[variable] || this.formatVariableName(variable);
+ }
+
+ formatVariableName(variable) {
+ return variable
+ .replace('--', '')
+ .replace(/-/g, ' ')
+ .replace(/\b\w/g, char => char.toUpperCase());
+ }
+
+ getCurrentCSSVariable(variable) {
+ return getComputedStyle(document.documentElement).getPropertyValue(variable).trim() || '#000000';
+ }
+
+ applyInlineCustomCSS(variable, value) {
+ document.documentElement.style.setProperty(variable, value);
+ }
+
+ openCustomThemeModal() {
+ const modal = document.getElementById(this.customThemeModalId);
+ if (modal) {
+ const currentTheme = this.preferencesManager.get('selectedTheme');
+ const activeThemeSet = this.findThemeSetByThemeValue(currentTheme);
+ const themeSetSelect = modal.querySelector('#theme-set-select');
+ if (themeSetSelect && activeThemeSet) {
+ themeSetSelect.value = activeThemeSet;
+ } else if (themeSetSelect && this.themeSets.length > 0) {
+ themeSetSelect.selectedIndex = 0;
+ }
+
+ modal.style.display = 'block';
+ this.resetCustomThemeForm();
+ this.populateCustomThemesList();
+ } else {
+ console.error('Custom Theme Modal not found.');
+ }
+ }
+
+ findThemeSetByThemeValue(themeValue) {
+ for (const theme of this.themes) {
+ if (theme.value === themeValue) {
+ return theme.themesSetName || 'Default Set';
+ }
+ }
+
+ for (const style of this.externalCustomThemes) {
+ if (style.themes.some(t => t.value === themeValue)) {
+ return style.styleName;
+ }
+ }
+
+ const customTheme = this.customThemes.find(t => t.value === themeValue);
+ if (customTheme) {
+ return customTheme.themesSetName;
+ }
+
+ return null;
+ }
+
+ closeCustomThemeModal() {
+ const modal = document.getElementById(this.customThemeModalId);
+ if (modal) {
+ modal.style.display = 'none';
+ const currentTheme = this.preferencesManager.get('selectedTheme') || this.getDefaultTheme();
+ document.documentElement.removeAttribute('style');
+ this.applyTheme(currentTheme);
+ const selector = document.getElementById('theme-selector-dropdown');
+ if (selector) {
+ selector.value = currentTheme;
+ }
+ this.clearNotification();
+ }
+ }
+
+ resetCustomThemeForm() {
+ const form = document.getElementById('custom-theme-form');
+ if (form) {
+ form.reset();
+ const modal = document.getElementById(this.customThemeModalId);
+ const themeSetSelect = modal.querySelector('#theme-set-select');
+ if (themeSetSelect && this.themeSets.length > 0) {
+
+ const activeTheme = this.preferencesManager.get('selectedTheme');
+ const activeSet = this.findThemeSetByThemeValue(activeTheme);
+ if (activeSet) {
+ themeSetSelect.value = activeSet;
+ }
+ }
+ this.cssVariables.forEach(variable => {
+ const input = document.getElementById(variable);
+ if (input) {
+ input.value = this.getCurrentCSSVariable(variable) || '#000000';
+ this.applyInlineCustomCSS(variable, input.value);
+ }
+ });
+ this.currentEditingThemeId = null;
+ const modalTitle = modal.querySelector('h2');
+ if (modalTitle) {
+ modalTitle.textContent = 'Create Custom Theme';
+ }
+ const saveThemeButton = modal.querySelector('#save-theme-button');
+ if (saveThemeButton) {
+ saveThemeButton.textContent = 'Save Theme & Close';
+ }
+ const saveAndContinueButton = modal.querySelector('#save-and-continue-theme-button');
+ if (saveAndContinueButton) {
+ saveAndContinueButton.textContent = 'Save Theme';
+ }
+ this.clearNotification();
+ }
+ }
+
+ populateCustomThemesList() {
+ const customThemesList = document.getElementById('custom-themes-list');
+ if (!customThemesList) return;
+
+ customThemesList.innerHTML = '';
+
+ const allCustomThemes = [
+ ...this.externalCustomThemes.flatMap(style => style.themes),
+ ...this.customThemes
+ ];
+
+ if (allCustomThemes.length === 0) {
+ const noThemesItem = document.createElement('li');
+ noThemesItem.textContent = 'No custom themes created yet.';
+ customThemesList.appendChild(noThemesItem);
+ return;
+ }
+
+ const groupedThemes = this.groupThemesBySet(allCustomThemes);
+
+ for (const [setName, themes] of groupedThemes) {
+ const themesSetItem = document.createElement('li');
+ themesSetItem.style.display = 'flex';
+ themesSetItem.style.justifyContent = 'space-between';
+ themesSetItem.style.alignItems = 'center';
+ themesSetItem.style.padding = '5px 0';
+ themesSetItem.style.marginTop = '10px';
+ themesSetItem.style.fontWeight = 'bold';
+
+ const setNameSpan = document.createElement('span');
+ setNameSpan.textContent = setName;
+
+ const buttonsContainer = document.createElement('div');
+
+ const deleteSetButton = document.createElement('button');
+ deleteSetButton.type = 'button';
+ deleteSetButton.textContent = 'Delete Set';
+ deleteSetButton.style.padding = '4px 8px';
+ deleteSetButton.style.cursor = 'pointer';
+ deleteSetButton.style.marginRight = '5px';
+ deleteSetButton.addEventListener('click', () => this.deleteThemeSet(setName));
+
+ const downloadSetButton = document.createElement('button');
+ downloadSetButton.type = 'button';
+ downloadSetButton.textContent = 'Download Set';
+ downloadSetButton.style.padding = '4px 8px';
+ downloadSetButton.style.cursor = 'pointer';
+ downloadSetButton.addEventListener('click', () => this.downloadThemeSet(setName));
+
+ buttonsContainer.appendChild(downloadSetButton);
+ buttonsContainer.appendChild(deleteSetButton);
+
+ themesSetItem.appendChild(setNameSpan);
+ themesSetItem.appendChild(buttonsContainer);
+ customThemesList.appendChild(themesSetItem);
+
+ themes.forEach(theme => {
+ const listItem = document.createElement('li');
+ listItem.style.display = 'flex';
+ listItem.style.justifyContent = 'space-between';
+ listItem.style.alignItems = 'center';
+ listItem.style.padding = '5px 0';
+ listItem.style.marginLeft = '20px';
+
+ const themeNameSpan = document.createElement('span');
+ themeNameSpan.textContent = `- ${theme.name}`;
+
+ const themeButtonsContainer = document.createElement('div');
+
+ if (this.customThemes.some(t => t.id === theme.id)) {
+ const editButton = document.createElement('button');
+ editButton.type = 'button';
+ editButton.textContent = 'Edit';
+ editButton.style.marginRight = '10px';
+ editButton.style.padding = '4px 8px';
+ editButton.style.cursor = 'pointer';
+ editButton.addEventListener('click', () => this.editCustomTheme(theme.id));
+ themeButtonsContainer.appendChild(editButton);
+ }
+
+ if (this.customThemes.some(t => t.id === theme.id)) {
+ const deleteButton = document.createElement('button');
+ deleteButton.type = 'button';
+ deleteButton.textContent = 'Delete';
+ deleteButton.style.padding = '4px 8px';
+ deleteButton.style.cursor = 'pointer';
+ deleteButton.addEventListener('click', () => this.deleteCustomTheme(theme.id));
+ themeButtonsContainer.appendChild(deleteButton);
+ }
+
+ listItem.appendChild(themeNameSpan);
+ listItem.appendChild(themeButtonsContainer);
+
+ customThemesList.appendChild(listItem);
+ });
+ }
+ }
+
+ downloadThemeSet(setName) {
+ const externalThemes = this.externalCustomThemes
+ .filter(style => style.styleName === setName)
+ .flatMap(style => style.themes);
+
+ const customThemes = this.customThemes
+ .filter(theme => theme.themesSetName === setName);
+
+ const allThemes = [...externalThemes, ...customThemes];
+
+ if (allThemes.length === 0) {
+ this.displayNotification(`No themes found in the set "${setName}".`, 'error');
+ return;
+ }
+
+ let cssContent = '';
+ allThemes.forEach(theme => {
+ cssContent += `[data-theme="${theme.value}"] {\n`;
+ if (this.customThemes.some(t => t.id === theme.id)) {
+ for (const [key, value] of Object.entries(theme.variables)) {
+ cssContent += ` ${key}: ${value};\n`;
+ }
+ } else {
+ for (const [key, value] of Object.entries(theme.variables)) {
+ cssContent += ` ${key}: ${value};\n`;
+ }
+ }
+ cssContent += '}\n\n';
+ });
+
+ const blob = new Blob([cssContent], { type: 'text/css' });
+ const fileName = this.generateFileName(setName);
+ const link = document.createElement('a');
+ link.href = URL.createObjectURL(blob);
+ link.download = fileName;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(link.href);
+
+ this.displayNotification(`Theme Set "${setName}" has been downloaded as "${fileName}".`, 'success');
+ }
+
+ generateFileName(setName) {
+ const fileName = setName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'custom-theme';
+ return `${fileName}.css`;
+ }
+
+ editCustomTheme(themeId) {
+ const theme = this.customThemes.find(t => t.id === themeId);
+ if (!theme) {
+ this.displayNotification(`Theme with ID "${themeId}" not found or cannot be edited (external themes).`, 'error');
+ return;
+ }
+
+ const form = document.getElementById('custom-theme-form');
+ if (form) {
+ const modal = document.getElementById(this.customThemeModalId);
+ const nameInput = modal.querySelector('#theme-name');
+ const themeSetSelect = modal.querySelector('#theme-set-select');
+
+ if (nameInput) {
+ nameInput.value = theme.name;
+ }
+ if (themeSetSelect) {
+ themeSetSelect.value = theme.themesSetName;
+ }
+ this.cssVariables.forEach(variable => {
+ const input = document.getElementById(variable);
+ if (input) {
+ input.value = theme.variables[variable] || '#000000';
+ this.applyInlineCustomCSS(variable, input.value);
+ }
+ });
+ this.currentEditingThemeId = themeId;
+ const modalTitle = modal.querySelector('h2');
+ if (modalTitle) {
+ modalTitle.textContent = 'Edit Custom Theme';
+ }
+ const saveThemeButton = modal.querySelector('#save-theme-button');
+ if (saveThemeButton) {
+ saveThemeButton.textContent = 'Update Theme & Close';
+ }
+ const saveAndContinueButton = modal.querySelector('#save-and-continue-theme-button');
+ if (saveAndContinueButton) {
+ saveAndContinueButton.textContent = 'Update Theme';
+ }
+ this.displayNotification(`Editing theme "${theme.name}". Make your changes and click "Update Theme".`, 'info');
+ }
+ }
+
+ deleteCustomTheme(themeId, confirmDelete = true) {
+ const theme = this.customThemes.find(t => t.id === themeId);
+ if (!theme) {
+ this.displayNotification(`Theme with ID "${themeId}" not found or cannot be deleted (external themes).`, 'error');
+ return;
+ }
+
+ if (confirmDelete) {
+ const userConfirmed = confirm(`Are you sure you want to delete the theme "${theme.name}"?`);
+ if (!userConfirmed) return;
+ }
+
+ const styleId = `custom-theme-${theme.value}`;
+ const styleElement = document.getElementById(styleId);
+ if (styleElement) {
+ styleElement.parentNode.removeChild(styleElement);
+ }
+
+ this.customThemes = this.customThemes.filter(t => t.id !== themeId);
+
+ this.saveCustomThemesToStorage();
+
+ this.addMenu();
+ this.populateCustomThemesList();
+
+ if (confirmDelete) {
+ this.displayNotification('Theme deleted successfully.', 'success');
+ }
+ }
+
+ saveCustomTheme(closeAfterSave = true) {
+ const form = document.getElementById('custom-theme-form');
+ if (!form) return;
+
+ const formData = new FormData(form);
+ const themeName = formData.get('themeName')?.trim() || `Theme ${Date.now()}`;
+ const themesSetName = formData.get('themeSet') || 'Default Set';
+ const themeValue = this.generateThemeValue(themeName);
+
+ const themeVariables = {};
+ this.cssVariables.forEach(variable => {
+ themeVariables[variable] = formData.get(variable) || '#000000';
+ });
+
+ if (this.currentEditingThemeId) {
+ const index = this.customThemes.findIndex(t => t.id === this.currentEditingThemeId);
+ if (index !== -1) {
+ this.customThemes[index] = {
+ ...this.customThemes[index],
+ name: themeName,
+ value: themeValue,
+ themesSetName,
+ variables: themeVariables
+ };
+ }
+ } else {
+ this.customThemes.push({
+ id: `custom-${Date.now()}`,
+ name: themeName,
+ value: themeValue,
+ themesSetName,
+ variables: themeVariables
+ });
+ }
+
+ this.appendCustomCSSToDocument(themeValue, themeVariables);
+ localStorage.setItem('customThemes', JSON.stringify(this.customThemes));
+ this.addMenu();
+ this.applyTheme(themeValue);
+
+ if (closeAfterSave) {
+ this.closeCustomThemeModal();
+ }
+ }
+
+ generateThemeValue(themeName) {
+ return themeName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || `custom-theme-${Date.now()}`;
+ }
+
+ loadCustomThemesFromStorage() {
+ const storedThemes = localStorage.getItem('customThemes');
+ if (storedThemes) {
+ this.customThemes = JSON.parse(storedThemes);
+ this.customThemes.forEach(theme => {
+ this.appendCustomCSSToDocument(theme.value, theme.variables);
+ });
+ }
+ }
+
+ saveCustomThemesToStorage() {
+ localStorage.setItem('customThemes', JSON.stringify(this.customThemes));
+ }
+
+ loadThemeSetsFromStorage() {
+ const storedSets = localStorage.getItem('themeSets');
+ if (storedSets) {
+ this.themeSets = JSON.parse(storedSets);
+ } else {
+ this.themeSets = [];
+ this.saveThemeSetsToStorage();
+ }
+ }
+
+ saveThemeSetsToStorage() {
+ localStorage.setItem('themeSets', JSON.stringify(this.themeSets));
+ }
+
+ generateCustomCSS(themeValue, variables) {
+ let css = `[data-theme="${themeValue}"] {\n`;
+ for (const [key, value] of Object.entries(variables)) {
+ css += ` ${key}: ${value};\n`;
+ }
+ css += '}';
+ return css;
+ }
+
+ appendCustomCSSToDocument(themeValue, variables) {
+ const styleId = `custom-theme-${themeValue}`;
+ let styleElement = document.getElementById(styleId);
+
+ if (styleElement) {
+ styleElement.parentNode.removeChild(styleElement);
+ }
+
+ styleElement = document.createElement('style');
+ styleElement.type = 'text/css';
+ styleElement.id = styleId;
+ let css = `[data-theme="${themeValue}"] {\n`;
+ for (const [key, value] of Object.entries(variables)) {
+ css += ` ${key}: ${value};\n`;
+ }
+ css += '}';
+ styleElement.appendChild(document.createTextNode(css));
+ document.head.appendChild(styleElement);
+ }
+
+ updateCustomCSSInDocument(themeValue, variables) {
+ this.appendCustomCSSToDocument(themeValue, variables);
+ }
+
+ appendExternalCSS(fileName, cssText) {
+ const style = document.createElement('style');
+ style.id = `external-${fileName}`;
+ style.appendChild(document.createTextNode(cssText));
+ document.head.appendChild(style);
+ }
+
+ static applyTheme(theme, preferencesManager) {
+ if (!theme || !preferencesManager) return;
+ document.documentElement.setAttribute('data-theme', theme);
+ preferencesManager.set('selectedTheme', theme);
+ }
+
+ onMouseDown(e) {
+ e.preventDefault();
+
+ const modal = document.getElementById(this.customThemeModalId);
+ if (!modal) return;
+
+ this.isDragging = true;
+
+ const rect = modal.getBoundingClientRect();
+ this.dragOffset.x = e.clientX - rect.left;
+ this.dragOffset.y = e.clientY - rect.top;
+
+ document.addEventListener('mousemove', this.onMouseMove);
+ document.addEventListener('mouseup', this.onMouseUp);
+ }
+
+ onMouseMove(e) {
+ if (!this.isDragging) return;
+
+ const newLeft = e.clientX - this.dragOffset.x;
+ const newTop = e.clientY - this.dragOffset.y;
+
+ const modal = document.getElementById(this.customThemeModalId);
+ if (!modal) return;
+
+ const modalWidth = modal.offsetWidth;
+ const modalHeight = modal.offsetHeight;
+ const windowWidth = window.innerWidth;
+ const windowHeight = window.innerHeight;
+
+ const clampedLeft = Math.max(0, Math.min(newLeft, windowWidth - modalWidth));
+ const clampedTop = Math.max(0, Math.min(newTop, windowHeight - modalHeight));
+
+ if (this.animationFrame) {
+ cancelAnimationFrame(this.animationFrame);
+ }
+
+ this.animationFrame = requestAnimationFrame(() => {
+ modal.style.left = `${clampedLeft}px`;
+ modal.style.top = `${clampedTop}px`;
+ modal.style.transform = `translate(0, 0)`;
+ });
+ }
+
+ onMouseUp() {
+ if (!this.isDragging) return;
+ this.isDragging = false;
+
+ document.removeEventListener('mousemove', this.onMouseMove);
+ document.removeEventListener('mouseup', this.onMouseUp);
+
+ if (this.animationFrame) {
+ cancelAnimationFrame(this.animationFrame);
+ this.animationFrame = null;
+ }
+ }
+
+ displayNotification(message, type) {
+ const notification = document.getElementById('theme-notification');
+ if (!notification) return;
+
+ notification.textContent = message;
+ notification.style.display = 'block';
+ notification.hidden = false;
+
+ switch (type) {
+ case 'success':
+ // notification.style.backgroundColor = '#d4edda';
+ notification.style.color = '#50B452';
+ // notification.style.border = '1px solid #c3e6cb';
+ break;
+ case 'error':
+ // notification.style.backgroundColor = '#f8d7da';
+ notification.style.color = '#D24141';
+ // notification.style.border = '1px solid #f5c6cb';
+ break;
+ case 'info':
+ // notification.style.backgroundColor = '#1e1e1f';
+ notification.style.color = '#628BC0';
+ // notification.style.border = '1px solid #bee5eb';
+ break;
+ default:
+ notification.style.backgroundColor = '#1e1e1f';
+ notification.style.color = '#bababa';
+ }
+
+ setTimeout(() => {
+ this.clearNotification();
+ }, 5000);
+ }
+
+
+ clearNotification() {
+ const notification = document.getElementById('theme-notification');
+ if (!notification) return;
+
+ notification.textContent = '';
+ notification.style.display = 'none';
+ notification.hidden = true;
+ }
+
+ updateCustomThemesListUI() {
+ this.populateCustomThemesList();
+ }
+}
+
+export default ThemeManager;
diff --git a/web/core/js/common/scripts/injectStylesheet.js b/web/core/js/common/scripts/injectStylesheet.js
new file mode 100644
index 0000000..475ec6d
--- /dev/null
+++ b/web/core/js/common/scripts/injectStylesheet.js
@@ -0,0 +1,29 @@
+function injectStylesheet(href, id = null) {
+ if (id && document.getElementById(id)) {
+ console.warn(`Stylesheet with id "${id}" is already injected.`);
+ return;
+ }
+
+ const link = document.createElement('link');
+ link.rel = 'preload';
+ link.as = 'style';
+ link.href = href;
+
+ if (id) {
+ link.id = id;
+ }
+
+ link.onload = function() {
+ this.onload = null;
+ this.rel = 'stylesheet';
+ console.log(`Stylesheet "${href}" has been loaded and applied.`);
+ };
+
+ link.onerror = function() {
+ console.error(`Failed to load stylesheet "${href}".`);
+ };
+
+ document.head.appendChild(link);
+}
+
+export default injectStylesheet;
diff --git a/web/core/js/common/scripts/preferences.js b/web/core/js/common/scripts/preferences.js
new file mode 100644
index 0000000..220aa0d
--- /dev/null
+++ b/web/core/js/common/scripts/preferences.js
@@ -0,0 +1,46 @@
+const PREFS_KEY = 'FlowMenuPref';
+
+class PreferencesManager {
+ constructor(defaultPrefs) {
+ this.preferences = { ...defaultPrefs };
+ this.loadPreferences();
+ }
+
+ loadPreferences() {
+ const storedPrefs = localStorage.getItem(PREFS_KEY);
+ if (storedPrefs) {
+ try {
+ const parsedPrefs = JSON.parse(storedPrefs);
+ this.preferences = { ...this.preferences, ...parsedPrefs };
+ } catch (e) {
+ console.error('Error parsing preferences from localStorage:', e);
+ }
+ }
+ }
+
+ savePreferences() {
+ try {
+ localStorage.setItem(PREFS_KEY, JSON.stringify(this.preferences));
+ } catch (e) {
+ console.error('Error saving preferences to localStorage:', e);
+ }
+ }
+
+ get(prefKey) {
+ return this.preferences[prefKey];
+ }
+
+ set(prefKey, value) {
+ this.preferences[prefKey] = value;
+ this.savePreferences();
+ }
+
+ addPreference(prefKey, defaultValue) {
+ if (!(prefKey in this.preferences)) {
+ this.preferences[prefKey] = defaultValue;
+ this.savePreferences();
+ }
+ }
+}
+
+export { PreferencesManager, };
diff --git a/web/core/main.js b/web/core/main.js
index daf580b..5c62a82 100644
--- a/web/core/main.js
+++ b/web/core/main.js
@@ -1,3 +1,4 @@
+
import Seeder from "./js/common/components/Seeder.js"
import Stepper from "./js/common/components/Stepper.js"
import MultiStepper from "./js/common/components/MultiStepper.js"
@@ -12,6 +13,9 @@ import { processWorkflowNodes } from './js/common/scripts/nodesscanner.js';
import { fetchWorkflow } from './js/common/scripts/fetchWorkflow.js';
import { fetchflowConfig } from './js/common/scripts/fetchflowConfig.js';
import { setFaviconStatus } from './js/common/scripts/favicon.js';
+import { PreferencesManager } from './js/common/scripts/preferences.js';
+import ThemeManager from './js/common/scripts/ThemeManager.js';
+import injectStylesheet from './js/common/scripts/injectStylesheet.js';
import { checkAndShowMissingPackagesDialog } from './js/common/components/missingPackagesDialog.js';
@@ -26,10 +30,28 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
let isProcessing = false;
initializeWebSocket(client_id);
setFaviconStatus.Default();
-
+ injectStylesheet('/core/css/main.css', 'main');
+ injectStylesheet('/core/css/themes.css', 'themes-stylesheet');
+
console.log("flowConfig",flowConfig)
console.log("workflow",workflow)
+
+ const defaultPreferences = {
+ selectedCategories: [],
+ favoritesFilterActive: false,
+ hideDescriptions: false,
+ hideTitles: false,
+ sortValue: 'nameAsc',
+ selectedTheme: null
+ };
+
+ const preferencesManager = new PreferencesManager(defaultPreferences);
+
+ ThemeManager.applyInitialTheme(preferencesManager);
+ const themeManager = new ThemeManager(preferencesManager);
+ themeManager.init();
+
function generateWorkflowControls(config) {
const container = document.getElementById('side-workflow-controls');
@@ -76,7 +98,6 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
});
}
-
function generateWorkflowInputs(config, options = { clearInputs: false }) {
const promptsContainer = document.getElementById('prompts');
config.workflowInputs.forEach((input, index) => {
@@ -141,6 +162,7 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
flowConfig.multiSteppers.forEach(config => {
new MultiStepper(config, workflow);
});
+
flowConfig.dropdownSteppers.forEach(config => {
new DropdownStepper(config, workflow);
});
@@ -255,6 +277,13 @@ import { checkAndShowMissingPackagesDialog } from './js/common/components/missin
interrupt();
});
-
-
+ document.addEventListener('DOMContentLoaded', () => {
+ const overlay = document.getElementById('css-loading-overlay');
+ overlay.classList.add('fade-out');
+
+ overlay.addEventListener('transitionend', () => {
+ overlay.style.display = 'none';
+ });
+ });
+
})(window, document, undefined);
\ No newline at end of file
diff --git a/web/core/media/ui/drop_image_rect_no_border_trans.png b/web/core/media/ui/drop_image_rect_no_border_trans.png
new file mode 100644
index 0000000..1b14f04
Binary files /dev/null and b/web/core/media/ui/drop_image_rect_no_border_trans.png differ
diff --git a/web/flow/index.html b/web/flow/index.html
index 1cf2fa0..a92dbba 100644
--- a/web/flow/index.html
+++ b/web/flow/index.html
@@ -5,435 +5,472 @@
+
+
+
+
+ Create Custom Theme
+ + + +
+
+
+
+
@@ -539,6 +607,8 @@
+
+
@@ -571,31 +641,12 @@
-