diff --git a/dist/index.css b/dist/index.css index b7530475..c0c88c5d 100644 --- a/dist/index.css +++ b/dist/index.css @@ -89,6 +89,56 @@ main { } } +/* src/colors.css */ +[color]:not([color=standard]) { + --primary: hsl(var(--hue), 100%, 80%); + --gradient-luma-1: 70%; + --gradient-luma-2: 90%; + --hover-luma: 75%; + --active-luma: 80%; + --text-luma: 20%; + --surface: + linear-gradient( + + 120deg, + hsl(var(--hue), 100%, var(--gradient-luma-1)), + hsl(var(--hue), 100%, var(--gradient-luma-2)) ); + --surface-hover: hsl(var(--hue), 100%, var(--hover-luma)); + --surface-active: hsl(var(--hue), 100%, var(--active-luma)); + --text-normal: hsl(var(--hue), 100%, var(--text-luma)); +} +@media (prefers-color-scheme: dark) { + [color]:not([color=standard]) { + --gradient-luma-1: 20%; + --gradient-luma-2: 15%; + --hover-luma: 25%; + --active-luma: 30%; + --text-luma: 90%; + } +} +[color]:not([color=standard]).fill-color { + color: white; + background-color: var(--primary); +} +[color=coral] { + --hue: 20; +} +[color=yellow] { + --hue: 50; +} +[color=mint] { + --hue: 150; +} +[color=lightblue] { + --hue: 180; +} +[color=blue] { + --hue: 220; +} +[color=purple] { + --hue: 280; +} + /* src/View/chatPage.css */ #chat-page-wrapper { display: none; diff --git a/dist/index.js b/dist/index.js index 334fbfe2..2289aed5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -353,11 +353,24 @@ chats: [DATA_VERSION, "chat"], chatInfo: (id) => [DATA_VERSION, "chat", id, "info"], chatLastUsedPage: (id) => [DATA_VERSION, "chat", id, "last-used-page"], + chatColor: (id) => [DATA_VERSION, "chat", id, "color"], chatMessages: (id) => [DATA_VERSION, "chat", id, "messages"], chatObjects: (id) => [DATA_VERSION, "chat", id, "objects"], chatOutbox: (id) => [DATA_VERSION, "chat", id, "outbox"] }; + // src/ViewModel/colors.ts + var Color = /* @__PURE__ */ ((Color2) => { + Color2["Standard"] = "standard"; + Color2["Coral"] = "coral"; + Color2["Yellow"] = "yellow"; + Color2["Mint"] = "mint"; + Color2["LightBlue"] = "lightblue"; + Color2["Blue"] = "blue"; + Color2["purple"] = "purple"; + return Color2; + })(Color || {}); + // src/Model/chatModel.ts var ChatModel = class { // data @@ -365,6 +378,7 @@ storageModel; chatListModel; info; + color; messages = /* @__PURE__ */ new Set(); objects = /* @__PURE__ */ new Map(); outbox = /* @__PURE__ */ new Set(); @@ -372,6 +386,9 @@ get infoPath() { return storageKeys.chatInfo(this.id); } + get colorPath() { + return storageKeys.chatColor(this.id); + } get messageDirPath() { return storageKeys.chatMessages(this.id); } @@ -397,10 +414,17 @@ this.info.secondaryChannels = secondaryChannels; this.storeInfo(); }; + setColor = (color) => { + this.color = color; + this.storeColor(); + }; // store & add storeInfo = () => { this.storageModel.storeStringifiable(this.infoPath, this.info); }; + storeColor = () => { + this.storageModel.store(this.colorPath, this.color); + }; addMessage = (message) => { if (!this.messages.has(message)) { this.messages.add(message); @@ -429,6 +453,15 @@ this.info = generateChatInfo("0"); } }; + restoreColor = () => { + const path = this.colorPath; + const color = this.storageModel.restore(path); + if (!color) { + this.color = "standard" /* Standard */; + } else { + this.color = color; + } + }; restoreMessages = () => { const messageIds = this.storageModel.list(this.messageDirPath); if (!Array.isArray(messageIds)) return; @@ -465,6 +498,7 @@ this.storageModel = storageModel2; this.chatListModel = chatListModel; this.restoreInfo(); + this.restoreColor(); } }; function generateChatInfo(primaryChannel) { @@ -532,6 +566,7 @@ index = new State(0); primaryChannel = new State(""); primaryChannelInput = new State(""); + color = new State("standard" /* Standard */); selectedPage = new State( "messages" /* Messages */ ); @@ -557,6 +592,10 @@ this.primaryChannel.value = this.chatModel.info.primaryChannel; this.chatListViewModel.updateIndices(); }; + setColor = (newColor) => { + this.color.value = newColor; + this.chatModel.setColor(newColor); + }; // restore restorePageSelection = () => { const path = storageKeys.chatLastUsedPage(this.chatModel.id); @@ -575,6 +614,7 @@ this.chatListViewModel = chatListViewModel2; this.primaryChannel.value = chatModel.info.primaryChannel; this.primaryChannelInput.value = chatModel.info.primaryChannel; + this.color.value = chatModel.color; this.restorePageSelection(); this.updateIndex(); } @@ -760,7 +800,31 @@ }, translations.general.setButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "check") - )))); + )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement( + "div", + { + class: "flex-row gap width-input" + }, + ...Object.values(Color).map((color) => { + const isSelected = createProxyState( + [chatViewModel.color], + () => chatViewModel.color.value == color + ); + function setColor() { + chatViewModel.setColor(color); + } + return /* @__PURE__ */ createElement( + "button", + { + color, + class: "fill-color width-100 flex", + style: "height: 2rem", + "toggle:selected": isSelected, + "on:click": setColor + } + ); + }) + ))); } // src/View/chatPage.tsx @@ -1156,7 +1220,7 @@ // src/View/Components/chatEntry.tsx function ChatEntry(chatViewModel) { - const view = /* @__PURE__ */ createElement("button", { class: "chat-entry tile", "on:click": chatViewModel.open }, /* @__PURE__ */ createElement( + const view = /* @__PURE__ */ createElement("button", { "set:color": chatViewModel.color, class: "chat-entry tile", "on:click": chatViewModel.open }, /* @__PURE__ */ createElement( "span", { class: "shadow", diff --git a/dist/styles.css b/dist/styles.css index b9a47039..86dbefa5 100644 --- a/dist/styles.css +++ b/dist/styles.css @@ -93,7 +93,11 @@ COLORS /* basic */ html { - background-color: var(--background); + background: var(--background); + color: var(--text-normal); +} + +.tile { color: var(--text-normal); } @@ -102,12 +106,12 @@ html { } .modal > div { - background-color: var(--background); + background: var(--background); } menu, header { - background-color: var(--background-transparent); + background: var(--background-transparent); } * { @@ -145,53 +149,53 @@ textarea:not(.tile *), select:not(.tile *), .surface, .tile { - background-color: var(--surface); + background: var(--surface); } @media (hover: hover) { button:hover, select:hover, button.tile.ghost:hover { - background-color: var(--surface-hover); + background: var(--surface-hover); } [selected]:hover { - background-color: var(--surface); + background: var(--surface); } } button:active, select:active, button.tile.ghost:active, [selected] { - background-color: var(--surface-active); + background: var(--surface-active); } [selected]:active { - background-color: var(--surface-hover); + background: var(--surface-hover); } .tile *, header button { - background-color: transparent; + background: transparent; } input:checked { - background-color: var(--primary); + background: var(--primary); } /* primary button */ button.primary { - background-color: var(--primary); + background: var(--primary); color: var(--text-on-color); } @media (hover: hover) { button.primary:hover { - background-color: var(--primary-hover); + background: var(--primary-hover); } } button.primary:active { - background-color: var(--primary-active); + background: var(--primary-active); } /* ghost button */ button.ghost:not(.tile) { - background-color: transparent; + background: transparent; } button.ghost { color: var(--link); @@ -201,25 +205,25 @@ button.ghost { } @media (hover: hover) { button.ghost:hover { - background-color: var(--background-hover); + background: var(--background-hover); } } button.ghost:active { - background-color: var(--background-active); + background: var(--background-active); } /* danger button */ button.danger { - background-color: var(--danger); + background: var(--danger); color: var(--text-on-color); } @media (hover: hover) { button.danger:hover { - background-color: var(--danger-hover); + background: var(--danger-hover); } } button.danger:active { - background-color: var(--danger-active); + background: var(--danger-active); } /* @@ -909,7 +913,7 @@ input[type="checkbox"]:checked::before { input[type="radio"]:checked::before { content: ""; - background-color: var(--text-on-color); + background: var(--text-on-color); border-radius: 100%; --inset: 4px; @@ -929,7 +933,7 @@ input[type="radio"]:checked::before { right: 0px; bottom: 0px; - background-color: var(--background); + background: var(--background); opacity: 0.6; } diff --git a/src/Model/chatModel.ts b/src/Model/chatModel.ts index 5a60e584..43227ced 100644 --- a/src/Model/chatModel.ts +++ b/src/Model/chatModel.ts @@ -3,6 +3,7 @@ import StorageModel, { storageKeys } from "./storageModel"; import ChatListModel from "./chatListModel"; +import { Color } from "../ViewModel/colors"; import { checkIsValidObject } from "./Utility/typeSafety"; import { createTimestamp } from "./Utility/utility"; import { v4 } from "uuid"; @@ -13,6 +14,7 @@ export class ChatModel { storageModel: StorageModel; chatListModel: ChatListModel; info: ChatInfo; + color: Color; messages = new Set(); objects = new Map(); @@ -23,6 +25,10 @@ export class ChatModel { return storageKeys.chatInfo(this.id); } + get colorPath(): string[] { + return storageKeys.chatColor(this.id); + } + get messageDirPath(): string[] { return storageKeys.chatMessages(this.id); } @@ -55,11 +61,20 @@ export class ChatModel { this.storeInfo(); }; + setColor = (color: Color): void => { + this.color = color; + this.storeColor(); + }; + // store & add storeInfo = (): void => { this.storageModel.storeStringifiable(this.infoPath, this.info); }; + storeColor = (): void => { + this.storageModel.store(this.colorPath, this.color); + }; + addMessage = (message: ChatMessage): void => { if (!this.messages.has(message)) { this.messages.add(message); @@ -81,7 +96,7 @@ export class ChatModel { this.chatListModel.untrackChat(this); // delete - const dirPath = [...storageKeys.chats, this.id]; + const dirPath: string[] = [...storageKeys.chats, this.id]; this.storageModel.removeRecursive(dirPath); }; @@ -89,9 +104,19 @@ export class ChatModel { restoreInfo = (): void => { const info: any = this.storageModel.restoreStringifiable(this.infoPath); if (info != null) { - this.info = info + this.info = info; } else { - this.info = generateChatInfo("0") + this.info = generateChatInfo("0"); + } + }; + + restoreColor = (): void => { + const path: string[] = this.colorPath; + const color: string | null = this.storageModel.restore(path); + if (!color) { + this.color = Color.Standard; + } else { + this.color = color as any; } }; @@ -135,12 +160,17 @@ export class ChatModel { }; // init - constructor(storageModel: StorageModel, chatListModel: ChatListModel, chatId: string) { + constructor( + storageModel: StorageModel, + chatListModel: ChatListModel, + chatId: string + ) { this.id = chatId; this.storageModel = storageModel; this.chatListModel = chatListModel; this.restoreInfo(); + this.restoreColor(); } } diff --git a/src/Model/storageModel.ts b/src/Model/storageModel.ts index 05c0af9d..64d85ea8 100644 --- a/src/Model/storageModel.ts +++ b/src/Model/storageModel.ts @@ -146,6 +146,7 @@ export const storageKeys = { chats: [DATA_VERSION, "chat"], chatInfo: (id: string) => [DATA_VERSION, "chat", id, "info"], chatLastUsedPage: (id: string) => [DATA_VERSION, "chat", id, "last-used-page"], + chatColor: (id: string) => [DATA_VERSION, "chat", id, "color"], chatMessages: (id: string) => [DATA_VERSION, "chat", id, "messages"], chatObjects: (id: string) => [DATA_VERSION, "chat", id, "objects"], chatOutbox: (id: string) => [DATA_VERSION, "chat", id, "outbox"], diff --git a/src/View/ChatPages/settingsPage.tsx b/src/View/ChatPages/settingsPage.tsx index 849f2a0f..caf28a4d 100644 --- a/src/View/ChatPages/settingsPage.tsx +++ b/src/View/ChatPages/settingsPage.tsx @@ -1,6 +1,7 @@ import * as React from "bloatless-react"; import ChatViewModel from "../../ViewModel/chatViewModel"; +import { Color } from "../../ViewModel/colors"; import { translations } from "../translations"; export function SettingsPage(chatViewModel: ChatViewModel) { @@ -32,6 +33,33 @@ export function SettingsPage(chatViewModel: ChatViewModel) { check + +
+ +
+ {...Object.values(Color).map((color) => { + const isSelected = React.createProxyState( + [chatViewModel.color], + () => chatViewModel.color.value == color + ); + + function setColor() { + chatViewModel.setColor(color); + } + + return ( + + ); + })} +
); diff --git a/src/View/Components/chatEntry.tsx b/src/View/Components/chatEntry.tsx index ce0a386f..5701b19c 100644 --- a/src/View/Components/chatEntry.tsx +++ b/src/View/Components/chatEntry.tsx @@ -6,7 +6,7 @@ import ChatViewModel from "../../ViewModel/chatViewModel"; export function ChatEntry(chatViewModel: ChatViewModel) { const view = ( -