diff --git a/dist/index.css b/dist/index.css
index facc517d..b27412e6 100644
--- a/dist/index.css
+++ b/dist/index.css
@@ -3,266 +3,265 @@
#home-page #chat-section,
#chat-page,
#chat-page #main > div {
- animation: slide-up var(--animation-duration) ease;
+ animation: slide-up var(--animation-duration) ease;
}
#background-wrapper {
- position: fixed;
- top: 0px;
- left: var(--safe-area-left);
- right: var(--safe-area-right);
- bottom: 0px;
- z-index: 1;
- pointer-events: none;
- overflow: hidden;
- opacity: 0.3;
+ position: fixed;
+ top: 0px;
+ left: var(--safe-area-left);
+ right: var(--safe-area-right);
+ bottom: 0px;
+ z-index: 1;
+ pointer-events: none;
+ overflow: hidden;
+ opacity: 0.3;
}
main {
- z-index: 2;
+ z-index: 2;
}
#background-wrapper div {
- content: "";
- position: absolute;
- top: -80px;
- left: -80px;
- right: -80px;
- bottom: -80px;
- z-index: -1;
- pointer-events: none;
- filter: blur(40px);
+ content: "";
+ position: absolute;
+ top: -80px;
+ left: -80px;
+ right: -80px;
+ bottom: -80px;
+ z-index: -1;
+ pointer-events: none;
+ filter: blur(40px);
}
#background-wrapper #sky {
- left: -100%;
- bottom: -50%;
- --color-1: hsl(188, 100%, 83%);
- --color-2: hsl(194, 72%, 56%);
- background-image: radial-gradient(var(--color-1), var(--color-2));
+ left: -100%;
+ bottom: -50%;
+ --color-1: hsl(188, 100%, 83%);
+ --color-2: hsl(194, 72%, 56%);
+ background-image: radial-gradient(var(--color-1), var(--color-2));
}
#background-wrapper #grass-1 {
- --color-1: hsl(150, 100%, 30%);
- --color-2: hsl(150, 100%, 25%);
- background-image:
- linear-gradient(
- 350deg,
- var(--color-1),
- var(--color-2) 30%,
- transparent 40%);
+ --color-1: hsl(150, 100%, 30%);
+ --color-2: hsl(150, 100%, 25%);
+ background-image: linear-gradient(
+ 350deg,
+ var(--color-1),
+ var(--color-2) 30%,
+ transparent 40%
+ );
}
#background-wrapper #grass-2 {
- --color-1: hsl(110, 100%, 40%);
- --color-2: hsl(110, 100%, 35%);
- background-image:
- linear-gradient(
- 9deg,
- var(--color-1),
- var(--color-2) 30%,
- transparent 35%);
+ --color-1: hsl(110, 100%, 40%);
+ --color-2: hsl(110, 100%, 35%);
+ background-image: linear-gradient(
+ 9deg,
+ var(--color-1),
+ var(--color-2) 30%,
+ transparent 35%
+ );
}
@media (prefers-color-scheme: dark) {
- #background-wrapper {
- opacity: 0.2;
- }
- #background-wrapper #sky {
- --color-1: hsl(188, 100%, 30%);
- --color-2: hsl(194, 72%, 24%);
- }
- #background-wrapper #grass-1 {
- --color-1: hsl(120, 30%, 30%);
- --color-2: hsl(120, 30%, 24%);
- }
- #background-wrapper #grass-2 {
- --color-1: hsl(110, 60%, 30%);
- --color-2: hsl(110, 50%, 24%);
- }
+ #background-wrapper {
+ opacity: 0.2;
+ }
+ #background-wrapper #sky {
+ --color-1: hsl(188, 100%, 30%);
+ --color-2: hsl(194, 72%, 24%);
+ }
+ #background-wrapper #grass-1 {
+ --color-1: hsl(120, 30%, 30%);
+ --color-2: hsl(120, 30%, 24%);
+ }
+ #background-wrapper #grass-2 {
+ --color-1: hsl(110, 60%, 30%);
+ --color-2: hsl(110, 50%, 24%);
+ }
}
/* src/colors.css */
-[color]:not([color=standard]) {
- --primary: hsl(var(--hue), 100%, var(--accent-luma));
- --primary-hover: hsl(var(--hue), 95%, var(--accent-luma));
- --primary-active: hsl(var(--hue), 80%, var(--accent-luma));
- --text-on-color: hsl(0, 0%, var(--accent-text-luma));
- --link: hsl(var(--hue), 100%, var(--accent-luma));
- --surface: hsla(var(--hue), 80%, 30%, 5%);
- --surface-hover: hsla(var(--hue), 100%, 5%, 10%);
- --surface-active: hsla(var(--hue), 100%, 5%, 15%);
+[color]:not([color="standard"]) {
+ --primary: hsl(var(--hue), 100%, var(--accent-luma));
+ --primary-hover: hsl(var(--hue), 95%, var(--accent-luma));
+ --primary-active: hsl(var(--hue), 80%, var(--accent-luma));
+ --text-on-color: hsl(0, 0%, var(--accent-text-luma));
+ --link: hsl(var(--hue), 100%, var(--accent-luma));
+ --surface: hsla(var(--hue), 80%, 30%, 5%);
+ --surface-hover: hsla(var(--hue), 100%, 5%, 10%);
+ --surface-active: hsla(var(--hue), 100%, 5%, 15%);
}
@media (prefers-color-scheme: dark) {
- [color]:not([color=standard]) {
- --accent-luma: var(--accent-luma-dark);
- --accent-text-luma: var(--accent-text-luma-dark);
- --surface: hsla(var(--hue), 100%, 95%, 5%);
- --surface-hover: hsla(var(--hue), 100%, 95%, 10%);
- --surface-active: hsla(var(--hue), 100%, 95%, 15%);
- }
+ [color]:not([color="standard"]) {
+ --accent-luma: var(--accent-luma-dark);
+ --accent-text-luma: var(--accent-text-luma-dark);
+ --surface: hsla(var(--hue), 100%, 95%, 5%);
+ --surface-hover: hsla(var(--hue), 100%, 95%, 10%);
+ --surface-active: hsla(var(--hue), 100%, 95%, 15%);
+ }
}
-[color]:not([color=standard]).tile {
- --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)),
- hsla(var(--hue), 100%, var(--gradient-luma-2), 90%) );
- --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));
+[color]:not([color="standard"]).tile {
+ --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)),
+ hsla(var(--hue), 100%, var(--gradient-luma-2), 90%)
+ );
+ --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]).tile {
- --gradient-luma-1: 20%;
- --gradient-luma-2: 15%;
- --hover-luma: 25%;
- --active-luma: 30%;
- --text-luma: 90%;
- }
+ [color]:not([color="standard"]).tile {
+ --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]:not([color="standard"]).fill-color {
+ color: white;
+ background-color: var(--primary);
}
-[color]:not([color=standard])[selected].fill-color {
- color: var(--primary);
+[color]:not([color="standard"])[selected].fill-color {
+ color: var(--primary);
}
@media (min-width: 1024px) {
- [color]:not([color=standard]).background {
- --luma: var(--background-luma);
- --sat: 80%;
- background: hsl(var(--hue), var(--sat), var(--luma));
- }
- @media (prefers-color-scheme: dark) {
- [color]:not([color=standard]).background {
- --luma: var(--background-luma-dark);
- --sat: 40%;
+ [color]:not([color="standard"]).background {
+ --luma: var(--background-luma);
+ --sat: 80%;
+ background: hsl(var(--hue), var(--sat), var(--luma));
+ }
+ @media (prefers-color-scheme: dark) {
+ [color]:not([color="standard"]).background {
+ --luma: var(--background-luma-dark);
+ --sat: 40%;
+ }
+ }
+ [color="standard"].background {
+ background: var(--surface);
}
- }
- [color=standard].background {
- background: var(--surface);
- }
}
-[color]:not([color=standard]).subtle-background {
- --luma: var(--subtle-luma);
- background: hsl(var(--hue), 100%, var(--luma), 50%);
+[color]:not([color="standard"]).subtle-background {
+ --luma: var(--subtle-luma);
+ background: hsl(var(--hue), 100%, var(--luma), 50%);
}
@media (prefers-color-scheme: dark) {
- [color]:not([color=standard]).subtle-background {
- --luma: var(--subtle-luma-dark);
- }
- @media (display-mode: standalone) {
- [color]:not([color=standard]).subtle-background {
- --luma: 0%;
+ [color]:not([color="standard"]).subtle-background {
+ --luma: var(--subtle-luma-dark);
+ }
+ @media (display-mode: standalone) {
+ [color]:not([color="standard"]).subtle-background {
+ --luma: 0%;
+ }
}
- }
}
-[color=coral] {
- --hue: 20;
- --accent-luma: 50%;
- --accent-luma-dark: 70%;
- --accent-text-luma: 100%;
- --accent-text-luma-dark: 100%;
- --background-luma: 90%;
- --background-luma-dark: 11%;
- --subtle-luma: 95%;
- --subtle-luma-dark: 5%;
+[color="coral"] {
+ --hue: 20;
+ --accent-luma: 50%;
+ --accent-luma-dark: 70%;
+ --accent-text-luma: 100%;
+ --accent-text-luma-dark: 100%;
+ --background-luma: 90%;
+ --background-luma-dark: 11%;
+ --subtle-luma: 95%;
+ --subtle-luma-dark: 5%;
}
-[color=yellow] {
- --hue: 50;
- --accent-luma: 50%;
- --accent-luma-dark: 60%;
- --accent-text-luma: 0%;
- --accent-text-luma-dark: 0%;
- --background-luma: 85%;
- --background-luma-dark: 11%;
- --subtle-luma: 95%;
- --subtle-luma-dark: 5%;
+[color="yellow"] {
+ --hue: 50;
+ --accent-luma: 50%;
+ --accent-luma-dark: 60%;
+ --accent-text-luma: 0%;
+ --accent-text-luma-dark: 0%;
+ --background-luma: 85%;
+ --background-luma-dark: 11%;
+ --subtle-luma: 95%;
+ --subtle-luma-dark: 5%;
}
-[color=mint] {
- --hue: 150;
- --accent-luma: 40%;
- --accent-luma-dark: 60%;
- --accent-text-luma: 100%;
- --accent-text-luma-dark: 0%;
- --background-luma: 85%;
- --background-luma-dark: 12%;
- --subtle-luma: 95%;
- --subtle-luma-dark: 5%;
+[color="mint"] {
+ --hue: 150;
+ --accent-luma: 40%;
+ --accent-luma-dark: 60%;
+ --accent-text-luma: 100%;
+ --accent-text-luma-dark: 0%;
+ --background-luma: 85%;
+ --background-luma-dark: 12%;
+ --subtle-luma: 95%;
+ --subtle-luma-dark: 5%;
}
-[color=lightblue] {
- --hue: 180;
- --accent-luma: 40%;
- --accent-luma-dark: 60%;
- --accent-text-luma: 0%;
- --accent-text-luma-dark: 0%;
- --background-luma: 85%;
- --background-luma-dark: 12%;
- --subtle-luma: 95%;
- --subtle-luma-dark: 5%;
+[color="lightblue"] {
+ --hue: 180;
+ --accent-luma: 40%;
+ --accent-luma-dark: 60%;
+ --accent-text-luma: 0%;
+ --accent-text-luma-dark: 0%;
+ --background-luma: 85%;
+ --background-luma-dark: 12%;
+ --subtle-luma: 95%;
+ --subtle-luma-dark: 5%;
}
-[color=blue] {
- --hue: 220;
- --accent-luma: 50%;
- --accent-luma-dark: 70%;
- --accent-text-luma: 100%;
- --accent-text-luma-dark: 100%;
- --background-luma: 90%;
- --background-luma-dark: 12%;
- --subtle-luma: 95%;
- --subtle-luma-dark: 5%;
+[color="blue"] {
+ --hue: 220;
+ --accent-luma: 50%;
+ --accent-luma-dark: 70%;
+ --accent-text-luma: 100%;
+ --accent-text-luma-dark: 100%;
+ --background-luma: 90%;
+ --background-luma-dark: 12%;
+ --subtle-luma: 95%;
+ --subtle-luma-dark: 5%;
}
-[color=purple] {
- --hue: 280;
- --accent-luma: 50%;
- --accent-luma-dark: 70%;
- --accent-text-luma: 100%;
- --accent-text-luma-dark: 100%;
- --background-luma: 90%;
- --background-luma-dark: 11%;
- --subtle-luma: 95%;
- --subtle-luma-dark: 5%;
+[color="purple"] {
+ --hue: 280;
+ --accent-luma: 50%;
+ --accent-luma-dark: 70%;
+ --accent-text-luma: 100%;
+ --accent-text-luma-dark: 100%;
+ --background-luma: 90%;
+ --background-luma-dark: 11%;
+ --subtle-luma: 95%;
+ --subtle-luma-dark: 5%;
}
/* src/coloredTile.css */
.colored-tile {
- position: relative;
- display: flex;
- align-items: flex-end !important;
- justify-content: start;
- overflow: hidden;
+ position: relative;
+ display: flex;
+ align-items: flex-end !important;
+ justify-content: start;
+ overflow: hidden;
}
.colored-tile h2 {
- margin: 0px;
+ margin: 0px;
}
.colored-tile .shadow {
- position: absolute;
- left: -15px;
- bottom: -45px;
- font-size: 7rem;
- opacity: 0.1;
- white-space: nowrap;
+ position: absolute;
+ left: -15px;
+ bottom: -45px;
+ font-size: 7rem;
+ opacity: 0.1;
+ white-space: nowrap;
}
/* src/View/chatPage.css */
#chat-page-wrapper {
- display: none;
+ display: none;
}
body[showing-chat] #home-page {
- display: none;
+ display: none;
}
body[showing-chat] #chat-page-wrapper {
- display: flex;
- position: absolute;
- top: 0px;
- left: 0px;
- right: 0px;
- bottom: 0px;
- background-color: var(--background-glass);
- z-index: 2;
+ display: flex;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ background-color: var(--background-glass);
+ z-index: 2;
}
#chat-page {
- --ribbon-size: var(--control-height);
+ --ribbon-size: var(--control-height);
}
#chat-page > div,
#chat-page > div > #ribbon,
@@ -273,569 +272,642 @@ body[showing-chat] #chat-page-wrapper {
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar,
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar > span,
#chat-page > div > #main > div > .pane-wrapper > .pane > .content {
- display: flex;
+ display: flex;
}
#chat-page > div {
- padding: 0px;
- overflow: hidden;
+ padding: 0px;
+ overflow: hidden;
}
#chat-page > div > #main {
- width: calc(100% - var(--ribbon-size) - 1px);
+ width: calc(100% - var(--ribbon-size) - 1px);
}
#chat-page #main > div,
#chat-page > div > #main > div > .pane-wrapper,
#chat-page > div > #main > div > .pane-wrapper > .pane,
#chat-page > div > #main > div > .pane-wrapper > .pane > .content {
- width: 100%;
- height: 100%;
- flex: 1;
+ width: 100%;
+ height: 100%;
+ flex: 1;
}
#chat-page > div > #main > div > .pane-wrapper,
#chat-page > div > #main > div > .pane-wrapper > .pane {
- flex-direction: column;
- scroll-snap-align: center;
- min-width: 100%;
- overflow: hidden;
+ flex-direction: column;
+ scroll-snap-align: center;
+ min-width: 100%;
+ overflow: hidden;
}
#chat-page > div > #main > div > .pane-wrapper > .pane:not(.side),
#chat-page > div > #main > div > .pane-wrapper > .pane:not(.side) .content {
- animation: slide-up var(--animation-duration);
+ animation: slide-up var(--animation-duration);
}
@media (min-width: 1024px) {
- #chat-page > div > #main > div > .pane-wrapper {
- min-width: unset;
- }
- #chat-page > div > #main > div > .pane-wrapper.side {
- width: 18rem;
- min-width: 18rem;
- max-width: 18rem;
- }
+ #chat-page > div > #main > div > .pane-wrapper {
+ min-width: unset;
+ }
+ #chat-page > div > #main > div > .pane-wrapper.side {
+ width: 18rem;
+ min-width: 18rem;
+ max-width: 18rem;
+ }
}
#chat-page > div > #main > div > .pane-wrapper > .pane > .content {
- flex-direction: column;
- overflow: auto;
- padding: var(--content-padding);
+ flex-direction: column;
+ overflow: auto;
+ padding: var(--content-padding);
}
#chat-page #ribbon,
#chat-page #main,
#chat-page #main .content {
- flex-direction: column;
+ flex-direction: column;
}
#chat-page > div > #ribbon,
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar {
- align-items: center;
+ align-items: center;
}
#chat-page > div > #ribbon {
- width: calc(var(--ribbon-size) + 1px);
- border-right: 1px solid var(--border);
- padding-bottom: var(--safe-area-bottom);
+ width: calc(var(--ribbon-size) + 1px);
+ border-right: 1px solid var(--border);
+ padding-bottom: var(--safe-area-bottom);
}
#chat-page > div > #ribbon > span,
-#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar > span.ribbon {
- gap: var(--gap-between-tiles);
- padding: var(--gap-between-tiles);
+#chat-page
+ > div
+ > #main
+ > div
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ > span.ribbon {
+ gap: var(--gap-between-tiles);
+ padding: var(--gap-between-tiles);
}
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar {
- justify-content: start;
- justify-content: space-between;
+ justify-content: start;
+ justify-content: space-between;
}
#chat-page > div > #ribbon > span {
- height: 100%;
- flex: 1;
- overflow-y: auto;
- flex-direction: column;
- justify-content: end;
+ height: 100%;
+ flex: 1;
+ overflow-y: auto;
+ flex-direction: column;
+ justify-content: end;
}
#chat-page > div > #ribbon button,
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar button {
- padding: 0px;
- width: var(--control-height);
- max-width: var(--control-height);
- height: var(--control-height);
- min-height: var(--control-height);
- align-items: center;
- justify-content: center;
+ padding: 0px;
+ width: var(--control-height);
+ max-width: var(--control-height);
+ height: var(--control-height);
+ min-height: var(--control-height);
+ align-items: center;
+ justify-content: center;
}
#chat-page > div > #ribbon button.ribbon-button,
-#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar button.ribbon-button {
- min-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
- width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
- max-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
- min-height: unset;
- height: calc(var(--control-height) - 2 * var(--gap-between-tiles));
- color: var(--primary);
+#chat-page
+ > div
+ > #main
+ > div
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ button.ribbon-button {
+ min-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
+ width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
+ max-width: calc(var(--control-height) - 2 * var(--gap-between-tiles));
+ min-height: unset;
+ height: calc(var(--control-height) - 2 * var(--gap-between-tiles));
+ color: var(--primary);
}
#chat-page > div > #ribbon button > .icon,
-#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar button > .icon {
- font-size: 1.1rem;
+#chat-page
+ > div
+ > #main
+ > div
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ button
+ > .icon {
+ font-size: 1.1rem;
}
#chat-page > div > #ribbon > span {
- width: 100%;
+ width: 100%;
}
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar {
- height: var(--ribbon-size);
- overflow-x: auto;
- padding: 0px;
+ height: var(--ribbon-size);
+ overflow-x: auto;
+ padding: 0px;
}
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar > span {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar > span.title {
- padding-left: var(--content-padding);
+ padding-left: var(--content-padding);
}
-#chat-page > div > #main > div > .pane-wrapper > .pane > .toolbar :is(input, select, button) {
- height: 100%;
- flex: 1;
+#chat-page
+ > div
+ > #main
+ > div
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ :is(input, select, button) {
+ height: 100%;
+ flex: 1;
}
/* src/View/homePage.css */
#home-page > div {
- display: flex;
- flex-direction: row;
- gap: var(--large-gap);
- scroll-snap-type: x mandatory;
- scroll-behavior: smooth;
- overflow: hidden;
- overflow-x: scroll;
- padding: 0px;
+ display: flex;
+ flex-direction: row;
+ gap: var(--large-gap);
+ scroll-snap-type: x mandatory;
+ scroll-behavior: smooth;
+ overflow: hidden;
+ overflow-x: scroll;
+ padding: 0px;
}
#home-page > div > div {
- display: flex;
- flex-direction: column;
- justify-content: start;
- gap: var(--gap-between-tiles);
- padding: var(--content-padding);
- padding-top: calc(var(--header-height) + var(--content-margin));
- width: 100%;
- min-width: 100%;
- height: 100%;
- overflow-y: auto;
- scroll-snap-align: center;
+ display: flex;
+ flex-direction: column;
+ justify-content: start;
+ gap: var(--gap-between-tiles);
+ padding: var(--content-padding);
+ padding-top: calc(var(--header-height) + var(--content-margin));
+ width: 100%;
+ min-width: 100%;
+ height: 100%;
+ overflow-y: auto;
+ scroll-snap-align: center;
}
#home-page #overview-section,
#home-page #chat-section {
- padding-bottom: calc(var(--content-padding) + var(--header-height) + var(--safe-area-bottom));
+ padding-bottom: calc(
+ var(--content-padding) + var(--header-height) + var(--safe-area-bottom)
+ );
}
#home-page #chat-grid {
- margin-top: var(--content-padding);
- display: grid;
- gap: var(--gap-between-tiles);
- grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
+ margin-top: var(--content-padding);
+ display: grid;
+ gap: var(--gap-between-tiles);
+ grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
}
.chat-entry[unread] {
- border: var(--outline-width) solid black;
+ border: var(--outline-width) solid black;
}
.chat-entry[unread]:not(:hover):not(:active) {
- background: rgb(255, 217, 0) !important;
- color: black;
+ background: rgb(255, 217, 0) !important;
+ color: black;
}
@media (min-width: 640px) {
- #home-page > div {
- overflow-x: hidden;
- }
- #home-page #overview-section {
- width: 20rem;
- min-width: 20rem;
- }
- #home-page #chat-section {
- flex: 1;
- min-width: unset;
- }
+ #home-page > div {
+ overflow-x: hidden;
+ }
+ #home-page #overview-section {
+ width: 20rem;
+ min-width: 20rem;
+ }
+ #home-page #chat-section {
+ flex: 1;
+ min-width: unset;
+ }
}
@media (min-width: 1200px) {
- #home-page #overview-section,
- #home-page #chat-section {
- padding-left: 3rem;
- padding-right: 3rem;
- }
- #home-page #chat-grid {
- margin-top: 3rem;
- }
- #home-page #overview-section {
- width: var(--max-input-width);
- min-width: var(--max-input-width);
- }
+ #home-page #overview-section,
+ #home-page #chat-section {
+ padding-left: 3rem;
+ padding-right: 3rem;
+ }
+ #home-page #chat-grid {
+ margin-top: 3rem;
+ }
+ #home-page #overview-section {
+ width: var(--max-input-width);
+ min-width: var(--max-input-width);
+ }
}
/* src/View/ChatPages/calendarPage.css */
-#chat-page > div > #main > #calendar-page > .pane-wrapper > .pane > .toolbar .input-wrapper {
- display: flex;
- padding: var(--gap-between-tiles);
- gap: var(--gap-between-tiles);
+#chat-page
+ > div
+ > #main
+ > #calendar-page
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ .input-wrapper {
+ display: flex;
+ padding: var(--gap-between-tiles);
+ gap: var(--gap-between-tiles);
}
-#chat-page > div > #main > #calendar-page > .pane-wrapper > .pane > .toolbar .input-wrapper > input {
- min-height: unset;
- height: calc(var(--control-height) - 2 * var(--gap-between-tiles));
- appearance: textfield;
- -moz-appearance: textfield;
- padding-right: 0px;
+#chat-page
+ > div
+ > #main
+ > #calendar-page
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ .input-wrapper
+ > input {
+ min-height: unset;
+ height: calc(var(--control-height) - 2 * var(--gap-between-tiles));
+ appearance: textfield;
+ -moz-appearance: textfield;
+ padding-right: 0px;
}
#calendar-page .input-wrapper > input::-webkit-outer-spin-button,
#calendar-page .input-wrapper > input::-webkit-innner-spin-button {
- appearance: none;
- -webkit-appearance: none;
- margin: 0px;
+ appearance: none;
+ -webkit-appearance: none;
+ margin: 0px;
}
-#chat-page > div > #main > #calendar-page > .pane-wrapper > .pane > .toolbar .input-wrapper > .year-input {
- width: 4.4rem;
+#chat-page
+ > div
+ > #main
+ > #calendar-page
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ .input-wrapper
+ > .year-input {
+ width: 4.4rem;
}
-#chat-page > div > #main > #calendar-page > .pane-wrapper > .pane > .toolbar .input-wrapper > .month-input {
- width: 3.1rem;
+#chat-page
+ > div
+ > #main
+ > #calendar-page
+ > .pane-wrapper
+ > .pane
+ > .toolbar
+ .input-wrapper
+ > .month-input {
+ width: 3.1rem;
}
@media (max-width: 1024px) {
- #chat-page > div > #main > #calendar-page {
- flex-direction: column;
- }
- #chat-page > div > #main > #calendar-page .grid-pane-wrapper {
- border-bottom: 1px solid var(--border);
- }
+ #chat-page > div > #main > #calendar-page {
+ flex-direction: column;
+ }
+ #chat-page > div > #main > #calendar-page .grid-pane-wrapper {
+ border-bottom: 1px solid var(--border);
+ }
}
/* src/View/Components/monthGrid.css */
.month-grid-wrapper {
- display: flex;
- flex-direction: column;
- gap: var(--gap-between-tiles);
- height: 100%;
- overflow: auto;
+ display: flex;
+ flex-direction: column;
+ gap: var(--gap-between-tiles);
+ height: 100%;
+ overflow: auto;
}
.month-grid-wrapper .day-labels {
- flex: 0;
- padding: var(--content-padding);
- padding-bottom: 0px;
+ flex: 0;
+ padding: var(--content-padding);
+ padding-bottom: 0px;
}
.month-grid-wrapper > div {
- display: grid;
- gap: var(--gap-between-tiles);
- grid-template-columns: repeat(7, minmax(0px, 1fr));
- animation: slide-up var(--animation-duration);
+ display: grid;
+ gap: var(--gap-between-tiles);
+ grid-template-columns: repeat(7, minmax(0px, 1fr));
+ animation: slide-up var(--animation-duration);
}
.month-grid {
- overflow: visible;
- padding: var(--content-padding);
- padding-top: 0px;
- flex: 1;
- grid-auto-rows: 1fr;
+ overflow: visible;
+ padding: var(--content-padding);
+ padding-top: 0px;
+ flex: 1;
+ grid-auto-rows: 1fr;
}
.month-grid .tile {
- animation: slide-up var(--animation-duration);
- overflow: hidden;
- align-items: start;
+ animation: slide-up var(--animation-duration);
+ overflow: hidden;
+ align-items: start;
}
.month-grid .tile[today] b {
- color: var(--primary);
+ color: var(--primary);
}
.month-grid .tile > div {
- width: 100%;
- height: 100%;
+ width: 100%;
+ height: 100%;
}
.month-grid .tile .event-count {
- display: none;
- background-color: var(--text-normal);
- color: var(--background);
- opacity: 0.6;
- border-radius: 100%;
- width: 1rem;
- height: 1rem;
- font-size: 0.6rem;
- font-weight: bold;
- align-items: center;
- justify-content: center;
-}
-.month-grid .tile .event-count[has-events] {
- background-color: var(--primary);
- opacity: 1;
-}
-.month-grid .tile .event-list {
- display: flex;
- flex-direction: column;
- height: 100%;
- width: 100%;
- gap: var(--gap-between-tiles);
-}
-.month-grid .tile .event-list span {
- animation: slide-up var(--animation-duration);
-}
-@media (max-width: 640px) and (max-height: 900px) {
- .month-grid .tile {
- padding: 0px;
- }
- .month-grid .tile > div {
+ display: none;
+ background-color: var(--text-normal);
+ color: var(--background);
+ opacity: 0.6;
+ border-radius: 100%;
+ width: 1rem;
+ height: 1rem;
+ font-size: 0.6rem;
+ font-weight: bold;
align-items: center;
justify-content: center;
- }
- .month-grid .tile .event-count {
+}
+.month-grid .tile .event-count[has-events] {
+ background-color: var(--primary);
+ opacity: 1;
+}
+.month-grid .tile .event-list {
display: flex;
- }
- .month-grid .tile .event-list {
- display: none;
- }
+ flex-direction: column;
+ height: 100%;
+ width: 100%;
+ gap: var(--gap-between-tiles);
+}
+.month-grid .tile .event-list span {
+ animation: slide-up var(--animation-duration);
+}
+@media (max-width: 640px) and (max-height: 900px) {
+ .month-grid .tile {
+ padding: 0px;
+ }
+ .month-grid .tile > div {
+ align-items: center;
+ justify-content: center;
+ }
+ .month-grid .tile .event-count {
+ display: flex;
+ }
+ .month-grid .tile .event-list {
+ display: none;
+ }
}
/* src/View/ChatPages/messagePage.css */
#chat-page #main #message-page .content {
- padding: 0px;
- overflow: hidden;
- display: grid;
- grid-template-rows: 1fr var(--control-height) var(--safe-area-bottom);
+ padding: 0px;
+ overflow: hidden;
+ display: grid;
+ grid-template-rows: 1fr var(--control-height) var(--safe-area-bottom);
}
#message-page #message-container {
- overflow: auto;
+ overflow: auto;
}
#message-page #composer {
- display: flex;
- justify-content: center;
+ display: flex;
+ justify-content: center;
}
#message-page #composer input {
- margin: 0px;
- width: 100%;
- max-width: unset;
- flex: 1;
- border-top: 1px solid var(--border);
- border-left: 1px solid var(--border);
-}
-@media (max-width: 700px) {
- #message-page #composer div {
+ margin: 0px;
width: 100%;
max-width: unset;
- display: flex;
- }
+ flex: 1;
+ border-top: 1px solid var(--border);
+ border-left: 1px solid var(--border);
+}
+@media (max-width: 700px) {
+ #message-page #composer div {
+ width: 100%;
+ max-width: unset;
+ display: flex;
+ }
}
@media (min-width: 700px) {
- #message-page #composer .content-width-constraint,
- #message-page #composer .input-width-constraint {
- display: flex;
- justify-content: flex-end;
- width: 100%;
- max-width: var(--max-content-width);
- }
- #message-page #composer .content-width-constraint {
- max-width: var(--max-content-width);
- }
- #message-page #composer .input-width-constraint {
- max-width: var(--max-input-width);
- }
+ #message-page #composer .content-width-constraint,
+ #message-page #composer .input-width-constraint {
+ display: flex;
+ justify-content: flex-end;
+ width: 100%;
+ max-width: var(--max-content-width);
+ }
+ #message-page #composer .content-width-constraint {
+ max-width: var(--max-content-width);
+ }
+ #message-page #composer .input-width-constraint {
+ max-width: var(--max-input-width);
+ }
}
#message-page #message-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: var(--content-padding);
- gap: var(--gap-between-tiles);
- height: auto;
- max-height: unset;
- scroll-behavior: smooth;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: var(--content-padding);
+ gap: var(--gap-between-tiles);
+ height: auto;
+ max-height: unset;
+ scroll-behavior: smooth;
}
.message-bubble {
- display: flex;
- width: 100%;
- max-width: var(--max-content-width);
+ display: flex;
+ width: 100%;
+ max-width: var(--max-content-width);
}
.message-bubble .tile.main {
- display: flex;
- flex-direction: row;
- align-items: start;
- padding: 0px;
- width: 100%;
- max-width: var(--max-input-width);
- overflow: hidden;
+ display: flex;
+ flex-direction: row;
+ align-items: start;
+ padding: 0px;
+ width: 100%;
+ max-width: var(--max-input-width);
+ overflow: hidden;
}
.message-bubble .tile.main .text-container {
- display: flex;
- flex-direction: column;
- overflow: hidden;
- width: 100%;
- flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ width: 100%;
+ flex: 1;
}
.message-bubble .tile.main .button-container {
- display: flex;
- width: var(--control-height);
- max-width: var(--control-height);
+ display: flex;
+ width: var(--control-height);
+ max-width: var(--control-height);
}
.message-bubble .tile.main button {
- justify-content: center;
- padding: 0px;
- width: 100%;
- --surface: transparent;
+ justify-content: center;
+ padding: 0px;
+ width: 100%;
+ --surface: transparent;
}
.message-bubble .tile.main .text-container {
- padding: var(--control-padding);
+ padding: var(--control-padding);
}
.message-bubble .tile.main .timestamp,
.message-bubble .tile.main .timestamp .icon,
.message-bubble .tile.main .sender-name {
- font-size: 0.8rem;
- opacity: 0.6;
+ font-size: 0.8rem;
+ opacity: 0.6;
}
.message-bubble .tile.main .timestamp {
- display: flex;
- align-items: center;
- gap: var(--gap-between-tiles);
+ display: flex;
+ align-items: center;
+ gap: var(--gap-between-tiles);
}
.message-bubble .tile.main .timestamp .icon {
- display: none;
+ display: none;
}
.message-bubble .tile.main .sender-name {
- font-weight: bold;
+ font-weight: bold;
}
.message-bubble .tile.main .body {
- word-break: break-word;
+ word-break: break-word;
}
.message-bubble[sentbyuser] {
- justify-content: flex-end;
+ justify-content: flex-end;
}
.message-bubble[sentbyuser] {
- align-items: flex-end;
+ align-items: flex-end;
}
.message-bubble[sentbyuser] .tile.main,
.message-bubble[sentbyuser] .tile.main .timestamp {
- flex-direction: row-reverse;
+ flex-direction: row-reverse;
}
.message-bubble[sentbyuser] .tile.main .timestamp .icon {
- display: flex;
+ display: flex;
}
.message-bubble[sentbyuser] .tile.main span {
- text-align: right;
+ text-align: right;
}
/* src/View/ChatPages/taskPage.css */
#chat-page > div > #main > #task-page > .pane-wrapper > .pane > .main-content {
- width: 100%;
- max-width: 100%;
- padding: 0px;
+ width: 100%;
+ max-width: 100%;
+ padding: 0px;
}
-#chat-page > div > #main > #task-page > .pane-wrapper > .pane > .main-content > div {
- animation: slide-up var(--animation-duration);
+#chat-page
+ > div
+ > #main
+ > #task-page
+ > .pane-wrapper
+ > .pane
+ > .main-content
+ > div {
+ animation: slide-up var(--animation-duration);
}
#chat-page > div > #main > #task-page .task-grid {
- display: flex;
- flex-direction: column;
- gap: var(--gap-between-tiles);
- padding: var(--content-padding);
+ display: flex;
+ flex-direction: column;
+ gap: var(--gap-between-tiles);
+ padding: var(--content-padding);
}
#chat-page > div > #main > #task-page .kanban-board-wrapper,
#chat-page > div > #main > #task-page .status-page-content {
- display: flex;
- gap: var(--large-gap);
- width: 100%;
- height: 100%;
- overflow: auto;
- padding: var(--content-padding);
+ display: flex;
+ gap: var(--large-gap);
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ padding: var(--content-padding);
}
#chat-page > div > #main > #task-page .kanban-board-wrapper .kanban-column,
#chat-page > div > #main > #task-page .status-grid-wrapper .status-column {
- display: flex;
- flex-direction: column;
- gap: var(--gap-between-tiles);
+ display: flex;
+ flex-direction: column;
+ gap: var(--gap-between-tiles);
}
#chat-page > div > #main > #task-page .property-input-wrapper {
- display: flex;
- flex-direction: row;
+ display: flex;
+ flex-direction: row;
}
#chat-page > div > #main > #task-page .kanban-board-wrapper .kanban-column,
#chat-page > div > #main > #task-page .property-input-wrapper,
#chat-page > div > #main > #task-page .status-grid-wrapper .status-column,
#chat-page > div > #main > #task-page .status-name-row > *,
#chat-page > div > #main > #task-page .status-name-row::before {
- width: 18rem;
- min-width: 18rem;
- max-width: 18rem;
+ width: 18rem;
+ min-width: 18rem;
+ max-width: 18rem;
}
#chat-page > div > #main > #task-page .status-name-row,
#chat-page > div > #main > #task-page .status-grid-wrapper {
- display: flex;
- gap: var(--large-gap);
+ display: flex;
+ gap: var(--large-gap);
}
#chat-page > div > #main > #task-page .status-page-content,
#chat-page > div > #main > #task-page .status-grid-wrapper {
- flex-direction: column;
+ flex-direction: column;
}
#chat-page > div > #main > #task-page .status-name-row::before {
- content: "";
- display: flex;
+ content: "";
+ display: flex;
}
#chat-page > div > #main > #task-page .board-close-button {
- display: none;
+ display: none;
}
#chat-page > div > #main > #task-page .board-toggle-button {
- display: flex;
+ display: flex;
}
-#chat-page > div > #main > #task-page[isshowingboard][showingboardlist=false] #board-list {
- display: none;
+#chat-page
+ > div
+ > #main
+ > #task-page[isshowingboard][showingboardlist="false"]
+ #board-list {
+ display: none;
}
@media (min-width: 640px) {
- #chat-page > div > #main > #task-page .task-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
- }
+ #chat-page > div > #main > #task-page .task-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
+ }
}
@media (max-width: 1024px) {
- #chat-page > div > #main > #task-page #board-list {
- display: false;
- }
- #chat-page > div > #main > #task-page #board-content {
- display: none;
- }
- #chat-page > div > #main > #task-page[isshowingboard] #board-list {
- display: none;
- }
- #chat-page > div > #main > #task-page[isshowingboard] #board-content {
- display: flex;
- }
- #chat-page > div > #main > #task-page .board-close-button {
- display: flex;
- }
- #chat-page > div > #main > #task-page .board-toggle-button {
- display: none;
- }
+ #chat-page > div > #main > #task-page #board-list {
+ display: false;
+ }
+ #chat-page > div > #main > #task-page #board-content {
+ display: none;
+ }
+ #chat-page > div > #main > #task-page[isshowingboard] #board-list {
+ display: none;
+ }
+ #chat-page > div > #main > #task-page[isshowingboard] #board-content {
+ display: flex;
+ }
+ #chat-page > div > #main > #task-page .board-close-button {
+ display: flex;
+ }
+ #chat-page > div > #main > #task-page .board-toggle-button {
+ display: none;
+ }
}
/* src/View/Components/fileBrowser.css */
.file-browser {
- width: 100%;
- height: 100%;
- display: flex;
- overflow-x: scroll;
- overflow-y: hidden;
- scroll-behavior: smooth;
- scroll-snap-type: x mandatory;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ scroll-behavior: smooth;
+ scroll-snap-type: x mandatory;
}
.file-browser > div {
- display: flex;
- flex-direction: column;
- width: 100%;
- min-width: 100%;
- flex: 0;
- scroll-snap-align: center;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ min-width: 100%;
+ flex: 0;
+ scroll-snap-align: center;
}
.file-browser .scroll-area {
- flex: 1;
- width: 100%;
- overflow-y: auto;
- padding: var(--content-padding);
+ flex: 1;
+ width: 100%;
+ overflow-y: auto;
+ padding: var(--content-padding);
}
.file-browser .detail-button-wrapper {
- display: flex;
- justify-content: end;
- border-top: 1px solid var(--border);
- padding: var(--content-padding);
+ display: flex;
+ justify-content: end;
+ border-top: 1px solid var(--border);
+ padding: var(--content-padding);
}
.file-browser .detail-button-wrapper button {
- max-width: 100%;
+ max-width: 100%;
}
.file-browser code {
- width: 100%;
- height: 100%;
- font-size: 0.9rem;
+ width: 100%;
+ height: 100%;
+ font-size: 0.9rem;
}
@media (min-width: 640px) {
- .file-browser > div {
- width: 50%;
- min-width: 50%;
- flex: 1;
- }
- .file-browser .detail-button-wrapper {
- display: none;
- }
+ .file-browser > div {
+ width: 50%;
+ min-width: 50%;
+ flex: 1;
+ }
+ .file-browser .detail-button-wrapper {
+ display: none;
+ }
}
diff --git a/dist/index.html b/dist/index.html
index f3dd6a16..e9e941aa 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -1,35 +1,35 @@
-
+
-
- Comms
+
+ Comms
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
diff --git a/dist/index.js b/dist/index.js
index de73d742..7a7cfb9b 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1,6149 +1,8140 @@
(() => {
- // node_modules/uuid/dist/esm-browser/stringify.js
- var byteToHex = [];
- for (i = 0; i < 256; ++i) {
- byteToHex.push((i + 256).toString(16).slice(1));
- }
- var i;
- function unsafeStringify(arr, offset = 0) {
- return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
- }
+ // node_modules/uuid/dist/esm-browser/stringify.js
+ var byteToHex = [];
+ for (i = 0; i < 256; ++i) {
+ byteToHex.push((i + 256).toString(16).slice(1));
+ }
+ var i;
+ function unsafeStringify(arr, offset = 0) {
+ return (
+ byteToHex[arr[offset + 0]] +
+ byteToHex[arr[offset + 1]] +
+ byteToHex[arr[offset + 2]] +
+ byteToHex[arr[offset + 3]] +
+ "-" +
+ byteToHex[arr[offset + 4]] +
+ byteToHex[arr[offset + 5]] +
+ "-" +
+ byteToHex[arr[offset + 6]] +
+ byteToHex[arr[offset + 7]] +
+ "-" +
+ byteToHex[arr[offset + 8]] +
+ byteToHex[arr[offset + 9]] +
+ "-" +
+ byteToHex[arr[offset + 10]] +
+ byteToHex[arr[offset + 11]] +
+ byteToHex[arr[offset + 12]] +
+ byteToHex[arr[offset + 13]] +
+ byteToHex[arr[offset + 14]] +
+ byteToHex[arr[offset + 15]]
+ ).toLowerCase();
+ }
- // node_modules/uuid/dist/esm-browser/rng.js
- var getRandomValues;
- var rnds8 = new Uint8Array(16);
- function rng() {
- if (!getRandomValues) {
- getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
- if (!getRandomValues) {
- throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
- }
- }
- return getRandomValues(rnds8);
- }
-
- // node_modules/uuid/dist/esm-browser/native.js
- var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
- var native_default = {
- randomUUID
- };
-
- // node_modules/uuid/dist/esm-browser/v4.js
- function v4(options, buf, offset) {
- if (native_default.randomUUID && !buf && !options) {
- return native_default.randomUUID();
- }
- options = options || {};
- var rnds = options.random || (options.rng || rng)();
- rnds[6] = rnds[6] & 15 | 64;
- rnds[8] = rnds[8] & 63 | 128;
- if (buf) {
- offset = offset || 0;
- for (var i = 0; i < 16; ++i) {
- buf[offset + i] = rnds[i];
- }
- return buf;
- }
- return unsafeStringify(rnds);
- }
- var v4_default = v4;
-
- // node_modules/bloatless-react/index.ts
- var State = class {
- _value;
- _bindings = /* @__PURE__ */ new Set();
- // init
- constructor(initialValue) {
- this._value = initialValue;
- }
- // value
- get value() {
- return this._value;
- }
- set value(newValue) {
- if (this._value == newValue) return;
- this._value = newValue;
- this.callSubscriptions();
- }
- // subscriptions
- callSubscriptions() {
- this._bindings.forEach((fn) => fn(this._value));
- }
- subscribe(fn) {
- this._bindings.add(fn);
- fn(this._value);
- }
- subscribeSilent(fn) {
- this._bindings.add(fn);
- }
- // stringify
- toString() {
- return JSON.stringify(this._value);
- }
- };
- var ListState = class extends State {
- additionHandlers = /* @__PURE__ */ new Set();
- removalHandlers = /* @__PURE__ */ new Map();
- // init
- constructor(initialItems) {
- super(new Set(initialItems));
- }
- // list
- add(...items) {
- items.forEach((item) => {
- this.value.add(item);
- this.additionHandlers.forEach((handler) => handler(item));
- });
- this.callSubscriptions();
- }
- remove(...items) {
- items.forEach((item) => {
- this.value.delete(item);
- if (!this.removalHandlers.has(item)) return;
- this.removalHandlers.get(item).forEach((handler) => handler(item));
- this.removalHandlers.delete(item);
- });
- this.callSubscriptions();
- }
- clear() {
- this.remove(...this.value.values());
- }
- // handlers
- handleAddition(handler) {
- this.additionHandlers.add(handler);
- [...this.value.values()].forEach(handler);
- }
- handleRemoval(item, handler) {
- if (!this.removalHandlers.has(item))
- this.removalHandlers.set(item, /* @__PURE__ */ new Set());
- this.removalHandlers.get(item).add(handler);
- }
- // stringification
- toString() {
- const array = [...this.value.values()];
- const json = JSON.stringify(array);
- return json;
- }
- };
- var MapState = class extends State {
- additionHandlers = /* @__PURE__ */ new Set();
- removalHandlers = /* @__PURE__ */ new Map();
- // init
- constructor(initialItems) {
- super(new Map(initialItems));
- }
- // list
- set(key, item) {
- this.remove(key);
- this.value.set(key, item);
- this.additionHandlers.forEach((handler) => handler(item));
- this.callSubscriptions();
- }
- remove(key) {
- const item = this.value.get(key);
- if (!item) return;
- this.value.delete(key);
- this.callSubscriptions();
- if (!this.removalHandlers.has(item)) return;
- this.removalHandlers.get(item).forEach((handler) => handler(item));
- this.removalHandlers.delete(item);
- }
- clear() {
- [...this.value.keys()].forEach((key) => this.remove(key));
- }
- // handlers
- handleAddition(handler) {
- this.additionHandlers.add(handler);
- [...this.value.values()].forEach(handler);
- }
- handleRemoval(item, handler) {
- if (!this.removalHandlers.has(item))
- this.removalHandlers.set(item, /* @__PURE__ */ new Set());
- this.removalHandlers.get(item).add(handler);
- }
- // stringification
- toString() {
- const array = [...this.value.entries()];
- const json = JSON.stringify(array);
- return json;
- }
- };
- function createProxyState(statesToSubscibe, fn) {
- const proxyState = new State(fn());
- statesToSubscibe.forEach(
- (state) => state.subscribe(() => proxyState.value = fn())
- );
- return proxyState;
- }
- function bulkSubscribe(statesToSubscibe, fn) {
- statesToSubscibe.forEach((state) => state.subscribeSilent(fn));
- }
- function createElement(tagName, attributes = {}, ...children) {
- const element = document.createElement(tagName);
- if (attributes != null)
- Object.entries(attributes).forEach((entry) => {
- const [attributename, value] = entry;
- const [directiveKey, directiveValue] = attributename.split(":");
- switch (directiveKey) {
- case "on": {
- switch (directiveValue) {
- case "enter": {
- element.addEventListener("keydown", (e) => {
- if (e.key != "Enter") return;
- value();
- });
- break;
- }
- default: {
- element.addEventListener(directiveValue, value);
- }
- }
- break;
- }
- case "subscribe": {
- const state = value;
- state.subscribe(
- (newValue) => element[directiveValue] = newValue
- );
- break;
- }
- case "bind": {
- const state = value;
- state.subscribe(
- (newValue) => element[directiveValue] = newValue
- );
- element.addEventListener(
- "input",
- () => state.value = element[directiveValue]
- );
- break;
- }
- case "toggle": {
- if (value.subscribe) {
- const state = value;
- state.subscribe(
- (newValue) => element.toggleAttribute(directiveValue, newValue)
- );
- } else {
- element.toggleAttribute(directiveValue, value);
- }
- break;
- }
- case "set": {
- const state = value;
- state.subscribe(
- (newValue) => element.setAttribute(directiveValue, newValue)
- );
- break;
- }
- case "children": {
- switch (directiveValue) {
- case "set": {
- const state = value;
- state.subscribe((newValue) => {
- element.innerHTML = "";
- element.append(...[newValue].flat());
- });
- break;
- }
- case "append":
- case "prepend": {
- try {
- const [listState, toElement] = value;
- listState.handleAddition((newItem) => {
- const child = toElement(newItem);
- listState.handleRemoval(
- newItem,
- () => child.remove()
- );
- if (directiveValue == "append") {
- element.append(child);
- } else if (directiveValue == "prepend") {
- element.prepend(child);
- }
- });
- } catch (error) {
- console.error(error);
- throw `error: cannot process subscribe:children directive.
- Usage: "children:append={[list, converter]}"; you can find a more detailed example in the documentation.`;
- }
- }
- }
- break;
- }
- default:
- element.setAttribute(attributename, value);
- }
- });
- children.filter((x) => x).forEach((child) => element.append(child));
- return element;
- }
-
- // src/Model/Utility/typeSafety.ts
- var DATA_VERSION = "v2";
- function checkIsValidObject(object) {
- return object.dataVersion == DATA_VERSION;
- }
- function checkMatchesObjectStructure(objectToCheck, reference) {
- if (typeof reference != "object") {
- return typeof objectToCheck == typeof reference;
- }
- for (const key of Object.keys(reference)) {
- const requiredType = typeof reference[key];
- const actualType = typeof objectToCheck[key];
- if (requiredType != actualType) return false;
- if (Array.isArray(reference[key]) || reference[key] instanceof Set) {
- if (objectToCheck[key].length == 0) continue;
- if (objectToCheck[key].size == 0) continue;
- const [firstOfObjectToCheck] = objectToCheck[key];
- const [fisrtOfReference] = reference[key];
- const doesFirstItemMatch = checkMatchesObjectStructure(
- firstOfObjectToCheck,
- fisrtOfReference
- );
- if (doesFirstItemMatch == false) return false;
- } else if (requiredType == "object") {
- const doesNestedObjectMatch = checkMatchesObjectStructure(
- objectToCheck[key],
- reference[key]
- );
- if (doesNestedObjectMatch == false) return false;
- }
- }
- return true;
- }
-
- // src/Model/Utility/utility.ts
- function generateRandomToken(length) {
- const array = new Uint8Array(length);
- crypto.getRandomValues(array);
- return array.join("");
- }
- function createTimestamp() {
- return (/* @__PURE__ */ new Date()).toISOString();
- }
- function checkDoesObjectMatchReference(reference, stringEntryObject) {
- reference_entry_loop: for (const referenceEntry of Object.entries(
- reference
- )) {
- const [referenceKey, referenceValue] = referenceEntry;
- const stringEntryObjectValue = stringEntryObject[referenceKey];
- if (referenceValue == void 0) return false;
- if (referenceValue[0] == "-") {
- const strippedReferenceValue = referenceValue.toString().substring(1);
- if (strippedReferenceValue == "" && stringEntryObjectValue != void 0 && stringEntryObjectValue != "") {
- return false;
- }
- if (stringEntryObjectValue == strippedReferenceValue) {
- return false;
- }
- } else {
- if (referenceValue == "" && (stringEntryObjectValue == void 0 || stringEntryObjectValue == "")) {
- return false;
- } else if (referenceValue == "") {
- continue reference_entry_loop;
- }
- if (stringEntryObjectValue != referenceValue) {
- return false;
- }
- }
- }
- return true;
- }
- function collectObjectValuesForKey(key, converter, objects) {
- const values = /* @__PURE__ */ new Set();
- for (const object of objects) {
- const stringEntryObject = converter(object);
- const stringEntryObjectValue = stringEntryObject[key];
- if (stringEntryObjectValue == void 0 || stringEntryObjectValue == "")
- continue;
- values.add(stringEntryObjectValue.toString());
- }
- return [...values.values()];
- }
- function checkDoesObjectMatchSearch(query, getStringsOfObject, object) {
- if (query == "") return true;
- const stringsInObject = getStringsOfObject(object);
- const wordsInObject = [];
- for (const string of stringsInObject) {
- const lowercaseWordsInString = string.toString().toLowerCase().split(" ").filter((word) => word != "");
- wordsInObject.push(...lowercaseWordsInString);
- }
- const lowercaseWordsInQuery = query.toLowerCase().split(" ").filter((word) => word != "");
- for (const queryWord of lowercaseWordsInQuery) {
- if (queryWord[0] == "-") {
- const wordContent = queryWord.substring(1);
- if (wordsInObject.includes(wordContent)) {
- return false;
- }
- } else {
- if (wordsInObject.includes(queryWord) == false) {
- return false;
- }
- }
- }
- return true;
- }
- var HandlerManager = class {
- constructor() {
- this.handlers = /* @__PURE__ */ new Set();
- // manage
- this.addHandler = (handler) => {
- this.handlers.add(handler);
- };
- this.deleteHandler = (handler) => {
- this.handlers.delete(handler);
- };
- // trigger
- this.trigger = (item) => {
- for (const handler of this.handlers) {
- handler(item);
- }
- };
- }
- };
- var IndexManager = class {
- // init
- constructor(itemToString) {
- this.sortedStrings = [];
- // methods
- this.update = (items) => {
- this.sortedStrings = [];
- let strings = [];
- for (const item of items) {
- const string = this.itemToString(item);
- strings.push(string);
- }
- this.sortedStrings = strings.sort(localeCompare);
- };
- this.getIndex = (item) => {
- const string = this.itemToString(item);
- const index = this.sortedStrings.indexOf(string);
- return index;
- };
- this.itemToString = itemToString;
- }
- };
- function getLocalStorageItemAndClear(key) {
- const value = localStorage.getItem(key);
- localStorage.removeItem(key);
- if (value != null) localStorage.setItem(`_${key}`, value);
- return value;
- }
- function stringify(data) {
- return JSON.stringify(data, null, 4);
- }
- function padZero(string, length) {
- return (string ?? "").padStart(length, "0");
- }
- function parse(string) {
- try {
- return JSON.parse(string);
- } catch {
- return {};
- }
- }
- function parseValidObject(string, reference) {
- const parsed = parse(string);
- if (checkIsValidObject(parsed) == false) return null;
- const doesMatchReference = checkMatchesObjectStructure(
- parsed,
- reference
- );
- if (doesMatchReference == false) return null;
- return parsed;
- }
- function parseOrFallback(inputString) {
- try {
- return JSON.parse(inputString);
- } catch {
- return inputString;
- }
- }
- function parseArray(inputString) {
- const parsed = parseOrFallback(inputString);
- if (Array.isArray(parsed) == false) return [];
- return parsed;
- }
- function localeCompare(a, b) {
- return a.localeCompare(b);
- }
-
- // src/Model/Global/storageModel.ts
- var PATH_COMPONENT_SEPARATOR = "\\";
- var StorageModel = class _StorageModel {
- // init
- constructor() {
- this.storageEntryTree = {};
- // read
- this.read = (pathComponents) => {
- const pathString = _StorageModel.pathComponentsToString(
- ...pathComponents
- );
- return localStorage.getItem(pathString);
- };
- this.list = (pathComponents) => {
- let currentParent = this.storageEntryTree;
- for (const component of pathComponents) {
- const nextParent = currentParent[component];
- if (nextParent == void 0) return [];
- currentParent = nextParent;
- }
- return [...Object.keys(currentParent).sort(localeCompare)];
- };
- // write
- this.write = (pathComponents, value) => {
- const pathString = _StorageModel.pathComponentsToString(
- ...pathComponents
- );
- localStorage.setItem(pathString, value);
- this.updateTree(...pathComponents);
- };
- this.remove = (pathComponents, shouldInitialize = true) => {
- const pathString = _StorageModel.pathComponentsToString(
- ...pathComponents
- );
- localStorage.removeItem(pathString);
- if (shouldInitialize == true) {
- this.initializeTree();
- }
- };
- this.rename = (sourcePathComponents, destinationPathComponents, shouldInitialize = true) => {
- const content = this.read(sourcePathComponents);
- if (content == null) return false;
- this.write(destinationPathComponents, content);
- this.remove(sourcePathComponents);
- if (shouldInitialize == true) {
- this.initializeTree();
- }
- return true;
- };
- // recursion
- this.recurse = (rootDirectory, fn) => {
- loop_over_files: for (const key of Object.keys(localStorage)) {
- const pathComponentsOfCurrentEntity = _StorageModel.stringToPathComponents(key);
- loop_over_path_components: for (let i = 0; i < rootDirectory.length; i++) {
- if (!pathComponentsOfCurrentEntity[i]) continue loop_over_files;
- if (pathComponentsOfCurrentEntity[i] != rootDirectory[i])
- continue loop_over_files;
- }
- fn(pathComponentsOfCurrentEntity);
- }
- this.initializeTree();
- };
- this.removeRecursively = (pathComponents) => {
- this.recurse(
- pathComponents,
- (path) => this.remove(path, false)
- );
- this.initializeTree();
- };
- this.renameRecursively = (sourcePathComponents, destinationPathComponents) => {
- this.recurse(sourcePathComponents, (path) => {
- const relativePathOfCurrentEntity = path.slice(
- sourcePathComponents.length
- );
- const destinationPathComponentsOfCurrentEntity = [
- ...destinationPathComponents,
- ...relativePathOfCurrentEntity
- ];
- this.rename(path, destinationPathComponentsOfCurrentEntity, false);
- });
- this.initializeTree();
- };
- // stringifiable
- this.writeStringifiable = (pathComponents, value) => {
- const valueString = stringify(value);
- this.write(pathComponents, valueString);
- };
- this.readStringifiable = (pathComponents, reference) => {
- const valueString = this.read(pathComponents);
- if (!valueString) return null;
- const object = parseValidObject(valueString, reference);
- if (object == null) return null;
- return object;
- };
- // cleaning
- this.removeJunk = () => {
- this.recurse([], (path) => {
- if (path[0] == DATA_VERSION) return;
- this.remove(path);
- });
- };
- // tree
- this.initializeTree = () => {
- console.log("initializing tree");
- this.storageEntryTree = {};
- for (const key of Object.keys(localStorage)) {
- const components = _StorageModel.stringToPathComponents(key);
- this.updateTree(...components);
- }
- };
- this.updateTree = (...pathComponents) => {
- let currentParent = this.storageEntryTree;
- for (const pathPart of pathComponents) {
- if (!currentParent[pathPart]) {
- currentParent[pathPart] = {};
- }
- currentParent = currentParent[pathPart];
- }
- };
- this.printTree = () => {
- return stringify(this.storageEntryTree);
- };
- this.initializeTree();
- }
- static {
- // utility
- this.getFileName = (pathComponents) => {
- return pathComponents[pathComponents.length - 1] || "\\";
- };
- }
- static {
- this.getFileNameFromString = (pathString) => {
- const pathComponents = this.stringToPathComponents(pathString);
- return pathComponents[pathComponents.length - 1] || "\\";
- };
- }
- static {
- this.pathComponentsToString = (...pathComponents) => {
- return pathComponents.filter((x) => x != "").join(PATH_COMPONENT_SEPARATOR);
- };
- }
- static {
- this.stringToPathComponents = (string) => {
- return string.split(PATH_COMPONENT_SEPARATOR).filter((x) => x != "");
- };
- }
- static {
- this.join = (...items) => {
- let allComponents = [];
- for (const item of items) {
- const parts = this.stringToPathComponents(item);
- allComponents.push(...parts);
- }
- return _StorageModel.pathComponentsToString(...allComponents);
- };
- }
- static getPath(locationName, filePath) {
- return [DATA_VERSION, locationName, ...filePath];
- }
- };
- var filePaths = {
- connectionModel: {
- socketAddress: ["socket-address"],
- reconnectAddress: ["reconnect-address"],
- outbox: ["outbox"],
- mailboxes: ["mailboxes"],
- previousAddresses: ["previous-addresses"]
- },
- chat: {
- base: [],
- chatBase: (id) => [id],
- info: (id) => [...filePaths.chat.chatBase(id), "info"],
- color: (id) => [...filePaths.chat.chatBase(id), "color"],
- messages: (id) => [...filePaths.chat.chatBase(id), "messages"],
- lastUsedPage: (id) => [
- ...filePaths.chat.chatBase(id),
- "last-used-page"
- ],
- files: (id) => [...filePaths.chat.chatBase(id), "files"]
- },
- settingsModel: {
- username: ["user-name"],
- firstDayOfWeek: ["first-day-of-week"]
- }
- };
-
- // src/Model/Files/calendarModel.ts
- var CalendarModel = class _CalendarModel {
- // init
- constructor(storageModel2, settingsModel2, fileModel) {
- // paths
- this.getBasePath = () => {
- return this.fileModel.getModelContainerPath(
- "calendar" /* ModelCalendar */
- );
- };
- this.getViewPath = () => {
- return [...this.getBasePath(), "view" /* ModelView */];
- };
- this.getMonthContainerPath = () => {
- return [...this.getBasePath(), "months" /* Months */];
- };
- this.getMonthPath = (monthString) => {
- return [...this.getMonthContainerPath(), monthString];
- };
- // main
- this.storeTaskReference = (taskFileContent) => {
- if (taskFileContent.date == void 0) return;
- const monthString = _CalendarModel.isoToMonthString(
- taskFileContent.date
- );
- const monthPath = this.getMonthPath(monthString);
- const referencePath = [...monthPath, taskFileContent.fileId];
- this.storageModel.write(referencePath, "");
- };
- this.deleteTaskReference = (monthString, taskId) => {
- const monthPath = this.getMonthPath(monthString);
- const referencePath = [...monthPath, taskId];
- this.storageModel.write(referencePath, "");
- };
- this.listTaskIds = (monthString) => {
- const monthPath = this.getMonthPath(monthString);
- return this.storageModel.list(monthPath);
- };
- this.generateMonthGrid = (year, month, defaultValueCreator) => {
- const date = /* @__PURE__ */ new Date();
- const isCurrentMonth = year == date.getFullYear() && month == date.getMonth() + 1;
- date.setDate(1);
- date.setMonth(month - 1);
- date.setFullYear(year);
- const firstWeekdayOfMonth = date.getDay();
- const firstDayOfWeekSetting = parseInt(
- this.settingsModel.firstDayOfWeek
- );
- const offset = firstWeekdayOfMonth < firstDayOfWeekSetting ? 7 - firstDayOfWeekSetting : firstWeekdayOfMonth - firstDayOfWeekSetting;
- date.setMonth(month);
- date.setDate(-1);
- const daysInMonth = date.getDate() + 1;
- const grid = {
- offset,
- firstDayOfWeek: parseInt(this.settingsModel.firstDayOfWeek),
- isCurrentMonth,
- year,
- month,
- days: {}
- };
- for (let i = 0; i < daysInMonth; i++) {
- const paddedDate = _CalendarModel.padDateOrMonth(
- (i + 1).toString()
- );
- grid.days[paddedDate] = defaultValueCreator();
- }
- return grid;
- };
- this.storageModel = storageModel2;
- this.settingsModel = settingsModel2;
- this.fileModel = fileModel;
- }
- static {
- // utility
- this.isoToMonthString = (dateISOString) => {
- const [year, month, _] = dateISOString.split("-");
- return _CalendarModel.getMonthString(year, month);
- };
- }
- static {
- this.isoToDateString = (dateISOString) => {
- const [year, month, date, _] = dateISOString.split("-");
- const paddedDate = _CalendarModel.padDateOrMonth(date ?? "");
- return paddedDate;
- };
- }
- static {
- this.getMonthString = (year = "", month = "") => {
- const paddedYear = year.padStart(4, "0");
- const paddedMonth = _CalendarModel.padDateOrMonth(month);
- return `${paddedYear}-${paddedMonth}`;
- };
- }
- static {
- this.getISODateString = (year, month, date) => {
- const monthString = _CalendarModel.getMonthString(year, month);
- const paddedDate = _CalendarModel.padDateOrMonth(date);
- return `${monthString}-${paddedDate}`;
- };
- }
- static {
- this.padDateOrMonth = (input) => {
- return input.padStart(2, "0");
- };
- }
- };
-
- // src/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/Files/boardsAndTasksModel.ts
- var BoardsAndTasksModel = class _BoardsAndTasksModel {
- // init
- constructor(storageModel2, settingsModel2, chatModel, fileModel) {
- // data
- this.boardHandlerManager = new HandlerManager();
- this.taskHandlerManager = new HandlerManager();
- // paths
- this.getBasePath = () => {
- return this.fileModel.getModelContainerPath("tasks" /* ModelTask */);
- };
- this.getViewPath = () => {
- return [...this.getBasePath(), "view" /* ModelView */];
- };
- this.getBoardFilePath = (boardId) => {
- return [...this.fileModel.getFilePath(boardId)];
- };
- this.getTaskFilePath = (taskId) => {
- return [...this.fileModel.getFilePath(taskId)];
- };
- this.getBoardContainerPath = () => {
- return [...this.getBasePath(), "boards" /* Boards */];
- };
- this.getBoardDirectoryPath = (boardId) => {
- return [...this.getBoardContainerPath(), boardId];
- };
- this.getTaskContainerPath = (boardId) => {
- return [
- ...this.getBoardDirectoryPath(boardId),
- "tasks" /* BoardTasks */
- ];
- };
- this.getTaskReferencePath = (boardId, fileId) => {
- return [...this.getTaskContainerPath(boardId), fileId];
- };
- // handlers
- this.handleFileContent = (fileContent) => {
- if (checkMatchesObjectStructure(
- fileContent,
- BoardInfoFileContentReference
- ) == true) {
- this.handleBoard(fileContent);
- } else if (checkMatchesObjectStructure(
- fileContent,
- TaskFileContentReference
- ) == true) {
- this.handleTask(fileContent);
- }
- };
- this.handleBoard = (boardInfoFileContent) => {
- this.updateBoard(boardInfoFileContent);
- };
- this.handleTask = (taskFileContent) => {
- this.updateTask(taskFileContent);
- };
- // boards
- this.createBoard = (name) => {
- const boardInfoFileContent = _BoardsAndTasksModel.createBoardInfoFileContent(
- v4_default(),
- name,
- "standard" /* Standard */
- );
- return boardInfoFileContent;
- };
- this.updateBoard = (boardInfoFileContent) => {
- this.storeBoard(boardInfoFileContent);
- this.boardHandlerManager.trigger(boardInfoFileContent);
- };
- this.updateBoardAndSend = (boardInfoFileContent) => {
- this.updateBoard(boardInfoFileContent);
- this.chatModel.sendMessage("", boardInfoFileContent);
- };
- this.storeBoard = (boardInfoFileContent) => {
- this.fileModel.storeFileContent(boardInfoFileContent);
- const boardDirectoryPath = this.getBoardDirectoryPath(
- boardInfoFileContent.fileId
- );
- this.storageModel.write(boardDirectoryPath, "");
- };
- this.deleteBoard = (boardId) => {
- const boardFilePath = this.getBoardFilePath(boardId);
- const boardDirectoryPath = this.getBoardDirectoryPath(boardId);
- this.storageModel.removeRecursively(boardFilePath);
- this.storageModel.removeRecursively(boardDirectoryPath);
- };
- this.listBoardIds = () => {
- const boardContainerPath = this.getBoardContainerPath();
- const boardIds = this.storageModel.list(boardContainerPath);
- return boardIds;
- };
- this.getBoardInfo = (fileId) => {
- const boardInfoFileContentOrNull = this.fileModel.getLatestFileContent(
- fileId,
- BoardInfoFileContentReference
- );
- return boardInfoFileContentOrNull;
- };
- this.getBoardName = (boardId) => {
- const boardInfo = this.getBoardInfo(boardId);
- if (boardInfo == null) return "";
- return boardInfo.name;
- };
- //tasks
- this.createTask = (boardId) => {
- const taskFileContent = _BoardsAndTasksModel.createTaskFileContent(v4_default(), "", boardId);
- return taskFileContent;
- };
- this.updateTask = (taskFileContent) => {
- this.storeTask(taskFileContent);
- this.taskHandlerManager.trigger(taskFileContent);
- };
- this.updateTaskAndSend = (taskFileContent) => {
- this.updateTask(taskFileContent);
- this.chatModel.sendMessage("", taskFileContent);
- };
- this.storeTask = (taskFileContent) => {
- this.fileModel.storeFileContent(taskFileContent);
- const taskReferencePath = this.getTaskReferencePath(
- taskFileContent.boardId,
- taskFileContent.fileId
- );
- this.storageModel.write(taskReferencePath, "");
- this.calendarModel.storeTaskReference(taskFileContent);
- };
- this.listTaskIds = (boardId) => {
- const taskContainerPath = this.getTaskContainerPath(boardId);
- const fileIds = this.storageModel.list(taskContainerPath);
- return fileIds;
- };
- this.listTaskVersionIds = (taskId) => {
- const versionIds = this.fileModel.listFileContentIds(taskId);
- return versionIds;
- };
- this.getLatestTaskFileContent = (taskId) => {
- const taskFileContentOrNull = this.fileModel.getLatestFileContent(
- taskId,
- TaskFileContentReference
- );
- return taskFileContentOrNull;
- };
- this.getSpecificTaskFileContent = (taskId, versionId) => {
- const taskFileContentOrNull = this.fileModel.getFileContent(
- taskId,
- versionId,
- TaskFileContentReference
- );
- return taskFileContentOrNull;
- };
- this.deleteTask = (boardId, taskId) => {
- const taskFilePath = this.getTaskFilePath(taskId);
- this.storageModel.removeRecursively(taskFilePath);
- this.deleteTaskReference(boardId, taskId);
- };
- this.deleteTaskReference = (boardId, taskId) => {
- const taskReferencePath = this.getTaskReferencePath(
- boardId,
- taskId
- );
- this.storageModel.removeRecursively(taskReferencePath);
- };
- this.storageModel = storageModel2;
- this.settingsModel = settingsModel2;
- this.chatModel = chatModel;
- this.fileModel = fileModel;
- this.calendarModel = new CalendarModel(
- this.storageModel,
- this.settingsModel,
- this.fileModel
- );
- }
- static {
- // utility
- this.createBoardInfoFileContent = (fileId, name, color) => {
- const fileContent = FileModel2.createFileContent(fileId, "board-info");
- return {
- ...fileContent,
- name,
- color
- };
- };
- }
- static {
- this.createTaskFileContent = (fileId, name, boardId) => {
- const fileContent = FileModel2.createFileContent(
- fileId,
- "task"
- );
- return {
- ...fileContent,
- name,
- boardId
- };
- };
- }
- };
- var BoardInfoFileContentReference = {
- dataVersion: DATA_VERSION,
- fileId: "string",
- fileContentId: "",
- creationDate: "",
- type: "board-info",
- name: "",
- color: ""
- };
- var TaskFileContentReference = {
- dataVersion: DATA_VERSION,
- fileId: "string",
- fileContentId: "",
- creationDate: "",
- type: "task",
- name: "",
- boardId: ""
- };
-
- // src/Model/Files/fileModel.ts
- var FileModel2 = class _FileModel {
- // init
- constructor(storageModel2, settingsModel2, chatModel) {
- // paths
- this.getBasePath = () => {
- return StorageModel.getPath(
- "chat" /* Chat */,
- filePaths.chat.files(this.chatModel.id)
- );
- };
- this.getFileContainerPath = () => {
- return [...this.getBasePath(), "data" /* Data */];
- };
- this.getModelContainerPath = (modelName) => {
- return [...this.getBasePath(), "model" /* Model */, modelName];
- };
- this.getFilePath = (fileId) => {
- return [...this.getFileContainerPath(), fileId];
- };
- this.getFileContentPath = (fileId, fileContentId) => {
- const filePath = this.getFilePath(fileId);
- return [...filePath, fileContentId];
- };
- // handlers
- this.handleStringifiedFileContent = (stringifiedFileContent) => {
- const fileContent = parseValidObject(
- stringifiedFileContent,
- FileContentReference
- );
- if (fileContent == null) return;
- this.handleFileContent(fileContent);
- };
- this.handleFileContent = (fileContent) => {
- const didStore = this.storeFileContent(fileContent);
- if (didStore == false) return;
- this.boardsAndTasksModel.handleFileContent(fileContent);
- };
- // methods
- this.addFileContentAndSend = (fileContent) => {
- this.handleFileContent(fileContent);
- this.chatModel.sendMessage("", fileContent);
- };
- // storage
- this.storeFileContent = (fileContent) => {
- const fileContentPath = this.getFileContentPath(
- fileContent.fileId,
- fileContent.fileContentId
- );
- const existingFileContent = this.storageModel.read(fileContentPath);
- if (existingFileContent != null) return false;
- const stringifiedContent = stringify(fileContent);
- this.storageModel.write(fileContentPath, stringifiedContent);
- return true;
- };
- this.listFileIds = () => {
- return this.storageModel.list(this.getBasePath());
- };
- this.listFileContentIds = (fileId) => {
- const filePath = this.getFilePath(fileId);
- return this.storageModel.list(filePath);
- };
- this.selectLatestFileContentId = (fileContentIds) => {
- return fileContentIds[fileContentIds.length - 1];
- };
- this.getFileContent = (fileId, fileContentName, reference) => {
- const filePath = this.getFileContentPath(
- fileId,
- fileContentName
- );
- const fileContentOrNull = this.storageModel.readStringifiable(
- filePath,
- reference
- );
- return fileContentOrNull;
- };
- this.getLatestFileContent = (fileId, reference) => {
- const fileContentsIds = this.listFileContentIds(fileId);
- const latestFileContentId = this.selectLatestFileContentId(fileContentsIds);
- if (latestFileContentId == void 0) return null;
- const fileContent = this.getFileContent(
- fileId,
- latestFileContentId,
- reference
- );
- return fileContent;
- };
- this.chatModel = chatModel;
- this.settingsModel = settingsModel2;
- this.storageModel = storageModel2;
- this.boardsAndTasksModel = new BoardsAndTasksModel(
- this.storageModel,
- this.settingsModel,
- chatModel,
- this
- );
- }
- static {
- // utility
- this.generateFileContentId = (creationDate) => {
- return creationDate + v4_default();
- };
- }
- static {
- this.createFileContent = (fileId, type) => {
- const creationDate = createTimestamp();
- const fileContentId = _FileModel.generateFileContentId(creationDate);
- return {
- dataVersion: DATA_VERSION,
- fileId,
- fileContentId,
- creationDate,
- type
- };
- };
- }
- };
- var FileContentReference = {
- dataVersion: DATA_VERSION,
- fileId: "",
- fileContentId: "",
- creationDate: "",
- type: ""
- };
-
- // src/Model/Utility/crypto.ts
- var IV_SIZE = 12;
- var ENCRYPTION_ALG = "AES-GCM";
- async function encryptString(plaintext, passphrase) {
- if (!window.crypto.subtle) return plaintext;
- const iv = generateIV();
- const key = await importKey(passphrase, "encrypt");
- const encryptedArray = await encrypt(iv, key, plaintext);
- const encryptionData = {
- iv: uInt8ToArray(iv),
- encryptedArray: uInt8ToArray(encryptedArray)
- };
- return btoa(JSON.stringify(encryptionData));
- }
- async function decryptString(cyphertext, passphrase) {
- try {
- const encrypionData = JSON.parse(atob(cyphertext));
- const iv = arrayToUint8(encrypionData.iv);
- const encryptedArray = arrayToUint8(encrypionData.encryptedArray);
- const key = await importKey(passphrase, "decrypt");
- return await decrypt(iv, key, encryptedArray);
- } catch {
- return cyphertext;
- }
- }
- function encode(string) {
- return new TextEncoder().encode(string);
- }
- function decode(array) {
- return new TextDecoder("utf-8").decode(array);
- }
- async function encrypt(iv, key, message) {
- const arrayBuffer = await window.crypto.subtle.encrypt(
- { name: ENCRYPTION_ALG, iv },
- key,
- encode(message)
- );
- return new Uint8Array(arrayBuffer);
- }
- async function decrypt(iv, key, cyphertext) {
- const arrayBuffer = await crypto.subtle.decrypt(
- { name: ENCRYPTION_ALG, iv },
- key,
- cyphertext
- );
- return arrayBufferToString(arrayBuffer);
- }
- async function hash(encoded) {
- return await crypto.subtle.digest("SHA-256", encoded);
- }
- function generateIV() {
- return crypto.getRandomValues(new Uint8Array(IV_SIZE));
- }
- async function importKey(passphrase, purpose) {
- return await crypto.subtle.importKey(
- "raw",
- await hash(encode(passphrase)),
- { name: ENCRYPTION_ALG },
- false,
- [purpose]
- );
- }
- function arrayBufferToString(arrayBuffer) {
- const uInt8Array = new Uint8Array(arrayBuffer);
- return decode(uInt8Array);
- }
- function uInt8ToArray(uInt8Array) {
- return Array.from(uInt8Array);
- }
- function arrayToUint8(array) {
- return new Uint8Array(array);
- }
-
- // src/Model/Chat/chatModel.ts
- var ChatModel = class _ChatModel {
- // init
- constructor(storageModel2, connectionModel2, settingsModel2, chatListModel2, chatId) {
- this.chatMessageHandlerManager = new HandlerManager();
- // paths
- this.getBasePath = () => {
- return StorageModel.getPath(
- "chat" /* Chat */,
- filePaths.chat.chatBase(this.id)
- );
- };
- this.getInfoPath = () => {
- return StorageModel.getPath(
- "chat" /* Chat */,
- filePaths.chat.info(this.id)
- );
- };
- this.getColorPath = () => {
- return StorageModel.getPath(
- "chat" /* Chat */,
- filePaths.chat.color(this.id)
- );
- };
- this.getMessageDirPath = () => {
- return StorageModel.getPath(
- "chat" /* Chat */,
- filePaths.chat.messages(this.id)
- );
- };
- this.getMessagePath = (id) => {
- return [...this.getMessageDirPath(), id];
- };
- // handlers
- this.handleMessage = (body) => {
- const chatMessage = parseValidObject(
- body,
- ChatMessageReference
- );
- if (chatMessage == null) return;
- chatMessage.status = "received" /* Received */;
- this.addMessage(chatMessage);
- this.info.hasUnreadMessages = true;
- this.storeInfo();
- };
- this.handleMessageSent = (chatMessage) => {
- chatMessage.status = "sent" /* Sent */;
- this.addMessage(chatMessage);
- };
- // settings
- this.setPrimaryChannel = (primaryChannel) => {
- this.info.primaryChannel = primaryChannel;
- this.storeInfo();
- this.subscribe();
- };
- this.setSecondaryChannels = (secondaryChannels) => {
- this.info.secondaryChannels = secondaryChannels;
- this.storeInfo();
- };
- this.setEncryptionKey = (key) => {
- this.info.encryptionKey = key;
- this.storeInfo();
- };
- this.setColor = (color) => {
- this.color = color;
- this.storeColor();
- };
- // messaging
- this.addMessage = async (chatMessage) => {
- await this.decryptMessage(chatMessage);
- if (chatMessage.body != "") {
- const messagePath = this.getMessagePath(chatMessage.id);
- this.storageModel.writeStringifiable(messagePath, chatMessage);
- this.chatMessageHandlerManager.trigger(chatMessage);
- }
- this.fileModel.handleStringifiedFileContent(
- chatMessage.stringifiedFile
- );
- };
- this.sendMessage = async (body, fileContent) => {
- const senderName = this.settingsModel.username;
- if (senderName == "") return false;
- const allChannels = [this.info.primaryChannel];
- for (const secondaryChannel of this.info.secondaryChannels) {
- allChannels.push(secondaryChannel);
- }
- const combinedChannel = allChannels.join("/");
- const chatMessage = await _ChatModel.createChatMessage(
- combinedChannel,
- senderName,
- this.info.encryptionKey,
- body,
- fileContent
- );
- this.addMessage(chatMessage);
- this.connectionModel.sendMessageOrStore(chatMessage);
- return true;
- };
- this.decryptMessage = async (chatMessage) => {
- const decryptedBody = await decryptString(
- chatMessage.body,
- this.info.encryptionKey
- );
- const decryptedFile = await decryptString(
- chatMessage.stringifiedFile ?? "",
- this.info.encryptionKey
- );
- chatMessage.body = decryptedBody;
- chatMessage.stringifiedFile = decryptedFile;
- };
- this.subscribe = () => {
- this.connectionModel.addChannel(this.info.primaryChannel);
- };
- this.markRead = () => {
- this.info.hasUnreadMessages = false;
- this.storeInfo();
- };
- // storage
- this.storeInfo = () => {
- this.storageModel.writeStringifiable(this.getInfoPath(), this.info);
- };
- this.storeColor = () => {
- this.storageModel.write(this.getColorPath(), this.color);
- };
- this.delete = () => {
- this.chatListModel.untrackChat(this);
- const dirPath = this.getBasePath();
- this.storageModel.removeRecursively(dirPath);
- };
- // load
- this.loadInfo = () => {
- const info = this.storageModel.readStringifiable(
- this.getInfoPath(),
- ChatInfoReference
- );
- if (info != null) {
- this.info = info;
- } else {
- this.info = _ChatModel.generateChatInfo("0");
- }
- };
- this.loadColor = () => {
- const path = this.getColorPath();
- const color = this.storageModel.read(path);
- if (!color) {
- this.color = "standard" /* Standard */;
- } else {
- this.color = color;
- }
- };
- this.id = chatId;
- this.connectionModel = connectionModel2;
- this.settingsModel = settingsModel2;
- this.storageModel = storageModel2;
- this.chatListModel = chatListModel2;
- this.loadInfo();
- this.loadColor();
- this.subscribe();
- this.fileModel = new FileModel2(
- this.storageModel,
- this.settingsModel,
- this
- );
- }
- get secondaryChannels() {
- return this.info.secondaryChannels.sort(localeCompare);
- }
- get messages() {
- const messageIds = this.storageModel.list(
- this.getMessageDirPath()
- );
- if (!Array.isArray(messageIds)) return [];
- const chatMessages = [];
- for (const messageId of messageIds) {
- const messagePath = this.getMessagePath(messageId);
- const chatMessage = this.storageModel.readStringifiable(
- messagePath,
- ChatMessageReference
- );
- chatMessages.push(chatMessage);
- }
- const sorted = chatMessages.sort(
- (a, b) => a.dateSent.localeCompare(b.dateSent)
- );
- return sorted;
- }
- static {
- // utility
- this.generateChatInfo = (primaryChannel) => {
- return {
- dataVersion: DATA_VERSION,
- primaryChannel,
- secondaryChannels: [],
- encryptionKey: "",
- hasUnreadMessages: false
- };
- };
- }
- static {
- this.createChatMessage = async (channel, sender, encryptionKey, body, fileContent) => {
- const chatMessage = {
- dataVersion: DATA_VERSION,
- id: v4_default(),
- channel,
- sender,
- body,
- dateSent: createTimestamp(),
- status: "outbox" /* Outbox */,
- stringifiedFile: ""
- };
- if (fileContent != void 0) {
- const stringifiedFile = stringify(fileContent);
- chatMessage.stringifiedFile = stringifiedFile;
- }
- if (encryptionKey != "") {
- chatMessage.body = await encryptString(
- chatMessage.body,
- encryptionKey
- );
- chatMessage.stringifiedFile = await encryptString(
- chatMessage.stringifiedFile,
- encryptionKey
- );
- }
- return chatMessage;
- };
- }
- };
- var ChatInfoReference = {
- dataVersion: DATA_VERSION,
- primaryChannel: "",
- secondaryChannels: [""],
- encryptionKey: "",
- hasUnreadMessages: true
- };
- var ChatMessageReference = {
- dataVersion: DATA_VERSION,
- id: "",
- channel: "",
- sender: "",
- body: "",
- dateSent: "",
- status: "",
- stringifiedFile: ""
- };
-
- // src/Model/Chat/chatListModel.ts
- var ChatListModel = class {
- // init
- constructor(storageModel2, settingsModel2, connectionModel2) {
- // data
- this.chatModels = /* @__PURE__ */ new Set();
- // handlers
- this.messageHandler = (data) => {
- const channel = data.messageChannel;
- const body = data.messageBody;
- if (channel == void 0) return;
- if (body == void 0) return;
- this.routeMessageToCorrectChatModel(
- channel,
- (chatModel) => chatModel.handleMessage(body)
- );
- };
- this.messageSentHandler = (chatMessage) => {
- const channel = chatMessage.channel;
- this.routeMessageToCorrectChatModel(
- channel,
- (chatModel) => chatModel.handleMessageSent(chatMessage)
- );
- };
- // methods
- this.routeMessageToCorrectChatModel = (channel, fn) => {
- const allChannels = channel.split("/");
- for (const chatModel of this.chatModels) {
- for (const channel2 of allChannels) {
- if (channel2 != chatModel.info.primaryChannel) continue;
- fn(chatModel);
- break;
- }
- }
- };
- // storage
- this.addChatModel = (chatModel) => {
- this.chatModels.add(chatModel);
- };
- this.createChat = (primaryChannel) => {
- const id = v4_default();
- const chatModel = new ChatModel(
- this.storageModel,
- this.connectionModel,
- this.settingsModel,
- this,
- id
- );
- chatModel.setPrimaryChannel(primaryChannel);
- this.addChatModel(chatModel);
- return chatModel;
- };
- this.untrackChat = (chat) => {
- this.chatModels.delete(chat);
- };
- // load
- this.loadChats = () => {
- const chatDir = StorageModel.getPath(
- "chat" /* Chat */,
- filePaths.chat.base
- );
- const chatIds = this.storageModel.list(chatDir);
- for (const chatId of chatIds) {
- const chatModel = new ChatModel(
- this.storageModel,
- this.connectionModel,
- this.settingsModel,
- this,
- chatId
- );
- this.addChatModel(chatModel);
- }
- };
- this.storageModel = storageModel2;
- this.settingsModel = settingsModel2;
- this.connectionModel = connectionModel2;
- this.loadChats();
- connectionModel2.messageHandlerManager.addHandler(this.messageHandler);
- connectionModel2.messageSentHandlerManager.addHandler(
- this.messageSentHandler
- );
- }
- };
-
- // src/View/utility.ts
- function allowDrop(event) {
- event.preventDefault();
- }
- function allowDrag(event) {
- event.dataTransfer?.setData("text", "");
- }
- function reload() {
- window.location.reload();
- }
-
- // src/ViewModel/Pages/taskViewModel.ts
- var TaskViewModel = class {
- // init
- constructor(coreViewModel, chatViewModel, boardsAndTasksModel, containingModel, taskFileContent) {
- this.coreViewModel = coreViewModel;
- this.chatViewModel = chatViewModel;
- // paths
- this.getFilePath = () => {
- return this.boardsAndTasksModel.getTaskFilePath(this.task.fileId);
- };
- // state
- this.index = new State(0);
- this.boardId = new State("");
- this.name = new State("");
- this.description = new State("");
- this.category = new State("");
- this.status = new State("");
- this.priority = new State("");
- this.date = new State("");
- this.time = new State("");
- this.selectedVersionId = new State("");
- this.versionIds = new ListState();
- // methods
- this.dragStart = (event) => {
- allowDrag(event);
- this.coreViewModel.draggedObject.value = this;
- };
- this.setCategoryAndStatus = (category, status) => {
- if (category != void 0) this.category.value = category;
- if (status != void 0) this.status.value = status;
- this.save();
- };
- this.setBoardId = (boardId) => {
- this.boardId.value = boardId;
- this.save();
- };
- this.setDate = (dateISOString) => {
- this.date.value = dateISOString;
- this.save();
- };
- // view
- this.open = () => {
- this.containingModel.selectTask(this);
- };
- this.close = () => {
- this.containingModel.closeTask();
- };
- this.closeAndDiscard = () => {
- this.close();
- this.loadTaskData();
- };
- this.closeAndSave = () => {
- this.close();
- this.save();
- };
- this.updateIndex = () => {
- const index = this.containingModel.taskIndexManager.getIndex(this);
- this.index.value = index;
- };
- this.updateSuggestions = () => {
- if (this.coreViewModel.taskCategorySuggestions.value.has(
- this.category.value
- ) == false) {
- this.coreViewModel.taskCategorySuggestions.add(this.category.value);
- }
- if (this.coreViewModel.taskStatusSuggestions.value.has(
- this.status.value
- ) == false) {
- this.coreViewModel.taskStatusSuggestions.add(this.status.value);
- }
- };
- // settings
- this.save = () => {
- const newTaskFileContent = BoardsAndTasksModel.createTaskFileContent(
- this.task.fileId,
- this.name.value,
- this.task.boardId
- );
- newTaskFileContent.boardId = this.boardId.value;
- newTaskFileContent.description = this.description.value;
- newTaskFileContent.status = this.status.value;
- newTaskFileContent.category = this.category.value;
- newTaskFileContent.priority = this.priority.value;
- newTaskFileContent.date = this.date.value;
- newTaskFileContent.time = this.time.value;
- this.boardsAndTasksModel.updateTaskAndSend(newTaskFileContent);
- this.containingModel.showTask(newTaskFileContent);
- this.containingModel.updateTaskIndices();
- this.updateSuggestions();
- };
- this.deleteTask = () => {
- this.close();
- this.boardsAndTasksModel.deleteTask(
- this.task.boardId,
- this.task.fileId
- );
- this.containingModel.removeTaskFromView(this.task);
- };
- // load
- this.loadVersionIds = () => {
- const versionIds = this.boardsAndTasksModel.listTaskVersionIds(this.task.fileId);
- const sortedVersionIds = versionIds.sort(localeCompare).reverse();
- this.versionIds.clear();
- this.versionIds.add(...sortedVersionIds);
- };
- this.switchVersion = (versionId) => {
- const taskFileContent = this.boardsAndTasksModel.getSpecificTaskFileContent(
- this.task.fileId,
- versionId
- );
- if (taskFileContent == null) return;
- this.task = taskFileContent;
- this.loadTaskData();
- };
- this.loadAllData = () => {
- this.loadTaskData();
- this.loadVersionIds();
- };
- this.loadTaskData = () => {
- this.boardId.value = this.task.boardId;
- this.name.value = this.task.name;
- this.description.value = this.task.description ?? "";
- this.category.value = this.task.category ?? "";
- this.status.value = this.task.status ?? "";
- this.priority.value = this.task.priority ?? "";
- this.date.value = this.task.date ?? "";
- this.time.value = this.task.time ?? "";
- this.selectedVersionId.value = this.task.fileContentId;
- this.updateSuggestions();
- };
- this.boardsAndTasksModel = boardsAndTasksModel;
- this.containingModel = containingModel;
- this.task = taskFileContent;
- this.loadAllData();
- this.selectedVersionId.subscribeSilent((selectedVersionId) => {
- this.switchVersion(selectedVersionId);
- });
- }
- get sortingString() {
- const splitDate = this.date.value.split("-");
- const year = padZero(splitDate[0], 4);
- const month = padZero(splitDate[1], 2);
- const date = padZero(splitDate[2], 2);
- const splitTime = this.time.value.split(":");
- const hour = padZero(splitTime[0], 2);
- const minute = padZero(splitTime[1], 2);
- const priorityNumber = parseInt(this.priority.value);
- const invertedPriority = 100 - priorityNumber;
- return year + month + date + hour + minute + invertedPriority + this.name.value;
- }
- static {
- // utility
- this.getStringsForFilter = (taskViewModel) => {
- return [
- taskViewModel.task.name,
- taskViewModel.task.description ?? "",
- taskViewModel.task.category ?? "",
- taskViewModel.task.status ?? "",
- taskViewModel.task.priority ?? "",
- taskViewModel.task.date ?? "",
- taskViewModel.task.time ?? ""
- ];
- };
- }
- };
-
- // src/ViewModel/Pages/taskContainingPageViewModel.ts
- var TaskContainingPageViewModel = class {
- // init
- constructor(coreViewModel, chatViewModel, boardsAndTasksModel) {
- this.coreViewModel = coreViewModel;
- this.chatViewModel = chatViewModel;
- // state
- this.taskIndexManager = new IndexManager(
- (taskViewModel) => taskViewModel.sortingString
- );
- this.selectedTaskViewModel = new State(void 0);
- this.taskViewModels = new MapState();
- // methods
- this.createTaskFromBoardId = (boardId) => {
- const taskFileContent = this.boardsAndTasksModel.createTask(boardId);
- const taskViewModel = new TaskViewModel(
- this.coreViewModel,
- this.chatViewModel,
- this.boardsAndTasksModel,
- this,
- taskFileContent
- );
- this.selectTask(taskViewModel);
- this.updateTaskIndices();
- };
- // view
- this.showTask = (taskFileContent) => {
- };
- this.removeTaskFromView = (taskFileContent) => {
- };
- this.selectTask = (selectedTask) => {
- this.selectedTaskViewModel.value = selectedTask;
- };
- this.closeTask = () => {
- this.selectedTaskViewModel.value = void 0;
- };
- this.updateTaskIndices = () => {
- this.taskIndexManager.update([...this.taskViewModels.value.values()]);
- for (const boardViewModel of this.taskViewModels.value.values()) {
- boardViewModel.updateIndex();
- }
- };
- this.boardsAndTasksModel = boardsAndTasksModel;
- }
- };
-
- // src/ViewModel/Pages/calendarPageViewModel.ts
- var CALENDAR_EVENT_BOARD_ID = "events";
- var CalendarPageViewModel = class extends TaskContainingPageViewModel {
- // init
- constructor(coreViewModel, chatViewModel, storageModel2, calendarModel, boardsAndTasksModel) {
- super(coreViewModel, chatViewModel, boardsAndTasksModel);
- this.coreViewModel = coreViewModel;
- this.chatViewModel = chatViewModel;
- // paths
- this.getBasePath = () => {
- return [...this.calendarModel.getViewPath()];
- };
- // state
- this.selectedYear = new State(0);
- this.selectedMonth = new State(0);
- this.selectedDate = new State(0);
- this.monthGrid = new State(void 0);
- // methods
- this.createEvent = () => {
- const taskFileContent = this.boardsAndTasksModel.createTask(CALENDAR_EVENT_BOARD_ID);
- taskFileContent.date = CalendarModel.getISODateString(
- this.selectedYear.value.toString(),
- this.selectedMonth.value.toString(),
- this.selectedDate.value.toString()
- );
- const taskViewModel = new TaskViewModel(
- this.coreViewModel,
- this.chatViewModel,
- this.boardsAndTasksModel,
- this,
- taskFileContent
- );
- this.selectTask(taskViewModel);
- this.updateTaskIndices();
- };
- this.getEventsForDate = () => {
- const paddedDate = CalendarModel.padDateOrMonth(
- this.selectedDate.toString()
- );
- if (this.monthGrid.value == void 0) {
- return void 0;
- }
- return this.monthGrid.value.days[paddedDate];
- };
- // view
- this.getTaskMapState = (taskFileContent) => {
- if (this.monthGrid.value == null) return null;
- const date = CalendarModel.isoToDateString(
- taskFileContent.date ?? ""
- );
- return this.monthGrid.value.days[date];
- };
- this.showTask = (taskFileContent) => {
- const monthString = CalendarModel.isoToMonthString(
- taskFileContent.date ?? ""
- );
- if (monthString == void 0 || monthString != this.monthString) {
- this.removeTaskFromView(taskFileContent);
- this.calendarModel.deleteTaskReference(
- this.monthString,
- taskFileContent.fileId
- );
- return;
- }
- const taskViewModel = new TaskViewModel(
- this.coreViewModel,
- this.chatViewModel,
- this.boardsAndTasksModel,
- this,
- taskFileContent
- );
- const mapState = this.getTaskMapState(taskFileContent);
- this.taskViewModels.handleRemoval(taskViewModel, () => {
- mapState?.remove(taskFileContent.fileId);
- });
- this.taskViewModels.remove(taskFileContent.fileId);
- this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
- mapState?.set(taskFileContent.fileId, taskViewModel);
- };
- this.removeTaskFromView = (taskFileContent) => {
- this.taskViewModels.remove(taskFileContent.fileId);
- };
- this.showToday = () => {
- const today = /* @__PURE__ */ new Date();
- this.selectedYear.value = today.getFullYear();
- this.selectedMonth.value = today.getMonth() + 1;
- this.selectedDate.value = today.getDate();
- };
- this.showPreviousMonth = () => {
- this.selectedMonth.value -= 1;
- if (this.selectedMonth.value <= 0) {
- this.selectedYear.value -= 1;
- this.selectedMonth.value = 12;
- }
- };
- this.showNextMonth = () => {
- this.selectedMonth.value += 1;
- if (this.selectedMonth.value >= 13) {
- this.selectedYear.value += 1;
- this.selectedMonth.value = 1;
- }
- };
- this.handleDrop = (year, month, date) => {
- const ISOString = CalendarModel.getISODateString(
- year,
- month,
- date
- );
- const draggedObject = this.coreViewModel.draggedObject.value;
- if (draggedObject instanceof TaskViewModel == false) return;
- draggedObject.setDate(ISOString);
- };
- // load
- this.loadMonthTasks = () => {
- this.monthGrid.value = this.calendarModel.generateMonthGrid(
- this.selectedYear.value,
- this.selectedMonth.value,
- () => new MapState()
- );
- const taskIds = this.calendarModel.listTaskIds(
- this.monthString
- );
- for (const taskId of taskIds) {
- const taskFileContent = this.boardsAndTasksModel.getLatestTaskFileContent(taskId);
- if (taskFileContent == null) continue;
- this.showTask(taskFileContent);
- }
- this.updateTaskIndices();
- };
- this.loadData = () => {
- this.loadMonthTasks();
- this.showToday();
- };
- this.storageModel = storageModel2;
- this.calendarModel = calendarModel;
- this.boardsAndTasksModel = boardsAndTasksModel;
- this.chatViewModel = chatViewModel;
- bulkSubscribe([this.selectedYear, this.selectedMonth], () => {
- this.loadMonthTasks();
- });
- boardsAndTasksModel.taskHandlerManager.addHandler(
- (taskFileContent) => {
- this.showTask(taskFileContent);
- }
- );
- }
- // data
- get monthString() {
- return CalendarModel.getMonthString(
- this.selectedYear.value.toString(),
- this.selectedMonth.value.toString()
- );
- }
- };
-
- // src/ViewModel/Chat/chatMessageViewModel.ts
- var ChatMessageViewModel = class {
- // init
- constructor(coreViewModel, messagePageViewModel, chatMessage, sentByUser) {
- this.coreViewModel = coreViewModel;
- this.body = new State("");
- this.status = new State(
- void 0
- );
- // state
- this.isPresentingInfoModal = new State(false);
- // methods
- this.copyMessage = () => {
- navigator.clipboard.writeText(this.body.value);
- };
- this.resendMessage = () => {
- this.messagePageViewModel.sendMessageFromBody(this.body.value);
- };
- this.decryptMessage = () => {
- this.messagePageViewModel.decryptMessage(this);
- };
- // view
- this.showInfoModal = () => {
- this.isPresentingInfoModal.value = true;
- };
- this.hideInfoModal = () => {
- this.isPresentingInfoModal.value = false;
- };
- // load
- this.loadData = () => {
- this.channel = this.chatMessage.channel;
- this.sender = this.chatMessage.sender;
- this.dateSent = new Date(this.chatMessage.dateSent).toLocaleString();
- this.body.value = this.chatMessage.body;
- this.status.value = this.chatMessage.status;
- };
- this.messagePageViewModel = messagePageViewModel;
- this.chatMessage = chatMessage;
- this.sentByUser = sentByUser;
- this.loadData();
- }
- };
-
- // src/ViewModel/Pages/messagePageViewModel.ts
- var MessagePageViewModel = class {
- // init
- constructor(coreViewModel, chatViewModel) {
- this.coreViewModel = coreViewModel;
- // state
- this.chatMessageViewModels = new MapState();
- this.composingMessage = new State("");
- // methods
- this.sendMessage = () => {
- if (this.cannotSendMessage.value == true) return;
- this.sendMessageFromBody(this.composingMessage.value);
- this.composingMessage.value = "";
- };
- this.sendMessageFromBody = (body) => {
- this.chatViewModel.chatModel.sendMessage(body);
- };
- this.decryptMessage = async (messageViewModel) => {
- const chatMessage = messageViewModel.chatMessage;
- await this.chatViewModel.chatModel.decryptMessage(chatMessage);
- this.chatViewModel.chatModel.addMessage(chatMessage);
- messageViewModel.loadData();
- };
- // view
- this.showChatMessage = (chatMessage) => {
- const chatMessageViewModel = new ChatMessageViewModel(
- this.coreViewModel,
- this,
- chatMessage,
- chatMessage.sender == this.chatViewModel.settingsViewModel.username.value
- );
- const existingChatMessageViewModel = this.chatMessageViewModels.value.get(chatMessage.id);
- if (existingChatMessageViewModel != void 0) {
- existingChatMessageViewModel.body.value = chatMessage.body;
- existingChatMessageViewModel.status.value = chatMessage.status;
- } else {
- this.chatMessageViewModels.set(
- chatMessage.id,
- chatMessageViewModel
- );
- }
- };
- // load
- this.loadData = () => {
- this.chatMessageViewModels.clear();
- for (const chatMessage of this.chatViewModel.chatModel.messages) {
- this.showChatMessage(chatMessage);
- }
- };
- this.chatViewModel = chatViewModel;
- this.cannotSendMessage = createProxyState(
- [
- this.chatViewModel.settingsViewModel.username,
- this.composingMessage
- ],
- () => this.chatViewModel.settingsViewModel.username.value == "" || this.composingMessage.value == ""
- );
- }
- };
-
- // src/ViewModel/Pages/settingsPageViewModel.ts
- var SettingsPageViewModel = class {
- // init
- constructor(coreViewModel, chatViewModel) {
- this.coreViewModel = coreViewModel;
- // state
- this.primaryChannel = new State("");
- this.primaryChannelInput = new State("");
- this.secondaryChannels = new ListState();
- this.newSecondaryChannelInput = new State("");
- this.encryptionKeyInput = new State("");
- this.shouldShowEncryptionKey = new State(false);
- this.encryptionKeyInputType = createProxyState(
- [this.shouldShowEncryptionKey],
- () => this.shouldShowEncryptionKey.value == true ? "text" : "password"
- );
- this.color = new State("standard" /* Standard */);
- // guards
- this.cannotSetPrimaryChannel = createProxyState(
- [this.primaryChannel, this.primaryChannelInput],
- () => this.primaryChannelInput.value == "" || this.primaryChannelInput.value == this.primaryChannel.value
- );
- this.cannotAddSecondaryChannel = createProxyState(
- [this.newSecondaryChannelInput],
- () => this.newSecondaryChannelInput.value == ""
- );
- // methods
- this.setPrimaryChannel = () => {
- this.chatViewModel.chatModel.setPrimaryChannel(
- this.primaryChannelInput.value
- );
- this.primaryChannel.value = this.chatViewModel.chatModel.info.primaryChannel;
- this.chatViewModel.chatListViewModel.updateIndices();
- };
- this.addSecondaryChannel = () => {
- this.secondaryChannels.add(this.newSecondaryChannelInput.value);
- this.newSecondaryChannelInput.value = "";
- this.storeSecondaryChannels();
- this.loadSecondaryChannels();
- };
- this.removeSecondaryChannel = (secondaryChannel) => {
- this.secondaryChannels.remove(secondaryChannel);
- this.storeSecondaryChannels();
- };
- this.storeSecondaryChannels = () => {
- this.chatViewModel.chatModel.setSecondaryChannels([
- ...this.secondaryChannels.value.values()
- ]);
- };
- this.setEncryptionKey = () => {
- this.chatViewModel.chatModel.setEncryptionKey(
- this.encryptionKeyInput.value
- );
- this.encryptionKeyInput.callSubscriptions();
- };
- this.applyColor = (newColor) => {
- this.chatViewModel.setColor(newColor);
- };
- this.remove = () => {
- this.chatViewModel.close();
- this.chatViewModel.chatModel.delete();
- this.chatViewModel.chatListViewModel.untrackChat(this.chatViewModel);
- };
- // load
- this.loadListRelevantData = () => {
- this.primaryChannel.value = this.chatViewModel.chatModel.info.primaryChannel;
- this.color.value = this.chatViewModel.chatModel.color;
- };
- this.loadData = () => {
- this.primaryChannelInput.value = this.chatViewModel.chatModel.info.primaryChannel;
- this.loadSecondaryChannels();
- this.encryptionKeyInput.value = this.chatViewModel.chatModel.info.encryptionKey;
- };
- this.loadSecondaryChannels = () => {
- this.secondaryChannels.clear();
- for (const secondaryChannel of this.chatViewModel.chatModel.secondaryChannels) {
- this.secondaryChannels.add(secondaryChannel);
- }
- };
- this.chatViewModel = chatViewModel;
- this.loadListRelevantData();
- this.cannotSetEncryptionKey = createProxyState(
- [this.encryptionKeyInput],
- () => this.encryptionKeyInput.value == this.chatViewModel.chatModel.info.encryptionKey
- );
- this.color.subscribe((newColor) => {
- this.applyColor(newColor);
- });
- }
- };
-
- // src/ViewModel/Utility/searchViewModel.ts
- var SearchViewModel = class {
- // init
- constructor(allObjects, matchingObjects, getStringsOfObject, suggestions) {
- // state
- this.appliedQuery = new State("");
- this.searchInput = new State("");
- // guards
- this.cannotApplySearch = createProxyState(
- [this.searchInput, this.appliedQuery],
- () => this.searchInput.value == this.appliedQuery.value
- );
- // methods
- this.search = (searchTerm) => {
- this.searchInput.value = searchTerm;
- this.applySearch();
- };
- this.applySearch = () => {
- this.appliedQuery.value = this.searchInput.value;
- this.matchingObjects.clear();
- for (const object of this.allObjects.value.values()) {
- const doesMatch = this.checkDoesMatchSearch(object);
- if (doesMatch == false) continue;
- this.matchingObjects.add(object);
- }
- };
- // utility
- this.checkDoesMatchSearch = (object) => {
- return checkDoesObjectMatchSearch(
- this.appliedQuery.value,
- this.getStringsOfObject,
- object
- );
- };
- this.allObjects = allObjects;
- this.matchingObjects = matchingObjects;
- this.getStringsOfObject = getStringsOfObject;
- this.suggestions = suggestions;
- this.allObjects.handleAddition((newObject) => {
- const doesMatch = this.checkDoesMatchSearch(newObject);
- if (doesMatch == false) {
- this.matchingObjects.remove(newObject);
- } else {
- if (this.matchingObjects.value.has(newObject)) return;
- this.matchingObjects.add(newObject);
- this.allObjects.handleRemoval(newObject, () => {
- this.matchingObjects.remove(newObject);
- });
- }
- });
- }
- };
-
- // src/ViewModel/Pages/boardViewModel.ts
- var BoardViewModel = class extends TaskContainingPageViewModel {
- // init
- constructor(coreViewModel, chatViewModel, storageModel2, boardsAndTasksModel, taskPageViewModel, boardInfo) {
- super(coreViewModel, chatViewModel, boardsAndTasksModel);
- this.coreViewModel = coreViewModel;
- this.chatViewModel = chatViewModel;
- // state
- this.name = new State("");
- this.color = new State("standard" /* Standard */);
- this.index = new State(0);
- this.selectedPage = new State(
- "list" /* List */
- );
- this.isPresentingSettingsModal = new State(false);
- this.isPresentingFilterModal = new State(false);
- this.filteredTaskViewModels = new ListState();
- // paths
- this.getBasePath = () => {
- return [
- ...this.taskPageViewModel.getBoardViewPath(this.boardInfo.fileId)
- ];
- };
- this.getLastUsedBoardPath = () => {
- return [...this.getBasePath(), "last-used-view" /* LastUsedView */];
- };
- this.getPreviousSearchesPath = () => {
- return [...this.getBasePath(), "previous-searches" /* PreviousSearches */];
- };
- this.getLastSearchPath = () => {
- return [...this.getBasePath(), "last-search" /* LastSearch */];
- };
- // settings
- this.saveSettings = () => {
- const newBoardInfoFileContent = BoardsAndTasksModel.createBoardInfoFileContent(
- this.boardInfo.fileId,
- this.name.value,
- this.color.value
- );
- this.taskPageViewModel.updateBoard(newBoardInfoFileContent);
- };
- this.applyColor = () => {
- this.taskPageViewModel.chatViewModel.setDisplayedColor(
- this.color.value
- );
- };
- this.deleteBoard = () => {
- this.taskPageViewModel.deleteBoard(this.boardInfo);
- this.chatViewModel.taskBoardSuggestions.remove(this.boardInfo.fileId);
- this.close();
- };
- // methods
- this.createTask = () => {
- this.createTaskFromBoardId(this.boardInfo.fileId);
- };
- this.handleDropWithinBoard = (category, status) => {
- const draggedObject = this.coreViewModel.draggedObject.value;
- if (draggedObject instanceof TaskViewModel == false) return;
- draggedObject.setCategoryAndStatus(category, status);
- };
- this.handleDropBetweenBoards = () => {
- const draggedObject = this.coreViewModel.draggedObject.value;
- if (draggedObject instanceof TaskViewModel == false) return;
- draggedObject.setBoardId(this.boardInfo.fileId);
- };
- // storage
- this.storeLastUsedView = () => {
- const path = this.getLastUsedBoardPath();
- const lastUsedView = this.selectedPage.value;
- this.storageModel.write(path, lastUsedView);
- };
- this.restoreLastUsedView = () => {
- const path = this.getLastUsedBoardPath();
- const lastUsedView = this.storageModel.read(path);
- if (lastUsedView == null) return;
- this.selectedPage.value = lastUsedView;
- };
- this.handleNewSearch = (searchTerm) => {
- const suggestionPath = [
- ...this.getPreviousSearchesPath(),
- searchTerm
- ];
- this.storageModel.write(suggestionPath, "");
- if (!this.coreViewModel.boardSearchSuggestions.value.has(searchTerm)) {
- this.coreViewModel.boardSearchSuggestions.add(searchTerm);
- }
- const lastSearchPath = this.getLastSearchPath();
- this.storageModel.write(lastSearchPath, searchTerm);
- };
- // view
- this.showTask = (taskFileContent) => {
- if (taskFileContent.boardId != this.boardInfo.fileId) {
- this.boardsAndTasksModel.deleteTaskReference(
- this.boardInfo.fileId,
- taskFileContent.fileId
- );
- this.removeTaskFromView(taskFileContent);
- return;
- }
- const taskViewModel = new TaskViewModel(
- this.coreViewModel,
- this.chatViewModel,
- this.boardsAndTasksModel,
- this,
- taskFileContent
- );
- this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
- };
- this.removeTaskFromView = (taskFileContent) => {
- this.taskViewModels.remove(taskFileContent.fileId);
- this.updateIndex();
- };
- this.select = () => {
- this.taskPageViewModel.selectBoard(this);
- };
- this.close = () => {
- this.taskPageViewModel.closeBoard();
- this.taskViewModels.clear();
- };
- this.showSettings = () => {
- this.isPresentingSettingsModal.value = true;
- };
- this.hideSettings = () => {
- this.saveSettings();
- this.isPresentingSettingsModal.value = false;
- };
- this.showFilterModal = () => {
- this.isPresentingFilterModal.value = true;
- };
- this.hideFilterModal = () => {
- this.isPresentingFilterModal.value = false;
- };
- this.updateIndex = () => {
- const index = this.taskPageViewModel.boardIndexManager.getIndex(this);
- this.index.value = index;
- };
- // load
- this.loadListRelevantData = () => {
- this.name.value = this.boardInfo.name;
- this.color.value = this.boardInfo.color;
- };
- this.loadTasks = () => {
- const taskIds = this.boardsAndTasksModel.listTaskIds(
- this.boardInfo.fileId
- );
- for (const taskId of taskIds) {
- if (this.taskViewModels.value.has(taskId)) return;
- const taskFileContent = this.boardsAndTasksModel.getLatestTaskFileContent(taskId);
- if (taskFileContent == null) continue;
- const taskViewModel = new TaskViewModel(
- this.coreViewModel,
- this.chatViewModel,
- this.boardsAndTasksModel,
- this,
- taskFileContent
- );
- this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
- }
- this.updateTaskIndices();
- };
- this.loadSearchSuggestions = () => {
- const dirPath = this.getPreviousSearchesPath();
- const searches = this.storageModel.list(dirPath);
- this.coreViewModel.boardSearchSuggestions.add(...searches);
- };
- this.restoreSearch = () => {
- const lastSearchPath = this.getLastSearchPath();
- const lastSearch = this.storageModel.read(lastSearchPath);
- if (lastSearch != null) {
- this.searchViewModel.search(lastSearch);
- }
- };
- this.loadData = () => {
- this.restoreLastUsedView();
- this.loadTasks();
- this.loadSearchSuggestions();
- };
- this.storageModel = storageModel2;
- this.boardsAndTasksModel = boardsAndTasksModel;
- this.taskPageViewModel = taskPageViewModel;
- this.boardInfo = boardInfo;
- this.loadListRelevantData();
- this.isSelected = createProxyState(
- [this.taskPageViewModel.selectedBoardId],
- () => this.taskPageViewModel.selectedBoardId.value == this.boardInfo.fileId
- );
- this.color.subscribe(() => {
- if (this.isSelected.value == false) return;
- this.applyColor();
- });
- this.selectedPage.subscribeSilent(() => {
- this.storeLastUsedView();
- });
- boardsAndTasksModel.taskHandlerManager.addHandler(
- (taskFileContent) => {
- if (taskFileContent.boardId != this.boardInfo.fileId) return;
- this.showTask(taskFileContent);
- this.updateTaskIndices();
- }
- );
- this.searchViewModel = new SearchViewModel(
- this.taskViewModels,
- this.filteredTaskViewModels,
- TaskViewModel.getStringsForFilter,
- this.coreViewModel.boardSearchSuggestions
- );
- this.searchViewModel.appliedQuery.subscribeSilent((newQuery) => {
- this.handleNewSearch(newQuery);
- });
- this.restoreSearch();
- }
- };
-
- // src/ViewModel/Pages/taskPageViewModel.ts
- var TaskPageViewModel = class {
- // init
- constructor(coreViewModel, chatViewModel, storageModel2, boardModel) {
- this.coreViewModel = coreViewModel;
- this.chatViewModel = chatViewModel;
- // data
- this.boardIndexManager = new IndexManager(
- (boardViewModel) => boardViewModel.name.value
- );
- // paths
- this.getBasePath = () => {
- return [...this.boardsAndTasksModel.getViewPath()];
- };
- this.getBoardViewPath = (boardId) => {
- return [...this.getBasePath(), boardId];
- };
- this.getLastUsedBoardPath = () => {
- return [...this.getBasePath(), "last-used-board" /* LastUsedBoard */];
- };
- // state
- this.newBoardNameInput = new State("");
- this.boardViewModels = new MapState();
- this.isShowingBoadList = new State(true);
- this.selectedBoardId = new State(
- void 0
- );
- // guards
- this.cannotCreateBoard = createProxyState(
- [this.newBoardNameInput],
- () => this.newBoardNameInput.value == ""
- );
- // methods
- this.createBoard = () => {
- if (this.cannotCreateBoard.value == true) return;
- const boardInfoFileContent = this.boardsAndTasksModel.createBoard(this.newBoardNameInput.value);
- this.newBoardNameInput.value = "";
- this.showBoardInList(boardInfoFileContent);
- this.boardsAndTasksModel.updateBoardAndSend(boardInfoFileContent);
- this.updateBoardIndices();
- };
- this.updateBoard = (boardInfoFileContent) => {
- this.boardsAndTasksModel.updateBoardAndSend(boardInfoFileContent);
- this.updateBoardIndices();
- };
- this.deleteBoard = (boardInfoFileContent) => {
- this.boardsAndTasksModel.deleteBoard(boardInfoFileContent.fileId);
- this.boardViewModels.remove(boardInfoFileContent.fileId);
- this.updateBoardIndices();
- };
- // view
- this.toggleBoardList = () => {
- this.isShowingBoadList.value = !this.isShowingBoadList.value;
- };
- this.showBoardInList = (boardInfo) => {
- const boardViewModel = new BoardViewModel(
- this.coreViewModel,
- this.chatViewModel,
- this.storageModel,
- this.boardsAndTasksModel,
- this,
- boardInfo
- );
- this.boardViewModels.set(boardInfo.fileId, boardViewModel);
- this.chatViewModel.taskBoardSuggestions.set(boardInfo.fileId, [
- boardInfo.fileId,
- this.boardsAndTasksModel.getBoardName(boardInfo.fileId)
- ]);
- };
- this.selectBoard = (boardViewModel) => {
- this.selectedBoardId.value = boardViewModel.boardInfo.fileId;
- this.chatViewModel.displayedColor.value = boardViewModel.color.value;
- this.storeLastUsedBoard();
- };
- this.closeBoard = () => {
- this.selectedBoardId.value = void 0;
- this.chatViewModel.resetColor();
- this.storeLastUsedBoard();
- };
- this.updateBoardIndices = () => {
- this.boardIndexManager.update([...this.boardViewModels.value.values()]);
- for (const boardViewModel of this.boardViewModels.value.values()) {
- boardViewModel.updateIndex();
- }
- };
- // storage
- this.storeLastUsedBoard = () => {
- const path = this.getLastUsedBoardPath();
- const lastUsedBoardId = this.selectedBoardId.value ?? "";
- this.storageModel.write(path, lastUsedBoardId);
- };
- this.openLastUsedBoard = () => {
- const path = this.getLastUsedBoardPath();
- const lastUsedBoardId = this.storageModel.read(path);
- if (lastUsedBoardId == null) return;
- const boardViewModel = this.boardViewModels.value.get(lastUsedBoardId);
- if (boardViewModel == void 0) return;
- this.selectBoard(boardViewModel);
- };
- // load
- this.loadData = () => {
- this.boardViewModels.clear();
- const boardIds = this.boardsAndTasksModel.listBoardIds();
- for (const boardId of boardIds) {
- const boardInfo = this.boardsAndTasksModel.getBoardInfo(boardId);
- if (boardInfo == null) continue;
- this.showBoardInList(boardInfo);
- }
- this.updateBoardIndices();
- this.openLastUsedBoard();
- };
- this.storageModel = storageModel2;
- this.boardsAndTasksModel = boardModel;
- this.chatViewModel = chatViewModel;
- boardModel.boardHandlerManager.addHandler(
- (boardInfoFileContent) => {
- this.showBoardInList(boardInfoFileContent);
- this.updateBoardIndices();
- }
- );
- }
- };
-
- // src/View/translations.ts
- var englishTranslations = {
- updater: {
- migrated: "Migrated"
- },
- general: {
- deleteItemButtonAudioLabel: "delete item",
- searchButtonAudioLabel: "search",
- abortButton: "Abort",
- cancelButton: "Cancel",
- closeButton: "Close",
- backButton: "Back",
- continueButton: "Continue",
- confirmButton: "Confirm",
- saveButton: "Save",
- setButton: "Set",
- reloadAppButton: "Reload App",
- fileVersionLabel: "Version",
- searchLabel: "Search",
- restoreConnection: "Restore connection"
- },
- regional: {
- weekdays: {
- full: [
- "Sunday",
- "Monday",
- "Tuesday",
- "Wednesday",
- "Thursday",
- "Friday",
- "Saturday"
- ],
- abbreviated: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
- }
- },
- homePage: {
- appName: "Comms",
- ///
- overviewHeadline: "Overview",
- serverAddress: "Server address",
- serverAddressPlaceholder: "wss://192.168.0.69:3000",
- connectAudioLabel: "connect to server",
- disconnectAudioLabel: "disconnect from server",
- manageConnectionsAudioLabel: "manage connections",
- yourNameLabel: "Your name",
- yourNamePlaceholder: "Jane Doe",
- setNameButtonAudioLabel: "set name",
- firstDayOfWeekLabel: "First day of week",
- manageStorageButton: "Manage storage",
- transferDataButton: "Data Transfer",
- scrollToChatButton: "Chats",
- ///
- backToOverviewAudioLabel: "go back to overview",
- chatsHeadline: "Chats",
- addChatAudioLabel: "name of new chat",
- addChatPlaceholder: "Add chat",
- addChatButton: "Add chat"
- },
- connectionModal: {
- connectionModalHeadline: "Manage Connections",
- ///
- connectButtonAudioLabel: "connect"
- },
- dataTransferModal: {
- transferDataHeadline: "Data Transfer",
- selectionDescription: "Select the data that you want to transfer.",
- dataEntryDescription: "Enter this data on the other device.",
- dataEntryInputDescription: "Enter the data displayed on the other device.",
- readyToReceiveDescription: "Click 'send' on the other device.",
- notConnectedError: "You are not connected to any server.",
- ///
- fromThisDeviceButton: "From this device",
- toThisDeviceButton: "To this device",
- ///
- generalHeadline: "General",
- connectionData: "Connection Data",
- settingsData: "Settings Data",
- chatsHeadline: "Chats",
- ///
- transferChannelHeadline: "Transfer Chanel",
- transferKeyHeadline: "Transfer Encryption Key",
- sendButton: "Send",
- sendAgainButton: "Send again",
- ///
- filesSentCount: (count) => `Files sent: ${count}.`,
- allFilesSent: "Done.",
- filesReceivedCount: (count) => `Files received: ${count}.`
- },
- storage: {
- noItemSelected: "No item selected",
- notAFile: "(not a file)",
- contentEmpty: "(empty)",
- path: "Path",
- content: "Content",
- deleteItem: "Delete item",
- removeJunkButton: "Delete junk files"
- },
- chatPage: {
- closeChatAudioLabe: "close chat",
- chatSettingsAudioLabel: "chat settings",
- pages: {
- settings: "Settings",
- messages: "Messages",
- tasks: "Tasks",
- calendar: "Calendar"
- },
- settings: {
- settingsHeadline: "Settings",
- primaryChannelLabel: "Primary channel",
- setPrimaryChannelButtonAudioLabel: "set primary channel",
- newSecondaryChannelPlaceholder: "Add secondary channel",
- newSecondaryChannelAudioLabel: "name of new secondary channel",
- addSecondaryChannelButtonAudioLabel: "add secondary channel",
- encryptionKeyLabel: "Encryption key",
- setEncryptionKeyButtonAudioLabel: "set encryption key",
- showEncryptionKey: "Show encryption key",
- deleteChatButton: "Delete entire chat"
- },
- message: {
- messagesHeadline: "Messages",
- ///
- composerInputPlaceholder: "Type a message...",
- sendMessageButtonAudioLabel: "send message",
- ///
- showMessageInfoButtonAudioLabel: "show message info",
- messageInfoHeadline: "Message Info",
- sentBy: "Sent by",
- timeSent: "Time sent",
- channel: "Channel",
- messageContent: "Message content",
- copyMessageButton: "Copy message",
- resendMessageButton: "Resend message",
- decryptMessageButton: "Decrypt message",
- deleteMessageButton: "Delete message"
- },
- task: {
- newBoardNamePlaceholder: "Create a board",
- createBoardButtonAudioLabel: "create board",
- ///
- noBoardSelected: "No board selected",
- boardNotFound: "Board not found",
- ///
- closeBoardButtonAudioLabel: "close board",
- toggleBoardButtonAudioLabel: "toggle board list",
- showBoardSettingsButtonAudioLabel: "show board settigns",
- listViewButtonAudioLabel: "list view",
- kanbanViewButtonAudioLabel: "kanban view",
- statusViewButtonAudioLabel: "status grid view",
- filterTasksButtonAudioLabel: "filter tasks",
- createTaskButtonAudioLabel: "create new task",
- ///
- boardSettingsHeadline: "Board Settings",
- boardNameInputLabel: "Board name",
- deleteBoardButton: "Delete board and all tasks",
- ///
- taskSettingsHeadline: "Edit Task",
- taskNameLabel: "Title",
- taskBoardLabel: "Board",
- taskCategoryLabel: "Category",
- taskStatusLabel: "Status",
- taskPriorityLabel: "Priority",
- taskDescriptionLabel: "Description",
- taskDateLabel: "Date",
- taskTimeLabel: "Time",
- deleteTaskButton: "Delete task",
- ///
- filterTasksHeadline: "Filter Tasks",
- ///
- renameCategoryInputPlaceholder: "Rename category,"
- },
- calendar: {
- eventsBoard: "Events",
- ///
- todayButtonAudioLabel: "go to today",
- previousMonthButtonAudioLabel: "previous month",
- nextMonthButtonAudioLabel: "next month",
- yearInputAudioLabel: "year",
- monthInputAudioLabel: "month",
- yearInputPlaceholder: "2000",
- monthInputPlaceholder: "01",
- ///
- searchEventsHeadline: "Search Events",
- ///
- events: "Events",
- noEvents: "No events"
- }
- }
- };
- var allTranslations = {
- en: englishTranslations,
- de: {
- updater: {
- migrated: "Migriert"
- },
- general: {
- deleteItemButtonAudioLabel: "element l\xF6schen",
- searchButtonAudioLabel: "suchen",
- abortButton: "Abbrechen",
- cancelButton: "Abbrechen",
- closeButton: "Schlie\xDFen",
- backButton: "Zur\xFCck",
- continueButton: "Weiter",
- confirmButton: "Best\xE4tigen",
- saveButton: "Speichern",
- setButton: "OK",
- reloadAppButton: "Neu laden",
- fileVersionLabel: "Version",
- searchLabel: "Suche",
- restoreConnection: "Verbindung wiederherstellen"
- },
- regional: {
- weekdays: {
- full: [
- "Sonntag",
- "Montag",
- "Dienstag",
- "Mittwoch",
- "Donnerstag",
- "Freitag",
- "Samstag"
- ],
- abbreviated: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]
- }
- },
- homePage: {
- appName: "Comms",
- overviewHeadline: "\xDCbersicht",
- serverAddress: "Serveradresse",
- serverAddressPlaceholder: "wss://192.168.0.69:3000",
- connectAudioLabel: "mit Server verbinden",
- disconnectAudioLabel: "vom Server trennen",
- manageConnectionsAudioLabel: "Verbindungen verwalten",
- yourNameLabel: "Dein Name",
- yourNamePlaceholder: "Max Mustermann",
- setNameButtonAudioLabel: "Name speichern",
- firstDayOfWeekLabel: "Erster Wochentag",
- manageStorageButton: "Daten verwalten",
- transferDataButton: "Daten\xFCbertragung",
- scrollToChatButton: "Chats",
- backToOverviewAudioLabel: "zur\xFCck zur \xFCbersicht",
- chatsHeadline: "Chats",
- addChatAudioLabel: "Name des neuen Chats",
- addChatPlaceholder: "Chat hinzuf\xFCgen",
- addChatButton: "Chat hinzuf\xFCgen"
- },
- connectionModal: {
- connectionModalHeadline: "Verbindungen verwalten",
- connectButtonAudioLabel: "verbinden"
- },
- dataTransferModal: {
- transferDataHeadline: "Daten\xFCbertragung",
- selectionDescription: "W\xE4hle die Daten aus, die du \xFCbertragen m\xF6chtest.",
- dataEntryDescription: "Gib diese Informationen auf dem anderen Ger\xE4t ein.",
- dataEntryInputDescription: "Gib die auf dem anderen Ger\xE4t angezeigten Informationen ein.",
- readyToReceiveDescription: "Klicke auf dem anderen Ger\xE4t auf 'Senden'.",
- notConnectedError: "Du bist mit keinem Server verbunden.",
- fromThisDeviceButton: "Von diesem Ger\xE4t",
- toThisDeviceButton: "An dieses Ger\xE4t",
- generalHeadline: "Allgemein",
- connectionData: "Verbindungsdaten",
- settingsData: "Einstellungen",
- chatsHeadline: "Chats",
- transferChannelHeadline: "\xDCbertragungskanal",
- transferKeyHeadline: "Schl\xFCssel",
- sendButton: "Senden",
- sendAgainButton: "Erneut senden",
- filesSentCount: (count) => `Dateien gesendet: ${count}.`,
- allFilesSent: "Fertig.",
- filesReceivedCount: (count) => `Dateien empfangen: ${count}.`
- },
- storage: {
- noItemSelected: "Kein Element ausgew\xE4hlt",
- notAFile: "(keine Datei)",
- contentEmpty: "(leer)",
- path: "Pfad",
- content: "Inhalt",
- deleteItem: "Element l\xF6schen",
- removeJunkButton: "Datenm\xFCll l\xF6schen"
- },
- chatPage: {
- closeChatAudioLabe: "Chat schlie\xDFen",
- chatSettingsAudioLabel: "Chateinstellungen",
- pages: {
- settings: "Einstellungen",
- messages: "Nachrichten",
- tasks: "Aufgaben",
- calendar: "Kalender"
- },
- settings: {
- settingsHeadline: "Einstellungen",
- primaryChannelLabel: "Hauptkanal",
- setPrimaryChannelButtonAudioLabel: "Hauptkanal festlegen",
- newSecondaryChannelPlaceholder: "Sekund\xE4ren Kanal hinzuf\xFCgen",
- newSecondaryChannelAudioLabel: "Name des neuen sekund\xE4ren Kanals",
- addSecondaryChannelButtonAudioLabel: "Sekund\xE4ren Kanal hinzuf\xFCgen",
- encryptionKeyLabel: "Schl\xFCssel",
- setEncryptionKeyButtonAudioLabel: "Schl\xFCssel festlegen",
- showEncryptionKey: "Schl\xFCssel anzeigen",
- deleteChatButton: "Gesamten Chat l\xF6schen"
- },
- message: {
- messagesHeadline: "Nachrichten",
- composerInputPlaceholder: "Schreib eine Nachricht...",
- sendMessageButtonAudioLabel: "nachricht senden",
- showMessageInfoButtonAudioLabel: "nachrichteninfo anzeigen",
- messageInfoHeadline: "Nachrichteninfo",
- sentBy: "Gesendet von",
- timeSent: "Sendezeit",
- channel: "Kanal",
- messageContent: "Nachrichteninhalt",
- copyMessageButton: "Nachricht kopieren",
- resendMessageButton: "Nachricht erneut senden",
- decryptMessageButton: "Nachricht entschl\xFCsseln",
- deleteMessageButton: "Nachricht l\xF6schen"
- },
- task: {
- newBoardNamePlaceholder: "Board erstellen",
- createBoardButtonAudioLabel: "board erstellen",
- noBoardSelected: "Kein Board ausgew\xE4hlt",
- boardNotFound: "Board nicht gefunden",
- closeBoardButtonAudioLabel: "board schlie\xDFen",
- toggleBoardButtonAudioLabel: "board-liste ein/ausblenden",
- showBoardSettingsButtonAudioLabel: "Board-Einstellungen anzeigen",
- listViewButtonAudioLabel: "Listenansicht",
- kanbanViewButtonAudioLabel: "Kanban-Ansicht",
- statusViewButtonAudioLabel: "Statusrasteransicht",
- filterTasksButtonAudioLabel: "Aufgaben filtern",
- createTaskButtonAudioLabel: "Neue Aufgabe erstellen",
- boardSettingsHeadline: "Board-Einstellungen",
- boardNameInputLabel: "Boardname",
- deleteBoardButton: "Board und alle Aufgaben l\xF6schen",
- taskSettingsHeadline: "Aufgabe bearbeiten",
- taskNameLabel: "Titel",
- taskBoardLabel: "Board",
- taskCategoryLabel: "Kategorie",
- taskStatusLabel: "Status",
- taskPriorityLabel: "Priorit\xE4t",
- taskDescriptionLabel: "Beschreibung",
- taskDateLabel: "Datum",
- taskTimeLabel: "Uhrzeit",
- deleteTaskButton: "Aufgabe l\xF6schen",
- filterTasksHeadline: "Aufgaben filtern",
- renameCategoryInputPlaceholder: "Kategorie umbenennen"
- },
- calendar: {
- eventsBoard: "Ereignisse",
- ///
- todayButtonAudioLabel: "gehe zu heute",
- previousMonthButtonAudioLabel: "vorheriger monat",
- nextMonthButtonAudioLabel: "n\xE4chster monat",
- yearInputAudioLabel: "Jahr",
- monthInputAudioLabel: "Monat",
- yearInputPlaceholder: "2000",
- monthInputPlaceholder: "01",
- searchEventsHeadline: "Ereignisse suchen",
- events: "Ereignisse",
- noEvents: "Keine Ereignisse"
- }
- }
- },
- es: {
- updater: {
- migrated: "Migrado"
- },
- general: {
- deleteItemButtonAudioLabel: "eliminar elemento",
- searchButtonAudioLabel: "buscar",
- abortButton: "Abortar",
- cancelButton: "Cancelar",
- closeButton: "Cerrar",
- backButton: "Atr\xE1s",
- continueButton: "Continuar",
- confirmButton: "Confirmar",
- saveButton: "Guardar",
- setButton: "OK",
- reloadAppButton: "Recargar app",
- fileVersionLabel: "Versi\xF3n",
- searchLabel: "Buscar",
- restoreConnection: "Conectar de nuevo"
- },
- regional: {
- weekdays: {
- full: [
- "Domingo",
- "Lunes",
- "Martes",
- "Mi\xE9rcoles",
- "Jueves",
- "Viernes",
- "S\xE1bado"
- ],
- abbreviated: ["Dom", "Lun", "Mar", "Mi\xE9", "Jue", "Vie", "S\xE1b"]
- }
- },
- homePage: {
- appName: "Comms",
- overviewHeadline: "Resumen",
- serverAddress: "Direcci\xF3n del servidor",
- serverAddressPlaceholder: "wss://192.168.0.69:3000",
- connectAudioLabel: "conectar al servidor",
- disconnectAudioLabel: "desconectar del servidor",
- manageConnectionsAudioLabel: "gestionar conexiones",
- yourNameLabel: "Tu nombre",
- yourNamePlaceholder: "Juan P\xE9rez",
- setNameButtonAudioLabel: "establecer nombre",
- firstDayOfWeekLabel: "Primer d\xEDa de la semana",
- manageStorageButton: "Gestionar almacenamiento",
- transferDataButton: "Transferencia de datos",
- scrollToChatButton: "Chats",
- backToOverviewAudioLabel: "volver al resumen",
- chatsHeadline: "Chats",
- addChatAudioLabel: "nombre del nuevo chat",
- addChatPlaceholder: "A\xF1adir chat",
- addChatButton: "A\xF1adir chat"
- },
- connectionModal: {
- connectionModalHeadline: "Gestionar Conexiones",
- connectButtonAudioLabel: "conectar"
- },
- dataTransferModal: {
- transferDataHeadline: "Transferencia de Datos",
- selectionDescription: "Selecciona los datos que quieres transferir.",
- dataEntryDescription: "Introduce estos datos en el otro dispositivo.",
- dataEntryInputDescription: "Introduce los datos mostrados en el otro dispositivo.",
- readyToReceiveDescription: "Haz clic en 'enviar' en el otro dispositivo.",
- notConnectedError: "No est\xE1s conectado a ning\xFAn servidor.",
- fromThisDeviceButton: "Desde este dispositivo",
- toThisDeviceButton: "A este dispositivo",
- generalHeadline: "General",
- connectionData: "Datos de Conexi\xF3n",
- settingsData: "Datos de Configuraci\xF3n",
- chatsHeadline: "Chats",
- transferChannelHeadline: "Canal de Transferencia",
- transferKeyHeadline: "Clave de Encriptaci\xF3n de Transferencia",
- sendButton: "Enviar",
- sendAgainButton: "Enviar otra vez",
- filesSentCount: (count) => `Archivos enviados: ${count}.`,
- allFilesSent: "Hecho.",
- filesReceivedCount: (count) => `Archivos recibidos: ${count}.`
- },
- storage: {
- noItemSelected: "Ning\xFAn elemento seleccionado",
- notAFile: "(no es un archivo)",
- contentEmpty: "(vac\xEDo)",
- path: "Ruta",
- content: "Contenido",
- deleteItem: "Eliminar elemento",
- removeJunkButton: "Eliminar archivos basura"
- },
- chatPage: {
- closeChatAudioLabe: "cerrar chat",
- chatSettingsAudioLabel: "configuraci\xF3n del chat",
- pages: {
- settings: "Configuraci\xF3n",
- messages: "Mensajes",
- tasks: "Tareas",
- calendar: "Calendario"
- },
- settings: {
- settingsHeadline: "Configuraci\xF3n",
- primaryChannelLabel: "Canal principal",
- setPrimaryChannelButtonAudioLabel: "establecer canal principal",
- newSecondaryChannelPlaceholder: "A\xF1adir canal secundario",
- newSecondaryChannelAudioLabel: "nombre del nuevo canal secundario",
- addSecondaryChannelButtonAudioLabel: "a\xF1adir canal secundario",
- encryptionKeyLabel: "Clave de encriptaci\xF3n",
- setEncryptionKeyButtonAudioLabel: "establecer clave de encriptaci\xF3n",
- showEncryptionKey: "Mostrar clave de encriptaci\xF3n",
- deleteChatButton: "Eliminar todo el chat"
- },
- message: {
- messagesHeadline: "Mensajes",
- composerInputPlaceholder: "Escribe un mensaje...",
- sendMessageButtonAudioLabel: "enviar mensaje",
- showMessageInfoButtonAudioLabel: "mostrar informaci\xF3n del mensaje",
- messageInfoHeadline: "Informaci\xF3n del Mensaje",
- sentBy: "Enviado por",
- timeSent: "Hora de env\xEDo",
- channel: "Canal",
- messageContent: "Contenido del mensaje",
- copyMessageButton: "Copiar mensaje",
- resendMessageButton: "Reenviar mensaje",
- decryptMessageButton: "Desencriptar mensaje",
- deleteMessageButton: "Eliminar mensaje"
- },
- task: {
- newBoardNamePlaceholder: "Crear un tablero",
- createBoardButtonAudioLabel: "crear tablero",
- noBoardSelected: "Ning\xFAn tablero seleccionado",
- boardNotFound: "Tablero no encontrado",
- closeBoardButtonAudioLabel: "cerrar tablero",
- toggleBoardButtonAudioLabel: "mostrar o ocultar lista de tableros",
- showBoardSettingsButtonAudioLabel: "mostrar configuraci\xF3n del tablero",
- listViewButtonAudioLabel: "vista de lista",
- kanbanViewButtonAudioLabel: "vista kanban",
- statusViewButtonAudioLabel: "vista de cuadr\xEDcula de estado",
- filterTasksButtonAudioLabel: "filtrar tareas",
- createTaskButtonAudioLabel: "crear nueva tarea",
- boardSettingsHeadline: "Configuraci\xF3n del Tablero",
- boardNameInputLabel: "Nombre del tablero",
- deleteBoardButton: "Eliminar tablero y todas las tareas",
- taskSettingsHeadline: "Editar Tarea",
- taskNameLabel: "T\xEDtulo",
- taskBoardLabel: "Tablero",
- taskCategoryLabel: "Categor\xEDa",
- taskStatusLabel: "Estado",
- taskPriorityLabel: "Prioridad",
- taskDescriptionLabel: "Descripci\xF3n",
- taskDateLabel: "Fecha",
- taskTimeLabel: "Hora",
- deleteTaskButton: "Eliminar tarea",
- filterTasksHeadline: "Filtrar Tareas",
- renameCategoryInputPlaceholder: "Renombrar categor\xEDa"
- },
- calendar: {
- eventsBoard: "Eventos",
- ///
- todayButtonAudioLabel: "ir a hoy",
- previousMonthButtonAudioLabel: "mes anterior",
- nextMonthButtonAudioLabel: "mes siguiente",
- yearInputAudioLabel: "a\xF1o",
- monthInputAudioLabel: "mes",
- yearInputPlaceholder: "2000",
- monthInputPlaceholder: "01",
- searchEventsHeadline: "Buscar Eventos",
- events: "Eventos",
- noEvents: "No hay eventos"
- }
- }
- }
- };
- var language = navigator.language.substring(0, 2);
- var translations = allTranslations[language] || allTranslations.en;
-
- // src/ViewModel/Chat/chatViewModel.ts
- var ChatViewModel = class {
- // init
- constructor(coreViewModel, storageModel2, chatModel, settingsViewModel2, connectionViewModel2, chatListViewModel2) {
- this.coreViewModel = coreViewModel;
- // state
- this.displayedColor = new State("standard" /* Standard */);
- this.selectedPage = new State(
- "messages" /* Messages */
- );
- this.index = new State(0);
- this.hasUnreadMessages = new State(false);
- this.taskBoardSuggestions = new MapState();
- // view
- this.open = () => {
- this.chatListViewModel.openChat(this);
- this.markRead();
- };
- this.close = () => {
- this.chatListViewModel.closeChat();
- };
- this.closeSubPages = () => {
- };
- this.setColor = (color) => {
- this.setDisplayedColor(color);
- this.chatModel.setColor(color);
- };
- this.setDisplayedColor = (color) => {
- this.displayedColor.value = color;
- };
- this.resetColor = () => {
- this.displayedColor.value = this.settingsPageViewModel.color.value;
- };
- this.updateIndex = () => {
- const index = this.chatListViewModel.chatIndexManager.getIndex(this);
- this.index.value = index;
- };
- this.markRead = () => {
- this.hasUnreadMessages.value = false;
- this.chatModel.markRead();
- };
- // load
- this.loadPageSelection = () => {
- const path = StorageModel.getPath(
- "chat" /* Chat */,
- filePaths.chat.lastUsedPage(this.chatModel.id)
- );
- const lastUsedPage = this.storageModel.read(path);
- if (lastUsedPage != null) {
- this.selectedPage.value = lastUsedPage;
- }
- this.selectedPage.subscribeSilent((newPage) => {
- this.storageModel.write(path, newPage);
- this.resetColor();
- });
- };
- this.loadInfo = () => {
- this.hasUnreadMessages.value = this.chatModel.info.hasUnreadMessages;
- this.taskBoardSuggestions.set(CALENDAR_EVENT_BOARD_ID, [
- CALENDAR_EVENT_BOARD_ID,
- translations.chatPage.calendar.eventsBoard
- ]);
- };
- this.storageModel = storageModel2;
- this.chatModel = chatModel;
- this.settingsViewModel = settingsViewModel2;
- this.connectionViewModel = connectionViewModel2;
- this.chatListViewModel = chatListViewModel2;
- this.calendarViewModel = new CalendarPageViewModel(
- coreViewModel,
- this,
- this.storageModel,
- this.chatModel.fileModel.boardsAndTasksModel.calendarModel,
- this.chatModel.fileModel.boardsAndTasksModel
- );
- this.taskPageViewModel = new TaskPageViewModel(
- this.coreViewModel,
- this,
- this.storageModel,
- this.chatModel.fileModel.boardsAndTasksModel
- );
- this.messagePageViewModel = new MessagePageViewModel(
- this.coreViewModel,
- this
- );
- this.settingsPageViewModel = new SettingsPageViewModel(
- this.coreViewModel,
- this
- );
- chatModel.chatMessageHandlerManager.addHandler(
- (chatMessage) => {
- this.messagePageViewModel.showChatMessage(chatMessage);
- this.markRead();
- }
- );
- this.loadPageSelection();
- this.resetColor();
- this.loadInfo();
- }
- };
-
- // src/ViewModel/Chat/chatListViewModel.ts
- var ChatListViewModel = class {
- // init
- constructor(coreViewModel, storageModel2, chatListModel2, settingsViewModel2, connectionViewModel2) {
- this.coreViewModel = coreViewModel;
- // data
- this.chatIndexManager = new IndexManager(
- (chatViewModel) => chatViewModel.settingsPageViewModel.primaryChannel.value
- );
- // state
- this.newChatPrimaryChannel = new State("");
- this.chatViewModels = new ListState();
- this.selectedChat = new State(
- void 0
- );
- // guards
- this.cannotCreateChat = createProxyState(
- [this.newChatPrimaryChannel],
- () => this.newChatPrimaryChannel.value == ""
- );
- // methods
- this.createChat = () => {
- const chatModel = this.chatListModel.createChat(
- this.newChatPrimaryChannel.value
- );
- this.newChatPrimaryChannel.value = "";
- const chatViewModel = this.createChatViewModel(chatModel);
- this.trackChat(chatViewModel);
- this.updateIndices();
- };
- this.trackChat = (chatViewModel) => {
- this.chatViewModels.add(chatViewModel);
- };
- this.untrackChat = (chatViewModel) => {
- this.chatListModel.untrackChat(chatViewModel.chatModel);
- this.chatViewModels.remove(chatViewModel);
- };
- this.createChatViewModel = (chatModel) => {
- return new ChatViewModel(
- this.coreViewModel,
- this.storageModel,
- chatModel,
- this.settingsViewModel,
- this.connectionViewModel,
- this
- );
- };
- this.updateIndices = () => {
- this.chatIndexManager.update([...this.chatViewModels.value.values()]);
- for (const chatViewModel of this.chatViewModels.value) {
- chatViewModel.updateIndex();
- }
- };
- // view
- this.openChat = (chatViewModel) => {
- this.selectedChat.value = chatViewModel;
- };
- this.closeChat = () => {
- this.selectedChat.value = void 0;
- };
- // load
- this.loadChats = () => {
- this.chatViewModels.clear();
- for (const chatModel of this.chatListModel.chatModels.values()) {
- const chatViewModel = this.createChatViewModel(chatModel);
- this.trackChat(chatViewModel);
- }
- this.updateIndices();
- };
- this.storageModel = storageModel2;
- this.chatListModel = chatListModel2;
- this.settingsViewModel = settingsViewModel2;
- this.connectionViewModel = connectionViewModel2;
- this.loadChats();
- }
- };
-
- // src/View/Components/monthGrid.tsx
- function MonthGrid2(monthGrid, selectedDate, handleDrop) {
- const dayLabels = [];
- let currentWeekday = monthGrid.firstDayOfWeek;
- while (dayLabels.length < 7) {
- dayLabels.push(
- /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.regional.weekdays.abbreviated[currentWeekday])
- );
- currentWeekday++;
- if (currentWeekday == 7) currentWeekday = 0;
- }
- const offsetElements = [];
- for (let i = 0; i < monthGrid.offset; i++) {
- offsetElements.push(/* @__PURE__ */ createElement("div", null));
- }
- const converter = (taskViewModel) => {
- const view = /* @__PURE__ */ createElement("span", { class: "ellipsis secondary" }, taskViewModel.task.name);
- taskViewModel.index.subscribe((newIndex) => {
- view.style.order = newIndex;
- });
- return view;
- };
- return /* @__PURE__ */ createElement("div", { class: "month-grid-wrapper" }, /* @__PURE__ */ createElement("div", { class: "day-labels" }, ...dayLabels), /* @__PURE__ */ createElement("div", { class: "month-grid" }, ...offsetElements, ...Object.entries(monthGrid.days).sort((a, b) => localeCompare(a[0], b[0])).map((entry) => {
- const [date, mapState] = entry;
- const isSelected = createProxyState(
- [selectedDate],
- () => selectedDate.value == parseInt(date)
- );
- const isToday = monthGrid.isCurrentMonth == true && parseInt(date) == (/* @__PURE__ */ new Date()).getDate();
- const eventCount = createProxyState(
- [mapState],
- () => mapState.value.size
- );
- const hasEvents = createProxyState(
- [eventCount],
- () => eventCount.value != 0
- );
- function select() {
- selectedDate.value = parseInt(date);
- }
- function drop() {
- handleDrop(date);
- }
- return /* @__PURE__ */ createElement(
- "button",
- {
- class: "tile",
- "on:click": select,
- "toggle:selected": isSelected,
- "toggle:today": isToday,
- "on:dragover": allowDrop,
- "on:drop": drop
- },
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, date), /* @__PURE__ */ createElement(
- "span",
- {
- class: "event-count",
- "toggle:has-events": hasEvents,
- "subscribe:innerText": eventCount
- }
- ), /* @__PURE__ */ createElement(
- "div",
- {
- class: "event-list",
- "children:append": [mapState, converter]
- }
- ))
- );
- })));
- }
-
- // src/View/Components/option.tsx
- function Option(text, value, selectedOnCreate) {
- return /* @__PURE__ */ createElement("option", { value, "toggle:selected": selectedOnCreate }, text);
- }
- var StringToOption = (string) => {
- return Option(string, string, false);
- };
- var VersionIdToOption = (versionId) => {
- const [date, rest] = versionId.split("T");
- const [time] = rest.split(".");
- const readableName = `${date} ${time}`;
- return Option(readableName, versionId, false);
- };
-
- // src/View/Components/dangerousActionButton.tsx
- function DangerousActionButton(label, icon, action) {
- const isActionRequested = new State(false);
- const cannotConfirm = createProxyState(
- [isActionRequested],
- () => isActionRequested.value == false
- );
- function requestAction() {
- isActionRequested.value = true;
- }
- function abort() {
- isActionRequested.value = false;
- }
- return /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("button", { class: "flex", "on:click": abort, "toggle:hidden": cannotConfirm }, translations.general.abortButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "undo")), /* @__PURE__ */ createElement(
- "button",
- {
- class: "danger flex",
- "on:click": requestAction,
- "toggle:hidden": isActionRequested
- },
- label,
- /* @__PURE__ */ createElement("span", { class: "icon" }, icon)
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "danger flex",
- "on:click": action,
- "toggle:hidden": cannotConfirm
- },
- translations.general.confirmButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "warning")
- ));
- }
-
- // src/View/Modals/taskSettingsModal.tsx
- function TaskSettingsModal(taskViewModel) {
- const categorySuggestionId = v4_default();
- const statusSuggestionId = v4_default();
- const BoardOptionConverter = (entry) => {
- const isSelected = entry[0] == taskViewModel.task.boardId;
- return Option(entry[1], entry[0], isSelected);
- };
- return /* @__PURE__ */ createElement("div", { class: "modal", open: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.taskSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "history"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.general.fileVersionLabel), /* @__PURE__ */ createElement(
- "select",
- {
- "bind:value": taskViewModel.selectedVersionId,
- "children:append": [
- taskViewModel.versionIds,
- VersionIdToOption
- ]
- }
- ), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskNameLabel), /* @__PURE__ */ createElement("input", { "bind:value": taskViewModel.name }))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "category"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskBoardLabel), /* @__PURE__ */ createElement(
- "select",
- {
- "bind:value": taskViewModel.boardId,
- "children:append": [
- taskViewModel.chatViewModel.taskBoardSuggestions,
- BoardOptionConverter
- ]
- }
- ), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDescriptionLabel), /* @__PURE__ */ createElement(
- "textarea",
- {
- rows: "10",
- "bind:value": taskViewModel.description
- }
- ))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "category"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskCategoryLabel), /* @__PURE__ */ createElement(
- "input",
- {
- "bind:value": taskViewModel.category,
- list: categorySuggestionId
- }
- ))), /* @__PURE__ */ createElement(
- "datalist",
- {
- hidden: true,
- id: categorySuggestionId,
- "children:append": [
- taskViewModel.coreViewModel.taskCategorySuggestions,
- StringToOption
- ]
- }
- ), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "clock_loader_40"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskStatusLabel), /* @__PURE__ */ createElement(
- "input",
- {
- "bind:value": taskViewModel.status,
- list: statusSuggestionId
- }
- ))), /* @__PURE__ */ createElement(
- "datalist",
- {
- hidden: true,
- id: statusSuggestionId,
- "children:append": [
- taskViewModel.coreViewModel.taskStatusSuggestions,
- StringToOption
- ]
- }
- ), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "priority_high"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskPriorityLabel), /* @__PURE__ */ createElement(
- "input",
- {
- type: "number",
- "bind:value": taskViewModel.priority
- }
- ))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskDateLabel), /* @__PURE__ */ createElement(
- "input",
- {
- type: "date",
- "bind:value": taskViewModel.date
- }
- ))), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.taskTimeLabel), /* @__PURE__ */ createElement(
- "input",
- {
- type: "time",
- "bind:value": taskViewModel.time
- }
- ))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
- translations.chatPage.task.deleteTaskButton,
- "delete_forever",
- taskViewModel.deleteTask
- ))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex",
- "on:click": taskViewModel.closeAndDiscard
- },
- translations.general.closeButton
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex primary",
- "on:click": taskViewModel.closeAndSave
- },
- translations.general.saveButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "save")
- ))));
- }
-
- // src/View/Components/taskEntry.tsx
- function TaskEntry(taskViewModel) {
- const details = {
- description: taskViewModel.description.value || "---",
- priority_high: taskViewModel.priority.value || "---",
- category: taskViewModel.category.value || "---",
- clock_loader_40: taskViewModel.status.value || "---",
- calendar_month: taskViewModel.date.value || "---",
- schedule: taskViewModel.time.value || "---"
- };
- const view = /* @__PURE__ */ createElement(
- "button",
- {
- draggable: "true",
- class: "tile flex-no",
- style: "user-select: none; -webkit-user-select: none",
- "on:click": taskViewModel.open,
- "on:dragstart": taskViewModel.dragStart
- },
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement(
- "b",
- {
- class: "ellipsis",
- "subscribe:innerText": taskViewModel.name
- }
- ), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
- "div",
- {
- class: "grid secondary",
- style: "grid-template-columns: repeat(2, 1fr); column-gap: 1rem; row-gap: .5rem"
- },
- ...Object.entries(details).map((entry) => /* @__PURE__ */ createElement(
- "span",
- {
- class: "flex-row align-center width-100 flex-no clip",
- style: "gap: 1rem"
- },
- /* @__PURE__ */ createElement("span", { class: "icon", style: "font-size: 1.1rem" }, entry[0]),
- /* @__PURE__ */ createElement("span", { class: "ellipsis" }, entry[1])
- ))
- ))
- );
- taskViewModel.index.subscribe((newIndex) => {
- view.style.order = newIndex;
- });
- return view;
- }
- var TaskViewModelToEntry = (taskViewModel) => {
- return TaskEntry(taskViewModel);
- };
-
- // src/View/ChatPages/calendarPage.tsx
- function CalendarPage(calendarPageViewModel) {
- calendarPageViewModel.loadData();
- const mainContent = createProxyState(
- [calendarPageViewModel.monthGrid],
- () => {
- const monthGrid = calendarPageViewModel.monthGrid.value;
- if (monthGrid == void 0) {
- return /* @__PURE__ */ createElement("div", null);
- } else {
- let drop = function(date) {
- calendarPageViewModel.handleDrop(
- monthGrid.year.toString(),
- monthGrid.month.toString(),
- date
- );
- };
- return MonthGrid2(
- monthGrid,
- calendarPageViewModel.selectedDate,
- drop
- );
- }
- }
- );
- const sidePaneContentWrapper = createProxyState(
- [
- calendarPageViewModel.selectedYear,
- calendarPageViewModel.selectedMonth,
- calendarPageViewModel.selectedDate
- ],
- () => {
- const listState = calendarPageViewModel.getEventsForDate();
- if (listState == void 0) {
- return /* @__PURE__ */ createElement("div", null);
- } else {
- const sidePaneContent = createProxyState(
- [listState],
- () => {
- if (listState.value.size == 0) {
- return /* @__PURE__ */ createElement("div", { class: "width-100 height-100 flex-column justify-center align-center" }, /* @__PURE__ */ createElement("span", { class: "secondary slide-up" }, translations.chatPage.calendar.noEvents));
- } else {
- return /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column gap slide-up padding-bottom",
- "children:append": [
- listState,
- TaskViewModelToEntry
- ]
- }
+ // node_modules/uuid/dist/esm-browser/rng.js
+ var getRandomValues;
+ var rnds8 = new Uint8Array(16);
+ function rng() {
+ if (!getRandomValues) {
+ getRandomValues =
+ typeof crypto !== "undefined" &&
+ crypto.getRandomValues &&
+ crypto.getRandomValues.bind(crypto);
+ if (!getRandomValues) {
+ throw new Error(
+ "crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported",
);
- }
}
- );
- return /* @__PURE__ */ createElement(
+ }
+ return getRandomValues(rnds8);
+ }
+
+ // node_modules/uuid/dist/esm-browser/native.js
+ var randomUUID =
+ typeof crypto !== "undefined" &&
+ crypto.randomUUID &&
+ crypto.randomUUID.bind(crypto);
+ var native_default = {
+ randomUUID,
+ };
+
+ // node_modules/uuid/dist/esm-browser/v4.js
+ function v4(options, buf, offset) {
+ if (native_default.randomUUID && !buf && !options) {
+ return native_default.randomUUID();
+ }
+ options = options || {};
+ var rnds = options.random || (options.rng || rng)();
+ rnds[6] = (rnds[6] & 15) | 64;
+ rnds[8] = (rnds[8] & 63) | 128;
+ if (buf) {
+ offset = offset || 0;
+ for (var i = 0; i < 16; ++i) {
+ buf[offset + i] = rnds[i];
+ }
+ return buf;
+ }
+ return unsafeStringify(rnds);
+ }
+ var v4_default = v4;
+
+ // node_modules/bloatless-react/index.ts
+ var State = class {
+ _value;
+ _bindings = /* @__PURE__ */ new Set();
+ // init
+ constructor(initialValue) {
+ this._value = initialValue;
+ }
+ // value
+ get value() {
+ return this._value;
+ }
+ set value(newValue) {
+ if (this._value == newValue) return;
+ this._value = newValue;
+ this.callSubscriptions();
+ }
+ // subscriptions
+ callSubscriptions() {
+ this._bindings.forEach((fn) => fn(this._value));
+ }
+ subscribe(fn) {
+ this._bindings.add(fn);
+ fn(this._value);
+ }
+ subscribeSilent(fn) {
+ this._bindings.add(fn);
+ }
+ // stringify
+ toString() {
+ return JSON.stringify(this._value);
+ }
+ };
+ var ListState = class extends State {
+ additionHandlers = /* @__PURE__ */ new Set();
+ removalHandlers = /* @__PURE__ */ new Map();
+ // init
+ constructor(initialItems) {
+ super(new Set(initialItems));
+ }
+ // list
+ add(...items) {
+ items.forEach((item) => {
+ this.value.add(item);
+ this.additionHandlers.forEach((handler) => handler(item));
+ });
+ this.callSubscriptions();
+ }
+ remove(...items) {
+ items.forEach((item) => {
+ this.value.delete(item);
+ if (!this.removalHandlers.has(item)) return;
+ this.removalHandlers
+ .get(item)
+ .forEach((handler) => handler(item));
+ this.removalHandlers.delete(item);
+ });
+ this.callSubscriptions();
+ }
+ clear() {
+ this.remove(...this.value.values());
+ }
+ // handlers
+ handleAddition(handler) {
+ this.additionHandlers.add(handler);
+ [...this.value.values()].forEach(handler);
+ }
+ handleRemoval(item, handler) {
+ if (!this.removalHandlers.has(item))
+ this.removalHandlers.set(item, /* @__PURE__ */ new Set());
+ this.removalHandlers.get(item).add(handler);
+ }
+ // stringification
+ toString() {
+ const array = [...this.value.values()];
+ const json = JSON.stringify(array);
+ return json;
+ }
+ };
+ var MapState = class extends State {
+ additionHandlers = /* @__PURE__ */ new Set();
+ removalHandlers = /* @__PURE__ */ new Map();
+ // init
+ constructor(initialItems) {
+ super(new Map(initialItems));
+ }
+ // list
+ set(key, item) {
+ this.remove(key);
+ this.value.set(key, item);
+ this.additionHandlers.forEach((handler) => handler(item));
+ this.callSubscriptions();
+ }
+ remove(key) {
+ const item = this.value.get(key);
+ if (!item) return;
+ this.value.delete(key);
+ this.callSubscriptions();
+ if (!this.removalHandlers.has(item)) return;
+ this.removalHandlers.get(item).forEach((handler) => handler(item));
+ this.removalHandlers.delete(item);
+ }
+ clear() {
+ [...this.value.keys()].forEach((key) => this.remove(key));
+ }
+ // handlers
+ handleAddition(handler) {
+ this.additionHandlers.add(handler);
+ [...this.value.values()].forEach(handler);
+ }
+ handleRemoval(item, handler) {
+ if (!this.removalHandlers.has(item))
+ this.removalHandlers.set(item, /* @__PURE__ */ new Set());
+ this.removalHandlers.get(item).add(handler);
+ }
+ // stringification
+ toString() {
+ const array = [...this.value.entries()];
+ const json = JSON.stringify(array);
+ return json;
+ }
+ };
+ function createProxyState(statesToSubscibe, fn) {
+ const proxyState = new State(fn());
+ statesToSubscibe.forEach((state) =>
+ state.subscribe(() => (proxyState.value = fn())),
+ );
+ return proxyState;
+ }
+ function bulkSubscribe(statesToSubscibe, fn) {
+ statesToSubscibe.forEach((state) => state.subscribeSilent(fn));
+ }
+ function createElement(tagName, attributes = {}, ...children) {
+ const element = document.createElement(tagName);
+ if (attributes != null)
+ Object.entries(attributes).forEach((entry) => {
+ const [attributename, value] = entry;
+ const [directiveKey, directiveValue] = attributename.split(":");
+ switch (directiveKey) {
+ case "on": {
+ switch (directiveValue) {
+ case "enter": {
+ element.addEventListener("keydown", (e) => {
+ if (e.key != "Enter") return;
+ value();
+ });
+ break;
+ }
+ default: {
+ element.addEventListener(directiveValue, value);
+ }
+ }
+ break;
+ }
+ case "subscribe": {
+ const state = value;
+ state.subscribe(
+ (newValue) => (element[directiveValue] = newValue),
+ );
+ break;
+ }
+ case "bind": {
+ const state = value;
+ state.subscribe(
+ (newValue) => (element[directiveValue] = newValue),
+ );
+ element.addEventListener(
+ "input",
+ () => (state.value = element[directiveValue]),
+ );
+ break;
+ }
+ case "toggle": {
+ if (value.subscribe) {
+ const state = value;
+ state.subscribe((newValue) =>
+ element.toggleAttribute(
+ directiveValue,
+ newValue,
+ ),
+ );
+ } else {
+ element.toggleAttribute(directiveValue, value);
+ }
+ break;
+ }
+ case "set": {
+ const state = value;
+ state.subscribe((newValue) =>
+ element.setAttribute(directiveValue, newValue),
+ );
+ break;
+ }
+ case "children": {
+ switch (directiveValue) {
+ case "set": {
+ const state = value;
+ state.subscribe((newValue) => {
+ element.innerHTML = "";
+ element.append(...[newValue].flat());
+ });
+ break;
+ }
+ case "append":
+ case "prepend": {
+ try {
+ const [listState, toElement] = value;
+ listState.handleAddition((newItem) => {
+ const child = toElement(newItem);
+ listState.handleRemoval(newItem, () =>
+ child.remove(),
+ );
+ if (directiveValue == "append") {
+ element.append(child);
+ } else if (
+ directiveValue == "prepend"
+ ) {
+ element.prepend(child);
+ }
+ });
+ } catch (error) {
+ console.error(error);
+ throw `error: cannot process subscribe:children directive.
+ Usage: "children:append={[list, converter]}"; you can find a more detailed example in the documentation.`;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ element.setAttribute(attributename, value);
+ }
+ });
+ children.filter((x) => x).forEach((child) => element.append(child));
+ return element;
+ }
+
+ // src/Model/Utility/typeSafety.ts
+ var DATA_VERSION = "v2";
+ function checkIsValidObject(object) {
+ return object.dataVersion == DATA_VERSION;
+ }
+ function checkMatchesObjectStructure(objectToCheck, reference) {
+ if (typeof reference != "object") {
+ return typeof objectToCheck == typeof reference;
+ }
+ for (const key of Object.keys(reference)) {
+ const requiredType = typeof reference[key];
+ const actualType = typeof objectToCheck[key];
+ if (requiredType != actualType) return false;
+ if (
+ Array.isArray(reference[key]) ||
+ reference[key] instanceof Set
+ ) {
+ if (objectToCheck[key].length == 0) continue;
+ if (objectToCheck[key].size == 0) continue;
+ const [firstOfObjectToCheck] = objectToCheck[key];
+ const [fisrtOfReference] = reference[key];
+ const doesFirstItemMatch = checkMatchesObjectStructure(
+ firstOfObjectToCheck,
+ fisrtOfReference,
+ );
+ if (doesFirstItemMatch == false) return false;
+ } else if (requiredType == "object") {
+ const doesNestedObjectMatch = checkMatchesObjectStructure(
+ objectToCheck[key],
+ reference[key],
+ );
+ if (doesNestedObjectMatch == false) return false;
+ }
+ }
+ return true;
+ }
+
+ // src/Model/Utility/utility.ts
+ function generateRandomToken(length) {
+ const array = new Uint8Array(length);
+ crypto.getRandomValues(array);
+ return array.join("");
+ }
+ function createTimestamp() {
+ return /* @__PURE__ */ new Date().toISOString();
+ }
+ function checkDoesObjectMatchReference(reference, stringEntryObject) {
+ reference_entry_loop: for (const referenceEntry of Object.entries(
+ reference,
+ )) {
+ const [referenceKey, referenceValue] = referenceEntry;
+ const stringEntryObjectValue = stringEntryObject[referenceKey];
+ if (referenceValue == void 0) return false;
+ if (referenceValue[0] == "-") {
+ const strippedReferenceValue = referenceValue
+ .toString()
+ .substring(1);
+ if (
+ strippedReferenceValue == "" &&
+ stringEntryObjectValue != void 0 &&
+ stringEntryObjectValue != ""
+ ) {
+ return false;
+ }
+ if (stringEntryObjectValue == strippedReferenceValue) {
+ return false;
+ }
+ } else {
+ if (
+ referenceValue == "" &&
+ (stringEntryObjectValue == void 0 ||
+ stringEntryObjectValue == "")
+ ) {
+ return false;
+ } else if (referenceValue == "") {
+ continue reference_entry_loop;
+ }
+ if (stringEntryObjectValue != referenceValue) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ function collectObjectValuesForKey(key, converter, objects) {
+ const values = /* @__PURE__ */ new Set();
+ for (const object of objects) {
+ const stringEntryObject = converter(object);
+ const stringEntryObjectValue = stringEntryObject[key];
+ if (
+ stringEntryObjectValue == void 0 ||
+ stringEntryObjectValue == ""
+ )
+ continue;
+ values.add(stringEntryObjectValue.toString());
+ }
+ return [...values.values()];
+ }
+ function checkDoesObjectMatchSearch(query, getStringsOfObject, object) {
+ if (query == "") return true;
+ const stringsInObject = getStringsOfObject(object);
+ const wordsInObject = [];
+ for (const string of stringsInObject) {
+ const lowercaseWordsInString = string
+ .toString()
+ .toLowerCase()
+ .split(" ")
+ .filter((word) => word != "");
+ wordsInObject.push(...lowercaseWordsInString);
+ }
+ const lowercaseWordsInQuery = query
+ .toLowerCase()
+ .split(" ")
+ .filter((word) => word != "");
+ for (const queryWord of lowercaseWordsInQuery) {
+ if (queryWord[0] == "-") {
+ const wordContent = queryWord.substring(1);
+ if (wordsInObject.includes(wordContent)) {
+ return false;
+ }
+ } else {
+ if (wordsInObject.includes(queryWord) == false) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ var HandlerManager = class {
+ constructor() {
+ this.handlers = /* @__PURE__ */ new Set();
+ // manage
+ this.addHandler = (handler) => {
+ this.handlers.add(handler);
+ };
+ this.deleteHandler = (handler) => {
+ this.handlers.delete(handler);
+ };
+ // trigger
+ this.trigger = (item) => {
+ for (const handler of this.handlers) {
+ handler(item);
+ }
+ };
+ }
+ };
+ var IndexManager = class {
+ // init
+ constructor(itemToString) {
+ this.sortedStrings = [];
+ // methods
+ this.update = (items) => {
+ this.sortedStrings = [];
+ let strings = [];
+ for (const item of items) {
+ const string = this.itemToString(item);
+ strings.push(string);
+ }
+ this.sortedStrings = strings.sort(localeCompare);
+ };
+ this.getIndex = (item) => {
+ const string = this.itemToString(item);
+ const index = this.sortedStrings.indexOf(string);
+ return index;
+ };
+ this.itemToString = itemToString;
+ }
+ };
+ function getLocalStorageItemAndClear(key) {
+ const value = localStorage.getItem(key);
+ localStorage.removeItem(key);
+ if (value != null) localStorage.setItem(`_${key}`, value);
+ return value;
+ }
+ function stringify(data) {
+ return JSON.stringify(data, null, 4);
+ }
+ function padZero(string, length) {
+ return (string ?? "").padStart(length, "0");
+ }
+ function parse(string) {
+ try {
+ return JSON.parse(string);
+ } catch {
+ return {};
+ }
+ }
+ function parseValidObject(string, reference) {
+ const parsed = parse(string);
+ if (checkIsValidObject(parsed) == false) return null;
+ const doesMatchReference = checkMatchesObjectStructure(
+ parsed,
+ reference,
+ );
+ if (doesMatchReference == false) return null;
+ return parsed;
+ }
+ function parseOrFallback(inputString) {
+ try {
+ return JSON.parse(inputString);
+ } catch {
+ return inputString;
+ }
+ }
+ function parseArray(inputString) {
+ const parsed = parseOrFallback(inputString);
+ if (Array.isArray(parsed) == false) return [];
+ return parsed;
+ }
+ function localeCompare(a, b) {
+ return a.localeCompare(b);
+ }
+
+ // src/Model/Global/storageModel.ts
+ var PATH_COMPONENT_SEPARATOR = "\\";
+ var StorageModel = class _StorageModel {
+ // init
+ constructor() {
+ this.storageEntryTree = {};
+ // read
+ this.read = (pathComponents) => {
+ const pathString = _StorageModel.pathComponentsToString(
+ ...pathComponents,
+ );
+ return localStorage.getItem(pathString);
+ };
+ this.list = (pathComponents) => {
+ let currentParent = this.storageEntryTree;
+ for (const component of pathComponents) {
+ const nextParent = currentParent[component];
+ if (nextParent == void 0) return [];
+ currentParent = nextParent;
+ }
+ return [...Object.keys(currentParent).sort(localeCompare)];
+ };
+ // write
+ this.write = (pathComponents, value) => {
+ const pathString = _StorageModel.pathComponentsToString(
+ ...pathComponents,
+ );
+ localStorage.setItem(pathString, value);
+ this.updateTree(...pathComponents);
+ };
+ this.remove = (pathComponents, shouldInitialize = true) => {
+ const pathString = _StorageModel.pathComponentsToString(
+ ...pathComponents,
+ );
+ localStorage.removeItem(pathString);
+ if (shouldInitialize == true) {
+ this.initializeTree();
+ }
+ };
+ this.rename = (
+ sourcePathComponents,
+ destinationPathComponents,
+ shouldInitialize = true,
+ ) => {
+ const content = this.read(sourcePathComponents);
+ if (content == null) return false;
+ this.write(destinationPathComponents, content);
+ this.remove(sourcePathComponents);
+ if (shouldInitialize == true) {
+ this.initializeTree();
+ }
+ return true;
+ };
+ // recursion
+ this.recurse = (rootDirectory, fn) => {
+ loop_over_files: for (const key of Object.keys(localStorage)) {
+ const pathComponentsOfCurrentEntity =
+ _StorageModel.stringToPathComponents(key);
+ loop_over_path_components: for (
+ let i = 0;
+ i < rootDirectory.length;
+ i++
+ ) {
+ if (!pathComponentsOfCurrentEntity[i])
+ continue loop_over_files;
+ if (
+ pathComponentsOfCurrentEntity[i] != rootDirectory[i]
+ )
+ continue loop_over_files;
+ }
+ fn(pathComponentsOfCurrentEntity);
+ }
+ this.initializeTree();
+ };
+ this.removeRecursively = (pathComponents) => {
+ this.recurse(pathComponents, (path) =>
+ this.remove(path, false),
+ );
+ this.initializeTree();
+ };
+ this.renameRecursively = (
+ sourcePathComponents,
+ destinationPathComponents,
+ ) => {
+ this.recurse(sourcePathComponents, (path) => {
+ const relativePathOfCurrentEntity = path.slice(
+ sourcePathComponents.length,
+ );
+ const destinationPathComponentsOfCurrentEntity = [
+ ...destinationPathComponents,
+ ...relativePathOfCurrentEntity,
+ ];
+ this.rename(
+ path,
+ destinationPathComponentsOfCurrentEntity,
+ false,
+ );
+ });
+ this.initializeTree();
+ };
+ // stringifiable
+ this.writeStringifiable = (pathComponents, value) => {
+ const valueString = stringify(value);
+ this.write(pathComponents, valueString);
+ };
+ this.readStringifiable = (pathComponents, reference) => {
+ const valueString = this.read(pathComponents);
+ if (!valueString) return null;
+ const object = parseValidObject(valueString, reference);
+ if (object == null) return null;
+ return object;
+ };
+ // cleaning
+ this.removeJunk = () => {
+ this.recurse([], (path) => {
+ if (path[0] == DATA_VERSION) return;
+ this.remove(path);
+ });
+ };
+ // tree
+ this.initializeTree = () => {
+ console.log("initializing tree");
+ this.storageEntryTree = {};
+ for (const key of Object.keys(localStorage)) {
+ const components =
+ _StorageModel.stringToPathComponents(key);
+ this.updateTree(...components);
+ }
+ };
+ this.updateTree = (...pathComponents) => {
+ let currentParent = this.storageEntryTree;
+ for (const pathPart of pathComponents) {
+ if (!currentParent[pathPart]) {
+ currentParent[pathPart] = {};
+ }
+ currentParent = currentParent[pathPart];
+ }
+ };
+ this.printTree = () => {
+ return stringify(this.storageEntryTree);
+ };
+ this.initializeTree();
+ }
+ static {
+ // utility
+ this.getFileName = (pathComponents) => {
+ return pathComponents[pathComponents.length - 1] || "\\";
+ };
+ }
+ static {
+ this.getFileNameFromString = (pathString) => {
+ const pathComponents = this.stringToPathComponents(pathString);
+ return pathComponents[pathComponents.length - 1] || "\\";
+ };
+ }
+ static {
+ this.pathComponentsToString = (...pathComponents) => {
+ return pathComponents
+ .filter((x) => x != "")
+ .join(PATH_COMPONENT_SEPARATOR);
+ };
+ }
+ static {
+ this.stringToPathComponents = (string) => {
+ return string
+ .split(PATH_COMPONENT_SEPARATOR)
+ .filter((x) => x != "");
+ };
+ }
+ static {
+ this.join = (...items) => {
+ let allComponents = [];
+ for (const item of items) {
+ const parts = this.stringToPathComponents(item);
+ allComponents.push(...parts);
+ }
+ return _StorageModel.pathComponentsToString(...allComponents);
+ };
+ }
+ static getPath(locationName, filePath) {
+ return [DATA_VERSION, locationName, ...filePath];
+ }
+ };
+ var filePaths = {
+ connectionModel: {
+ socketAddress: ["socket-address"],
+ reconnectAddress: ["reconnect-address"],
+ outbox: ["outbox"],
+ mailboxes: ["mailboxes"],
+ previousAddresses: ["previous-addresses"],
+ },
+ chat: {
+ base: [],
+ chatBase: (id) => [id],
+ info: (id) => [...filePaths.chat.chatBase(id), "info"],
+ color: (id) => [...filePaths.chat.chatBase(id), "color"],
+ messages: (id) => [...filePaths.chat.chatBase(id), "messages"],
+ lastUsedPage: (id) => [
+ ...filePaths.chat.chatBase(id),
+ "last-used-page",
+ ],
+ files: (id) => [...filePaths.chat.chatBase(id), "files"],
+ },
+ settingsModel: {
+ username: ["user-name"],
+ firstDayOfWeek: ["first-day-of-week"],
+ },
+ };
+
+ // src/Model/Files/calendarModel.ts
+ var CalendarModel = class _CalendarModel {
+ // init
+ constructor(storageModel2, settingsModel2, fileModel) {
+ // paths
+ this.getBasePath = () => {
+ return this.fileModel.getModelContainerPath(
+ "calendar" /* ModelCalendar */,
+ );
+ };
+ this.getViewPath = () => {
+ return [...this.getBasePath(), "view" /* ModelView */];
+ };
+ this.getMonthContainerPath = () => {
+ return [...this.getBasePath(), "months" /* Months */];
+ };
+ this.getMonthPath = (monthString) => {
+ return [...this.getMonthContainerPath(), monthString];
+ };
+ // main
+ this.storeTaskReference = (taskFileContent) => {
+ if (taskFileContent.date == void 0) return;
+ const monthString = _CalendarModel.isoToMonthString(
+ taskFileContent.date,
+ );
+ const monthPath = this.getMonthPath(monthString);
+ const referencePath = [...monthPath, taskFileContent.fileId];
+ this.storageModel.write(referencePath, "");
+ };
+ this.deleteTaskReference = (monthString, taskId) => {
+ const monthPath = this.getMonthPath(monthString);
+ const referencePath = [...monthPath, taskId];
+ this.storageModel.write(referencePath, "");
+ };
+ this.listTaskIds = (monthString) => {
+ const monthPath = this.getMonthPath(monthString);
+ return this.storageModel.list(monthPath);
+ };
+ this.generateMonthGrid = (year, month, defaultValueCreator) => {
+ const date = /* @__PURE__ */ new Date();
+ const isCurrentMonth =
+ year == date.getFullYear() && month == date.getMonth() + 1;
+ date.setDate(1);
+ date.setMonth(month - 1);
+ date.setFullYear(year);
+ const firstWeekdayOfMonth = date.getDay();
+ const firstDayOfWeekSetting = parseInt(
+ this.settingsModel.firstDayOfWeek,
+ );
+ const offset =
+ firstWeekdayOfMonth < firstDayOfWeekSetting
+ ? 7 - firstDayOfWeekSetting
+ : firstWeekdayOfMonth - firstDayOfWeekSetting;
+ date.setMonth(month);
+ date.setDate(-1);
+ const daysInMonth = date.getDate() + 1;
+ const grid = {
+ offset,
+ firstDayOfWeek: parseInt(this.settingsModel.firstDayOfWeek),
+ isCurrentMonth,
+ year,
+ month,
+ days: {},
+ };
+ for (let i = 0; i < daysInMonth; i++) {
+ const paddedDate = _CalendarModel.padDateOrMonth(
+ (i + 1).toString(),
+ );
+ grid.days[paddedDate] = defaultValueCreator();
+ }
+ return grid;
+ };
+ this.storageModel = storageModel2;
+ this.settingsModel = settingsModel2;
+ this.fileModel = fileModel;
+ }
+ static {
+ // utility
+ this.isoToMonthString = (dateISOString) => {
+ const [year, month, _] = dateISOString.split("-");
+ return _CalendarModel.getMonthString(year, month);
+ };
+ }
+ static {
+ this.isoToDateString = (dateISOString) => {
+ const [year, month, date, _] = dateISOString.split("-");
+ const paddedDate = _CalendarModel.padDateOrMonth(date ?? "");
+ return paddedDate;
+ };
+ }
+ static {
+ this.getMonthString = (year = "", month = "") => {
+ const paddedYear = year.padStart(4, "0");
+ const paddedMonth = _CalendarModel.padDateOrMonth(month);
+ return `${paddedYear}-${paddedMonth}`;
+ };
+ }
+ static {
+ this.getISODateString = (year, month, date) => {
+ const monthString = _CalendarModel.getMonthString(year, month);
+ const paddedDate = _CalendarModel.padDateOrMonth(date);
+ return `${monthString}-${paddedDate}`;
+ };
+ }
+ static {
+ this.padDateOrMonth = (input) => {
+ return input.padStart(2, "0");
+ };
+ }
+ };
+
+ // src/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/Files/boardsAndTasksModel.ts
+ var BoardsAndTasksModel = class _BoardsAndTasksModel {
+ // init
+ constructor(storageModel2, settingsModel2, chatModel, fileModel) {
+ // data
+ this.boardHandlerManager = new HandlerManager();
+ this.taskHandlerManager = new HandlerManager();
+ // paths
+ this.getBasePath = () => {
+ return this.fileModel.getModelContainerPath(
+ "tasks" /* ModelTask */,
+ );
+ };
+ this.getViewPath = () => {
+ return [...this.getBasePath(), "view" /* ModelView */];
+ };
+ this.getBoardFilePath = (boardId) => {
+ return [...this.fileModel.getFilePath(boardId)];
+ };
+ this.getTaskFilePath = (taskId) => {
+ return [...this.fileModel.getFilePath(taskId)];
+ };
+ this.getBoardContainerPath = () => {
+ return [...this.getBasePath(), "boards" /* Boards */];
+ };
+ this.getBoardDirectoryPath = (boardId) => {
+ return [...this.getBoardContainerPath(), boardId];
+ };
+ this.getTaskContainerPath = (boardId) => {
+ return [
+ ...this.getBoardDirectoryPath(boardId),
+ "tasks" /* BoardTasks */,
+ ];
+ };
+ this.getTaskReferencePath = (boardId, fileId) => {
+ return [...this.getTaskContainerPath(boardId), fileId];
+ };
+ // handlers
+ this.handleFileContent = (fileContent) => {
+ if (
+ checkMatchesObjectStructure(
+ fileContent,
+ BoardInfoFileContentReference,
+ ) == true
+ ) {
+ this.handleBoard(fileContent);
+ } else if (
+ checkMatchesObjectStructure(
+ fileContent,
+ TaskFileContentReference,
+ ) == true
+ ) {
+ this.handleTask(fileContent);
+ }
+ };
+ this.handleBoard = (boardInfoFileContent) => {
+ this.updateBoard(boardInfoFileContent);
+ };
+ this.handleTask = (taskFileContent) => {
+ this.updateTask(taskFileContent);
+ };
+ // boards
+ this.createBoard = (name) => {
+ const boardInfoFileContent =
+ _BoardsAndTasksModel.createBoardInfoFileContent(
+ v4_default(),
+ name,
+ "standard" /* Standard */,
+ );
+ return boardInfoFileContent;
+ };
+ this.updateBoard = (boardInfoFileContent) => {
+ this.storeBoard(boardInfoFileContent);
+ this.boardHandlerManager.trigger(boardInfoFileContent);
+ };
+ this.updateBoardAndSend = (boardInfoFileContent) => {
+ this.updateBoard(boardInfoFileContent);
+ this.chatModel.sendMessage("", boardInfoFileContent);
+ };
+ this.storeBoard = (boardInfoFileContent) => {
+ this.fileModel.storeFileContent(boardInfoFileContent);
+ const boardDirectoryPath = this.getBoardDirectoryPath(
+ boardInfoFileContent.fileId,
+ );
+ this.storageModel.write(boardDirectoryPath, "");
+ };
+ this.deleteBoard = (boardId) => {
+ const boardFilePath = this.getBoardFilePath(boardId);
+ const boardDirectoryPath = this.getBoardDirectoryPath(boardId);
+ this.storageModel.removeRecursively(boardFilePath);
+ this.storageModel.removeRecursively(boardDirectoryPath);
+ };
+ this.listBoardIds = () => {
+ const boardContainerPath = this.getBoardContainerPath();
+ const boardIds = this.storageModel.list(boardContainerPath);
+ return boardIds;
+ };
+ this.getBoardInfo = (fileId) => {
+ const boardInfoFileContentOrNull =
+ this.fileModel.getLatestFileContent(
+ fileId,
+ BoardInfoFileContentReference,
+ );
+ return boardInfoFileContentOrNull;
+ };
+ this.getBoardName = (boardId) => {
+ const boardInfo = this.getBoardInfo(boardId);
+ if (boardInfo == null) return "";
+ return boardInfo.name;
+ };
+ //tasks
+ this.createTask = (boardId) => {
+ const taskFileContent =
+ _BoardsAndTasksModel.createTaskFileContent(
+ v4_default(),
+ "",
+ boardId,
+ );
+ return taskFileContent;
+ };
+ this.updateTask = (taskFileContent) => {
+ this.storeTask(taskFileContent);
+ this.taskHandlerManager.trigger(taskFileContent);
+ };
+ this.updateTaskAndSend = (taskFileContent) => {
+ this.updateTask(taskFileContent);
+ this.chatModel.sendMessage("", taskFileContent);
+ };
+ this.storeTask = (taskFileContent) => {
+ this.fileModel.storeFileContent(taskFileContent);
+ const taskReferencePath = this.getTaskReferencePath(
+ taskFileContent.boardId,
+ taskFileContent.fileId,
+ );
+ this.storageModel.write(taskReferencePath, "");
+ this.calendarModel.storeTaskReference(taskFileContent);
+ };
+ this.listTaskIds = (boardId) => {
+ const taskContainerPath = this.getTaskContainerPath(boardId);
+ const fileIds = this.storageModel.list(taskContainerPath);
+ return fileIds;
+ };
+ this.listTaskVersionIds = (taskId) => {
+ const versionIds = this.fileModel.listFileContentIds(taskId);
+ return versionIds;
+ };
+ this.getLatestTaskFileContent = (taskId) => {
+ const taskFileContentOrNull =
+ this.fileModel.getLatestFileContent(
+ taskId,
+ TaskFileContentReference,
+ );
+ return taskFileContentOrNull;
+ };
+ this.getSpecificTaskFileContent = (taskId, versionId) => {
+ const taskFileContentOrNull = this.fileModel.getFileContent(
+ taskId,
+ versionId,
+ TaskFileContentReference,
+ );
+ return taskFileContentOrNull;
+ };
+ this.deleteTask = (boardId, taskId) => {
+ const taskFilePath = this.getTaskFilePath(taskId);
+ this.storageModel.removeRecursively(taskFilePath);
+ this.deleteTaskReference(boardId, taskId);
+ };
+ this.deleteTaskReference = (boardId, taskId) => {
+ const taskReferencePath = this.getTaskReferencePath(
+ boardId,
+ taskId,
+ );
+ this.storageModel.removeRecursively(taskReferencePath);
+ };
+ this.storageModel = storageModel2;
+ this.settingsModel = settingsModel2;
+ this.chatModel = chatModel;
+ this.fileModel = fileModel;
+ this.calendarModel = new CalendarModel(
+ this.storageModel,
+ this.settingsModel,
+ this.fileModel,
+ );
+ }
+ static {
+ // utility
+ this.createBoardInfoFileContent = (fileId, name, color) => {
+ const fileContent = FileModel2.createFileContent(
+ fileId,
+ "board-info",
+ );
+ return {
+ ...fileContent,
+ name,
+ color,
+ };
+ };
+ }
+ static {
+ this.createTaskFileContent = (fileId, name, boardId) => {
+ const fileContent = FileModel2.createFileContent(
+ fileId,
+ "task",
+ );
+ return {
+ ...fileContent,
+ name,
+ boardId,
+ };
+ };
+ }
+ };
+ var BoardInfoFileContentReference = {
+ dataVersion: DATA_VERSION,
+ fileId: "string",
+ fileContentId: "",
+ creationDate: "",
+ type: "board-info",
+ name: "",
+ color: "",
+ };
+ var TaskFileContentReference = {
+ dataVersion: DATA_VERSION,
+ fileId: "string",
+ fileContentId: "",
+ creationDate: "",
+ type: "task",
+ name: "",
+ boardId: "",
+ };
+
+ // src/Model/Files/fileModel.ts
+ var FileModel2 = class _FileModel {
+ // init
+ constructor(storageModel2, settingsModel2, chatModel) {
+ // paths
+ this.getBasePath = () => {
+ return StorageModel.getPath(
+ "chat" /* Chat */,
+ filePaths.chat.files(this.chatModel.id),
+ );
+ };
+ this.getFileContainerPath = () => {
+ return [...this.getBasePath(), "data" /* Data */];
+ };
+ this.getModelContainerPath = (modelName) => {
+ return [...this.getBasePath(), "model" /* Model */, modelName];
+ };
+ this.getFilePath = (fileId) => {
+ return [...this.getFileContainerPath(), fileId];
+ };
+ this.getFileContentPath = (fileId, fileContentId) => {
+ const filePath = this.getFilePath(fileId);
+ return [...filePath, fileContentId];
+ };
+ // handlers
+ this.handleStringifiedFileContent = (stringifiedFileContent) => {
+ const fileContent = parseValidObject(
+ stringifiedFileContent,
+ FileContentReference,
+ );
+ if (fileContent == null) return;
+ this.handleFileContent(fileContent);
+ };
+ this.handleFileContent = (fileContent) => {
+ const didStore = this.storeFileContent(fileContent);
+ if (didStore == false) return;
+ this.boardsAndTasksModel.handleFileContent(fileContent);
+ };
+ // methods
+ this.addFileContentAndSend = (fileContent) => {
+ this.handleFileContent(fileContent);
+ this.chatModel.sendMessage("", fileContent);
+ };
+ // storage
+ this.storeFileContent = (fileContent) => {
+ const fileContentPath = this.getFileContentPath(
+ fileContent.fileId,
+ fileContent.fileContentId,
+ );
+ const existingFileContent =
+ this.storageModel.read(fileContentPath);
+ if (existingFileContent != null) return false;
+ const stringifiedContent = stringify(fileContent);
+ this.storageModel.write(fileContentPath, stringifiedContent);
+ return true;
+ };
+ this.listFileIds = () => {
+ return this.storageModel.list(this.getBasePath());
+ };
+ this.listFileContentIds = (fileId) => {
+ const filePath = this.getFilePath(fileId);
+ return this.storageModel.list(filePath);
+ };
+ this.selectLatestFileContentId = (fileContentIds) => {
+ return fileContentIds[fileContentIds.length - 1];
+ };
+ this.getFileContent = (fileId, fileContentName, reference) => {
+ const filePath = this.getFileContentPath(
+ fileId,
+ fileContentName,
+ );
+ const fileContentOrNull = this.storageModel.readStringifiable(
+ filePath,
+ reference,
+ );
+ return fileContentOrNull;
+ };
+ this.getLatestFileContent = (fileId, reference) => {
+ const fileContentsIds = this.listFileContentIds(fileId);
+ const latestFileContentId =
+ this.selectLatestFileContentId(fileContentsIds);
+ if (latestFileContentId == void 0) return null;
+ const fileContent = this.getFileContent(
+ fileId,
+ latestFileContentId,
+ reference,
+ );
+ return fileContent;
+ };
+ this.chatModel = chatModel;
+ this.settingsModel = settingsModel2;
+ this.storageModel = storageModel2;
+ this.boardsAndTasksModel = new BoardsAndTasksModel(
+ this.storageModel,
+ this.settingsModel,
+ chatModel,
+ this,
+ );
+ }
+ static {
+ // utility
+ this.generateFileContentId = (creationDate) => {
+ return creationDate + v4_default();
+ };
+ }
+ static {
+ this.createFileContent = (fileId, type) => {
+ const creationDate = createTimestamp();
+ const fileContentId =
+ _FileModel.generateFileContentId(creationDate);
+ return {
+ dataVersion: DATA_VERSION,
+ fileId,
+ fileContentId,
+ creationDate,
+ type,
+ };
+ };
+ }
+ };
+ var FileContentReference = {
+ dataVersion: DATA_VERSION,
+ fileId: "",
+ fileContentId: "",
+ creationDate: "",
+ type: "",
+ };
+
+ // src/Model/Utility/crypto.ts
+ var IV_SIZE = 12;
+ var ENCRYPTION_ALG = "AES-GCM";
+ async function encryptString(plaintext, passphrase) {
+ if (!window.crypto.subtle) return plaintext;
+ const iv = generateIV();
+ const key = await importKey(passphrase, "encrypt");
+ const encryptedArray = await encrypt(iv, key, plaintext);
+ const encryptionData = {
+ iv: uInt8ToArray(iv),
+ encryptedArray: uInt8ToArray(encryptedArray),
+ };
+ return btoa(JSON.stringify(encryptionData));
+ }
+ async function decryptString(cyphertext, passphrase) {
+ try {
+ const encrypionData = JSON.parse(atob(cyphertext));
+ const iv = arrayToUint8(encrypionData.iv);
+ const encryptedArray = arrayToUint8(encrypionData.encryptedArray);
+ const key = await importKey(passphrase, "decrypt");
+ return await decrypt(iv, key, encryptedArray);
+ } catch {
+ return cyphertext;
+ }
+ }
+ function encode(string) {
+ return new TextEncoder().encode(string);
+ }
+ function decode(array) {
+ return new TextDecoder("utf-8").decode(array);
+ }
+ async function encrypt(iv, key, message) {
+ const arrayBuffer = await window.crypto.subtle.encrypt(
+ { name: ENCRYPTION_ALG, iv },
+ key,
+ encode(message),
+ );
+ return new Uint8Array(arrayBuffer);
+ }
+ async function decrypt(iv, key, cyphertext) {
+ const arrayBuffer = await crypto.subtle.decrypt(
+ { name: ENCRYPTION_ALG, iv },
+ key,
+ cyphertext,
+ );
+ return arrayBufferToString(arrayBuffer);
+ }
+ async function hash(encoded) {
+ return await crypto.subtle.digest("SHA-256", encoded);
+ }
+ function generateIV() {
+ return crypto.getRandomValues(new Uint8Array(IV_SIZE));
+ }
+ async function importKey(passphrase, purpose) {
+ return await crypto.subtle.importKey(
+ "raw",
+ await hash(encode(passphrase)),
+ { name: ENCRYPTION_ALG },
+ false,
+ [purpose],
+ );
+ }
+ function arrayBufferToString(arrayBuffer) {
+ const uInt8Array = new Uint8Array(arrayBuffer);
+ return decode(uInt8Array);
+ }
+ function uInt8ToArray(uInt8Array) {
+ return Array.from(uInt8Array);
+ }
+ function arrayToUint8(array) {
+ return new Uint8Array(array);
+ }
+
+ // src/Model/Chat/chatModel.ts
+ var ChatModel = class _ChatModel {
+ // init
+ constructor(
+ storageModel2,
+ connectionModel2,
+ settingsModel2,
+ chatListModel2,
+ chatId,
+ ) {
+ this.chatMessageHandlerManager = new HandlerManager();
+ // paths
+ this.getBasePath = () => {
+ return StorageModel.getPath(
+ "chat" /* Chat */,
+ filePaths.chat.chatBase(this.id),
+ );
+ };
+ this.getInfoPath = () => {
+ return StorageModel.getPath(
+ "chat" /* Chat */,
+ filePaths.chat.info(this.id),
+ );
+ };
+ this.getColorPath = () => {
+ return StorageModel.getPath(
+ "chat" /* Chat */,
+ filePaths.chat.color(this.id),
+ );
+ };
+ this.getMessageDirPath = () => {
+ return StorageModel.getPath(
+ "chat" /* Chat */,
+ filePaths.chat.messages(this.id),
+ );
+ };
+ this.getMessagePath = (id) => {
+ return [...this.getMessageDirPath(), id];
+ };
+ // handlers
+ this.handleMessage = (body) => {
+ const chatMessage = parseValidObject(
+ body,
+ ChatMessageReference,
+ );
+ if (chatMessage == null) return;
+ chatMessage.status = "received" /* Received */;
+ this.addMessage(chatMessage);
+ this.info.hasUnreadMessages = true;
+ this.storeInfo();
+ };
+ this.handleMessageSent = (chatMessage) => {
+ chatMessage.status = "sent" /* Sent */;
+ this.addMessage(chatMessage);
+ };
+ // settings
+ this.setPrimaryChannel = (primaryChannel) => {
+ this.info.primaryChannel = primaryChannel;
+ this.storeInfo();
+ this.subscribe();
+ };
+ this.setSecondaryChannels = (secondaryChannels) => {
+ this.info.secondaryChannels = secondaryChannels;
+ this.storeInfo();
+ };
+ this.setEncryptionKey = (key) => {
+ this.info.encryptionKey = key;
+ this.storeInfo();
+ };
+ this.setColor = (color) => {
+ this.color = color;
+ this.storeColor();
+ };
+ // messaging
+ this.addMessage = async (chatMessage) => {
+ await this.decryptMessage(chatMessage);
+ if (chatMessage.body != "") {
+ const messagePath = this.getMessagePath(chatMessage.id);
+ this.storageModel.writeStringifiable(
+ messagePath,
+ chatMessage,
+ );
+ this.chatMessageHandlerManager.trigger(chatMessage);
+ }
+ this.fileModel.handleStringifiedFileContent(
+ chatMessage.stringifiedFile,
+ );
+ };
+ this.sendMessage = async (body, fileContent) => {
+ const senderName = this.settingsModel.username;
+ if (senderName == "") return false;
+ const allChannels = [this.info.primaryChannel];
+ for (const secondaryChannel of this.info.secondaryChannels) {
+ allChannels.push(secondaryChannel);
+ }
+ const combinedChannel = allChannels.join("/");
+ const chatMessage = await _ChatModel.createChatMessage(
+ combinedChannel,
+ senderName,
+ this.info.encryptionKey,
+ body,
+ fileContent,
+ );
+ this.addMessage(chatMessage);
+ this.connectionModel.sendMessageOrStore(chatMessage);
+ return true;
+ };
+ this.decryptMessage = async (chatMessage) => {
+ const decryptedBody = await decryptString(
+ chatMessage.body,
+ this.info.encryptionKey,
+ );
+ const decryptedFile = await decryptString(
+ chatMessage.stringifiedFile ?? "",
+ this.info.encryptionKey,
+ );
+ chatMessage.body = decryptedBody;
+ chatMessage.stringifiedFile = decryptedFile;
+ };
+ this.subscribe = () => {
+ this.connectionModel.addChannel(this.info.primaryChannel);
+ };
+ this.markRead = () => {
+ this.info.hasUnreadMessages = false;
+ this.storeInfo();
+ };
+ // storage
+ this.storeInfo = () => {
+ this.storageModel.writeStringifiable(
+ this.getInfoPath(),
+ this.info,
+ );
+ };
+ this.storeColor = () => {
+ this.storageModel.write(this.getColorPath(), this.color);
+ };
+ this.delete = () => {
+ this.chatListModel.untrackChat(this);
+ const dirPath = this.getBasePath();
+ this.storageModel.removeRecursively(dirPath);
+ };
+ // load
+ this.loadInfo = () => {
+ const info = this.storageModel.readStringifiable(
+ this.getInfoPath(),
+ ChatInfoReference,
+ );
+ if (info != null) {
+ this.info = info;
+ } else {
+ this.info = _ChatModel.generateChatInfo("0");
+ }
+ };
+ this.loadColor = () => {
+ const path = this.getColorPath();
+ const color = this.storageModel.read(path);
+ if (!color) {
+ this.color = "standard" /* Standard */;
+ } else {
+ this.color = color;
+ }
+ };
+ this.id = chatId;
+ this.connectionModel = connectionModel2;
+ this.settingsModel = settingsModel2;
+ this.storageModel = storageModel2;
+ this.chatListModel = chatListModel2;
+ this.loadInfo();
+ this.loadColor();
+ this.subscribe();
+ this.fileModel = new FileModel2(
+ this.storageModel,
+ this.settingsModel,
+ this,
+ );
+ }
+ get secondaryChannels() {
+ return this.info.secondaryChannels.sort(localeCompare);
+ }
+ get messages() {
+ const messageIds = this.storageModel.list(this.getMessageDirPath());
+ if (!Array.isArray(messageIds)) return [];
+ const chatMessages = [];
+ for (const messageId of messageIds) {
+ const messagePath = this.getMessagePath(messageId);
+ const chatMessage = this.storageModel.readStringifiable(
+ messagePath,
+ ChatMessageReference,
+ );
+ chatMessages.push(chatMessage);
+ }
+ const sorted = chatMessages.sort((a, b) =>
+ a.dateSent.localeCompare(b.dateSent),
+ );
+ return sorted;
+ }
+ static {
+ // utility
+ this.generateChatInfo = (primaryChannel) => {
+ return {
+ dataVersion: DATA_VERSION,
+ primaryChannel,
+ secondaryChannels: [],
+ encryptionKey: "",
+ hasUnreadMessages: false,
+ };
+ };
+ }
+ static {
+ this.createChatMessage = async (
+ channel,
+ sender,
+ encryptionKey,
+ body,
+ fileContent,
+ ) => {
+ const chatMessage = {
+ dataVersion: DATA_VERSION,
+ id: v4_default(),
+ channel,
+ sender,
+ body,
+ dateSent: createTimestamp(),
+ status: "outbox" /* Outbox */,
+ stringifiedFile: "",
+ };
+ if (fileContent != void 0) {
+ const stringifiedFile = stringify(fileContent);
+ chatMessage.stringifiedFile = stringifiedFile;
+ }
+ if (encryptionKey != "") {
+ chatMessage.body = await encryptString(
+ chatMessage.body,
+ encryptionKey,
+ );
+ chatMessage.stringifiedFile = await encryptString(
+ chatMessage.stringifiedFile,
+ encryptionKey,
+ );
+ }
+ return chatMessage;
+ };
+ }
+ };
+ var ChatInfoReference = {
+ dataVersion: DATA_VERSION,
+ primaryChannel: "",
+ secondaryChannels: [""],
+ encryptionKey: "",
+ hasUnreadMessages: true,
+ };
+ var ChatMessageReference = {
+ dataVersion: DATA_VERSION,
+ id: "",
+ channel: "",
+ sender: "",
+ body: "",
+ dateSent: "",
+ status: "",
+ stringifiedFile: "",
+ };
+
+ // src/Model/Chat/chatListModel.ts
+ var ChatListModel = class {
+ // init
+ constructor(storageModel2, settingsModel2, connectionModel2) {
+ // data
+ this.chatModels = /* @__PURE__ */ new Set();
+ // handlers
+ this.messageHandler = (data) => {
+ const channel = data.messageChannel;
+ const body = data.messageBody;
+ if (channel == void 0) return;
+ if (body == void 0) return;
+ this.routeMessageToCorrectChatModel(channel, (chatModel) =>
+ chatModel.handleMessage(body),
+ );
+ };
+ this.messageSentHandler = (chatMessage) => {
+ const channel = chatMessage.channel;
+ this.routeMessageToCorrectChatModel(channel, (chatModel) =>
+ chatModel.handleMessageSent(chatMessage),
+ );
+ };
+ // methods
+ this.routeMessageToCorrectChatModel = (channel, fn) => {
+ const allChannels = channel.split("/");
+ for (const chatModel of this.chatModels) {
+ for (const channel2 of allChannels) {
+ if (channel2 != chatModel.info.primaryChannel) continue;
+ fn(chatModel);
+ break;
+ }
+ }
+ };
+ // storage
+ this.addChatModel = (chatModel) => {
+ this.chatModels.add(chatModel);
+ };
+ this.createChat = (primaryChannel) => {
+ const id = v4_default();
+ const chatModel = new ChatModel(
+ this.storageModel,
+ this.connectionModel,
+ this.settingsModel,
+ this,
+ id,
+ );
+ chatModel.setPrimaryChannel(primaryChannel);
+ this.addChatModel(chatModel);
+ return chatModel;
+ };
+ this.untrackChat = (chat) => {
+ this.chatModels.delete(chat);
+ };
+ // load
+ this.loadChats = () => {
+ const chatDir = StorageModel.getPath(
+ "chat" /* Chat */,
+ filePaths.chat.base,
+ );
+ const chatIds = this.storageModel.list(chatDir);
+ for (const chatId of chatIds) {
+ const chatModel = new ChatModel(
+ this.storageModel,
+ this.connectionModel,
+ this.settingsModel,
+ this,
+ chatId,
+ );
+ this.addChatModel(chatModel);
+ }
+ };
+ this.storageModel = storageModel2;
+ this.settingsModel = settingsModel2;
+ this.connectionModel = connectionModel2;
+ this.loadChats();
+ connectionModel2.messageHandlerManager.addHandler(
+ this.messageHandler,
+ );
+ connectionModel2.messageSentHandlerManager.addHandler(
+ this.messageSentHandler,
+ );
+ }
+ };
+
+ // src/View/utility.ts
+ function allowDrop(event) {
+ event.preventDefault();
+ }
+ function allowDrag(event) {
+ event.dataTransfer?.setData("text", "");
+ }
+ function reload() {
+ window.location.reload();
+ }
+
+ // src/ViewModel/Pages/taskViewModel.ts
+ var TaskViewModel = class {
+ // init
+ constructor(
+ coreViewModel,
+ chatViewModel,
+ boardsAndTasksModel,
+ containingModel,
+ taskFileContent,
+ ) {
+ this.coreViewModel = coreViewModel;
+ this.chatViewModel = chatViewModel;
+ // paths
+ this.getFilePath = () => {
+ return this.boardsAndTasksModel.getTaskFilePath(
+ this.task.fileId,
+ );
+ };
+ // state
+ this.index = new State(0);
+ this.boardId = new State("");
+ this.name = new State("");
+ this.description = new State("");
+ this.category = new State("");
+ this.status = new State("");
+ this.priority = new State("");
+ this.date = new State("");
+ this.time = new State("");
+ this.selectedVersionId = new State("");
+ this.versionIds = new ListState();
+ // methods
+ this.dragStart = (event) => {
+ allowDrag(event);
+ this.coreViewModel.draggedObject.value = this;
+ };
+ this.setCategoryAndStatus = (category, status) => {
+ if (category != void 0) this.category.value = category;
+ if (status != void 0) this.status.value = status;
+ this.save();
+ };
+ this.setBoardId = (boardId) => {
+ this.boardId.value = boardId;
+ this.save();
+ };
+ this.setDate = (dateISOString) => {
+ this.date.value = dateISOString;
+ this.save();
+ };
+ // view
+ this.open = () => {
+ this.containingModel.selectTask(this);
+ };
+ this.close = () => {
+ this.containingModel.closeTask();
+ };
+ this.closeAndDiscard = () => {
+ this.close();
+ this.loadTaskData();
+ };
+ this.closeAndSave = () => {
+ this.close();
+ this.save();
+ };
+ this.updateIndex = () => {
+ const index =
+ this.containingModel.taskIndexManager.getIndex(this);
+ this.index.value = index;
+ };
+ this.updateSuggestions = () => {
+ if (
+ this.coreViewModel.taskCategorySuggestions.value.has(
+ this.category.value,
+ ) == false
+ ) {
+ this.coreViewModel.taskCategorySuggestions.add(
+ this.category.value,
+ );
+ }
+ if (
+ this.coreViewModel.taskStatusSuggestions.value.has(
+ this.status.value,
+ ) == false
+ ) {
+ this.coreViewModel.taskStatusSuggestions.add(
+ this.status.value,
+ );
+ }
+ };
+ // settings
+ this.save = () => {
+ const newTaskFileContent =
+ BoardsAndTasksModel.createTaskFileContent(
+ this.task.fileId,
+ this.name.value,
+ this.task.boardId,
+ );
+ newTaskFileContent.boardId = this.boardId.value;
+ newTaskFileContent.description = this.description.value;
+ newTaskFileContent.status = this.status.value;
+ newTaskFileContent.category = this.category.value;
+ newTaskFileContent.priority = this.priority.value;
+ newTaskFileContent.date = this.date.value;
+ newTaskFileContent.time = this.time.value;
+ this.boardsAndTasksModel.updateTaskAndSend(newTaskFileContent);
+ this.containingModel.showTask(newTaskFileContent);
+ this.containingModel.updateTaskIndices();
+ this.updateSuggestions();
+ };
+ this.deleteTask = () => {
+ this.close();
+ this.boardsAndTasksModel.deleteTask(
+ this.task.boardId,
+ this.task.fileId,
+ );
+ this.containingModel.removeTaskFromView(this.task);
+ };
+ // load
+ this.loadVersionIds = () => {
+ const versionIds = this.boardsAndTasksModel.listTaskVersionIds(
+ this.task.fileId,
+ );
+ const sortedVersionIds = versionIds
+ .sort(localeCompare)
+ .reverse();
+ this.versionIds.clear();
+ this.versionIds.add(...sortedVersionIds);
+ };
+ this.switchVersion = (versionId) => {
+ const taskFileContent =
+ this.boardsAndTasksModel.getSpecificTaskFileContent(
+ this.task.fileId,
+ versionId,
+ );
+ if (taskFileContent == null) return;
+ this.task = taskFileContent;
+ this.loadTaskData();
+ };
+ this.loadAllData = () => {
+ this.loadTaskData();
+ this.loadVersionIds();
+ };
+ this.loadTaskData = () => {
+ this.boardId.value = this.task.boardId;
+ this.name.value = this.task.name;
+ this.description.value = this.task.description ?? "";
+ this.category.value = this.task.category ?? "";
+ this.status.value = this.task.status ?? "";
+ this.priority.value = this.task.priority ?? "";
+ this.date.value = this.task.date ?? "";
+ this.time.value = this.task.time ?? "";
+ this.selectedVersionId.value = this.task.fileContentId;
+ this.updateSuggestions();
+ };
+ this.boardsAndTasksModel = boardsAndTasksModel;
+ this.containingModel = containingModel;
+ this.task = taskFileContent;
+ this.loadAllData();
+ this.selectedVersionId.subscribeSilent((selectedVersionId) => {
+ this.switchVersion(selectedVersionId);
+ });
+ }
+ get sortingString() {
+ const splitDate = this.date.value.split("-");
+ const year = padZero(splitDate[0], 4);
+ const month = padZero(splitDate[1], 2);
+ const date = padZero(splitDate[2], 2);
+ const splitTime = this.time.value.split(":");
+ const hour = padZero(splitTime[0], 2);
+ const minute = padZero(splitTime[1], 2);
+ const priorityNumber = parseInt(this.priority.value);
+ const invertedPriority = 100 - priorityNumber;
+ return (
+ year +
+ month +
+ date +
+ hour +
+ minute +
+ invertedPriority +
+ this.name.value
+ );
+ }
+ static {
+ // utility
+ this.getStringsForFilter = (taskViewModel) => {
+ return [
+ taskViewModel.task.name,
+ taskViewModel.task.description ?? "",
+ taskViewModel.task.category ?? "",
+ taskViewModel.task.status ?? "",
+ taskViewModel.task.priority ?? "",
+ taskViewModel.task.date ?? "",
+ taskViewModel.task.time ?? "",
+ ];
+ };
+ }
+ };
+
+ // src/ViewModel/Pages/taskContainingPageViewModel.ts
+ var TaskContainingPageViewModel = class {
+ // init
+ constructor(coreViewModel, chatViewModel, boardsAndTasksModel) {
+ this.coreViewModel = coreViewModel;
+ this.chatViewModel = chatViewModel;
+ // state
+ this.taskIndexManager = new IndexManager(
+ (taskViewModel) => taskViewModel.sortingString,
+ );
+ this.selectedTaskViewModel = new State(void 0);
+ this.taskViewModels = new MapState();
+ // methods
+ this.createTaskFromBoardId = (boardId) => {
+ const taskFileContent =
+ this.boardsAndTasksModel.createTask(boardId);
+ const taskViewModel = new TaskViewModel(
+ this.coreViewModel,
+ this.chatViewModel,
+ this.boardsAndTasksModel,
+ this,
+ taskFileContent,
+ );
+ this.selectTask(taskViewModel);
+ this.updateTaskIndices();
+ };
+ // view
+ this.showTask = (taskFileContent) => {};
+ this.removeTaskFromView = (taskFileContent) => {};
+ this.selectTask = (selectedTask) => {
+ this.selectedTaskViewModel.value = selectedTask;
+ };
+ this.closeTask = () => {
+ this.selectedTaskViewModel.value = void 0;
+ };
+ this.updateTaskIndices = () => {
+ this.taskIndexManager.update([
+ ...this.taskViewModels.value.values(),
+ ]);
+ for (const boardViewModel of this.taskViewModels.value.values()) {
+ boardViewModel.updateIndex();
+ }
+ };
+ this.boardsAndTasksModel = boardsAndTasksModel;
+ }
+ };
+
+ // src/ViewModel/Pages/calendarPageViewModel.ts
+ var CALENDAR_EVENT_BOARD_ID = "events";
+ var CalendarPageViewModel = class extends TaskContainingPageViewModel {
+ // init
+ constructor(
+ coreViewModel,
+ chatViewModel,
+ storageModel2,
+ calendarModel,
+ boardsAndTasksModel,
+ ) {
+ super(coreViewModel, chatViewModel, boardsAndTasksModel);
+ this.coreViewModel = coreViewModel;
+ this.chatViewModel = chatViewModel;
+ // paths
+ this.getBasePath = () => {
+ return [...this.calendarModel.getViewPath()];
+ };
+ // state
+ this.selectedYear = new State(0);
+ this.selectedMonth = new State(0);
+ this.selectedDate = new State(0);
+ this.monthGrid = new State(void 0);
+ // methods
+ this.createEvent = () => {
+ const taskFileContent = this.boardsAndTasksModel.createTask(
+ CALENDAR_EVENT_BOARD_ID,
+ );
+ taskFileContent.date = CalendarModel.getISODateString(
+ this.selectedYear.value.toString(),
+ this.selectedMonth.value.toString(),
+ this.selectedDate.value.toString(),
+ );
+ const taskViewModel = new TaskViewModel(
+ this.coreViewModel,
+ this.chatViewModel,
+ this.boardsAndTasksModel,
+ this,
+ taskFileContent,
+ );
+ this.selectTask(taskViewModel);
+ this.updateTaskIndices();
+ };
+ this.getEventsForDate = () => {
+ const paddedDate = CalendarModel.padDateOrMonth(
+ this.selectedDate.toString(),
+ );
+ if (this.monthGrid.value == void 0) {
+ return void 0;
+ }
+ return this.monthGrid.value.days[paddedDate];
+ };
+ // view
+ this.getTaskMapState = (taskFileContent) => {
+ if (this.monthGrid.value == null) return null;
+ const date = CalendarModel.isoToDateString(
+ taskFileContent.date ?? "",
+ );
+ return this.monthGrid.value.days[date];
+ };
+ this.showTask = (taskFileContent) => {
+ const monthString = CalendarModel.isoToMonthString(
+ taskFileContent.date ?? "",
+ );
+ if (monthString == void 0 || monthString != this.monthString) {
+ this.removeTaskFromView(taskFileContent);
+ this.calendarModel.deleteTaskReference(
+ this.monthString,
+ taskFileContent.fileId,
+ );
+ return;
+ }
+ const taskViewModel = new TaskViewModel(
+ this.coreViewModel,
+ this.chatViewModel,
+ this.boardsAndTasksModel,
+ this,
+ taskFileContent,
+ );
+ const mapState = this.getTaskMapState(taskFileContent);
+ this.taskViewModels.handleRemoval(taskViewModel, () => {
+ mapState?.remove(taskFileContent.fileId);
+ });
+ this.taskViewModels.remove(taskFileContent.fileId);
+ this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
+ mapState?.set(taskFileContent.fileId, taskViewModel);
+ };
+ this.removeTaskFromView = (taskFileContent) => {
+ this.taskViewModels.remove(taskFileContent.fileId);
+ };
+ this.showToday = () => {
+ const today = /* @__PURE__ */ new Date();
+ this.selectedYear.value = today.getFullYear();
+ this.selectedMonth.value = today.getMonth() + 1;
+ this.selectedDate.value = today.getDate();
+ };
+ this.showPreviousMonth = () => {
+ this.selectedMonth.value -= 1;
+ if (this.selectedMonth.value <= 0) {
+ this.selectedYear.value -= 1;
+ this.selectedMonth.value = 12;
+ }
+ };
+ this.showNextMonth = () => {
+ this.selectedMonth.value += 1;
+ if (this.selectedMonth.value >= 13) {
+ this.selectedYear.value += 1;
+ this.selectedMonth.value = 1;
+ }
+ };
+ this.handleDrop = (year, month, date) => {
+ const ISOString = CalendarModel.getISODateString(
+ year,
+ month,
+ date,
+ );
+ const draggedObject = this.coreViewModel.draggedObject.value;
+ if (draggedObject instanceof TaskViewModel == false) return;
+ draggedObject.setDate(ISOString);
+ };
+ // load
+ this.loadMonthTasks = () => {
+ this.monthGrid.value = this.calendarModel.generateMonthGrid(
+ this.selectedYear.value,
+ this.selectedMonth.value,
+ () => new MapState(),
+ );
+ const taskIds = this.calendarModel.listTaskIds(
+ this.monthString,
+ );
+ for (const taskId of taskIds) {
+ const taskFileContent =
+ this.boardsAndTasksModel.getLatestTaskFileContent(
+ taskId,
+ );
+ if (taskFileContent == null) continue;
+ this.showTask(taskFileContent);
+ }
+ this.updateTaskIndices();
+ };
+ this.loadData = () => {
+ this.loadMonthTasks();
+ this.showToday();
+ };
+ this.storageModel = storageModel2;
+ this.calendarModel = calendarModel;
+ this.boardsAndTasksModel = boardsAndTasksModel;
+ this.chatViewModel = chatViewModel;
+ bulkSubscribe([this.selectedYear, this.selectedMonth], () => {
+ this.loadMonthTasks();
+ });
+ boardsAndTasksModel.taskHandlerManager.addHandler(
+ (taskFileContent) => {
+ this.showTask(taskFileContent);
+ },
+ );
+ }
+ // data
+ get monthString() {
+ return CalendarModel.getMonthString(
+ this.selectedYear.value.toString(),
+ this.selectedMonth.value.toString(),
+ );
+ }
+ };
+
+ // src/ViewModel/Chat/chatMessageViewModel.ts
+ var ChatMessageViewModel = class {
+ // init
+ constructor(
+ coreViewModel,
+ messagePageViewModel,
+ chatMessage,
+ sentByUser,
+ ) {
+ this.coreViewModel = coreViewModel;
+ this.body = new State("");
+ this.status = new State(void 0);
+ // state
+ this.isPresentingInfoModal = new State(false);
+ // methods
+ this.copyMessage = () => {
+ navigator.clipboard.writeText(this.body.value);
+ };
+ this.resendMessage = () => {
+ this.messagePageViewModel.sendMessageFromBody(this.body.value);
+ };
+ this.decryptMessage = () => {
+ this.messagePageViewModel.decryptMessage(this);
+ };
+ // view
+ this.showInfoModal = () => {
+ this.isPresentingInfoModal.value = true;
+ };
+ this.hideInfoModal = () => {
+ this.isPresentingInfoModal.value = false;
+ };
+ // load
+ this.loadData = () => {
+ this.channel = this.chatMessage.channel;
+ this.sender = this.chatMessage.sender;
+ this.dateSent = new Date(
+ this.chatMessage.dateSent,
+ ).toLocaleString();
+ this.body.value = this.chatMessage.body;
+ this.status.value = this.chatMessage.status;
+ };
+ this.messagePageViewModel = messagePageViewModel;
+ this.chatMessage = chatMessage;
+ this.sentByUser = sentByUser;
+ this.loadData();
+ }
+ };
+
+ // src/ViewModel/Pages/messagePageViewModel.ts
+ var MessagePageViewModel = class {
+ // init
+ constructor(coreViewModel, chatViewModel) {
+ this.coreViewModel = coreViewModel;
+ // state
+ this.chatMessageViewModels = new MapState();
+ this.composingMessage = new State("");
+ // methods
+ this.sendMessage = () => {
+ if (this.cannotSendMessage.value == true) return;
+ this.sendMessageFromBody(this.composingMessage.value);
+ this.composingMessage.value = "";
+ };
+ this.sendMessageFromBody = (body) => {
+ this.chatViewModel.chatModel.sendMessage(body);
+ };
+ this.decryptMessage = async (messageViewModel) => {
+ const chatMessage = messageViewModel.chatMessage;
+ await this.chatViewModel.chatModel.decryptMessage(chatMessage);
+ this.chatViewModel.chatModel.addMessage(chatMessage);
+ messageViewModel.loadData();
+ };
+ // view
+ this.showChatMessage = (chatMessage) => {
+ const chatMessageViewModel = new ChatMessageViewModel(
+ this.coreViewModel,
+ this,
+ chatMessage,
+ chatMessage.sender ==
+ this.chatViewModel.settingsViewModel.username.value,
+ );
+ const existingChatMessageViewModel =
+ this.chatMessageViewModels.value.get(chatMessage.id);
+ if (existingChatMessageViewModel != void 0) {
+ existingChatMessageViewModel.body.value = chatMessage.body;
+ existingChatMessageViewModel.status.value =
+ chatMessage.status;
+ } else {
+ this.chatMessageViewModels.set(
+ chatMessage.id,
+ chatMessageViewModel,
+ );
+ }
+ };
+ // load
+ this.loadData = () => {
+ this.chatMessageViewModels.clear();
+ for (const chatMessage of this.chatViewModel.chatModel
+ .messages) {
+ this.showChatMessage(chatMessage);
+ }
+ };
+ this.chatViewModel = chatViewModel;
+ this.cannotSendMessage = createProxyState(
+ [
+ this.chatViewModel.settingsViewModel.username,
+ this.composingMessage,
+ ],
+ () =>
+ this.chatViewModel.settingsViewModel.username.value == "" ||
+ this.composingMessage.value == "",
+ );
+ }
+ };
+
+ // src/ViewModel/Pages/settingsPageViewModel.ts
+ var SettingsPageViewModel = class {
+ // init
+ constructor(coreViewModel, chatViewModel) {
+ this.coreViewModel = coreViewModel;
+ // state
+ this.primaryChannel = new State("");
+ this.primaryChannelInput = new State("");
+ this.secondaryChannels = new ListState();
+ this.newSecondaryChannelInput = new State("");
+ this.encryptionKeyInput = new State("");
+ this.shouldShowEncryptionKey = new State(false);
+ this.encryptionKeyInputType = createProxyState(
+ [this.shouldShowEncryptionKey],
+ () =>
+ this.shouldShowEncryptionKey.value == true
+ ? "text"
+ : "password",
+ );
+ this.color = new State("standard" /* Standard */);
+ // guards
+ this.cannotSetPrimaryChannel = createProxyState(
+ [this.primaryChannel, this.primaryChannelInput],
+ () =>
+ this.primaryChannelInput.value == "" ||
+ this.primaryChannelInput.value == this.primaryChannel.value,
+ );
+ this.cannotAddSecondaryChannel = createProxyState(
+ [this.newSecondaryChannelInput],
+ () => this.newSecondaryChannelInput.value == "",
+ );
+ // methods
+ this.setPrimaryChannel = () => {
+ this.chatViewModel.chatModel.setPrimaryChannel(
+ this.primaryChannelInput.value,
+ );
+ this.primaryChannel.value =
+ this.chatViewModel.chatModel.info.primaryChannel;
+ this.chatViewModel.chatListViewModel.updateIndices();
+ };
+ this.addSecondaryChannel = () => {
+ this.secondaryChannels.add(this.newSecondaryChannelInput.value);
+ this.newSecondaryChannelInput.value = "";
+ this.storeSecondaryChannels();
+ this.loadSecondaryChannels();
+ };
+ this.removeSecondaryChannel = (secondaryChannel) => {
+ this.secondaryChannels.remove(secondaryChannel);
+ this.storeSecondaryChannels();
+ };
+ this.storeSecondaryChannels = () => {
+ this.chatViewModel.chatModel.setSecondaryChannels([
+ ...this.secondaryChannels.value.values(),
+ ]);
+ };
+ this.setEncryptionKey = () => {
+ this.chatViewModel.chatModel.setEncryptionKey(
+ this.encryptionKeyInput.value,
+ );
+ this.encryptionKeyInput.callSubscriptions();
+ };
+ this.applyColor = (newColor) => {
+ this.chatViewModel.setColor(newColor);
+ };
+ this.remove = () => {
+ this.chatViewModel.close();
+ this.chatViewModel.chatModel.delete();
+ this.chatViewModel.chatListViewModel.untrackChat(
+ this.chatViewModel,
+ );
+ };
+ // load
+ this.loadListRelevantData = () => {
+ this.primaryChannel.value =
+ this.chatViewModel.chatModel.info.primaryChannel;
+ this.color.value = this.chatViewModel.chatModel.color;
+ };
+ this.loadData = () => {
+ this.primaryChannelInput.value =
+ this.chatViewModel.chatModel.info.primaryChannel;
+ this.loadSecondaryChannels();
+ this.encryptionKeyInput.value =
+ this.chatViewModel.chatModel.info.encryptionKey;
+ };
+ this.loadSecondaryChannels = () => {
+ this.secondaryChannels.clear();
+ for (const secondaryChannel of this.chatViewModel.chatModel
+ .secondaryChannels) {
+ this.secondaryChannels.add(secondaryChannel);
+ }
+ };
+ this.chatViewModel = chatViewModel;
+ this.loadListRelevantData();
+ this.cannotSetEncryptionKey = createProxyState(
+ [this.encryptionKeyInput],
+ () =>
+ this.encryptionKeyInput.value ==
+ this.chatViewModel.chatModel.info.encryptionKey,
+ );
+ this.color.subscribe((newColor) => {
+ this.applyColor(newColor);
+ });
+ }
+ };
+
+ // src/ViewModel/Utility/searchViewModel.ts
+ var SearchViewModel = class {
+ // init
+ constructor(
+ allObjects,
+ matchingObjects,
+ getStringsOfObject,
+ suggestions,
+ ) {
+ // state
+ this.appliedQuery = new State("");
+ this.searchInput = new State("");
+ // guards
+ this.cannotApplySearch = createProxyState(
+ [this.searchInput, this.appliedQuery],
+ () => this.searchInput.value == this.appliedQuery.value,
+ );
+ // methods
+ this.search = (searchTerm) => {
+ this.searchInput.value = searchTerm;
+ this.applySearch();
+ };
+ this.applySearch = () => {
+ this.appliedQuery.value = this.searchInput.value;
+ this.matchingObjects.clear();
+ for (const object of this.allObjects.value.values()) {
+ const doesMatch = this.checkDoesMatchSearch(object);
+ if (doesMatch == false) continue;
+ this.matchingObjects.add(object);
+ }
+ };
+ // utility
+ this.checkDoesMatchSearch = (object) => {
+ return checkDoesObjectMatchSearch(
+ this.appliedQuery.value,
+ this.getStringsOfObject,
+ object,
+ );
+ };
+ this.allObjects = allObjects;
+ this.matchingObjects = matchingObjects;
+ this.getStringsOfObject = getStringsOfObject;
+ this.suggestions = suggestions;
+ this.allObjects.handleAddition((newObject) => {
+ const doesMatch = this.checkDoesMatchSearch(newObject);
+ if (doesMatch == false) {
+ this.matchingObjects.remove(newObject);
+ } else {
+ if (this.matchingObjects.value.has(newObject)) return;
+ this.matchingObjects.add(newObject);
+ this.allObjects.handleRemoval(newObject, () => {
+ this.matchingObjects.remove(newObject);
+ });
+ }
+ });
+ }
+ };
+
+ // src/ViewModel/Pages/boardViewModel.ts
+ var BoardViewModel = class extends TaskContainingPageViewModel {
+ // init
+ constructor(
+ coreViewModel,
+ chatViewModel,
+ storageModel2,
+ boardsAndTasksModel,
+ taskPageViewModel,
+ boardInfo,
+ ) {
+ super(coreViewModel, chatViewModel, boardsAndTasksModel);
+ this.coreViewModel = coreViewModel;
+ this.chatViewModel = chatViewModel;
+ // state
+ this.name = new State("");
+ this.color = new State("standard" /* Standard */);
+ this.index = new State(0);
+ this.selectedPage = new State("list" /* List */);
+ this.isPresentingSettingsModal = new State(false);
+ this.isPresentingFilterModal = new State(false);
+ this.filteredTaskViewModels = new ListState();
+ // paths
+ this.getBasePath = () => {
+ return [
+ ...this.taskPageViewModel.getBoardViewPath(
+ this.boardInfo.fileId,
+ ),
+ ];
+ };
+ this.getLastUsedBoardPath = () => {
+ return [
+ ...this.getBasePath(),
+ "last-used-view" /* LastUsedView */,
+ ];
+ };
+ this.getPreviousSearchesPath = () => {
+ return [
+ ...this.getBasePath(),
+ "previous-searches" /* PreviousSearches */,
+ ];
+ };
+ this.getLastSearchPath = () => {
+ return [...this.getBasePath(), "last-search" /* LastSearch */];
+ };
+ // settings
+ this.saveSettings = () => {
+ const newBoardInfoFileContent =
+ BoardsAndTasksModel.createBoardInfoFileContent(
+ this.boardInfo.fileId,
+ this.name.value,
+ this.color.value,
+ );
+ this.taskPageViewModel.updateBoard(newBoardInfoFileContent);
+ };
+ this.applyColor = () => {
+ this.taskPageViewModel.chatViewModel.setDisplayedColor(
+ this.color.value,
+ );
+ };
+ this.deleteBoard = () => {
+ this.taskPageViewModel.deleteBoard(this.boardInfo);
+ this.chatViewModel.taskBoardSuggestions.remove(
+ this.boardInfo.fileId,
+ );
+ this.close();
+ };
+ // methods
+ this.createTask = () => {
+ this.createTaskFromBoardId(this.boardInfo.fileId);
+ };
+ this.handleDropWithinBoard = (category, status) => {
+ const draggedObject = this.coreViewModel.draggedObject.value;
+ if (draggedObject instanceof TaskViewModel == false) return;
+ draggedObject.setCategoryAndStatus(category, status);
+ };
+ this.handleDropBetweenBoards = () => {
+ const draggedObject = this.coreViewModel.draggedObject.value;
+ if (draggedObject instanceof TaskViewModel == false) return;
+ draggedObject.setBoardId(this.boardInfo.fileId);
+ };
+ // storage
+ this.storeLastUsedView = () => {
+ const path = this.getLastUsedBoardPath();
+ const lastUsedView = this.selectedPage.value;
+ this.storageModel.write(path, lastUsedView);
+ };
+ this.restoreLastUsedView = () => {
+ const path = this.getLastUsedBoardPath();
+ const lastUsedView = this.storageModel.read(path);
+ if (lastUsedView == null) return;
+ this.selectedPage.value = lastUsedView;
+ };
+ this.handleNewSearch = (searchTerm) => {
+ const suggestionPath = [
+ ...this.getPreviousSearchesPath(),
+ searchTerm,
+ ];
+ this.storageModel.write(suggestionPath, "");
+ if (
+ !this.coreViewModel.boardSearchSuggestions.value.has(
+ searchTerm,
+ )
+ ) {
+ this.coreViewModel.boardSearchSuggestions.add(searchTerm);
+ }
+ const lastSearchPath = this.getLastSearchPath();
+ this.storageModel.write(lastSearchPath, searchTerm);
+ };
+ // view
+ this.showTask = (taskFileContent) => {
+ if (taskFileContent.boardId != this.boardInfo.fileId) {
+ this.boardsAndTasksModel.deleteTaskReference(
+ this.boardInfo.fileId,
+ taskFileContent.fileId,
+ );
+ this.removeTaskFromView(taskFileContent);
+ return;
+ }
+ const taskViewModel = new TaskViewModel(
+ this.coreViewModel,
+ this.chatViewModel,
+ this.boardsAndTasksModel,
+ this,
+ taskFileContent,
+ );
+ this.taskViewModels.set(taskFileContent.fileId, taskViewModel);
+ };
+ this.removeTaskFromView = (taskFileContent) => {
+ this.taskViewModels.remove(taskFileContent.fileId);
+ this.updateIndex();
+ };
+ this.select = () => {
+ this.taskPageViewModel.selectBoard(this);
+ };
+ this.close = () => {
+ this.taskPageViewModel.closeBoard();
+ this.taskViewModels.clear();
+ };
+ this.showSettings = () => {
+ this.isPresentingSettingsModal.value = true;
+ };
+ this.hideSettings = () => {
+ this.saveSettings();
+ this.isPresentingSettingsModal.value = false;
+ };
+ this.showFilterModal = () => {
+ this.isPresentingFilterModal.value = true;
+ };
+ this.hideFilterModal = () => {
+ this.isPresentingFilterModal.value = false;
+ };
+ this.updateIndex = () => {
+ const index =
+ this.taskPageViewModel.boardIndexManager.getIndex(this);
+ this.index.value = index;
+ };
+ // load
+ this.loadListRelevantData = () => {
+ this.name.value = this.boardInfo.name;
+ this.color.value = this.boardInfo.color;
+ };
+ this.loadTasks = () => {
+ const taskIds = this.boardsAndTasksModel.listTaskIds(
+ this.boardInfo.fileId,
+ );
+ for (const taskId of taskIds) {
+ if (this.taskViewModels.value.has(taskId)) return;
+ const taskFileContent =
+ this.boardsAndTasksModel.getLatestTaskFileContent(
+ taskId,
+ );
+ if (taskFileContent == null) continue;
+ const taskViewModel = new TaskViewModel(
+ this.coreViewModel,
+ this.chatViewModel,
+ this.boardsAndTasksModel,
+ this,
+ taskFileContent,
+ );
+ this.taskViewModels.set(
+ taskFileContent.fileId,
+ taskViewModel,
+ );
+ }
+ this.updateTaskIndices();
+ };
+ this.loadSearchSuggestions = () => {
+ const dirPath = this.getPreviousSearchesPath();
+ const searches = this.storageModel.list(dirPath);
+ this.coreViewModel.boardSearchSuggestions.add(...searches);
+ };
+ this.restoreSearch = () => {
+ const lastSearchPath = this.getLastSearchPath();
+ const lastSearch = this.storageModel.read(lastSearchPath);
+ if (lastSearch != null) {
+ this.searchViewModel.search(lastSearch);
+ }
+ };
+ this.loadData = () => {
+ this.restoreLastUsedView();
+ this.loadTasks();
+ this.loadSearchSuggestions();
+ };
+ this.storageModel = storageModel2;
+ this.boardsAndTasksModel = boardsAndTasksModel;
+ this.taskPageViewModel = taskPageViewModel;
+ this.boardInfo = boardInfo;
+ this.loadListRelevantData();
+ this.isSelected = createProxyState(
+ [this.taskPageViewModel.selectedBoardId],
+ () =>
+ this.taskPageViewModel.selectedBoardId.value ==
+ this.boardInfo.fileId,
+ );
+ this.color.subscribe(() => {
+ if (this.isSelected.value == false) return;
+ this.applyColor();
+ });
+ this.selectedPage.subscribeSilent(() => {
+ this.storeLastUsedView();
+ });
+ boardsAndTasksModel.taskHandlerManager.addHandler(
+ (taskFileContent) => {
+ if (taskFileContent.boardId != this.boardInfo.fileId)
+ return;
+ this.showTask(taskFileContent);
+ this.updateTaskIndices();
+ },
+ );
+ this.searchViewModel = new SearchViewModel(
+ this.taskViewModels,
+ this.filteredTaskViewModels,
+ TaskViewModel.getStringsForFilter,
+ this.coreViewModel.boardSearchSuggestions,
+ );
+ this.searchViewModel.appliedQuery.subscribeSilent((newQuery) => {
+ this.handleNewSearch(newQuery);
+ });
+ this.restoreSearch();
+ }
+ };
+
+ // src/ViewModel/Pages/taskPageViewModel.ts
+ var TaskPageViewModel = class {
+ // init
+ constructor(coreViewModel, chatViewModel, storageModel2, boardModel) {
+ this.coreViewModel = coreViewModel;
+ this.chatViewModel = chatViewModel;
+ // data
+ this.boardIndexManager = new IndexManager(
+ (boardViewModel) => boardViewModel.name.value,
+ );
+ // paths
+ this.getBasePath = () => {
+ return [...this.boardsAndTasksModel.getViewPath()];
+ };
+ this.getBoardViewPath = (boardId) => {
+ return [...this.getBasePath(), boardId];
+ };
+ this.getLastUsedBoardPath = () => {
+ return [
+ ...this.getBasePath(),
+ "last-used-board" /* LastUsedBoard */,
+ ];
+ };
+ // state
+ this.newBoardNameInput = new State("");
+ this.boardViewModels = new MapState();
+ this.isShowingBoadList = new State(true);
+ this.selectedBoardId = new State(void 0);
+ // guards
+ this.cannotCreateBoard = createProxyState(
+ [this.newBoardNameInput],
+ () => this.newBoardNameInput.value == "",
+ );
+ // methods
+ this.createBoard = () => {
+ if (this.cannotCreateBoard.value == true) return;
+ const boardInfoFileContent =
+ this.boardsAndTasksModel.createBoard(
+ this.newBoardNameInput.value,
+ );
+ this.newBoardNameInput.value = "";
+ this.showBoardInList(boardInfoFileContent);
+ this.boardsAndTasksModel.updateBoardAndSend(
+ boardInfoFileContent,
+ );
+ this.updateBoardIndices();
+ };
+ this.updateBoard = (boardInfoFileContent) => {
+ this.boardsAndTasksModel.updateBoardAndSend(
+ boardInfoFileContent,
+ );
+ this.updateBoardIndices();
+ };
+ this.deleteBoard = (boardInfoFileContent) => {
+ this.boardsAndTasksModel.deleteBoard(
+ boardInfoFileContent.fileId,
+ );
+ this.boardViewModels.remove(boardInfoFileContent.fileId);
+ this.updateBoardIndices();
+ };
+ // view
+ this.toggleBoardList = () => {
+ this.isShowingBoadList.value = !this.isShowingBoadList.value;
+ };
+ this.showBoardInList = (boardInfo) => {
+ const boardViewModel = new BoardViewModel(
+ this.coreViewModel,
+ this.chatViewModel,
+ this.storageModel,
+ this.boardsAndTasksModel,
+ this,
+ boardInfo,
+ );
+ this.boardViewModels.set(boardInfo.fileId, boardViewModel);
+ this.chatViewModel.taskBoardSuggestions.set(boardInfo.fileId, [
+ boardInfo.fileId,
+ this.boardsAndTasksModel.getBoardName(boardInfo.fileId),
+ ]);
+ };
+ this.selectBoard = (boardViewModel) => {
+ this.selectedBoardId.value = boardViewModel.boardInfo.fileId;
+ this.chatViewModel.displayedColor.value =
+ boardViewModel.color.value;
+ this.storeLastUsedBoard();
+ };
+ this.closeBoard = () => {
+ this.selectedBoardId.value = void 0;
+ this.chatViewModel.resetColor();
+ this.storeLastUsedBoard();
+ };
+ this.updateBoardIndices = () => {
+ this.boardIndexManager.update([
+ ...this.boardViewModels.value.values(),
+ ]);
+ for (const boardViewModel of this.boardViewModels.value.values()) {
+ boardViewModel.updateIndex();
+ }
+ };
+ // storage
+ this.storeLastUsedBoard = () => {
+ const path = this.getLastUsedBoardPath();
+ const lastUsedBoardId = this.selectedBoardId.value ?? "";
+ this.storageModel.write(path, lastUsedBoardId);
+ };
+ this.openLastUsedBoard = () => {
+ const path = this.getLastUsedBoardPath();
+ const lastUsedBoardId = this.storageModel.read(path);
+ if (lastUsedBoardId == null) return;
+ const boardViewModel =
+ this.boardViewModels.value.get(lastUsedBoardId);
+ if (boardViewModel == void 0) return;
+ this.selectBoard(boardViewModel);
+ };
+ // load
+ this.loadData = () => {
+ this.boardViewModels.clear();
+ const boardIds = this.boardsAndTasksModel.listBoardIds();
+ for (const boardId of boardIds) {
+ const boardInfo =
+ this.boardsAndTasksModel.getBoardInfo(boardId);
+ if (boardInfo == null) continue;
+ this.showBoardInList(boardInfo);
+ }
+ this.updateBoardIndices();
+ this.openLastUsedBoard();
+ };
+ this.storageModel = storageModel2;
+ this.boardsAndTasksModel = boardModel;
+ this.chatViewModel = chatViewModel;
+ boardModel.boardHandlerManager.addHandler(
+ (boardInfoFileContent) => {
+ this.showBoardInList(boardInfoFileContent);
+ this.updateBoardIndices();
+ },
+ );
+ }
+ };
+
+ // src/View/translations.ts
+ var englishTranslations = {
+ updater: {
+ migrated: "Migrated",
+ },
+ general: {
+ deleteItemButtonAudioLabel: "delete item",
+ searchButtonAudioLabel: "search",
+ abortButton: "Abort",
+ cancelButton: "Cancel",
+ closeButton: "Close",
+ backButton: "Back",
+ continueButton: "Continue",
+ confirmButton: "Confirm",
+ saveButton: "Save",
+ setButton: "Set",
+ reloadAppButton: "Reload App",
+ fileVersionLabel: "Version",
+ searchLabel: "Search",
+ restoreConnection: "Restore connection",
+ },
+ regional: {
+ weekdays: {
+ full: [
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ ],
+ abbreviated: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ },
+ },
+ homePage: {
+ appName: "Comms",
+ ///
+ overviewHeadline: "Overview",
+ serverAddress: "Server address",
+ serverAddressPlaceholder: "wss://192.168.0.69:3000",
+ connectAudioLabel: "connect to server",
+ disconnectAudioLabel: "disconnect from server",
+ manageConnectionsAudioLabel: "manage connections",
+ yourNameLabel: "Your name",
+ yourNamePlaceholder: "Jane Doe",
+ setNameButtonAudioLabel: "set name",
+ firstDayOfWeekLabel: "First day of week",
+ manageStorageButton: "Manage storage",
+ transferDataButton: "Data Transfer",
+ scrollToChatButton: "Chats",
+ ///
+ backToOverviewAudioLabel: "go back to overview",
+ chatsHeadline: "Chats",
+ addChatAudioLabel: "name of new chat",
+ addChatPlaceholder: "Add chat",
+ addChatButton: "Add chat",
+ },
+ connectionModal: {
+ connectionModalHeadline: "Manage Connections",
+ ///
+ connectButtonAudioLabel: "connect",
+ },
+ dataTransferModal: {
+ transferDataHeadline: "Data Transfer",
+ selectionDescription: "Select the data that you want to transfer.",
+ dataEntryDescription: "Enter this data on the other device.",
+ dataEntryInputDescription:
+ "Enter the data displayed on the other device.",
+ readyToReceiveDescription: "Click 'send' on the other device.",
+ notConnectedError: "You are not connected to any server.",
+ ///
+ fromThisDeviceButton: "From this device",
+ toThisDeviceButton: "To this device",
+ ///
+ generalHeadline: "General",
+ connectionData: "Connection Data",
+ settingsData: "Settings Data",
+ chatsHeadline: "Chats",
+ ///
+ transferChannelHeadline: "Transfer Chanel",
+ transferKeyHeadline: "Transfer Encryption Key",
+ sendButton: "Send",
+ sendAgainButton: "Send again",
+ ///
+ filesSentCount: (count) => `Files sent: ${count}.`,
+ allFilesSent: "Done.",
+ filesReceivedCount: (count) => `Files received: ${count}.`,
+ },
+ storage: {
+ noItemSelected: "No item selected",
+ notAFile: "(not a file)",
+ contentEmpty: "(empty)",
+ path: "Path",
+ content: "Content",
+ deleteItem: "Delete item",
+ removeJunkButton: "Delete junk files",
+ },
+ chatPage: {
+ closeChatAudioLabe: "close chat",
+ chatSettingsAudioLabel: "chat settings",
+ pages: {
+ settings: "Settings",
+ messages: "Messages",
+ tasks: "Tasks",
+ calendar: "Calendar",
+ },
+ settings: {
+ settingsHeadline: "Settings",
+ primaryChannelLabel: "Primary channel",
+ setPrimaryChannelButtonAudioLabel: "set primary channel",
+ newSecondaryChannelPlaceholder: "Add secondary channel",
+ newSecondaryChannelAudioLabel: "name of new secondary channel",
+ addSecondaryChannelButtonAudioLabel: "add secondary channel",
+ encryptionKeyLabel: "Encryption key",
+ setEncryptionKeyButtonAudioLabel: "set encryption key",
+ showEncryptionKey: "Show encryption key",
+ deleteChatButton: "Delete entire chat",
+ },
+ message: {
+ messagesHeadline: "Messages",
+ ///
+ composerInputPlaceholder: "Type a message...",
+ sendMessageButtonAudioLabel: "send message",
+ ///
+ showMessageInfoButtonAudioLabel: "show message info",
+ messageInfoHeadline: "Message Info",
+ sentBy: "Sent by",
+ timeSent: "Time sent",
+ channel: "Channel",
+ messageContent: "Message content",
+ copyMessageButton: "Copy message",
+ resendMessageButton: "Resend message",
+ decryptMessageButton: "Decrypt message",
+ deleteMessageButton: "Delete message",
+ },
+ task: {
+ newBoardNamePlaceholder: "Create a board",
+ createBoardButtonAudioLabel: "create board",
+ ///
+ noBoardSelected: "No board selected",
+ boardNotFound: "Board not found",
+ ///
+ closeBoardButtonAudioLabel: "close board",
+ toggleBoardButtonAudioLabel: "toggle board list",
+ showBoardSettingsButtonAudioLabel: "show board settigns",
+ listViewButtonAudioLabel: "list view",
+ kanbanViewButtonAudioLabel: "kanban view",
+ statusViewButtonAudioLabel: "status grid view",
+ filterTasksButtonAudioLabel: "filter tasks",
+ createTaskButtonAudioLabel: "create new task",
+ ///
+ boardSettingsHeadline: "Board Settings",
+ boardNameInputLabel: "Board name",
+ deleteBoardButton: "Delete board and all tasks",
+ ///
+ taskSettingsHeadline: "Edit Task",
+ taskNameLabel: "Title",
+ taskBoardLabel: "Board",
+ taskCategoryLabel: "Category",
+ taskStatusLabel: "Status",
+ taskPriorityLabel: "Priority",
+ taskDescriptionLabel: "Description",
+ taskDateLabel: "Date",
+ taskTimeLabel: "Time",
+ deleteTaskButton: "Delete task",
+ ///
+ filterTasksHeadline: "Filter Tasks",
+ ///
+ renameCategoryInputPlaceholder: "Rename category,",
+ },
+ calendar: {
+ eventsBoard: "Events",
+ ///
+ todayButtonAudioLabel: "go to today",
+ previousMonthButtonAudioLabel: "previous month",
+ nextMonthButtonAudioLabel: "next month",
+ yearInputAudioLabel: "year",
+ monthInputAudioLabel: "month",
+ yearInputPlaceholder: "2000",
+ monthInputPlaceholder: "01",
+ ///
+ searchEventsHeadline: "Search Events",
+ ///
+ events: "Events",
+ noEvents: "No events",
+ },
+ },
+ };
+ var allTranslations = {
+ en: englishTranslations,
+ de: {
+ updater: {
+ migrated: "Migriert",
+ },
+ general: {
+ deleteItemButtonAudioLabel: "element l\xF6schen",
+ searchButtonAudioLabel: "suchen",
+ abortButton: "Abbrechen",
+ cancelButton: "Abbrechen",
+ closeButton: "Schlie\xDFen",
+ backButton: "Zur\xFCck",
+ continueButton: "Weiter",
+ confirmButton: "Best\xE4tigen",
+ saveButton: "Speichern",
+ setButton: "OK",
+ reloadAppButton: "Neu laden",
+ fileVersionLabel: "Version",
+ searchLabel: "Suche",
+ restoreConnection: "Verbindung wiederherstellen",
+ },
+ regional: {
+ weekdays: {
+ full: [
+ "Sonntag",
+ "Montag",
+ "Dienstag",
+ "Mittwoch",
+ "Donnerstag",
+ "Freitag",
+ "Samstag",
+ ],
+ abbreviated: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
+ },
+ },
+ homePage: {
+ appName: "Comms",
+ overviewHeadline: "\xDCbersicht",
+ serverAddress: "Serveradresse",
+ serverAddressPlaceholder: "wss://192.168.0.69:3000",
+ connectAudioLabel: "mit Server verbinden",
+ disconnectAudioLabel: "vom Server trennen",
+ manageConnectionsAudioLabel: "Verbindungen verwalten",
+ yourNameLabel: "Dein Name",
+ yourNamePlaceholder: "Max Mustermann",
+ setNameButtonAudioLabel: "Name speichern",
+ firstDayOfWeekLabel: "Erster Wochentag",
+ manageStorageButton: "Daten verwalten",
+ transferDataButton: "Daten\xFCbertragung",
+ scrollToChatButton: "Chats",
+ backToOverviewAudioLabel: "zur\xFCck zur \xFCbersicht",
+ chatsHeadline: "Chats",
+ addChatAudioLabel: "Name des neuen Chats",
+ addChatPlaceholder: "Chat hinzuf\xFCgen",
+ addChatButton: "Chat hinzuf\xFCgen",
+ },
+ connectionModal: {
+ connectionModalHeadline: "Verbindungen verwalten",
+ connectButtonAudioLabel: "verbinden",
+ },
+ dataTransferModal: {
+ transferDataHeadline: "Daten\xFCbertragung",
+ selectionDescription:
+ "W\xE4hle die Daten aus, die du \xFCbertragen m\xF6chtest.",
+ dataEntryDescription:
+ "Gib diese Informationen auf dem anderen Ger\xE4t ein.",
+ dataEntryInputDescription:
+ "Gib die auf dem anderen Ger\xE4t angezeigten Informationen ein.",
+ readyToReceiveDescription:
+ "Klicke auf dem anderen Ger\xE4t auf 'Senden'.",
+ notConnectedError: "Du bist mit keinem Server verbunden.",
+ fromThisDeviceButton: "Von diesem Ger\xE4t",
+ toThisDeviceButton: "An dieses Ger\xE4t",
+ generalHeadline: "Allgemein",
+ connectionData: "Verbindungsdaten",
+ settingsData: "Einstellungen",
+ chatsHeadline: "Chats",
+ transferChannelHeadline: "\xDCbertragungskanal",
+ transferKeyHeadline: "Schl\xFCssel",
+ sendButton: "Senden",
+ sendAgainButton: "Erneut senden",
+ filesSentCount: (count) => `Dateien gesendet: ${count}.`,
+ allFilesSent: "Fertig.",
+ filesReceivedCount: (count) => `Dateien empfangen: ${count}.`,
+ },
+ storage: {
+ noItemSelected: "Kein Element ausgew\xE4hlt",
+ notAFile: "(keine Datei)",
+ contentEmpty: "(leer)",
+ path: "Pfad",
+ content: "Inhalt",
+ deleteItem: "Element l\xF6schen",
+ removeJunkButton: "Datenm\xFCll l\xF6schen",
+ },
+ chatPage: {
+ closeChatAudioLabe: "Chat schlie\xDFen",
+ chatSettingsAudioLabel: "Chateinstellungen",
+ pages: {
+ settings: "Einstellungen",
+ messages: "Nachrichten",
+ tasks: "Aufgaben",
+ calendar: "Kalender",
+ },
+ settings: {
+ settingsHeadline: "Einstellungen",
+ primaryChannelLabel: "Hauptkanal",
+ setPrimaryChannelButtonAudioLabel: "Hauptkanal festlegen",
+ newSecondaryChannelPlaceholder:
+ "Sekund\xE4ren Kanal hinzuf\xFCgen",
+ newSecondaryChannelAudioLabel:
+ "Name des neuen sekund\xE4ren Kanals",
+ addSecondaryChannelButtonAudioLabel:
+ "Sekund\xE4ren Kanal hinzuf\xFCgen",
+ encryptionKeyLabel: "Schl\xFCssel",
+ setEncryptionKeyButtonAudioLabel: "Schl\xFCssel festlegen",
+ showEncryptionKey: "Schl\xFCssel anzeigen",
+ deleteChatButton: "Gesamten Chat l\xF6schen",
+ },
+ message: {
+ messagesHeadline: "Nachrichten",
+ composerInputPlaceholder: "Schreib eine Nachricht...",
+ sendMessageButtonAudioLabel: "nachricht senden",
+ showMessageInfoButtonAudioLabel: "nachrichteninfo anzeigen",
+ messageInfoHeadline: "Nachrichteninfo",
+ sentBy: "Gesendet von",
+ timeSent: "Sendezeit",
+ channel: "Kanal",
+ messageContent: "Nachrichteninhalt",
+ copyMessageButton: "Nachricht kopieren",
+ resendMessageButton: "Nachricht erneut senden",
+ decryptMessageButton: "Nachricht entschl\xFCsseln",
+ deleteMessageButton: "Nachricht l\xF6schen",
+ },
+ task: {
+ newBoardNamePlaceholder: "Board erstellen",
+ createBoardButtonAudioLabel: "board erstellen",
+ noBoardSelected: "Kein Board ausgew\xE4hlt",
+ boardNotFound: "Board nicht gefunden",
+ closeBoardButtonAudioLabel: "board schlie\xDFen",
+ toggleBoardButtonAudioLabel: "board-liste ein/ausblenden",
+ showBoardSettingsButtonAudioLabel:
+ "Board-Einstellungen anzeigen",
+ listViewButtonAudioLabel: "Listenansicht",
+ kanbanViewButtonAudioLabel: "Kanban-Ansicht",
+ statusViewButtonAudioLabel: "Statusrasteransicht",
+ filterTasksButtonAudioLabel: "Aufgaben filtern",
+ createTaskButtonAudioLabel: "Neue Aufgabe erstellen",
+ boardSettingsHeadline: "Board-Einstellungen",
+ boardNameInputLabel: "Boardname",
+ deleteBoardButton: "Board und alle Aufgaben l\xF6schen",
+ taskSettingsHeadline: "Aufgabe bearbeiten",
+ taskNameLabel: "Titel",
+ taskBoardLabel: "Board",
+ taskCategoryLabel: "Kategorie",
+ taskStatusLabel: "Status",
+ taskPriorityLabel: "Priorit\xE4t",
+ taskDescriptionLabel: "Beschreibung",
+ taskDateLabel: "Datum",
+ taskTimeLabel: "Uhrzeit",
+ deleteTaskButton: "Aufgabe l\xF6schen",
+ filterTasksHeadline: "Aufgaben filtern",
+ renameCategoryInputPlaceholder: "Kategorie umbenennen",
+ },
+ calendar: {
+ eventsBoard: "Ereignisse",
+ ///
+ todayButtonAudioLabel: "gehe zu heute",
+ previousMonthButtonAudioLabel: "vorheriger monat",
+ nextMonthButtonAudioLabel: "n\xE4chster monat",
+ yearInputAudioLabel: "Jahr",
+ monthInputAudioLabel: "Monat",
+ yearInputPlaceholder: "2000",
+ monthInputPlaceholder: "01",
+ searchEventsHeadline: "Ereignisse suchen",
+ events: "Ereignisse",
+ noEvents: "Keine Ereignisse",
+ },
+ },
+ },
+ es: {
+ updater: {
+ migrated: "Migrado",
+ },
+ general: {
+ deleteItemButtonAudioLabel: "eliminar elemento",
+ searchButtonAudioLabel: "buscar",
+ abortButton: "Abortar",
+ cancelButton: "Cancelar",
+ closeButton: "Cerrar",
+ backButton: "Atr\xE1s",
+ continueButton: "Continuar",
+ confirmButton: "Confirmar",
+ saveButton: "Guardar",
+ setButton: "OK",
+ reloadAppButton: "Recargar app",
+ fileVersionLabel: "Versi\xF3n",
+ searchLabel: "Buscar",
+ restoreConnection: "Conectar de nuevo",
+ },
+ regional: {
+ weekdays: {
+ full: [
+ "Domingo",
+ "Lunes",
+ "Martes",
+ "Mi\xE9rcoles",
+ "Jueves",
+ "Viernes",
+ "S\xE1bado",
+ ],
+ abbreviated: [
+ "Dom",
+ "Lun",
+ "Mar",
+ "Mi\xE9",
+ "Jue",
+ "Vie",
+ "S\xE1b",
+ ],
+ },
+ },
+ homePage: {
+ appName: "Comms",
+ overviewHeadline: "Resumen",
+ serverAddress: "Direcci\xF3n del servidor",
+ serverAddressPlaceholder: "wss://192.168.0.69:3000",
+ connectAudioLabel: "conectar al servidor",
+ disconnectAudioLabel: "desconectar del servidor",
+ manageConnectionsAudioLabel: "gestionar conexiones",
+ yourNameLabel: "Tu nombre",
+ yourNamePlaceholder: "Juan P\xE9rez",
+ setNameButtonAudioLabel: "establecer nombre",
+ firstDayOfWeekLabel: "Primer d\xEDa de la semana",
+ manageStorageButton: "Gestionar almacenamiento",
+ transferDataButton: "Transferencia de datos",
+ scrollToChatButton: "Chats",
+ backToOverviewAudioLabel: "volver al resumen",
+ chatsHeadline: "Chats",
+ addChatAudioLabel: "nombre del nuevo chat",
+ addChatPlaceholder: "A\xF1adir chat",
+ addChatButton: "A\xF1adir chat",
+ },
+ connectionModal: {
+ connectionModalHeadline: "Gestionar Conexiones",
+ connectButtonAudioLabel: "conectar",
+ },
+ dataTransferModal: {
+ transferDataHeadline: "Transferencia de Datos",
+ selectionDescription:
+ "Selecciona los datos que quieres transferir.",
+ dataEntryDescription:
+ "Introduce estos datos en el otro dispositivo.",
+ dataEntryInputDescription:
+ "Introduce los datos mostrados en el otro dispositivo.",
+ readyToReceiveDescription:
+ "Haz clic en 'enviar' en el otro dispositivo.",
+ notConnectedError:
+ "No est\xE1s conectado a ning\xFAn servidor.",
+ fromThisDeviceButton: "Desde este dispositivo",
+ toThisDeviceButton: "A este dispositivo",
+ generalHeadline: "General",
+ connectionData: "Datos de Conexi\xF3n",
+ settingsData: "Datos de Configuraci\xF3n",
+ chatsHeadline: "Chats",
+ transferChannelHeadline: "Canal de Transferencia",
+ transferKeyHeadline:
+ "Clave de Encriptaci\xF3n de Transferencia",
+ sendButton: "Enviar",
+ sendAgainButton: "Enviar otra vez",
+ filesSentCount: (count) => `Archivos enviados: ${count}.`,
+ allFilesSent: "Hecho.",
+ filesReceivedCount: (count) => `Archivos recibidos: ${count}.`,
+ },
+ storage: {
+ noItemSelected: "Ning\xFAn elemento seleccionado",
+ notAFile: "(no es un archivo)",
+ contentEmpty: "(vac\xEDo)",
+ path: "Ruta",
+ content: "Contenido",
+ deleteItem: "Eliminar elemento",
+ removeJunkButton: "Eliminar archivos basura",
+ },
+ chatPage: {
+ closeChatAudioLabe: "cerrar chat",
+ chatSettingsAudioLabel: "configuraci\xF3n del chat",
+ pages: {
+ settings: "Configuraci\xF3n",
+ messages: "Mensajes",
+ tasks: "Tareas",
+ calendar: "Calendario",
+ },
+ settings: {
+ settingsHeadline: "Configuraci\xF3n",
+ primaryChannelLabel: "Canal principal",
+ setPrimaryChannelButtonAudioLabel:
+ "establecer canal principal",
+ newSecondaryChannelPlaceholder:
+ "A\xF1adir canal secundario",
+ newSecondaryChannelAudioLabel:
+ "nombre del nuevo canal secundario",
+ addSecondaryChannelButtonAudioLabel:
+ "a\xF1adir canal secundario",
+ encryptionKeyLabel: "Clave de encriptaci\xF3n",
+ setEncryptionKeyButtonAudioLabel:
+ "establecer clave de encriptaci\xF3n",
+ showEncryptionKey: "Mostrar clave de encriptaci\xF3n",
+ deleteChatButton: "Eliminar todo el chat",
+ },
+ message: {
+ messagesHeadline: "Mensajes",
+ composerInputPlaceholder: "Escribe un mensaje...",
+ sendMessageButtonAudioLabel: "enviar mensaje",
+ showMessageInfoButtonAudioLabel:
+ "mostrar informaci\xF3n del mensaje",
+ messageInfoHeadline: "Informaci\xF3n del Mensaje",
+ sentBy: "Enviado por",
+ timeSent: "Hora de env\xEDo",
+ channel: "Canal",
+ messageContent: "Contenido del mensaje",
+ copyMessageButton: "Copiar mensaje",
+ resendMessageButton: "Reenviar mensaje",
+ decryptMessageButton: "Desencriptar mensaje",
+ deleteMessageButton: "Eliminar mensaje",
+ },
+ task: {
+ newBoardNamePlaceholder: "Crear un tablero",
+ createBoardButtonAudioLabel: "crear tablero",
+ noBoardSelected: "Ning\xFAn tablero seleccionado",
+ boardNotFound: "Tablero no encontrado",
+ closeBoardButtonAudioLabel: "cerrar tablero",
+ toggleBoardButtonAudioLabel:
+ "mostrar o ocultar lista de tableros",
+ showBoardSettingsButtonAudioLabel:
+ "mostrar configuraci\xF3n del tablero",
+ listViewButtonAudioLabel: "vista de lista",
+ kanbanViewButtonAudioLabel: "vista kanban",
+ statusViewButtonAudioLabel:
+ "vista de cuadr\xEDcula de estado",
+ filterTasksButtonAudioLabel: "filtrar tareas",
+ createTaskButtonAudioLabel: "crear nueva tarea",
+ boardSettingsHeadline: "Configuraci\xF3n del Tablero",
+ boardNameInputLabel: "Nombre del tablero",
+ deleteBoardButton: "Eliminar tablero y todas las tareas",
+ taskSettingsHeadline: "Editar Tarea",
+ taskNameLabel: "T\xEDtulo",
+ taskBoardLabel: "Tablero",
+ taskCategoryLabel: "Categor\xEDa",
+ taskStatusLabel: "Estado",
+ taskPriorityLabel: "Prioridad",
+ taskDescriptionLabel: "Descripci\xF3n",
+ taskDateLabel: "Fecha",
+ taskTimeLabel: "Hora",
+ deleteTaskButton: "Eliminar tarea",
+ filterTasksHeadline: "Filtrar Tareas",
+ renameCategoryInputPlaceholder: "Renombrar categor\xEDa",
+ },
+ calendar: {
+ eventsBoard: "Eventos",
+ ///
+ todayButtonAudioLabel: "ir a hoy",
+ previousMonthButtonAudioLabel: "mes anterior",
+ nextMonthButtonAudioLabel: "mes siguiente",
+ yearInputAudioLabel: "a\xF1o",
+ monthInputAudioLabel: "mes",
+ yearInputPlaceholder: "2000",
+ monthInputPlaceholder: "01",
+ searchEventsHeadline: "Buscar Eventos",
+ events: "Eventos",
+ noEvents: "No hay eventos",
+ },
+ },
+ },
+ };
+ var language = navigator.language.substring(0, 2);
+ var translations = allTranslations[language] || allTranslations.en;
+
+ // src/ViewModel/Chat/chatViewModel.ts
+ var ChatViewModel = class {
+ // init
+ constructor(
+ coreViewModel,
+ storageModel2,
+ chatModel,
+ settingsViewModel2,
+ connectionViewModel2,
+ chatListViewModel2,
+ ) {
+ this.coreViewModel = coreViewModel;
+ // state
+ this.displayedColor = new State("standard" /* Standard */);
+ this.selectedPage = new State("messages" /* Messages */);
+ this.index = new State(0);
+ this.hasUnreadMessages = new State(false);
+ this.taskBoardSuggestions = new MapState();
+ // view
+ this.open = () => {
+ this.chatListViewModel.openChat(this);
+ this.markRead();
+ };
+ this.close = () => {
+ this.chatListViewModel.closeChat();
+ };
+ this.closeSubPages = () => {};
+ this.setColor = (color) => {
+ this.setDisplayedColor(color);
+ this.chatModel.setColor(color);
+ };
+ this.setDisplayedColor = (color) => {
+ this.displayedColor.value = color;
+ };
+ this.resetColor = () => {
+ this.displayedColor.value =
+ this.settingsPageViewModel.color.value;
+ };
+ this.updateIndex = () => {
+ const index =
+ this.chatListViewModel.chatIndexManager.getIndex(this);
+ this.index.value = index;
+ };
+ this.markRead = () => {
+ this.hasUnreadMessages.value = false;
+ this.chatModel.markRead();
+ };
+ // load
+ this.loadPageSelection = () => {
+ const path = StorageModel.getPath(
+ "chat" /* Chat */,
+ filePaths.chat.lastUsedPage(this.chatModel.id),
+ );
+ const lastUsedPage = this.storageModel.read(path);
+ if (lastUsedPage != null) {
+ this.selectedPage.value = lastUsedPage;
+ }
+ this.selectedPage.subscribeSilent((newPage) => {
+ this.storageModel.write(path, newPage);
+ this.resetColor();
+ });
+ };
+ this.loadInfo = () => {
+ this.hasUnreadMessages.value =
+ this.chatModel.info.hasUnreadMessages;
+ this.taskBoardSuggestions.set(CALENDAR_EVENT_BOARD_ID, [
+ CALENDAR_EVENT_BOARD_ID,
+ translations.chatPage.calendar.eventsBoard,
+ ]);
+ };
+ this.storageModel = storageModel2;
+ this.chatModel = chatModel;
+ this.settingsViewModel = settingsViewModel2;
+ this.connectionViewModel = connectionViewModel2;
+ this.chatListViewModel = chatListViewModel2;
+ this.calendarViewModel = new CalendarPageViewModel(
+ coreViewModel,
+ this,
+ this.storageModel,
+ this.chatModel.fileModel.boardsAndTasksModel.calendarModel,
+ this.chatModel.fileModel.boardsAndTasksModel,
+ );
+ this.taskPageViewModel = new TaskPageViewModel(
+ this.coreViewModel,
+ this,
+ this.storageModel,
+ this.chatModel.fileModel.boardsAndTasksModel,
+ );
+ this.messagePageViewModel = new MessagePageViewModel(
+ this.coreViewModel,
+ this,
+ );
+ this.settingsPageViewModel = new SettingsPageViewModel(
+ this.coreViewModel,
+ this,
+ );
+ chatModel.chatMessageHandlerManager.addHandler((chatMessage) => {
+ this.messagePageViewModel.showChatMessage(chatMessage);
+ this.markRead();
+ });
+ this.loadPageSelection();
+ this.resetColor();
+ this.loadInfo();
+ }
+ };
+
+ // src/ViewModel/Chat/chatListViewModel.ts
+ var ChatListViewModel = class {
+ // init
+ constructor(
+ coreViewModel,
+ storageModel2,
+ chatListModel2,
+ settingsViewModel2,
+ connectionViewModel2,
+ ) {
+ this.coreViewModel = coreViewModel;
+ // data
+ this.chatIndexManager = new IndexManager(
+ (chatViewModel) =>
+ chatViewModel.settingsPageViewModel.primaryChannel.value,
+ );
+ // state
+ this.newChatPrimaryChannel = new State("");
+ this.chatViewModels = new ListState();
+ this.selectedChat = new State(void 0);
+ // guards
+ this.cannotCreateChat = createProxyState(
+ [this.newChatPrimaryChannel],
+ () => this.newChatPrimaryChannel.value == "",
+ );
+ // methods
+ this.createChat = () => {
+ const chatModel = this.chatListModel.createChat(
+ this.newChatPrimaryChannel.value,
+ );
+ this.newChatPrimaryChannel.value = "";
+ const chatViewModel = this.createChatViewModel(chatModel);
+ this.trackChat(chatViewModel);
+ this.updateIndices();
+ };
+ this.trackChat = (chatViewModel) => {
+ this.chatViewModels.add(chatViewModel);
+ };
+ this.untrackChat = (chatViewModel) => {
+ this.chatListModel.untrackChat(chatViewModel.chatModel);
+ this.chatViewModels.remove(chatViewModel);
+ };
+ this.createChatViewModel = (chatModel) => {
+ return new ChatViewModel(
+ this.coreViewModel,
+ this.storageModel,
+ chatModel,
+ this.settingsViewModel,
+ this.connectionViewModel,
+ this,
+ );
+ };
+ this.updateIndices = () => {
+ this.chatIndexManager.update([
+ ...this.chatViewModels.value.values(),
+ ]);
+ for (const chatViewModel of this.chatViewModels.value) {
+ chatViewModel.updateIndex();
+ }
+ };
+ // view
+ this.openChat = (chatViewModel) => {
+ this.selectedChat.value = chatViewModel;
+ };
+ this.closeChat = () => {
+ this.selectedChat.value = void 0;
+ };
+ // load
+ this.loadChats = () => {
+ this.chatViewModels.clear();
+ for (const chatModel of this.chatListModel.chatModels.values()) {
+ const chatViewModel = this.createChatViewModel(chatModel);
+ this.trackChat(chatViewModel);
+ }
+ this.updateIndices();
+ };
+ this.storageModel = storageModel2;
+ this.chatListModel = chatListModel2;
+ this.settingsViewModel = settingsViewModel2;
+ this.connectionViewModel = connectionViewModel2;
+ this.loadChats();
+ }
+ };
+
+ // src/View/Components/monthGrid.tsx
+ function MonthGrid2(monthGrid, selectedDate, handleDrop) {
+ const dayLabels = [];
+ let currentWeekday = monthGrid.firstDayOfWeek;
+ while (dayLabels.length < 7) {
+ dayLabels.push(
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.regional.weekdays.abbreviated[currentWeekday],
+ ),
+ );
+ currentWeekday++;
+ if (currentWeekday == 7) currentWeekday = 0;
+ }
+ const offsetElements = [];
+ for (let i = 0; i < monthGrid.offset; i++) {
+ offsetElements.push(/* @__PURE__ */ createElement("div", null));
+ }
+ const converter = (taskViewModel) => {
+ const view = /* @__PURE__ */ createElement(
+ "span",
+ { class: "ellipsis secondary" },
+ taskViewModel.task.name,
+ );
+ taskViewModel.index.subscribe((newIndex) => {
+ view.style.order = newIndex;
+ });
+ return view;
+ };
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "month-grid-wrapper" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "day-labels" },
+ ...dayLabels,
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "month-grid" },
+ ...offsetElements,
+ ...Object.entries(monthGrid.days)
+ .sort((a, b) => localeCompare(a[0], b[0]))
+ .map((entry) => {
+ const [date, mapState] = entry;
+ const isSelected = createProxyState(
+ [selectedDate],
+ () => selectedDate.value == parseInt(date),
+ );
+ const isToday =
+ monthGrid.isCurrentMonth == true &&
+ parseInt(date) ==
+ /* @__PURE__ */ new Date().getDate();
+ const eventCount = createProxyState(
+ [mapState],
+ () => mapState.value.size,
+ );
+ const hasEvents = createProxyState(
+ [eventCount],
+ () => eventCount.value != 0,
+ );
+ function select() {
+ selectedDate.value = parseInt(date);
+ }
+ function drop() {
+ handleDrop(date);
+ }
+ return /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "tile",
+ "on:click": select,
+ "toggle:selected": isSelected,
+ "toggle:today": isToday,
+ "on:dragover": allowDrop,
+ "on:drop": drop,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement("b", null, date),
+ /* @__PURE__ */ createElement("span", {
+ class: "event-count",
+ "toggle:has-events": hasEvents,
+ "subscribe:innerText": eventCount,
+ }),
+ /* @__PURE__ */ createElement("div", {
+ class: "event-list",
+ "children:append": [mapState, converter],
+ }),
+ ),
+ );
+ }),
+ ),
+ );
+ }
+
+ // src/View/Components/option.tsx
+ function Option(text, value, selectedOnCreate) {
+ return /* @__PURE__ */ createElement(
+ "option",
+ { value, "toggle:selected": selectedOnCreate },
+ text,
+ );
+ }
+ var StringToOption = (string) => {
+ return Option(string, string, false);
+ };
+ var VersionIdToOption = (versionId) => {
+ const [date, rest] = versionId.split("T");
+ const [time] = rest.split(".");
+ const readableName = `${date} ${time}`;
+ return Option(readableName, versionId, false);
+ };
+
+ // src/View/Components/dangerousActionButton.tsx
+ function DangerousActionButton(label, icon, action) {
+ const isActionRequested = new State(false);
+ const cannotConfirm = createProxyState(
+ [isActionRequested],
+ () => isActionRequested.value == false,
+ );
+ function requestAction() {
+ isActionRequested.value = true;
+ }
+ function abort() {
+ isActionRequested.value = false;
+ }
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex",
+ "on:click": abort,
+ "toggle:hidden": cannotConfirm,
+ },
+ translations.general.abortButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "undo",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "danger flex",
+ "on:click": requestAction,
+ "toggle:hidden": isActionRequested,
+ },
+ label,
+ /* @__PURE__ */ createElement("span", { class: "icon" }, icon),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "danger flex",
+ "on:click": action,
+ "toggle:hidden": cannotConfirm,
+ },
+ translations.general.confirmButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "warning",
+ ),
+ ),
+ );
+ }
+
+ // src/View/Modals/taskSettingsModal.tsx
+ function TaskSettingsModal(taskViewModel) {
+ const categorySuggestionId = v4_default();
+ const statusSuggestionId = v4_default();
+ const BoardOptionConverter = (entry) => {
+ const isSelected = entry[0] == taskViewModel.task.boardId;
+ return Option(entry[1], entry[0], isSelected);
+ };
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", open: true },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.chatPage.task.taskSettingsHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "history",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.general.fileVersionLabel,
+ ),
+ /* @__PURE__ */ createElement("select", {
+ "bind:value": taskViewModel.selectedVersionId,
+ "children:append": [
+ taskViewModel.versionIds,
+ VersionIdToOption,
+ ],
+ }),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_drop_down",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "label",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskNameLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "bind:value": taskViewModel.name,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "category",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskBoardLabel,
+ ),
+ /* @__PURE__ */ createElement("select", {
+ "bind:value": taskViewModel.boardId,
+ "children:append": [
+ taskViewModel.chatViewModel
+ .taskBoardSuggestions,
+ BoardOptionConverter,
+ ],
+ }),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_drop_down",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "description",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskDescriptionLabel,
+ ),
+ /* @__PURE__ */ createElement("textarea", {
+ rows: "10",
+ "bind:value": taskViewModel.description,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "category",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskCategoryLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "bind:value": taskViewModel.category,
+ list: categorySuggestionId,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement("datalist", {
+ hidden: true,
+ id: categorySuggestionId,
+ "children:append": [
+ taskViewModel.coreViewModel.taskCategorySuggestions,
+ StringToOption,
+ ],
+ }),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "clock_loader_40",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskStatusLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "bind:value": taskViewModel.status,
+ list: statusSuggestionId,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement("datalist", {
+ hidden: true,
+ id: statusSuggestionId,
+ "children:append": [
+ taskViewModel.coreViewModel.taskStatusSuggestions,
+ StringToOption,
+ ],
+ }),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "priority_high",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskPriorityLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ type: "number",
+ "bind:value": taskViewModel.priority,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "calendar_month",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskDateLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ type: "date",
+ "bind:value": taskViewModel.date,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "schedule",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.taskTimeLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ type: "time",
+ "bind:value": taskViewModel.time,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "width-input" },
+ DangerousActionButton(
+ translations.chatPage.task.deleteTaskButton,
+ "delete_forever",
+ taskViewModel.deleteTask,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-100" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex",
+ "on:click": taskViewModel.closeAndDiscard,
+ },
+ translations.general.closeButton,
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex primary",
+ "on:click": taskViewModel.closeAndSave,
+ },
+ translations.general.saveButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "save",
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ // src/View/Components/taskEntry.tsx
+ function TaskEntry(taskViewModel) {
+ const details = {
+ description: taskViewModel.description.value || "---",
+ priority_high: taskViewModel.priority.value || "---",
+ category: taskViewModel.category.value || "---",
+ clock_loader_40: taskViewModel.status.value || "---",
+ calendar_month: taskViewModel.date.value || "---",
+ schedule: taskViewModel.time.value || "---",
+ };
+ const view = /* @__PURE__ */ createElement(
+ "button",
+ {
+ draggable: "true",
+ class: "tile flex-no",
+ style: "user-select: none; -webkit-user-select: none",
+ "on:click": taskViewModel.open,
+ "on:dragstart": taskViewModel.dragStart,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement("b", {
+ class: "ellipsis",
+ "subscribe:innerText": taskViewModel.name,
+ }),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "grid secondary",
+ style: "grid-template-columns: repeat(2, 1fr); column-gap: 1rem; row-gap: .5rem",
+ },
+ ...Object.entries(details).map((entry) =>
+ /* @__PURE__ */ createElement(
+ "span",
+ {
+ class: "flex-row align-center width-100 flex-no clip",
+ style: "gap: 1rem",
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon", style: "font-size: 1.1rem" },
+ entry[0],
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "ellipsis" },
+ entry[1],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ taskViewModel.index.subscribe((newIndex) => {
+ view.style.order = newIndex;
+ });
+ return view;
+ }
+ var TaskViewModelToEntry = (taskViewModel) => {
+ return TaskEntry(taskViewModel);
+ };
+
+ // src/View/ChatPages/calendarPage.tsx
+ function CalendarPage(calendarPageViewModel) {
+ calendarPageViewModel.loadData();
+ const mainContent = createProxyState(
+ [calendarPageViewModel.monthGrid],
+ () => {
+ const monthGrid = calendarPageViewModel.monthGrid.value;
+ if (monthGrid == void 0) {
+ return /* @__PURE__ */ createElement("div", null);
+ } else {
+ let drop = function (date) {
+ calendarPageViewModel.handleDrop(
+ monthGrid.year.toString(),
+ monthGrid.month.toString(),
+ date,
+ );
+ };
+ return MonthGrid2(
+ monthGrid,
+ calendarPageViewModel.selectedDate,
+ drop,
+ );
+ }
+ },
+ );
+ const sidePaneContentWrapper = createProxyState(
+ [
+ calendarPageViewModel.selectedYear,
+ calendarPageViewModel.selectedMonth,
+ calendarPageViewModel.selectedDate,
+ ],
+ () => {
+ const listState = calendarPageViewModel.getEventsForDate();
+ if (listState == void 0) {
+ return /* @__PURE__ */ createElement("div", null);
+ } else {
+ const sidePaneContent = createProxyState(
+ [listState],
+ () => {
+ if (listState.value.size == 0) {
+ return /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "width-100 height-100 flex-column justify-center align-center",
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary slide-up" },
+ translations.chatPage.calendar.noEvents,
+ ),
+ );
+ } else {
+ return /* @__PURE__ */ createElement("div", {
+ class: "flex-column gap slide-up padding-bottom",
+ "children:append": [
+ listState,
+ TaskViewModelToEntry,
+ ],
+ });
+ }
+ },
+ );
+ return /* @__PURE__ */ createElement("div", {
+ class: "width-100 height-100",
+ "children:set": sidePaneContent,
+ });
+ }
+ },
+ );
+ const taskSettingsModal = createProxyState(
+ [calendarPageViewModel.selectedTaskViewModel],
+ () => {
+ if (
+ calendarPageViewModel.selectedTaskViewModel.value == void 0
+ ) {
+ return /* @__PURE__ */ createElement("div", null);
+ } else {
+ return TaskSettingsModal(
+ calendarPageViewModel.selectedTaskViewModel.value,
+ );
+ }
+ },
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { id: "calendar-page" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane-wrapper grid-pane-wrapper" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "toolbar" },
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.calendar
+ .todayButtonAudioLabel,
+ "on:click": calendarPageViewModel.showToday,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "today",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.calendar
+ .previousMonthButtonAudioLabel,
+ "on:click":
+ calendarPageViewModel.showPreviousMonth,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_back",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "input-wrapper" },
+ /* @__PURE__ */ createElement("input", {
+ class: "year-input",
+ type: "number",
+ "aria-label":
+ translations.chatPage.calendar
+ .yearInputAudioLabel,
+ placeholder:
+ translations.chatPage.calendar
+ .yearInputPlaceholder,
+ "bind:value":
+ calendarPageViewModel.selectedYear,
+ }),
+ /* @__PURE__ */ createElement("input", {
+ class: "month-input",
+ type: "number",
+ "aria-label":
+ translations.chatPage.calendar
+ .monthInputAudioLabel,
+ placeholder:
+ translations.chatPage.calendar
+ .monthInputPlaceholder,
+ "bind:value":
+ calendarPageViewModel.selectedMonth,
+ }),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.calendar
+ .nextMonthButtonAudioLabel,
+ "on:click":
+ calendarPageViewModel.showNextMonth,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.task
+ .createTaskButtonAudioLabel,
+ "on:click":
+ calendarPageViewModel.createEvent,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "add",
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "content padding-0",
+ "children:set": mainContent,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "pane-wrapper side background",
+ "set:color":
+ calendarPageViewModel.chatViewModel.displayedColor,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane" },
+ /* @__PURE__ */ createElement("div", {
+ class: "content",
+ "children:set": sidePaneContentWrapper,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ "children:set": taskSettingsModal,
+ }),
+ );
+ }
+
+ // src/View/Components/ribbonButton.tsx
+ function RibbonButton(label, icon, isSelected, select) {
+ return /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ribbon-button",
+ "aria-label": label,
+ "toggle:selected": isSelected,
+ "on:click": select,
+ },
+ /* @__PURE__ */ createElement("span", { class: "icon" }, icon),
+ );
+ }
+
+ // src/View/Components/chatViewToggleButton.tsx
+ function ChatViewToggleButton(label, icon, page, chatViewModel) {
+ function select() {
+ chatViewModel.selectedPage.value = page;
+ }
+ const isSelected = createProxyState(
+ [chatViewModel.selectedPage],
+ () => chatViewModel.selectedPage.value == page,
+ );
+ return RibbonButton(label, icon, isSelected, select);
+ }
+
+ // src/View/Modals/chatMessageInfoModal.tsx
+ function ChatMessageInfoModal(chatMessageViewModel) {
+ return /* @__PURE__ */ createElement(
"div",
{
- class: "width-100 height-100",
- "children:set": sidePaneContent
- }
- );
- }
- }
- );
- const taskSettingsModal = createProxyState(
- [calendarPageViewModel.selectedTaskViewModel],
- () => {
- if (calendarPageViewModel.selectedTaskViewModel.value == void 0) {
- return /* @__PURE__ */ createElement("div", null);
- } else {
- return TaskSettingsModal(
- calendarPageViewModel.selectedTaskViewModel.value
- );
- }
- }
- );
- return /* @__PURE__ */ createElement("div", { id: "calendar-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper grid-pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.calendar.todayButtonAudioLabel,
- "on:click": calendarPageViewModel.showToday
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "today")
- )), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.calendar.previousMonthButtonAudioLabel,
- "on:click": calendarPageViewModel.showPreviousMonth
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_back")
- ), /* @__PURE__ */ createElement("span", { class: "input-wrapper" }, /* @__PURE__ */ createElement(
- "input",
- {
- class: "year-input",
- type: "number",
- "aria-label": translations.chatPage.calendar.yearInputAudioLabel,
- placeholder: translations.chatPage.calendar.yearInputPlaceholder,
- "bind:value": calendarPageViewModel.selectedYear
- }
- ), /* @__PURE__ */ createElement(
- "input",
- {
- class: "month-input",
- type: "number",
- "aria-label": translations.chatPage.calendar.monthInputAudioLabel,
- placeholder: translations.chatPage.calendar.monthInputPlaceholder,
- "bind:value": calendarPageViewModel.selectedMonth
- }
- )), /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.calendar.nextMonthButtonAudioLabel,
- "on:click": calendarPageViewModel.showNextMonth
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
- )), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.task.createTaskButtonAudioLabel,
- "on:click": calendarPageViewModel.createEvent
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "add")
- ))), /* @__PURE__ */ createElement(
- "div",
- {
- class: "content padding-0",
- "children:set": mainContent
- }
- ))), /* @__PURE__ */ createElement(
- "div",
- {
- class: "pane-wrapper side background",
- "set:color": calendarPageViewModel.chatViewModel.displayedColor
- },
- /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement(
- "div",
- {
- class: "content",
- "children:set": sidePaneContentWrapper
- }
- ))
- ), /* @__PURE__ */ createElement("div", { "children:set": taskSettingsModal }));
- }
-
- // src/View/Components/ribbonButton.tsx
- function RibbonButton(label, icon, isSelected, select) {
- return /* @__PURE__ */ createElement(
- "button",
- {
- class: "ribbon-button",
- "aria-label": label,
- "toggle:selected": isSelected,
- "on:click": select
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, icon)
- );
- }
-
- // src/View/Components/chatViewToggleButton.tsx
- function ChatViewToggleButton(label, icon, page, chatViewModel) {
- function select() {
- chatViewModel.selectedPage.value = page;
+ class: "modal",
+ "toggle:open": chatMessageViewModel.isPresentingInfoModal,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.chatPage.message.messageInfoHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-column gap" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "account_circle",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.message.sentBy,
+ ),
+ /* @__PURE__ */ createElement(
+ "b",
+ { class: "break-word" },
+ chatMessageViewModel.sender,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "schedule",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.message.timeSent,
+ ),
+ /* @__PURE__ */ createElement(
+ "b",
+ { class: "break-word" },
+ chatMessageViewModel.dateSent,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "forum",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.message.channel,
+ ),
+ /* @__PURE__ */ createElement(
+ "b",
+ { class: "break-word" },
+ chatMessageViewModel.channel,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "description",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.message
+ .messageContent,
+ ),
+ /* @__PURE__ */ createElement("b", {
+ class: "break-word",
+ "subscribe:innerText":
+ chatMessageViewModel.body,
+ }),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-column gap" },
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": chatMessageViewModel.copyMessage },
+ translations.chatPage.message.copyMessageButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "content_copy",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": chatMessageViewModel.resendMessage },
+ translations.chatPage.message.resendMessageButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "redo",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": chatMessageViewModel.decryptMessage },
+ translations.chatPage.message.decryptMessageButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "key",
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": chatMessageViewModel.hideInfoModal },
+ translations.general.closeButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ ),
+ );
}
- const isSelected = createProxyState(
- [chatViewModel.selectedPage],
- () => chatViewModel.selectedPage.value == page
- );
- return RibbonButton(label, icon, isSelected, select);
- }
- // src/View/Modals/chatMessageInfoModal.tsx
- function ChatMessageInfoModal(chatMessageViewModel) {
- return /* @__PURE__ */ createElement(
- "div",
- {
- class: "modal",
- "toggle:open": chatMessageViewModel.isPresentingInfoModal
- },
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.message.messageInfoHeadline), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.sentBy), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.sender))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "schedule"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.timeSent), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.dateSent))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.channel), /* @__PURE__ */ createElement("b", { class: "break-word" }, chatMessageViewModel.channel))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "description"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.message.messageContent), /* @__PURE__ */ createElement(
- "b",
- {
- class: "break-word",
- "subscribe:innerText": chatMessageViewModel.body
- }
- )))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.copyMessage }, translations.chatPage.message.copyMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "content_copy")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.resendMessage }, translations.chatPage.message.resendMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "redo")), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.decryptMessage }, translations.chatPage.message.decryptMessageButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "key")))), /* @__PURE__ */ createElement("button", { "on:click": chatMessageViewModel.hideInfoModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close")))
- );
- }
-
- // src/View/Components/chatMessage.tsx
- function ChatMessage2(chatMessageViewModel) {
- const statusIcon = createProxyState(
- [chatMessageViewModel.status],
- () => {
- switch (chatMessageViewModel.status.value) {
- case "outbox" /* Outbox */:
- return "hourglass_top";
- case "sent" /* Sent */:
- return "check";
- case "received" /* Received */:
- return "done_all";
- default:
- return "warning";
- }
- }
- );
- return /* @__PURE__ */ createElement(
- "div",
- {
- class: "message-bubble",
- "toggle:sentbyuser": chatMessageViewModel.sentByUser
- },
- /* @__PURE__ */ createElement("div", { class: "main tile" }, /* @__PURE__ */ createElement("div", { class: "text-container" }, /* @__PURE__ */ createElement("span", { class: "sender-name ellipsis" }, chatMessageViewModel.sender), /* @__PURE__ */ createElement(
- "span",
- {
- class: "body",
- "subscribe:innerText": chatMessageViewModel.body
- }
- ), /* @__PURE__ */ createElement("span", { class: "timestamp ellipsis" }, /* @__PURE__ */ createElement(
- "span",
- {
- class: "icon",
- "subscribe:innerText": statusIcon
- }
- ), chatMessageViewModel.dateSent)), /* @__PURE__ */ createElement("div", { class: "button-container" }, /* @__PURE__ */ createElement(
- "button",
- {
- "on:click": chatMessageViewModel.showInfoModal,
- "aria-label": translations.chatPage.message.showMessageInfoButtonAudioLabel
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "info")
- ))),
- ChatMessageInfoModal(chatMessageViewModel)
- );
- }
- var ChatMessageViewModelToView = (chatMessageViewModel) => {
- return ChatMessage2(chatMessageViewModel);
- };
-
- // src/View/ChatPages/messagePage.tsx
- function MessagePage(messagePageViewModel) {
- messagePageViewModel.loadData();
- const messageContainer = /* @__PURE__ */ createElement(
- "div",
- {
- id: "message-container",
- "children:append": [
- messagePageViewModel.chatMessageViewModels,
- ChatMessageViewModelToView
- ]
- }
- );
- function scrollDown() {
- messageContainer.scrollTop = messageContainer.scrollHeight;
+ // src/View/Components/chatMessage.tsx
+ function ChatMessage2(chatMessageViewModel) {
+ const statusIcon = createProxyState(
+ [chatMessageViewModel.status],
+ () => {
+ switch (chatMessageViewModel.status.value) {
+ case "outbox" /* Outbox */:
+ return "hourglass_top";
+ case "sent" /* Sent */:
+ return "check";
+ case "received" /* Received */:
+ return "done_all";
+ default:
+ return "warning";
+ }
+ },
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "message-bubble",
+ "toggle:sentbyuser": chatMessageViewModel.sentByUser,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "main tile" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "text-container" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "sender-name ellipsis" },
+ chatMessageViewModel.sender,
+ ),
+ /* @__PURE__ */ createElement("span", {
+ class: "body",
+ "subscribe:innerText": chatMessageViewModel.body,
+ }),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "timestamp ellipsis" },
+ /* @__PURE__ */ createElement("span", {
+ class: "icon",
+ "subscribe:innerText": statusIcon,
+ }),
+ chatMessageViewModel.dateSent,
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "button-container" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ "on:click": chatMessageViewModel.showInfoModal,
+ "aria-label":
+ translations.chatPage.message
+ .showMessageInfoButtonAudioLabel,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "info",
+ ),
+ ),
+ ),
+ ),
+ ChatMessageInfoModal(chatMessageViewModel),
+ );
}
- function scrollDownIfApplicable() {
- const scrollFromBottom = messageContainer.scrollHeight - (messageContainer.scrollTop + messageContainer.offsetHeight);
- if (scrollFromBottom > 400) return;
- scrollDown();
- }
- messagePageViewModel.chatMessageViewModels.subscribeSilent(
- scrollDownIfApplicable
- );
- setTimeout(() => scrollDown(), 100);
- return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, translations.chatPage.message.messagesHeadline)), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement(
- "input",
- {
- "bind:value": messagePageViewModel.composingMessage,
- "on:enter": messagePageViewModel.sendMessage,
- placeholder: translations.chatPage.message.composerInputPlaceholder
- }
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "aria-label": translations.chatPage.message.sendMessageButtonAudioLabel,
- "on:click": messagePageViewModel.sendMessage,
- "toggle:disabled": messagePageViewModel.cannotSendMessage
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "send")
- ))))))));
- }
-
- // src/View/Components/colorPicker.tsx
- function ColorPicker(selectedColor) {
- return /* @__PURE__ */ createElement("div", { class: "flex-row gap width-input" }, ...Object.values(Color).map((color) => {
- const isSelected = createProxyState(
- [selectedColor],
- () => selectedColor.value == color
- );
- function setColor() {
- selectedColor.value = color;
- }
- return /* @__PURE__ */ createElement(
- "button",
- {
- color,
- class: "fill-color width-100 flex",
- style: "height: 2rem",
- "toggle:selected": isSelected,
- "on:click": setColor
- }
- );
- }));
- }
-
- // src/View/Components/deletableListItem.tsx
- function DeletableListItem(text, primaryButton, ondelete) {
- return /* @__PURE__ */ createElement("div", { class: "tile flex-row justify-apart align-center padding-0" }, /* @__PURE__ */ createElement("span", { class: "padding-h ellipsis" }, text), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, primaryButton, /* @__PURE__ */ createElement(
- "button",
- {
- class: "danger",
- "aria-label": translations.general.deleteItemButtonAudioLabel,
- "on:click": ondelete
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "delete")
- )));
- }
-
- // src/View/ChatPages/settingsPage.tsx
- function SettingsPage(settingsPageViewModel) {
- settingsPageViewModel.loadData();
- const secondaryChannelConverter = (secondaryChannel) => {
- return DeletableListItem(secondaryChannel, /* @__PURE__ */ createElement("span", null), () => {
- settingsPageViewModel.removeSecondaryChannel(secondaryChannel);
- });
+ var ChatMessageViewModelToView = (chatMessageViewModel) => {
+ return ChatMessage2(chatMessageViewModel);
};
- return /* @__PURE__ */ createElement("div", { id: "settings-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, translations.chatPage.settings.settingsHeadline)), /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.primaryChannelLabel), /* @__PURE__ */ createElement(
- "input",
- {
- "bind:value": settingsPageViewModel.primaryChannelInput,
- "on:enter": settingsPageViewModel.setPrimaryChannel
- }
- ))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "width-50",
- "aria-label": translations.chatPage.settings.setPrimaryChannelButtonAudioLabel,
- "on:click": settingsPageViewModel.setPrimaryChannel,
- "toggle:disabled": settingsPageViewModel.cannotSetPrimaryChannel
- },
- translations.general.setButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "check")
- )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row width-input margin-bottom" }, /* @__PURE__ */ createElement(
- "input",
- {
- "aria-label": translations.chatPage.settings.newSecondaryChannelAudioLabel,
- placeholder: translations.chatPage.settings.newSecondaryChannelPlaceholder,
- "bind:value": settingsPageViewModel.newSecondaryChannelInput,
- "on:enter": settingsPageViewModel.addSecondaryChannel
- }
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "aria-label": translations.chatPage.settings.addSecondaryChannelButtonAudioLabel,
- "on:click": settingsPageViewModel.addSecondaryChannel,
- "toggle:disabled": settingsPageViewModel.cannotAddSecondaryChannel
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "add")
- )), /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column gap width-input",
- "children:append": [
- settingsPageViewModel.secondaryChannels,
- secondaryChannelConverter
- ]
- }
- ), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.settings.encryptionKeyLabel), /* @__PURE__ */ createElement(
- "input",
- {
- "bind:value": settingsPageViewModel.encryptionKeyInput,
- "on:enter": settingsPageViewModel.setEncryptionKey,
- "set:type": settingsPageViewModel.encryptionKeyInputType
- }
- ))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end width-input" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "width-50",
- "aria-label": translations.chatPage.settings.setEncryptionKeyButtonAudioLabel,
- "on:click": settingsPageViewModel.setEncryptionKey,
- "toggle:disabled": settingsPageViewModel.cannotSetEncryptionKey
- },
- translations.general.setButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "check")
- )), /* @__PURE__ */ createElement("label", { class: "inline" }, /* @__PURE__ */ createElement(
- "input",
- {
- type: "checkbox",
- "bind:checked": settingsPageViewModel.shouldShowEncryptionKey
- }
- ), translations.chatPage.settings.showEncryptionKey), /* @__PURE__ */ createElement("hr", null), ColorPicker(settingsPageViewModel.color), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
- translations.chatPage.settings.deleteChatButton,
- "chat_error",
- settingsPageViewModel.remove
- ))))));
- }
- // src/View/Components/propertyValueList.tsx
- function PropertyValueList(propertyKey, stringEntryObjectConverter, objects, viewBuilder) {
- const propertyValues = new ListState();
- const sortedPropertyValues = createSortedPropertyValueState(propertyValues);
- objects.subscribe(() => {
- collectPropertyValuesToState(
+ // src/View/ChatPages/messagePage.tsx
+ function MessagePage(messagePageViewModel) {
+ messagePageViewModel.loadData();
+ const messageContainer = /* @__PURE__ */ createElement("div", {
+ id: "message-container",
+ "children:append": [
+ messagePageViewModel.chatMessageViewModels,
+ ChatMessageViewModelToView,
+ ],
+ });
+ function scrollDown() {
+ messageContainer.scrollTop = messageContainer.scrollHeight;
+ }
+ function scrollDownIfApplicable() {
+ const scrollFromBottom =
+ messageContainer.scrollHeight -
+ (messageContainer.scrollTop + messageContainer.offsetHeight);
+ if (scrollFromBottom > 400) return;
+ scrollDown();
+ }
+ messagePageViewModel.chatMessageViewModels.subscribeSilent(
+ scrollDownIfApplicable,
+ );
+ setTimeout(() => scrollDown(), 100);
+ return /* @__PURE__ */ createElement(
+ "div",
+ { id: "message-page" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane-wrapper" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "toolbar" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "title" },
+ translations.chatPage.message.messagesHeadline,
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "content" },
+ messageContainer,
+ /* @__PURE__ */ createElement(
+ "div",
+ { id: "composer" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "content-width-constraint" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "input-width-constraint" },
+ /* @__PURE__ */ createElement("input", {
+ "bind:value":
+ messagePageViewModel.composingMessage,
+ "on:enter":
+ messagePageViewModel.sendMessage,
+ placeholder:
+ translations.chatPage.message
+ .composerInputPlaceholder,
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "aria-label":
+ translations.chatPage.message
+ .sendMessageButtonAudioLabel,
+ "on:click":
+ messagePageViewModel.sendMessage,
+ "toggle:disabled":
+ messagePageViewModel.cannotSendMessage,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "send",
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ // src/View/Components/colorPicker.tsx
+ function ColorPicker(selectedColor) {
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row gap width-input" },
+ ...Object.values(Color).map((color) => {
+ const isSelected = createProxyState(
+ [selectedColor],
+ () => selectedColor.value == color,
+ );
+ function setColor() {
+ selectedColor.value = color;
+ }
+ return /* @__PURE__ */ createElement("button", {
+ color,
+ class: "fill-color width-100 flex",
+ style: "height: 2rem",
+ "toggle:selected": isSelected,
+ "on:click": setColor,
+ });
+ }),
+ );
+ }
+
+ // src/View/Components/deletableListItem.tsx
+ function DeletableListItem(text, primaryButton, ondelete) {
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile flex-row justify-apart align-center padding-0" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "padding-h ellipsis" },
+ text,
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row justify-end" },
+ primaryButton,
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "danger",
+ "aria-label":
+ translations.general.deleteItemButtonAudioLabel,
+ "on:click": ondelete,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "delete",
+ ),
+ ),
+ ),
+ );
+ }
+
+ // src/View/ChatPages/settingsPage.tsx
+ function SettingsPage(settingsPageViewModel) {
+ settingsPageViewModel.loadData();
+ const secondaryChannelConverter = (secondaryChannel) => {
+ return DeletableListItem(
+ secondaryChannel,
+ /* @__PURE__ */ createElement("span", null),
+ () => {
+ settingsPageViewModel.removeSecondaryChannel(
+ secondaryChannel,
+ );
+ },
+ );
+ };
+ return /* @__PURE__ */ createElement(
+ "div",
+ { id: "settings-page" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane-wrapper" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "toolbar" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "title" },
+ translations.chatPage.settings.settingsHeadline,
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "content" },
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "forum",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.settings
+ .primaryChannelLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "bind:value":
+ settingsPageViewModel.primaryChannelInput,
+ "on:enter":
+ settingsPageViewModel.setPrimaryChannel,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row justify-end width-input" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "width-50",
+ "aria-label":
+ translations.chatPage.settings
+ .setPrimaryChannelButtonAudioLabel,
+ "on:click":
+ settingsPageViewModel.setPrimaryChannel,
+ "toggle:disabled":
+ settingsPageViewModel.cannotSetPrimaryChannel,
+ },
+ translations.general.setButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "check",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-input margin-bottom" },
+ /* @__PURE__ */ createElement("input", {
+ "aria-label":
+ translations.chatPage.settings
+ .newSecondaryChannelAudioLabel,
+ placeholder:
+ translations.chatPage.settings
+ .newSecondaryChannelPlaceholder,
+ "bind:value":
+ settingsPageViewModel.newSecondaryChannelInput,
+ "on:enter":
+ settingsPageViewModel.addSecondaryChannel,
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "aria-label":
+ translations.chatPage.settings
+ .addSecondaryChannelButtonAudioLabel,
+ "on:click":
+ settingsPageViewModel.addSecondaryChannel,
+ "toggle:disabled":
+ settingsPageViewModel.cannotAddSecondaryChannel,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "add",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "flex-column gap width-input",
+ "children:append": [
+ settingsPageViewModel.secondaryChannels,
+ secondaryChannelConverter,
+ ],
+ }),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "key",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.settings
+ .encryptionKeyLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "bind:value":
+ settingsPageViewModel.encryptionKeyInput,
+ "on:enter":
+ settingsPageViewModel.setEncryptionKey,
+ "set:type":
+ settingsPageViewModel.encryptionKeyInputType,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row justify-end width-input" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "width-50",
+ "aria-label":
+ translations.chatPage.settings
+ .setEncryptionKeyButtonAudioLabel,
+ "on:click":
+ settingsPageViewModel.setEncryptionKey,
+ "toggle:disabled":
+ settingsPageViewModel.cannotSetEncryptionKey,
+ },
+ translations.general.setButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "check",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "inline" },
+ /* @__PURE__ */ createElement("input", {
+ type: "checkbox",
+ "bind:checked":
+ settingsPageViewModel.shouldShowEncryptionKey,
+ }),
+ translations.chatPage.settings.showEncryptionKey,
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ ColorPicker(settingsPageViewModel.color),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "width-input" },
+ DangerousActionButton(
+ translations.chatPage.settings.deleteChatButton,
+ "chat_error",
+ settingsPageViewModel.remove,
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ // src/View/Components/propertyValueList.tsx
+ function PropertyValueList(
propertyKey,
stringEntryObjectConverter,
objects,
- propertyValues
- );
- });
- return viewBuilder(propertyValues, sortedPropertyValues);
- }
- function collectPropertyValuesToState(propertyKey, stringEntryObjectConverter, objects, propertyValues) {
- const values = collectObjectValuesForKey(
- propertyKey,
- stringEntryObjectConverter,
- [...objects.value.values()]
- );
- for (const existingValue of values) {
- if (propertyValues.value.has(existingValue)) continue;
- propertyValues.add(existingValue);
- }
- for (const displayedValue of propertyValues.value.values()) {
- if (values.includes(displayedValue) == false) {
- propertyValues.remove(displayedValue);
- }
- }
- }
- function createSortedPropertyValueState(propertyValues) {
- return createProxyState(
- [propertyValues],
- () => [...propertyValues.value.values()].sort(localeCompare)
- );
- }
- function createPropertyValueIndexState(sortedKeys, key) {
- return createProxyState(
- [sortedKeys],
- () => sortedKeys.value.indexOf(key)
- );
- }
-
- // src/View/Components/filteredList.tsx
- function FilteredList(reference, stringEntryObjectConverter, objects, viewBuilder) {
- const matchingObjects = new ListState();
- objects.handleAddition((newObject) => {
- const doesMatch = checkDoesObjectMatchReference(
- reference,
- stringEntryObjectConverter(newObject)
- );
- if (doesMatch == false) return;
- matchingObjects.add(newObject);
- objects.handleRemoval(newObject, () => {
- matchingObjects.remove(newObject);
- });
- });
- return viewBuilder(matchingObjects);
- }
-
- // src/ViewModel/Utility/taskPropertyBulkChangeViewModel.ts
- var TaskPropertyBulkChangeViewModel = class {
- // init
- constructor(taskViewModels, valueSetter, initialValue) {
- // state
- this.inputValue = new State("");
- // methods
- this.set = () => {
- if (this.cannotSet.value == true) return;
- this.taskViewModels.value.forEach((taskViewModel) => {
- this.setValue(this.inputValue.value, taskViewModel);
+ viewBuilder,
+ ) {
+ const propertyValues = new ListState();
+ const sortedPropertyValues =
+ createSortedPropertyValueState(propertyValues);
+ objects.subscribe(() => {
+ collectPropertyValuesToState(
+ propertyKey,
+ stringEntryObjectConverter,
+ objects,
+ propertyValues,
+ );
});
- };
- this.taskViewModels = taskViewModels;
- this.inputValue.value = initialValue;
- this.setValue = valueSetter;
- this.cannotSet = createProxyState(
- [this.inputValue],
- () => this.inputValue.value == "" || this.inputValue.value == initialValue
- );
+ return viewBuilder(propertyValues, sortedPropertyValues);
}
- };
- var TaskCategoryBulkChangeViewModel = class extends TaskPropertyBulkChangeViewModel {
- constructor(taskViewModels, initialValue) {
- super(
- taskViewModels,
- (newCategory, taskViewModel) => {
- taskViewModel.category.value = newCategory;
- taskViewModel.save();
- },
- initialValue
- );
- }
- };
- var TaskStatusBulkChangeViewModel = class extends TaskPropertyBulkChangeViewModel {
- constructor(taskViewModels, initialValue) {
- super(
- taskViewModels,
- (newStatus, taskViewModel) => {
- taskViewModel.status.value = newStatus;
- taskViewModel.save();
- },
- initialValue
- );
- }
- };
-
- // src/View/ChatPages/boardKanbanPage.tsx
- function BoardKanbanPage(boardViewModel) {
- return PropertyValueList(
- "category",
- (taskViewModel) => taskViewModel.task,
- boardViewModel.taskViewModels,
- (categories, sortedCategories) => {
- const categoryNameConverter = (categoryName) => {
- const index = createPropertyValueIndexState(
- sortedCategories,
- categoryName
- );
- return KanbanBoard(categoryName, index, boardViewModel);
- };
- return /* @__PURE__ */ createElement(
- "div",
- {
- class: "kanban-board-wrapper",
- "children:append": [categories, categoryNameConverter]
- }
+ function collectPropertyValuesToState(
+ propertyKey,
+ stringEntryObjectConverter,
+ objects,
+ propertyValues,
+ ) {
+ const values = collectObjectValuesForKey(
+ propertyKey,
+ stringEntryObjectConverter,
+ [...objects.value.values()],
);
- }
- );
- }
- function KanbanBoard(categoryName, index, boardViewModel) {
- return FilteredList(
- { category: categoryName },
- (taskViewModel) => taskViewModel.task,
- boardViewModel.filteredTaskViewModels,
- (taskViewModels) => {
- const viewModel = new TaskCategoryBulkChangeViewModel(
- taskViewModels,
- categoryName
- );
- function drop() {
- boardViewModel.handleDropWithinBoard(categoryName);
+ for (const existingValue of values) {
+ if (propertyValues.value.has(existingValue)) continue;
+ propertyValues.add(existingValue);
}
- const view = /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column flex-no",
- "on:dragover": allowDrop,
- "on:drop": drop
- },
- /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
- "input",
- {
- placeholder: translations.chatPage.task.renameCategoryInputPlaceholder,
- "bind:value": viewModel.inputValue,
- "on:enter": viewModel.set
+ for (const displayedValue of propertyValues.value.values()) {
+ if (values.includes(displayedValue) == false) {
+ propertyValues.remove(displayedValue);
}
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "on:click": viewModel.set,
- "toggle:disabled": viewModel.cannotSet
+ }
+ }
+ function createSortedPropertyValueState(propertyValues) {
+ return createProxyState([propertyValues], () =>
+ [...propertyValues.value.values()].sort(localeCompare),
+ );
+ }
+ function createPropertyValueIndexState(sortedKeys, key) {
+ return createProxyState([sortedKeys], () =>
+ sortedKeys.value.indexOf(key),
+ );
+ }
+
+ // src/View/Components/filteredList.tsx
+ function FilteredList(
+ reference,
+ stringEntryObjectConverter,
+ objects,
+ viewBuilder,
+ ) {
+ const matchingObjects = new ListState();
+ objects.handleAddition((newObject) => {
+ const doesMatch = checkDoesObjectMatchReference(
+ reference,
+ stringEntryObjectConverter(newObject),
+ );
+ if (doesMatch == false) return;
+ matchingObjects.add(newObject);
+ objects.handleRemoval(newObject, () => {
+ matchingObjects.remove(newObject);
+ });
+ });
+ return viewBuilder(matchingObjects);
+ }
+
+ // src/ViewModel/Utility/taskPropertyBulkChangeViewModel.ts
+ var TaskPropertyBulkChangeViewModel = class {
+ // init
+ constructor(taskViewModels, valueSetter, initialValue) {
+ // state
+ this.inputValue = new State("");
+ // methods
+ this.set = () => {
+ if (this.cannotSet.value == true) return;
+ this.taskViewModels.value.forEach((taskViewModel) => {
+ this.setValue(this.inputValue.value, taskViewModel);
+ });
+ };
+ this.taskViewModels = taskViewModels;
+ this.inputValue.value = initialValue;
+ this.setValue = valueSetter;
+ this.cannotSet = createProxyState(
+ [this.inputValue],
+ () =>
+ this.inputValue.value == "" ||
+ this.inputValue.value == initialValue,
+ );
+ }
+ };
+ var TaskCategoryBulkChangeViewModel = class extends TaskPropertyBulkChangeViewModel {
+ constructor(taskViewModels, initialValue) {
+ super(
+ taskViewModels,
+ (newCategory, taskViewModel) => {
+ taskViewModel.category.value = newCategory;
+ taskViewModel.save();
+ },
+ initialValue,
+ );
+ }
+ };
+ var TaskStatusBulkChangeViewModel = class extends TaskPropertyBulkChangeViewModel {
+ constructor(taskViewModels, initialValue) {
+ super(
+ taskViewModels,
+ (newStatus, taskViewModel) => {
+ taskViewModel.status.value = newStatus;
+ taskViewModel.save();
+ },
+ initialValue,
+ );
+ }
+ };
+
+ // src/View/ChatPages/boardKanbanPage.tsx
+ function BoardKanbanPage(boardViewModel) {
+ return PropertyValueList(
+ "category",
+ (taskViewModel) => taskViewModel.task,
+ boardViewModel.taskViewModels,
+ (categories, sortedCategories) => {
+ const categoryNameConverter = (categoryName) => {
+ const index = createPropertyValueIndexState(
+ sortedCategories,
+ categoryName,
+ );
+ return KanbanBoard(categoryName, index, boardViewModel);
+ };
+ return /* @__PURE__ */ createElement("div", {
+ class: "kanban-board-wrapper",
+ "children:append": [categories, categoryNameConverter],
+ });
},
- /* @__PURE__ */ createElement("span", { class: "icon" }, "check")
- )),
- /* @__PURE__ */ createElement("hr", null),
- /* @__PURE__ */ createElement(
+ );
+ }
+ function KanbanBoard(categoryName, index, boardViewModel) {
+ return FilteredList(
+ { category: categoryName },
+ (taskViewModel) => taskViewModel.task,
+ boardViewModel.filteredTaskViewModels,
+ (taskViewModels) => {
+ const viewModel = new TaskCategoryBulkChangeViewModel(
+ taskViewModels,
+ categoryName,
+ );
+ function drop() {
+ boardViewModel.handleDropWithinBoard(categoryName);
+ }
+ const view = /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "flex-column flex-no",
+ "on:dragover": allowDrop,
+ "on:drop": drop,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-input" },
+ /* @__PURE__ */ createElement("input", {
+ placeholder:
+ translations.chatPage.task
+ .renameCategoryInputPlaceholder,
+ "bind:value": viewModel.inputValue,
+ "on:enter": viewModel.set,
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "on:click": viewModel.set,
+ "toggle:disabled": viewModel.cannotSet,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "check",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement("div", {
+ class: "kanban-column",
+ "children:append": [
+ taskViewModels,
+ TaskViewModelToEntry,
+ ],
+ }),
+ );
+ index.subscribe((newIndex) => {
+ view.style.order = newIndex;
+ });
+ return view;
+ },
+ );
+ }
+
+ // src/View/Modals/boardSettingsModal.tsx
+ function BoardSettingsModal(boardViewModel) {
+ return /* @__PURE__ */ createElement(
"div",
{
- class: "kanban-column",
- "children:append": [taskViewModels, TaskViewModelToEntry]
- }
- )
+ class: "modal",
+ "toggle:open": boardViewModel.isPresentingSettingsModal,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.chatPage.task.boardSettingsHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "label",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.chatPage.task.boardNameInputLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "on:enter": boardViewModel.saveSettings,
+ "bind:value": boardViewModel.name,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ ColorPicker(boardViewModel.color),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "width-input" },
+ DangerousActionButton(
+ translations.chatPage.task.deleteBoardButton,
+ "delete_forever",
+ boardViewModel.deleteBoard,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": boardViewModel.hideSettings },
+ translations.general.closeButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ ),
);
- index.subscribe((newIndex) => {
- view.style.order = newIndex;
+ }
+
+ // src/View/ChatPages/boardStatusGridPage.tsx
+ function BoardStatusGridPage(boardViewModel) {
+ const statuses = new ListState();
+ const sortedStatuses = createSortedPropertyValueState(statuses);
+ boardViewModel.taskViewModels.subscribe(() => {
+ collectPropertyValuesToState(
+ "status",
+ (taskViewModel) => taskViewModel.task,
+ boardViewModel.taskViewModels,
+ statuses,
+ );
});
- return view;
- }
- );
- }
-
- // src/View/Modals/boardSettingsModal.tsx
- function BoardSettingsModal(boardViewModel) {
- return /* @__PURE__ */ createElement(
- "div",
- {
- class: "modal",
- "toggle:open": boardViewModel.isPresentingSettingsModal
- },
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.chatPage.task.boardSettingsHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "label"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.chatPage.task.boardNameInputLabel), /* @__PURE__ */ createElement(
- "input",
- {
- "on:enter": boardViewModel.saveSettings,
- "bind:value": boardViewModel.name
- }
- ))), /* @__PURE__ */ createElement("hr", null), ColorPicker(boardViewModel.color), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "width-input" }, DangerousActionButton(
- translations.chatPage.task.deleteBoardButton,
- "delete_forever",
- boardViewModel.deleteBoard
- ))), /* @__PURE__ */ createElement("button", { "on:click": boardViewModel.hideSettings }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close")))
- );
- }
-
- // src/View/ChatPages/boardStatusGridPage.tsx
- function BoardStatusGridPage(boardViewModel) {
- const statuses = new ListState();
- const sortedStatuses = createSortedPropertyValueState(statuses);
- boardViewModel.taskViewModels.subscribe(() => {
- collectPropertyValuesToState(
- "status",
- (taskViewModel) => taskViewModel.task,
- boardViewModel.taskViewModels,
- statuses
- );
- });
- const statusNameCellConverter = (statusName) => {
- const index = createPropertyValueIndexState(sortedStatuses, statusName);
- return StatusNameCell(statusName, index, boardViewModel);
- };
- return /* @__PURE__ */ createElement("div", { class: "status-page-content" }, /* @__PURE__ */ createElement(
- "div",
- {
- class: "status-name-row",
- "children:append": [statuses, statusNameCellConverter]
- }
- ), PropertyValueList(
- "category",
- (taskViewModel) => taskViewModel.task,
- boardViewModel.filteredTaskViewModels,
- (categories, sortedCategories) => {
- const categoryRowConverter = (categoryName) => {
- const index = createPropertyValueIndexState(
- sortedCategories,
- categoryName
- );
- return CategoryRow(
- categoryName,
- index,
- statuses,
- sortedStatuses,
- boardViewModel
- );
+ const statusNameCellConverter = (statusName) => {
+ const index = createPropertyValueIndexState(
+ sortedStatuses,
+ statusName,
+ );
+ return StatusNameCell(statusName, index, boardViewModel);
};
return /* @__PURE__ */ createElement(
- "div",
- {
- class: "status-grid-wrapper",
- "children:append": [categories, categoryRowConverter]
- }
+ "div",
+ { class: "status-page-content" },
+ /* @__PURE__ */ createElement("div", {
+ class: "status-name-row",
+ "children:append": [statuses, statusNameCellConverter],
+ }),
+ PropertyValueList(
+ "category",
+ (taskViewModel) => taskViewModel.task,
+ boardViewModel.filteredTaskViewModels,
+ (categories, sortedCategories) => {
+ const categoryRowConverter = (categoryName) => {
+ const index = createPropertyValueIndexState(
+ sortedCategories,
+ categoryName,
+ );
+ return CategoryRow(
+ categoryName,
+ index,
+ statuses,
+ sortedStatuses,
+ boardViewModel,
+ );
+ };
+ return /* @__PURE__ */ createElement("div", {
+ class: "status-grid-wrapper",
+ "children:append": [categories, categoryRowConverter],
+ });
+ },
+ ),
);
- }
- ));
- }
- function StatusNameCell(statusName, index, boardViewModel) {
- const taskViewModelsWithMatchingStatus = new ListState();
- boardViewModel.filteredTaskViewModels.handleAddition(
- (taskViewModel) => {
- const doesMatchStatus = taskViewModel.task.status == statusName;
- if (doesMatchStatus == false) return;
- taskViewModelsWithMatchingStatus.add(taskViewModel);
- boardViewModel.filteredTaskViewModels.handleRemoval(
- taskViewModel,
- () => {
- taskViewModelsWithMatchingStatus.remove(taskViewModel);
- }
+ }
+ function StatusNameCell(statusName, index, boardViewModel) {
+ const taskViewModelsWithMatchingStatus = new ListState();
+ boardViewModel.filteredTaskViewModels.handleAddition(
+ (taskViewModel) => {
+ const doesMatchStatus = taskViewModel.task.status == statusName;
+ if (doesMatchStatus == false) return;
+ taskViewModelsWithMatchingStatus.add(taskViewModel);
+ boardViewModel.filteredTaskViewModels.handleRemoval(
+ taskViewModel,
+ () => {
+ taskViewModelsWithMatchingStatus.remove(taskViewModel);
+ },
+ );
+ },
);
- }
- );
- const viewModel = new TaskStatusBulkChangeViewModel(
- taskViewModelsWithMatchingStatus,
- statusName
- );
- const view = /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement(
- "input",
- {
- placeholder: translations.chatPage.task.renameCategoryInputPlaceholder,
- "bind:value": viewModel.inputValue,
- "on:enter": viewModel.set
- }
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "on:click": viewModel.set,
- "toggle:disabled": viewModel.cannotSet
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "check")
- )));
- index.subscribe((newIndex) => {
- view.style.order = newIndex;
- });
- return view;
- }
- function CategoryRow(categoryName, index, allStatuses, sortedStatuses, boardViewModel) {
- return FilteredList(
- { category: categoryName },
- (taskViewModel) => taskViewModel.task,
- boardViewModel.filteredTaskViewModels,
- (taskViewModels) => {
- const statusNameConverter = (statusName) => {
- const index2 = createPropertyValueIndexState(
- sortedStatuses,
- statusName
- );
- return CategoryStatusColumn(
- categoryName,
+ const viewModel = new TaskStatusBulkChangeViewModel(
+ taskViewModelsWithMatchingStatus,
statusName,
- index2,
- boardViewModel,
- taskViewModels
- );
- };
- const viewModel = new TaskCategoryBulkChangeViewModel(
- taskViewModels,
- categoryName
);
- const view = /* @__PURE__ */ createElement("div", { class: "flex-row flex-no large-gap" }, /* @__PURE__ */ createElement("div", { class: "property-input-wrapper" }, /* @__PURE__ */ createElement(
- "input",
- {
- placeholder: translations.chatPage.task.renameCategoryInputPlaceholder,
- "bind:value": viewModel.inputValue,
- "on:enter": viewModel.set
- }
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "on:click": viewModel.set,
- "toggle:disabled": viewModel.cannotSet
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "check")
- )), /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-row large-gap padding-right",
- "children:append": [allStatuses, statusNameConverter]
- }
- ));
+ const view = /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "property-input-wrapper" },
+ /* @__PURE__ */ createElement("input", {
+ placeholder:
+ translations.chatPage.task
+ .renameCategoryInputPlaceholder,
+ "bind:value": viewModel.inputValue,
+ "on:enter": viewModel.set,
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "on:click": viewModel.set,
+ "toggle:disabled": viewModel.cannotSet,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "check",
+ ),
+ ),
+ ),
+ );
index.subscribe((newIndex) => {
- view.style.order = newIndex;
+ view.style.order = newIndex;
});
return view;
- }
- );
- }
- function CategoryStatusColumn(categoryName, statusName, index, boardViewModel, taskViewModelsWithMatchingCategory) {
- const taskViewModels = new ListState();
- taskViewModelsWithMatchingCategory.handleAddition((taskViewModel) => {
- const doesMatchStatus = taskViewModel.status.value == statusName;
- if (doesMatchStatus == false) return;
- taskViewModels.add(taskViewModel);
- taskViewModelsWithMatchingCategory.handleRemoval(taskViewModel, () => {
- taskViewModels.remove(taskViewModel);
- });
- });
- function drop() {
- boardViewModel.handleDropWithinBoard(categoryName, statusName);
}
- const view = /* @__PURE__ */ createElement(
- "div",
- {
- class: "status-column gap",
- "on:dragover": allowDrop,
- "on:drop": drop,
- "children:append": [taskViewModels, TaskViewModelToEntry]
- }
- );
- index.subscribe((newIndex) => {
- view.style.order = newIndex;
- });
- return view;
- }
-
- // src/View/Components/boardViewToggleButton.tsx
- function BoardViewToggleButton(label, icon, page, boardViewModel) {
- function select() {
- boardViewModel.selectedPage.value = page;
+ function CategoryRow(
+ categoryName,
+ index,
+ allStatuses,
+ sortedStatuses,
+ boardViewModel,
+ ) {
+ return FilteredList(
+ { category: categoryName },
+ (taskViewModel) => taskViewModel.task,
+ boardViewModel.filteredTaskViewModels,
+ (taskViewModels) => {
+ const statusNameConverter = (statusName) => {
+ const index2 = createPropertyValueIndexState(
+ sortedStatuses,
+ statusName,
+ );
+ return CategoryStatusColumn(
+ categoryName,
+ statusName,
+ index2,
+ boardViewModel,
+ taskViewModels,
+ );
+ };
+ const viewModel = new TaskCategoryBulkChangeViewModel(
+ taskViewModels,
+ categoryName,
+ );
+ const view = /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row flex-no large-gap" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "property-input-wrapper" },
+ /* @__PURE__ */ createElement("input", {
+ placeholder:
+ translations.chatPage.task
+ .renameCategoryInputPlaceholder,
+ "bind:value": viewModel.inputValue,
+ "on:enter": viewModel.set,
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "on:click": viewModel.set,
+ "toggle:disabled": viewModel.cannotSet,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "check",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "flex-row large-gap padding-right",
+ "children:append": [allStatuses, statusNameConverter],
+ }),
+ );
+ index.subscribe((newIndex) => {
+ view.style.order = newIndex;
+ });
+ return view;
+ },
+ );
}
- const isSelected = createProxyState(
- [boardViewModel.selectedPage],
- () => boardViewModel.selectedPage.value == page
- );
- return RibbonButton(label, icon, isSelected, select);
- }
-
- // src/View/Modals/searchModal.tsx
- function SearchModal(searchViewModel, headline, converter, isOpen) {
- function close() {
- isOpen.value = false;
- }
- const suggestionId = v4_default();
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
- "input",
- {
- placeholder: translations.general.searchLabel,
- "bind:value": searchViewModel.searchInput,
- "on:enter": searchViewModel.applySearch,
- list: suggestionId
- }
- ), /* @__PURE__ */ createElement(
- "datalist",
- {
- hidden: true,
- id: suggestionId,
- "children:append": [
- searchViewModel.suggestions,
- StringToOption
- ]
- }
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "aria-label": translations.general.searchButtonAudioLabel,
- "on:click": searchViewModel.applySearch,
- "toggle:disabled": searchViewModel.cannotApplySearch
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "search")
- )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
- "div",
- {
- class: "grid gap",
- style: "grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))",
- "children:append": [
- searchViewModel.matchingObjects,
- converter
- ]
- }
- )), /* @__PURE__ */ createElement("button", { "on:click": close }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
- }
-
- // src/View/ChatPages/boardPage.tsx
- function BoardPage(boardViewModel) {
- boardViewModel.loadData();
- const mainContent = createProxyState(
- [boardViewModel.selectedPage],
- () => {
- switch (boardViewModel.selectedPage.value) {
- case "kanban" /* Kanban */: {
- return BoardKanbanPage(boardViewModel);
- }
- case "status-grid" /* StatusGrid */: {
- return BoardStatusGridPage(boardViewModel);
- }
- default: {
- return /* @__PURE__ */ createElement(
- "div",
- {
- class: "task-grid",
- "children:append": [
- boardViewModel.filteredTaskViewModels,
- TaskViewModelToEntry
- ]
- }
+ function CategoryStatusColumn(
+ categoryName,
+ statusName,
+ index,
+ boardViewModel,
+ taskViewModelsWithMatchingCategory,
+ ) {
+ const taskViewModels = new ListState();
+ taskViewModelsWithMatchingCategory.handleAddition((taskViewModel) => {
+ const doesMatchStatus = taskViewModel.status.value == statusName;
+ if (doesMatchStatus == false) return;
+ taskViewModels.add(taskViewModel);
+ taskViewModelsWithMatchingCategory.handleRemoval(
+ taskViewModel,
+ () => {
+ taskViewModels.remove(taskViewModel);
+ },
);
- }
- }
- }
- );
- const taskSettingsModal = createProxyState(
- [boardViewModel.selectedTaskViewModel],
- () => {
- if (boardViewModel.selectedTaskViewModel.value == void 0) {
- return /* @__PURE__ */ createElement("div", null);
- } else {
- return TaskSettingsModal(
- boardViewModel.selectedTaskViewModel.value
- );
- }
- }
- );
- return /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost board-close-button",
- "aria-label": translations.chatPage.task.closeBoardButtonAudioLabel,
- "on:click": boardViewModel.close
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_back")
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost board-toggle-button inset-outline",
- "aria-label": translations.chatPage.task.toggleBoardButtonAudioLabel,
- "on:click": boardViewModel.taskPageViewModel.toggleBoardList,
- "toggle:selected": boardViewModel.taskPageViewModel.isShowingBoadList
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "dock_to_right")
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.task.boardSettingsHeadline,
- "on:click": boardViewModel.showSettings
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "settings")
- )), /* @__PURE__ */ createElement("span", { class: "scroll-h ribbon" }, BoardViewToggleButton(
- translations.chatPage.task.listViewButtonAudioLabel,
- "view_list",
- "list" /* List */,
- boardViewModel
- ), BoardViewToggleButton(
- translations.chatPage.task.kanbanViewButtonAudioLabel,
- "view_kanban",
- "kanban" /* Kanban */,
- boardViewModel
- ), BoardViewToggleButton(
- translations.chatPage.task.statusViewButtonAudioLabel,
- "grid_view",
- "status-grid" /* StatusGrid */,
- boardViewModel
- )), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.task.filterTasksButtonAudioLabel,
- "on:click": boardViewModel.showFilterModal
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "filter_alt")
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.task.createTaskButtonAudioLabel,
- "on:click": boardViewModel.createTask
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "add")
- ))), /* @__PURE__ */ createElement("div", { class: "content main-content", "children:set": mainContent }), BoardSettingsModal(boardViewModel), SearchModal(
- boardViewModel.searchViewModel,
- translations.chatPage.task.filterTasksHeadline,
- TaskViewModelToEntry,
- boardViewModel.isPresentingFilterModal
- ), /* @__PURE__ */ createElement("div", { "children:set": taskSettingsModal }));
- }
-
- // src/View/Components/boardEntry.tsx
- function BoardEntry(boardViewModel) {
- const view = /* @__PURE__ */ createElement(
- "button",
- {
- "set:color": boardViewModel.color,
- class: "tile colored-tile",
- "toggle:selected": boardViewModel.isSelected,
- "on:click": boardViewModel.select,
- "on:dragover": allowDrop,
- "on:drop": boardViewModel.handleDropBetweenBoards
- },
- /* @__PURE__ */ createElement(
- "span",
- {
- class: "shadow",
- "subscribe:innerText": boardViewModel.name
- }
- ),
- /* @__PURE__ */ createElement("b", { "subscribe:innerText": boardViewModel.name })
- );
- boardViewModel.index.subscribe((newIndex) => {
- view.style.order = newIndex;
- });
- return view;
- }
- var BoardViewModelToEntry = (boardViewModel) => {
- return BoardEntry(boardViewModel);
- };
-
- // src/View/ChatPages/taskPage.tsx
- function TaskPage(taskPageViewModel) {
- taskPageViewModel.loadData();
- const isShowingBoard = createProxyState(
- [taskPageViewModel.selectedBoardId],
- () => taskPageViewModel.selectedBoardId.value != void 0
- );
- const paneContent = createProxyState(
- [taskPageViewModel.selectedBoardId],
- () => {
- const selectedBoardId = taskPageViewModel.selectedBoardId.value;
- if (selectedBoardId == void 0) {
- return /* @__PURE__ */ createElement("div", { class: "pane align-center justify-center" }, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.chatPage.task.noBoardSelected));
- }
- const selectedBoard = taskPageViewModel.boardViewModels.value.get(selectedBoardId);
- if (selectedBoard == void 0) {
- return /* @__PURE__ */ createElement("div", { class: "pane align-center justify-center" }, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.chatPage.task.boardNotFound));
- }
- return BoardPage(selectedBoard);
- }
- );
- return /* @__PURE__ */ createElement(
- "div",
- {
- id: "task-page",
- "toggle:isshowingboard": isShowingBoard,
- "set:showingboardlist": taskPageViewModel.isShowingBoadList
- },
- /* @__PURE__ */ createElement(
- "div",
- {
- id: "board-list",
- class: "pane-wrapper side background",
- "set:color": taskPageViewModel.chatViewModel.displayedColor
- },
- /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
- "input",
- {
- class: "no-outline",
- "bind:value": taskPageViewModel.newBoardNameInput,
- "on:enter": taskPageViewModel.createBoard,
- placeholder: translations.chatPage.task.newBoardNamePlaceholder
- }
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "aria-label": translations.chatPage.task.createBoardButtonAudioLabel,
- "on:click": taskPageViewModel.createBoard,
- "toggle:disabled": taskPageViewModel.cannotCreateBoard
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "add")
- ))), /* @__PURE__ */ createElement("div", { class: "content" }, /* @__PURE__ */ createElement(
- "div",
- {
- class: "grid gap",
- style: "grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr))",
- "children:append": [
- taskPageViewModel.boardViewModels,
- BoardViewModelToEntry
- ]
- }
- )))
- ),
- /* @__PURE__ */ createElement(
- "div",
- {
- id: "board-content",
- class: "pane-wrapper",
- "children:set": paneContent
- }
- )
- );
- }
-
- // src/View/chatPage.tsx
- function ChatPage(chatViewModel) {
- const mainContent = createProxyState(
- [chatViewModel.selectedPage],
- () => {
- chatViewModel.closeSubPages();
- switch (chatViewModel.selectedPage.value) {
- case "settings" /* Settings */: {
- return SettingsPage(chatViewModel.settingsPageViewModel);
- }
- case "tasks" /* Tasks */: {
- return TaskPage(chatViewModel.taskPageViewModel);
- }
- case "calendar" /* Calendar */: {
- return CalendarPage(chatViewModel.calendarViewModel);
- }
- default: {
- return MessagePage(chatViewModel.messagePageViewModel);
- }
- }
- }
- );
- return /* @__PURE__ */ createElement(
- "article",
- {
- id: "chat-page",
- "set:color": chatViewModel.displayedColor,
- class: "subtle-background"
- },
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { id: "ribbon" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "ghost",
- "aria-label": translations.chatPage.closeChatAudioLabe,
- "on:click": chatViewModel.close
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "close")
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "danger",
- "aria-label": translations.general.restoreConnection,
- "on:click": chatViewModel.connectionViewModel.showConnectionModal,
- "toggle:hidden": chatViewModel.connectionViewModel.isConnected
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "signal_disconnected")
- ), /* @__PURE__ */ createElement("span", null, ChatViewToggleButton(
- translations.chatPage.pages.calendar,
- "calendar_month",
- "calendar" /* Calendar */,
- chatViewModel
- ), ChatViewToggleButton(
- translations.chatPage.pages.tasks,
- "task_alt",
- "tasks" /* Tasks */,
- chatViewModel
- ), ChatViewToggleButton(
- translations.chatPage.pages.messages,
- "forum",
- "messages" /* Messages */,
- chatViewModel
- ), ChatViewToggleButton(
- translations.chatPage.pages.settings,
- "settings",
- "settings" /* Settings */,
- chatViewModel
- ))), /* @__PURE__ */ createElement("div", { id: "main", "children:set": mainContent }))
- );
- }
-
- // src/View/chatPageWrapper.tsx
- function ChatPageWrapper(chatListViewModel2) {
- const chatPageContent = createProxyState(
- [chatListViewModel2.selectedChat],
- () => {
- if (chatListViewModel2.selectedChat.value == void 0) {
- return /* @__PURE__ */ createElement("div", null);
- } else {
- return ChatPage(chatListViewModel2.selectedChat.value);
- }
- }
- );
- return /* @__PURE__ */ createElement("div", { id: "chat-page-wrapper", "children:set": chatPageContent });
- }
-
- // src/View/Modals/connectionModal.tsx
- function ConnectionModal(connectionViewModel2) {
- const previousAddressConverter = (address) => {
- function connnect() {
- connectionViewModel2.connectToAddress(address);
- }
- const cannotConnect = createProxyState(
- [connectionViewModel2.isConnected],
- () => connectionViewModel2.isConnected.value == true && connectionViewModel2.connectionModel.address == address
- );
- return DeletableListItem(
- address,
- /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "on:click": connnect,
- "toggle:disabled": cannotConnect,
- "aria-label": translations.connectionModal.connectButtonAudioLabel
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "link")
- ),
- () => {
- connectionViewModel2.removePreviousAddress(address);
- }
- );
- };
- return /* @__PURE__ */ createElement(
- "div",
- {
- class: "modal",
- "toggle:open": connectionViewModel2.isShowingConnectionModal
- },
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.connectionModal.connectionModalHeadline), /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column gap",
- "children:append": [
- connectionViewModel2.previousAddresses,
- previousAddressConverter
- ]
- }
- )), /* @__PURE__ */ createElement("button", { "on:click": connectionViewModel2.hideConnectionModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close")))
- );
- }
-
- // ../ui-base/index.ts
- var UDNFrontend = class {
- ws;
- // HANDLERS
- connectionHandler = () => {
- };
- disconnectionHandler = () => {
- };
- messageHandler = (data) => {
- };
- mailboxHandler = (mailboxId) => {
- };
- mailboxConnectionHandler = (mailboxId) => {
- };
- mailboxDeleteHandler = (mailboxId) => {
- };
- // INIT
- set onconnect(handler) {
- this.connectionHandler = handler;
- }
- set ondisconnect(handler) {
- this.disconnectionHandler = handler;
- }
- set onmessage(handler) {
- this.messageHandler = handler;
- }
- set onmailboxcreate(handler) {
- this.mailboxHandler = handler;
- }
- set onmailboxconnect(handler) {
- this.mailboxConnectionHandler = handler;
- }
- set onmailboxdelete(handler) {
- this.mailboxDeleteHandler = handler;
- }
- // UTILITY METHODS
- send(messageObject) {
- if (this.ws == void 0) return false;
- if (this.ws.readyState != 1) return false;
- const messageString = JSON.stringify(messageObject);
- this.ws.send(messageString);
- return true;
- }
- // PUBLIC METHODS
- // connection
- connect(address) {
- try {
- this.disconnect();
- this.ws = new WebSocket(address);
- this.ws.addEventListener("open", this.connectionHandler);
- this.ws.addEventListener("close", this.disconnectionHandler);
- this.ws.addEventListener("message", (message) => {
- const dataString = message.data.toString();
- const data = JSON.parse(dataString);
- if (data.assignedMailboxId) {
- return this.mailboxHandler(data.assignedMailboxId);
- } else if (data.connectedMailboxId) {
- return this.mailboxConnectionHandler(data.connectedMailboxId);
- } else if (data.deletedMailbox) {
- return this.mailboxDeleteHandler(data.deletedMailbox);
- } else {
- this.send({ uuid: data.uuid, confirmingMessageReceived: true });
- this.messageHandler(data);
- }
});
- } catch (error) {
- console.error(error);
- }
+ function drop() {
+ boardViewModel.handleDropWithinBoard(categoryName, statusName);
+ }
+ const view = /* @__PURE__ */ createElement("div", {
+ class: "status-column gap",
+ "on:dragover": allowDrop,
+ "on:drop": drop,
+ "children:append": [taskViewModels, TaskViewModelToEntry],
+ });
+ index.subscribe((newIndex) => {
+ view.style.order = newIndex;
+ });
+ return view;
}
- disconnect() {
- this.ws?.close();
- }
- // message
- sendMessage(channel, body) {
- const messageObject = {
- messageChannel: channel,
- messageBody: body
- };
- return this.send(messageObject);
- }
- // subscription
- subscribe(channel) {
- const messageObject = {
- subscribeChannel: channel
- };
- return this.send(messageObject);
- }
- unsubscribe(channel) {
- const messageObject = {
- unsubscribeChannel: channel
- };
- return this.send(messageObject);
- }
- // mailbox
- requestMailbox() {
- const messageObject = {
- requestingMailboxSetup: true
- };
- return this.send(messageObject);
- }
- connectMailbox(mailboxId) {
- const messageObject = {
- requestedMailbox: mailboxId
- };
- return this.send(messageObject);
- }
- deleteMailbox(mailboxId) {
- const messageObject = {
- deletingMailbox: mailboxId
- };
- return this.send(messageObject);
- }
- };
- // src/Model/Global/connectionModel.ts
- var ConnectionModel = class {
- // init
- constructor(storageModel2) {
- this.reconnectInterval = void 0;
- this.shouldAttemptReconnect = false;
- this.connectionChangeHandlerManager = new HandlerManager();
- this.messageHandlerManager = new HandlerManager();
- this.messageSentHandlerManager = new HandlerManager();
- this.channelsToSubscribe = /* @__PURE__ */ new Set();
- // handlers
- this.handleMessage = (data) => {
- this.messageHandlerManager.trigger(data);
- };
- this.handleConnectionChange = () => {
- console.log("connection status:", this.isConnected, this.address);
- this.connectionChangeHandlerManager.trigger();
- if (this.isConnected == false) {
- if (this.shouldAttemptReconnect == false) return;
- this.reconnectInterval = setInterval(() => {
- if (this.isConnected == true) {
- clearInterval(this.reconnectInterval);
- return;
- }
- this.reconnect();
- }, 5e3);
- return;
+ // src/View/Components/boardViewToggleButton.tsx
+ function BoardViewToggleButton(label, icon, page, boardViewModel) {
+ function select() {
+ boardViewModel.selectedPage.value = page;
}
- if (this.address == void 0) return;
- this.storeAddress(this.address);
- this.sendSubscriptionRequest();
- this.sendMessagesInOutbox();
- this.shouldAttemptReconnect = true;
- };
- // connection
- this.connect = (address) => {
- this.udn.connect(address);
- };
- this.disconnect = () => {
- this.udn.disconnect();
- const reconnectAddressPath = StorageModel.getPath(
- "connection" /* ConnectionModel */,
- filePaths.connectionModel.reconnectAddress
+ const isSelected = createProxyState(
+ [boardViewModel.selectedPage],
+ () => boardViewModel.selectedPage.value == page,
);
- this.storageModel.remove(reconnectAddressPath);
- this.shouldAttemptReconnect = false;
- };
- this.reconnect = () => {
- const reconnectAddressPath = this.getReconnectAddressPath();
- const reconnectAddress = this.storageModel.read(reconnectAddressPath);
- if (reconnectAddress == null) return;
- console.log("reconnecting...");
- this.connect(reconnectAddress);
- };
- // mailbox
- this.getMailboxPath = (address) => {
- const mailboxDirPath = StorageModel.getPath(
- "connection" /* ConnectionModel */,
- filePaths.connectionModel.mailboxes
- );
- const mailboxFilePath = [...mailboxDirPath, address];
- return mailboxFilePath;
- };
- this.requestNewMailbox = () => {
- console.log("requesting new mailbox");
- this.udn.requestMailbox();
- };
- this.connectMailbox = () => {
- if (this.address == void 0) return;
- const mailboxId = this.storageModel.read(
- this.getMailboxPath(this.address)
- );
- console.log("connecting mailbox", mailboxId);
- if (mailboxId == null) return this.requestNewMailbox();
- this.udn.connectMailbox(mailboxId);
- };
- this.storeMailbox = (mailboxId) => {
- if (this.address == void 0) return;
- this.storageModel.write(this.getMailboxPath(this.address), mailboxId);
- };
- // subscription
- this.addChannel = (channel) => {
- this.channelsToSubscribe.add(channel);
- this.sendSubscriptionRequest();
- };
- this.sendSubscriptionRequest = () => {
- if (this.isConnected == false) return;
- for (const channel of this.channelsToSubscribe) {
- this.udn.subscribe(channel);
- }
- this.connectMailbox();
- };
- // outbox
- this.getOutboxPath = () => {
- return StorageModel.getPath(
- "connection" /* ConnectionModel */,
- filePaths.connectionModel.outbox
- );
- };
- this.getOutboxMessags = () => {
- const outboxPath = this.getOutboxPath();
- const messageIds = this.storageModel.list(outboxPath);
- let chatMessages = [];
- for (const messageId of messageIds) {
- const chatMessage = this.storageModel.readStringifiable(
- [...outboxPath, messageId],
- ChatMessageReference
- );
- if (chatMessage == null) continue;
- chatMessages.push(chatMessage);
- }
- return chatMessages;
- };
- this.addToOutbox = (chatMessage) => {
- const messagePath = [...this.getOutboxPath(), chatMessage.id];
- this.storageModel.writeStringifiable(messagePath, chatMessage);
- };
- this.removeFromOutbox = (chatMessage) => {
- const messagePath = [...this.getOutboxPath(), chatMessage.id];
- this.storageModel.remove(messagePath);
- };
- this.sendMessagesInOutbox = () => {
- const messages = this.getOutboxMessags();
- for (const message of messages) {
- const isSent = this.tryToSendMessage(message);
- if (isSent == false) return;
- this.removeFromOutbox(message);
- }
- };
- // messaging
- this.sendMessageOrStore = (chatMessage) => {
- const isSent = this.tryToSendMessage(chatMessage);
- if (isSent == true) return;
- this.addToOutbox(chatMessage);
- };
- this.tryToSendMessage = (chatMessage) => {
- const stringifiedBody = stringify(chatMessage);
- const isSent = this.sendPlainMessage(
- chatMessage.channel,
- stringifiedBody
- );
- if (isSent) this.messageSentHandlerManager.trigger(chatMessage);
- return isSent;
- };
- this.sendPlainMessage = (channel, body) => {
- return this.udn.sendMessage(channel, body);
- };
- // storage
- this.getPreviousAddressPath = () => {
- return StorageModel.getPath(
- "connection" /* ConnectionModel */,
- filePaths.connectionModel.previousAddresses
- );
- };
- this.getAddressPath = (address) => {
- const dirPath = this.getPreviousAddressPath();
- return [...dirPath, address];
- };
- this.getReconnectAddressPath = () => {
- return StorageModel.getPath(
- "connection" /* ConnectionModel */,
- filePaths.connectionModel.reconnectAddress
- );
- };
- this.storeAddress = (address) => {
- const addressPath = this.getAddressPath(address);
- this.storageModel.write(addressPath, "");
- const reconnectAddressPath = this.getReconnectAddressPath();
- this.storageModel.write(reconnectAddressPath, address);
- };
- this.removeAddress = (address) => {
- const addressPath = this.getAddressPath(address);
- this.storageModel.remove(addressPath);
- };
- this.udn = new UDNFrontend();
- this.storageModel = storageModel2;
- this.udn.onmessage = (data) => {
- this.handleMessage(data);
- };
- this.udn.onconnect = () => {
- this.handleConnectionChange();
- };
- this.udn.ondisconnect = () => {
- this.handleConnectionChange();
- };
- this.udn.onmailboxcreate = (mailboxId) => {
- console.log("created mailbox", mailboxId);
- this.storeMailbox(mailboxId);
- this.connectMailbox();
- };
- this.udn.onmailboxdelete = (mailboxId) => {
- console.log(`mailbox ${mailboxId} deleted`);
- this.requestNewMailbox();
- };
- this.udn.onmailboxconnect = (mailboxId) => {
- console.log(`using mailbox ${mailboxId}`);
- };
- this.reconnect();
+ return RibbonButton(label, icon, isSelected, select);
}
- // data
- get isConnected() {
- return this.udn.ws != void 0 && this.udn.ws.readyState == 1;
- }
- get address() {
- return this.udn.ws?.url;
- }
- get addresses() {
- const dirPath = this.getPreviousAddressPath();
- return this.storageModel.list(dirPath);
- }
- };
- // src/ViewModel/Global/connectionViewModel.ts
- var ConnectionViewModel = class {
- // init
- constructor(coreViewModel, connectionModel2) {
- this.coreViewModel = coreViewModel;
- // state
- this.serverAddressInput = new State("");
- this.isConnected = new State(false);
- this.isShowingConnectionModal = new State(false);
- this.previousAddresses = new ListState();
- // guards
- this.cannotConnect = createProxyState(
- [this.serverAddressInput, this.isConnected],
- () => this.isConnected.value == true && this.serverAddressInput.value == this.connectionModel.address || this.serverAddressInput.value == ""
- );
- this.cannotDisonnect = createProxyState(
- [this.isConnected],
- () => this.isConnected.value == false
- );
- this.hasNoPreviousConnections = createProxyState(
- [this.previousAddresses],
- () => this.previousAddresses.value.size == 0
- );
- // handlers
- this.connectionChangeHandler = () => {
- this.isConnected.value = this.connectionModel.isConnected;
- if (this.connectionModel.isConnected == false) return;
- if (this.connectionModel.address == void 0) return;
- this.serverAddressInput.value = this.connectionModel.address;
- if (!this.previousAddresses.value.has(this.connectionModel.address)) {
- this.previousAddresses.add(this.connectionModel.address);
+ // src/View/Modals/searchModal.tsx
+ function SearchModal(searchViewModel, headline, converter, isOpen) {
+ function close() {
+ isOpen.value = false;
}
- };
- // methods
- this.connect = () => {
- this.connectToAddress(this.serverAddressInput.value);
- };
- this.connectToAddress = (address) => {
- this.connectionModel.connect(address);
- };
- this.disconnect = () => {
- this.connectionModel.disconnect();
- };
- this.removePreviousAddress = (address) => {
- this.connectionModel.removeAddress(address);
- this.updatePreviousAddresses();
- };
- // view
- this.showConnectionModal = () => {
- this.isShowingConnectionModal.value = true;
- };
- this.hideConnectionModal = () => {
- this.isShowingConnectionModal.value = false;
- };
- this.updatePreviousAddresses = () => {
- this.previousAddresses.clear();
- this.previousAddresses.add(...this.connectionModel.addresses);
- };
- this.connectionModel = connectionModel2;
- this.updatePreviousAddresses();
- connectionModel2.connectionChangeHandlerManager.addHandler(
- this.connectionChangeHandler
- );
- }
- };
-
- // src/ViewModel/Global/coreViewModel.ts
- var CoreViewModel = class {
- constructor() {
- // DRAG & DROP
- this.draggedObject = new State(void 0);
- // SUGGESTIONS
- // boards & tasks
- this.boardSearchSuggestions = new ListState();
- this.taskCategorySuggestions = new ListState();
- this.taskStatusSuggestions = new ListState();
- }
- };
-
- // src/ViewModel/Global/fileTransferViewModel.ts
- var FileTransferViewModel = class {
- // init
- constructor(fileTransferModel2, chatListModel2) {
- // state
- this.presentedModal = new State(void 0);
- this.generalFileOptions = new ListState();
- this.chatFileOptions = new ListState();
- this.selectedPaths = new ListState();
- this.transferChannel = new State("");
- this.transferKey = new State("");
- this.receivingTransferChannel = new State("");
- this.receivingTransferKey = new State("");
- this.filePathsSent = new ListState();
- this.filesSentCount = createProxyState(
- [this.filePathsSent],
- () => this.filePathsSent.value.size
- );
- this.filesSentText = createProxyState(
- [this.filesSentCount],
- () => translations.dataTransferModal.filesSentCount(
- this.filesSentCount.value
- )
- );
- this.filePathsReceived = new ListState();
- this.filesReceivedCount = createProxyState(
- [this.filePathsReceived],
- () => this.filePathsReceived.value.size
- );
- this.filesReceivedText = createProxyState(
- [this.filesReceivedCount],
- () => translations.dataTransferModal.filesReceivedCount(
- this.filesReceivedCount.value
- )
- );
- // guards
- this.hasNoPathsSelected = createProxyState(
- [this.selectedPaths],
- () => this.selectedPaths.value.size == 0
- );
- this.didNotFinishSending = new State(true);
- this.cannotPrepareToReceive = createProxyState(
- [this.receivingTransferChannel, this.receivingTransferKey],
- () => this.receivingTransferChannel.value == "" || this.receivingTransferKey.value == ""
- );
- // handlers
- this.handleReceivedFile = (path) => {
- this.filePathsReceived.add(path);
- };
- // methods
- this.getOptions = () => {
- this.generalFileOptions.clear();
- this.chatFileOptions.clear();
- this.selectedPaths.clear();
- this.generalFileOptions.add(
- {
- label: translations.dataTransferModal.connectionData,
- path: StorageModel.getPath(
- "connection" /* ConnectionModel */,
- filePaths.connectionModel.previousAddresses
- )
- },
- {
- label: translations.dataTransferModal.settingsData,
- path: StorageModel.getPath(
- "settings" /* SettingsModel */,
- []
- )
- }
- );
- const chatModels = this.chatListModel.chatModels;
- for (const chatModel of chatModels) {
- this.chatFileOptions.add({
- label: chatModel.info.primaryChannel,
- path: chatModel.getBasePath()
- });
- }
- };
- this.getTransferData = () => {
- const transferData = this.fileTransferModel.generateTransferData();
- this.transferChannel.value = transferData.channel;
- this.transferKey.value = transferData.key;
- };
- // view
- this.showDirectionSelectionModal = () => {
- this.presentedModal.value = 0 /* DirectionSelection */;
- this.getOptions();
- };
- this.showFileSelectionModal = () => {
- this.presentedModal.value = 1 /* FileSelection */;
- };
- this.showTransferDataModal = () => {
- this.presentedModal.value = 2 /* TransferDataDisplay */;
- this.getTransferData();
- };
- this.initiateTransfer = () => {
- this.presentedModal.value = 3 /* TransferDisplay */;
- this.didNotFinishSending.value = true;
- this.filePathsSent.clear();
- this.fileTransferModel.sendFiles(
- this.selectedPaths.value.values(),
- (path) => {
- console.log(path);
- this.filePathsSent.add(path);
- }
- );
- this.didNotFinishSending.value = false;
- };
- this.showTransferDataInputModal = () => {
- this.presentedModal.value = 4 /* TransferDataInput */;
- };
- this.prepareReceivingData = () => {
- this.presentedModal.value = 5 /* ReceptionDisplay */;
- this.filePathsReceived.clear();
- const transferData = {
- channel: this.receivingTransferChannel.value,
- key: this.receivingTransferKey.value
- };
- this.fileTransferModel.prepareToReceive(transferData);
- };
- this.hideModal = () => {
- this.presentedModal.value = void 0;
- };
- this.fileTransferModel = fileTransferModel2;
- this.chatListModel = chatListModel2;
- this.fileTransferModel.fileHandlerManager.addHandler(
- this.handleReceivedFile
- );
- }
- };
-
- // src/View/Components/textSpan.tsx
- var StringToTextSpan = (string) => {
- return /* @__PURE__ */ createElement("span", { class: "ellipsis" }, string);
- };
-
- // src/View/Modals/dataTransferModal.tsx
- function DataTransferModalWrapper(connectionViewModel2, fileTransferViewModel2) {
- return /* @__PURE__ */ createElement("div", null, DirectionSelectionModal(
- connectionViewModel2,
- fileTransferViewModel2
- ), FileSelectionModal(fileTransferViewModel2), TransferDataDisplayModal(fileTransferViewModel2), TransferDisplayModal(fileTransferViewModel2), TransferDataInputModal(fileTransferViewModel2), DataReceptionModal(fileTransferViewModel2));
- }
- function DirectionSelectionModal(connectionViewModel2, fileTransferViewModel2) {
- const isPresented = createProxyState(
- [fileTransferViewModel2.presentedModal],
- () => fileTransferViewModel2.presentedModal.value == 0 /* DirectionSelection */
- );
- const isDisconnected = createProxyState(
- [connectionViewModel2.isConnected],
- () => connectionViewModel2.isConnected.value == false
- );
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement(
- "p",
- {
- class: "error",
- "toggle:hidden": connectionViewModel2.isConnected
- },
- translations.dataTransferModal.notConnectedError
- ), /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column gap content-margin-bottom",
- "toggle:hidden": isDisconnected
- },
- /* @__PURE__ */ createElement(
- "button",
- {
- class: "tile",
- "on:click": fileTransferViewModel2.showFileSelectionModal
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "upload"),
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.dataTransferModal.fromThisDeviceButton)),
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
- ),
- /* @__PURE__ */ createElement(
- "button",
- {
- class: "tile",
- "on:click": fileTransferViewModel2.showTransferDataInputModal
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "download"),
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.dataTransferModal.toThisDeviceButton)),
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
- )
- )), /* @__PURE__ */ createElement("button", { "on:click": fileTransferViewModel2.hideModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
- }
- function FileSelectionModal(fileTransferViewModel2) {
- const OptionConverter = (fileOption) => {
- return OptionEntry(fileOption, fileTransferViewModel2);
- };
- function OptionEntry(fileOption, fileTransferViewModel3) {
- const isSelected = new State(false);
- if (fileTransferViewModel3.selectedPaths.value.has(fileOption.path)) {
- isSelected.value = true;
- }
- isSelected.subscribeSilent((isSelected2) => {
- if (isSelected2 == true) {
- fileTransferViewModel3.selectedPaths.add(fileOption.path);
- } else {
- fileTransferViewModel3.selectedPaths.remove(fileOption.path);
- }
- });
- function toggle() {
- isSelected.value = !isSelected.value;
- }
- return /* @__PURE__ */ createElement("button", { class: "tile", "toggle:selected": isSelected, "on:click": toggle }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", { class: "ellipsis" }, fileOption.label), /* @__PURE__ */ createElement("span", { class: "secondary ellipsis" }, StorageModel.pathComponentsToString(
- ...fileOption.path
- ))));
- }
- const isPresented = createProxyState(
- [fileTransferViewModel2.presentedModal],
- () => fileTransferViewModel2.presentedModal.value == 1 /* FileSelection */
- );
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.selectionDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("h3", null, translations.dataTransferModal.generalHeadline), /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column gap content-margin-bottom",
- "children:append": [
- fileTransferViewModel2.generalFileOptions,
- OptionConverter
- ]
- }
- ), /* @__PURE__ */ createElement("h3", null, translations.dataTransferModal.chatsHeadline), /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column gap",
- "children:append": [
- fileTransferViewModel2.chatFileOptions,
- OptionConverter
- ]
- }
- )), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex",
- "on:click": fileTransferViewModel2.showDirectionSelectionModal
- },
- translations.general.backButton
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary flex",
- "on:click": fileTransferViewModel2.showTransferDataModal,
- "toggle:disabled": fileTransferViewModel2.hasNoPathsSelected
- },
- translations.general.continueButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
- ))));
- }
- function TransferDataDisplayModal(fileTransferViewModel2) {
- const isPresented = createProxyState(
- [fileTransferViewModel2.presentedModal],
- () => fileTransferViewModel2.presentedModal.value == 2 /* TransferDataDisplay */
- );
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.dataEntryDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap content-margin-bottom" }, /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.transferChannelHeadline), /* @__PURE__ */ createElement(
- "b",
- {
- "subscribe:innerText": fileTransferViewModel2.transferChannel
- }
- ))), /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.transferKeyHeadline), /* @__PURE__ */ createElement(
- "b",
- {
- "subscribe:innerText": fileTransferViewModel2.transferKey
- }
- ))))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex",
- "on:click": fileTransferViewModel2.showFileSelectionModal
- },
- translations.general.backButton
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary flex",
- "on:click": fileTransferViewModel2.initiateTransfer
- },
- translations.dataTransferModal.sendButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
- ))));
- }
- function TransferDisplayModal(fileTransferViewModel2) {
- const isPresented = createProxyState(
- [fileTransferViewModel2.presentedModal],
- () => fileTransferViewModel2.presentedModal.value == 3 /* TransferDisplay */
- );
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement(
- "p",
- {
- class: "secondary",
- "subscribe:innerText": fileTransferViewModel2.filesSentText
- }
- ), /* @__PURE__ */ createElement(
- "p",
- {
- class: "secondary",
- "toggle:hidden": fileTransferViewModel2.didNotFinishSending
- },
- translations.dataTransferModal.allFilesSent
- ), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
- "div",
- {
- class: "tile flex-column align-start",
- "children:append": [
- fileTransferViewModel2.filePathsSent,
- StringToTextSpan
- ]
- }
- )), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex",
- "on:click": fileTransferViewModel2.initiateTransfer
- },
- translations.dataTransferModal.sendAgainButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "restart_alt")
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex",
- "on:click": fileTransferViewModel2.hideModal,
- "toggle:disabled": fileTransferViewModel2.didNotFinishSending
- },
- translations.general.closeButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "close")
- ))));
- }
- function TransferDataInputModal(fileTransferViewModel2) {
- const isPresented = createProxyState(
- [fileTransferViewModel2.presentedModal],
- () => fileTransferViewModel2.presentedModal.value == 4 /* TransferDataInput */
- );
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.dataEntryInputDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap content-margin-bottom" }, /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.transferChannelHeadline), /* @__PURE__ */ createElement(
- "input",
- {
- "bind:value": fileTransferViewModel2.receivingTransferChannel
- }
- ))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.dataTransferModal.transferKeyHeadline), /* @__PURE__ */ createElement(
- "input",
- {
- "bind:value": fileTransferViewModel2.receivingTransferKey
- }
- ))))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex",
- "on:click": fileTransferViewModel2.showDirectionSelectionModal
- },
- translations.general.backButton
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary flex",
- "on:click": fileTransferViewModel2.prepareReceivingData,
- "toggle:disabled": fileTransferViewModel2.cannotPrepareToReceive
- },
- translations.general.continueButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")
- ))));
- }
- function DataReceptionModal(fileTransferViewModel2) {
- const isPresented = createProxyState(
- [fileTransferViewModel2.presentedModal],
- () => fileTransferViewModel2.presentedModal.value == 5 /* ReceptionDisplay */
- );
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement("p", { class: "secondary" }, translations.dataTransferModal.readyToReceiveDescription), /* @__PURE__ */ createElement(
- "p",
- {
- class: "secondary",
- "subscribe:innerText": fileTransferViewModel2.filesReceivedText
- }
- ), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
- "div",
- {
- class: "tile flex-column align-start",
- "children:append": [
- fileTransferViewModel2.filePathsReceived,
- StringToTextSpan
- ]
- }
- )), /* @__PURE__ */ createElement("button", { "on:click": reload }, translations.general.reloadAppButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "refresh"))));
- }
-
- // src/Model/Global/fileTransferModel.ts
- var FileTransferModel = class {
- // init
- constructor(storageModel2, connectionModel2) {
- this.fileHandlerManager = new HandlerManager();
- // general
- this.generateTransferData = () => {
- const transferData = {
- channel: generateRandomToken(2).substring(0, 4),
- key: generateRandomToken(3).substring(0, 6)
- };
- this.transferData = transferData;
- return transferData;
- };
- this.prepareToReceive = (transferData) => {
- this.connectionModel.addChannel(transferData.channel);
- this.transferData = transferData;
- };
- // handlers
- this.handleMessage = (data) => {
- if (this.transferData == void 0) return;
- if (data.messageBody == void 0) return;
- if (data.messageChannel != this.transferData.channel) return;
- this.handleFile(data.messageBody);
- };
- this.handleFile = async (encryptedFileData) => {
- if (this.transferData == void 0) return;
- const decrypted = await decryptString(
- encryptedFileData,
- this.transferData.key
- );
- console.log(decrypted);
- const parsed = parse(decrypted);
- const isFileData = checkMatchesObjectStructure(
- parsed,
- FileDataReference
- );
- if (isFileData == false) return;
- const fileData = parsed;
- this.storageModel.write(fileData.path, fileData.body);
- const pathString = StorageModel.pathComponentsToString(
- ...fileData.path
- );
- this.fileHandlerManager.trigger(pathString);
- };
- // sending
- this.sendFiles = (directoryPaths, callback) => {
- for (const directoryPath of directoryPaths) {
- this.storageModel.recurse(directoryPath, (filePath) => {
- this.sendFile(filePath);
- const pathString = StorageModel.pathComponentsToString(
- ...filePath
- );
- callback(pathString);
- });
- }
- };
- this.sendFile = async (filePath) => {
- if (this.transferData == void 0) return;
- const fileContent = this.storageModel.read(filePath);
- if (fileContent == null) return;
- const fileData = {
- path: filePath,
- body: fileContent
- };
- const stringifiedFileData = stringify(fileData);
- const encryptedFileData = await encryptString(
- stringifiedFileData,
- this.transferData.key
- );
- this.connectionModel.sendPlainMessage(
- this.transferData.channel,
- encryptedFileData
- );
- };
- this.storageModel = storageModel2;
- this.connectionModel = connectionModel2;
- this.connectionModel.messageHandlerManager.addHandler(
- this.handleMessage
- );
- }
- };
- var FileDataReference = {
- path: [""],
- body: ""
- };
-
- // src/View/Components/chatEntry.tsx
- function ChatEntry(chatViewModel) {
- const view = /* @__PURE__ */ createElement(
- "button",
- {
- class: "tile colored-tile chat-entry",
- "set:color": chatViewModel.settingsPageViewModel.color,
- style: "height: 8rem",
- "on:click": chatViewModel.open,
- "toggle:unread": chatViewModel.hasUnreadMessages
- },
- /* @__PURE__ */ createElement(
- "span",
- {
- class: "shadow",
- "subscribe:innerText": chatViewModel.settingsPageViewModel.primaryChannel
- }
- ),
- /* @__PURE__ */ createElement(
- "h2",
- {
- "subscribe:innerText": chatViewModel.settingsPageViewModel.primaryChannel
- }
- )
- );
- chatViewModel.index.subscribe((newIndex) => {
- view.style.order = newIndex;
- });
- return view;
- }
- var ChatViewModelToChatEntry = (chatViewModel) => {
- return ChatEntry(chatViewModel);
- };
-
- // src/View/homePage.tsx
- function HomePage(storageViewModel2, settingsViewModel2, connectionViewModel2, fileTransferViewModel2, chatListViewModel2) {
- const overviewSection = /* @__PURE__ */ createElement("div", { id: "overview-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.overviewHeadline), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "cell_tower"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.serverAddress), /* @__PURE__ */ createElement(
- "input",
- {
- list: "previous-connection-list",
- placeholder: translations.homePage.serverAddressPlaceholder,
- "bind:value": connectionViewModel2.serverAddressInput,
- "on:enter": connectionViewModel2.connect
- }
- ), /* @__PURE__ */ createElement(
- "datalist",
- {
- hidden: true,
- id: "previous-connection-list",
- "children:append": [
- connectionViewModel2.previousAddresses,
- StringToOption
- ]
- }
- ))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "danger flex justify-center",
- "aria-label": translations.homePage.disconnectAudioLabel,
- "on:click": connectionViewModel2.disconnect,
- "toggle:disabled": connectionViewModel2.cannotDisonnect
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "link_off")
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "flex justify-center",
- "aria-label": translations.homePage.manageConnectionsAudioLabel,
- "on:click": connectionViewModel2.showConnectionModal,
- "toggle:disabled": connectionViewModel2.hasNoPreviousConnections
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "history")
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary flex justify-center",
- "aria-label": translations.homePage.connectAudioLabel,
- "on:click": connectionViewModel2.connect,
- "toggle:disabled": connectionViewModel2.cannotConnect
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "link")
- )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "account_circle"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.yourNameLabel), /* @__PURE__ */ createElement(
- "input",
- {
- placeholder: translations.homePage.yourNamePlaceholder,
- "bind:value": settingsViewModel2.usernameInput,
- "on:enter": settingsViewModel2.setName
- }
- ))), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "width-50",
- "on:click": settingsViewModel2.setName,
- "toggle:disabled": settingsViewModel2.cannotSetName,
- "aria-label": translations.homePage.setNameButtonAudioLabel
- },
- translations.general.setButton,
- /* @__PURE__ */ createElement("span", { class: "icon" }, "check")
- )), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile flex-no" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "calendar_month"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.firstDayOfWeekLabel), /* @__PURE__ */ createElement("select", { "bind:value": settingsViewModel2.firstDayOfWeekInput }, ...translations.regional.weekdays.full.map(
- (weekdayName, i) => Option(
- weekdayName,
- i.toString(),
- i.toString() == settingsViewModel2.firstDayOfWeekInput.value
- )
- )), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement(
- "button",
- {
- class: "tile flex-no",
- "on:click": fileTransferViewModel2.showDirectionSelectionModal
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "sync_alt"),
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.transferDataButton))
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "tile flex-no",
- "on:click": storageViewModel2.showStorageModal
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "hard_drive_2"),
- /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.homePage.manageStorageButton))
- ), /* @__PURE__ */ createElement("div", { class: "mobile-only" }, /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-row justify-end" }, /* @__PURE__ */ createElement("button", { class: "ghost width-50", "on:click": scrollToChat }, translations.homePage.scrollToChatButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))));
- const chatSection = /* @__PURE__ */ createElement("div", { id: "chat-section" }, /* @__PURE__ */ createElement("h2", null, translations.homePage.chatsHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
- "input",
- {
- placeholder: translations.homePage.addChatPlaceholder,
- "aria-label": translations.homePage.addChatAudioLabel,
- "bind:value": chatListViewModel2.newChatPrimaryChannel,
- "on:enter": chatListViewModel2.createChat
- }
- ), /* @__PURE__ */ createElement(
- "button",
- {
- class: "primary",
- "aria-label": translations.homePage.addChatButton,
- "on:click": chatListViewModel2.createChat,
- "toggle:disabled": chatListViewModel2.cannotCreateChat
- },
- /* @__PURE__ */ createElement("span", { class: "icon" }, "add")
- )), /* @__PURE__ */ createElement(
- "div",
- {
- id: "chat-grid",
- "children:append": [
- chatListViewModel2.chatViewModels,
- ChatViewModelToChatEntry
- ]
- }
- ));
- function scrollToChat() {
- chatSection.scrollIntoView();
- }
- return /* @__PURE__ */ createElement("article", { id: "home-page" }, /* @__PURE__ */ createElement("div", null, overviewSection, chatSection));
- }
-
- // src/Model/Global/settingsModel.ts
- var SettingsModel = class {
- // storage
- setName(newValue) {
- this.username = newValue;
- const path = StorageModel.getPath(
- "settings" /* SettingsModel */,
- filePaths.settingsModel.username
- );
- this.storageModel.write(path, newValue);
- }
- setFirstDayOfWeek(newValue) {
- this.firstDayOfWeek = newValue;
- const path = StorageModel.getPath(
- "settings" /* SettingsModel */,
- filePaths.settingsModel.firstDayOfWeek
- );
- this.storageModel.write(path, newValue);
- }
- // load
- loadUsernam() {
- const path = StorageModel.getPath(
- "settings" /* SettingsModel */,
- filePaths.settingsModel.username
- );
- const content = this.storageModel.read(path);
- this.username = content ?? "";
- }
- loadFirstDayofWeek() {
- const path = StorageModel.getPath(
- "settings" /* SettingsModel */,
- filePaths.settingsModel.firstDayOfWeek
- );
- const content = this.storageModel.read(path);
- this.firstDayOfWeek = content ?? "0";
- }
- // init
- constructor(storageModel2) {
- this.storageModel = storageModel2;
- this.loadUsernam();
- this.loadFirstDayofWeek();
- }
- };
-
- // src/ViewModel/Global/settingsViewModel.ts
- var SettingsViewModel = class {
- // init
- constructor(coreViewModel, settingsModel2) {
- this.coreViewModel = coreViewModel;
- // state
- this.username = new State("");
- this.usernameInput = new State("");
- this.firstDayOfWeekInput = new State("0");
- // guards
- this.cannotSetName = createProxyState(
- [this.usernameInput],
- () => this.usernameInput.value == "" || this.usernameInput.value == this.settingsModel.username
- );
- // methods
- this.setName = () => {
- this.settingsModel.setName(this.usernameInput.value);
- this.username.value = this.settingsModel.username;
- this.usernameInput.callSubscriptions();
- };
- this.setFirstDayofWeek = () => {
- this.settingsModel.setFirstDayOfWeek(this.firstDayOfWeekInput.value);
- };
- this.settingsModel = settingsModel2;
- this.username.value = settingsModel2.username;
- this.usernameInput.value = settingsModel2.username;
- this.firstDayOfWeekInput.value = settingsModel2.firstDayOfWeek;
- this.firstDayOfWeekInput.subscribe(this.setFirstDayofWeek);
- }
- };
-
- // src/View/Components/directoryItemList.tsx
- function DirectoryItemList(storageViewModel2, pathString = PATH_COMPONENT_SEPARATOR) {
- const StringToDirectoryItemList = (pathString2) => DirectoryItemList(storageViewModel2, pathString2);
- const path = StorageModel.stringToPathComponents(pathString);
- const fileName = StorageModel.getFileName(path);
- const items = new ListState();
- const style = `text-indent: ${path.length}rem`;
- function loadItems() {
- items.clear();
- const directoryItems = storageViewModel2.storageModel.list(path);
- for (const directoryItem of directoryItems) {
- const itemPath = [...path, directoryItem];
- const pathString2 = StorageModel.pathComponentsToString(...itemPath);
- items.add(pathString2);
- }
- }
- function select() {
- storageViewModel2.selectedPath.value = pathString;
- }
- storageViewModel2.lastDeletedItemPath.subscribe((lastDeletedItemPath) => {
- if (!items.value.has(lastDeletedItemPath)) return;
- select();
- setTimeout(() => loadItems(), 50);
- });
- const isSelected = createProxyState(
- [storageViewModel2.selectedPath],
- () => storageViewModel2.selectedPath.value == pathString
- );
- isSelected.subscribe(() => {
- if (isSelected.value == false) return;
- loadItems();
- });
- return /* @__PURE__ */ createElement("div", { class: "flex-column" }, /* @__PURE__ */ createElement(
- "button",
- {
- class: "width-100 flex-1 clip",
- "toggle:selected": isSelected,
- "on:click": select
- },
- /* @__PURE__ */ createElement("span", { class: "ellipsis width-100 flex-1", style }, fileName)
- ), /* @__PURE__ */ createElement(
- "div",
- {
- class: "flex-column",
- "children:append": [items, StringToDirectoryItemList]
- }
- ));
- }
-
- // src/View/Components/fileBrowser.tsx
- function FileBrowser(storageViewModel2) {
- const detailView = createProxyState(
- [storageViewModel2.selectedPath],
- () => {
- if (storageViewModel2.selectedPath.value == PATH_COMPONENT_SEPARATOR)
- return /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, translations.storage.noItemSelected), /* @__PURE__ */ createElement("hr", null), DangerousActionButton(
- translations.storage.removeJunkButton,
- "delete_forever",
- storageViewModel2.removeJunk
- ));
- return /* @__PURE__ */ createElement("div", { class: "flex-column gap" }, /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.storage.path), /* @__PURE__ */ createElement(
- "span",
- {
- class: "break-all",
- "subscribe:innerText": storageViewModel2.selectedPath
- }
- ))), /* @__PURE__ */ createElement("div", { class: "tile flex-no" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.storage.content), /* @__PURE__ */ createElement(
- "code",
- {
- "subscribe:innerText": storageViewModel2.selectedFileContent
- }
- ))), DangerousActionButton(
- translations.storage.deleteItem,
- "delete_forever",
- storageViewModel2.deleteSelectedItem
- ));
- }
- );
- const view = /* @__PURE__ */ createElement("div", { class: "file-browser" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { class: "scroll-area" }, DirectoryItemList(storageViewModel2)), /* @__PURE__ */ createElement("div", { class: "detail-button-wrapper" }, /* @__PURE__ */ createElement("button", { class: "ghost", "on:click": scrollToDetails }, /* @__PURE__ */ createElement(
- "span",
- {
- class: "ellipsis",
- "subscribe:innerText": storageViewModel2.selectedFileName
- }
- ), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward")))), /* @__PURE__ */ createElement("div", { class: "scroll-area", "children:set": detailView }));
- function scrollToDetails() {
- view.scrollLeft = view.scrollWidth;
- }
- return view;
- }
-
- // src/View/Modals/storageModal.tsx
- function StorageModal(storageViewModel2) {
- return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": storageViewModel2.isShowingStorageModal }, /* @__PURE__ */ createElement("div", { style: "max-width: 64rem" }, /* @__PURE__ */ createElement("main", { class: "padding-0" }, FileBrowser(storageViewModel2)), /* @__PURE__ */ createElement("button", { "on:click": storageViewModel2.hideStorageModal }, translations.general.closeButton, /* @__PURE__ */ createElement("span", { class: "icon" }, "close"))));
- }
-
- // src/ViewModel/Global/storageViewModel.ts
- var StorageViewModel = class {
- // init
- constructor(coreViewModel, storageModel2) {
- this.coreViewModel = coreViewModel;
- // state
- this.isShowingStorageModal = new State(false);
- this.selectedPath = new State(
- PATH_COMPONENT_SEPARATOR
- );
- this.didMakeChanges = new State(false);
- this.lastDeletedItemPath = new State("");
- // methods
- this.getSelectedItemContent = () => {
- const path = StorageModel.stringToPathComponents(
- this.selectedPath.value
- );
- const content = this.storageModel.read(path);
- return (content ?? translations.storage.notAFile) || translations.storage.contentEmpty;
- };
- this.deleteSelectedItem = () => {
- const path = StorageModel.stringToPathComponents(
- this.selectedPath.value
- );
- this.lastDeletedItemPath.value = this.selectedPath.value;
- this.storageModel.removeRecursively(path);
- this.didMakeChanges.value = true;
- };
- this.removeJunk = () => {
- this.storageModel.removeJunk();
- this.selectedPath.value = PATH_COMPONENT_SEPARATOR;
- };
- // view
- this.showStorageModal = () => {
- this.isShowingStorageModal.value = true;
- };
- this.hideStorageModal = () => {
- if (this.didMakeChanges.value == true) {
- window.location.reload();
- return;
- }
- this.isShowingStorageModal.value = false;
- };
- this.storageModel = storageModel2;
- this.selectedFileName = createProxyState(
- [this.selectedPath],
- () => StorageModel.getFileNameFromString(this.selectedPath.value)
- );
- this.selectedFileContent = createProxyState(
- [this.selectedPath],
- () => this.getSelectedItemContent()
- );
- }
- };
-
- // src/Upgrader/v1.ts
- var v1Upgrader = class {
- // init
- constructor(settingsModel2, connectionModel2, chatListModel2) {
- this.settingsModel = settingsModel2;
- this.connectionModel = connectionModel2;
- this.chatListModel = chatListModel2;
- // general
- this.migrateSettings = () => {
- const name = getLocalStorageItemAndClear("sender-name");
- if (name != null) {
- const parsedName = parseOrFallback(name);
- this.settingsModel.setName(parsedName);
- }
- const firstDayOfWeek = getLocalStorageItemAndClear("first-day-of-week");
- if (firstDayOfWeek != null) {
- const parsedFirstDayOfWeek = parseOrFallback(firstDayOfWeek);
- this.settingsModel.setFirstDayOfWeek(parsedFirstDayOfWeek);
- }
- };
- this.migrateConnections = () => {
- const previousAddressString = getLocalStorageItemAndClear("previous-addresses");
- if (previousAddressString == null) return;
- const previousAddresses = parseArray(previousAddressString);
- for (const address of previousAddresses) {
- if (typeof address != "string") continue;
- this.connectionModel.storeAddress(address);
- }
- };
- // chats
- this.migrateChats = () => {
- const chatIdString = getLocalStorageItemAndClear("chat-ids");
- if (chatIdString == null) return;
- const chatIds = parseArray(chatIdString);
- for (const chatId of chatIds) {
- if (typeof chatId != "string") continue;
- this.migrateChatById(chatId);
- }
- };
- this.migrateChatById = (id) => {
- const primaryChannel = getLocalStorageItemAndClear(
- storageKeys.primaryChannel(id)
- );
- if (primaryChannel == null) return;
- const parsedPriamryChannel = parseOrFallback(primaryChannel);
- const chatModel = this.chatListModel.createChat(parsedPriamryChannel);
- const secondaryChannelString = getLocalStorageItemAndClear(storageKeys.secondaryChannels(id));
- if (secondaryChannelString != null) {
- const potentialSecondaryChannels = parseArray(
- secondaryChannelString
- );
- const confirmedSecondaryChannels = [];
- for (const secondaryChannel of potentialSecondaryChannels) {
- if (typeof secondaryChannel != "string") continue;
- confirmedSecondaryChannels.push(secondaryChannel);
- }
- chatModel.setSecondaryChannels(confirmedSecondaryChannels);
- }
- const encryptionKey = getLocalStorageItemAndClear(
- storageKeys.encyptionKey(id)
- );
- if (encryptionKey != null) {
- const parsedEncryptionKey = parseOrFallback(encryptionKey);
- chatModel.setEncryptionKey(parsedEncryptionKey);
- }
- const messagesString = getLocalStorageItemAndClear(storageKeys.messages(id)) ?? "";
- const messageOutboxString = getLocalStorageItemAndClear(storageKeys.outbox(id)) ?? "";
- const potentialMessages = parseArray(messagesString);
- const potentialMessagesInOutbox = parseArray(messageOutboxString);
- const addMessages = (potentialMessages2, status) => {
- for (const potentialMessage of potentialMessages2) {
- const isV1ChatMessage = checkMatchesObjectStructure(
- potentialMessage,
- V1ChatMessageReference
- );
- if (isV1ChatMessage == false) continue;
- const v1ChatMessage = potentialMessage;
- const convertedChatMessage = {
- dataVersion: "v2",
- id: v4_default(),
- channel: v1ChatMessage.channel,
- sender: v1ChatMessage.sender,
- body: v1ChatMessage.body,
- dateSent: v1ChatMessage.isoDate,
- status,
- stringifiedFile: ""
- };
- chatModel.addMessage(convertedChatMessage);
- if (status == "outbox" /* Outbox */) {
- this.connectionModel.sendMessageOrStore(
- convertedChatMessage
- );
- }
- }
- };
- addMessages(potentialMessages, "received" /* Received */);
- addMessages(potentialMessagesInOutbox, "outbox" /* Outbox */);
- const objectsString = getLocalStorageItemAndClear(storageKeys.objects(id)) ?? "";
- const objectOutboxString = getLocalStorageItemAndClear(storageKeys.itemOutbox(id)) ?? "";
- const potentialObjects = parseArray(objectsString);
- const potentialObjectsInOutbox = parseArray(objectOutboxString);
- const addObjects = (potentialObjects2) => {
- const board = chatModel.fileModel.boardsAndTasksModel.createBoard(
- translations.updater.migrated
- );
- chatModel.fileModel.boardsAndTasksModel.updateBoard(board);
- for (const potentialObjectEntry of potentialObjects2) {
- const potentialObject = potentialObjectEntry[1];
- const isV1MessageObject = checkIsV1MessageObject(potentialObject);
- if (isV1MessageObject == false) continue;
- const objectId = potentialObject.id;
- const objectName = potentialObject.title;
- const contentVersions = Object.values(
- potentialObject.contentVersions
- );
- for (const potentialVersion of contentVersions) {
- const isV1MessageObjectContent = checkIsV1MessageObjectContent(potentialVersion);
- if (isV1MessageObjectContent == false) continue;
- const convertedTaskFileContent = {
- dataVersion: "v2",
- fileId: objectId,
- fileContentId: FileModel2.generateFileContentId(
- potentialVersion.isoDateVersionCreated
+ const suggestionId = v4_default();
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", "toggle:open": isOpen },
+ /* @__PURE__ */ createElement(
+ "div",
+ { style: "max-width: 64rem" },
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement("h2", null, headline),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-input" },
+ /* @__PURE__ */ createElement("input", {
+ placeholder: translations.general.searchLabel,
+ "bind:value": searchViewModel.searchInput,
+ "on:enter": searchViewModel.applySearch,
+ list: suggestionId,
+ }),
+ /* @__PURE__ */ createElement("datalist", {
+ hidden: true,
+ id: suggestionId,
+ "children:append": [
+ searchViewModel.suggestions,
+ StringToOption,
+ ],
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "aria-label":
+ translations.general.searchButtonAudioLabel,
+ "on:click": searchViewModel.applySearch,
+ "toggle:disabled":
+ searchViewModel.cannotApplySearch,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "search",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement("div", {
+ class: "grid gap",
+ style: "grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))",
+ "children:append": [
+ searchViewModel.matchingObjects,
+ converter,
+ ],
+ }),
),
- creationDate: potentialVersion.isoDateVersionCreated,
- type: "task",
- name: objectName ?? "",
- boardId: board.fileId ?? "",
- description: potentialVersion.noteContent ?? "",
- category: potentialVersion.categoryName ?? "",
- status: potentialVersion.status ?? "",
- priority: potentialVersion.priority ?? "",
- date: potentialVersion.date ?? "",
- time: potentialVersion.time ?? ""
- };
- console.log(convertedTaskFileContent);
- chatModel.fileModel.handleFileContent(
- convertedTaskFileContent
- );
- }
- }
- };
- addObjects([...potentialObjects, ...potentialObjectsInOutbox]);
- };
- this.migrateSettings();
- this.migrateConnections();
- this.migrateChats();
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": close },
+ translations.general.closeButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ ),
+ );
}
- };
- var storageKeys = {
- viewType(id) {
- return id + "view-type";
- },
- hasUnread(id) {
- return id + "has-unread-messages";
- },
- primaryChannel(id) {
- return id + "primary-channel";
- },
- secondaryChannels(id) {
- return id + "secondary-channels";
- },
- encyptionKey(id) {
- return id + "encryption-key";
- },
- messages(id) {
- return id + "messages";
- },
- objects(id) {
- return id + "items";
- },
- outbox(id) {
- return id + "outbox";
- },
- itemOutbox(id) {
- return id + "item-outbox";
- },
- composingMessage(id) {
- return id + "composing-message";
- }
- };
- var V1ChatMessageReference = {
- channel: "",
- sender: "",
- body: "",
- isoDate: ""
- };
- function checkIsV1MessageObject(object) {
- if (object.id == void 0) return false;
- if (object.title == void 0) return false;
- if (object.contentVersions == void 0) return false;
- return true;
- }
- function checkIsV1MessageObjectContent(object) {
- if (object.id == void 0) return false;
- if (object.isoDateVersionCreated == void 0) return false;
- return true;
- }
- // src/index.tsx
- var storageModel = new StorageModel();
- var settingsModel = new SettingsModel(storageModel);
- var connectionModel = new ConnectionModel(storageModel);
- var chatListModel = new ChatListModel(
- storageModel,
- settingsModel,
- connectionModel
- );
- var fileTransferModel = new FileTransferModel(storageModel, connectionModel);
- new v1Upgrader(settingsModel, connectionModel, chatListModel);
- var coreVieWModel = new CoreViewModel();
- var storageViewModel = new StorageViewModel(coreVieWModel, storageModel);
- var settingsViewModel = new SettingsViewModel(coreVieWModel, settingsModel);
- var connectionViewModel = new ConnectionViewModel(
- coreVieWModel,
- connectionModel
- );
- var chatListViewModel = new ChatListViewModel(
- coreVieWModel,
- storageModel,
- chatListModel,
- settingsViewModel,
- connectionViewModel
- );
- var fileTransferViewModel = new FileTransferViewModel(
- fileTransferModel,
- chatListModel
- );
- chatListViewModel.selectedChat.subscribe(() => {
- document.body.toggleAttribute(
- "showing-chat",
- chatListViewModel.selectedChat.value != void 0
+ // src/View/ChatPages/boardPage.tsx
+ function BoardPage(boardViewModel) {
+ boardViewModel.loadData();
+ const mainContent = createProxyState(
+ [boardViewModel.selectedPage],
+ () => {
+ switch (boardViewModel.selectedPage.value) {
+ case "kanban" /* Kanban */: {
+ return BoardKanbanPage(boardViewModel);
+ }
+ case "status-grid" /* StatusGrid */: {
+ return BoardStatusGridPage(boardViewModel);
+ }
+ default: {
+ return /* @__PURE__ */ createElement("div", {
+ class: "task-grid",
+ "children:append": [
+ boardViewModel.filteredTaskViewModels,
+ TaskViewModelToEntry,
+ ],
+ });
+ }
+ }
+ },
+ );
+ const taskSettingsModal = createProxyState(
+ [boardViewModel.selectedTaskViewModel],
+ () => {
+ if (boardViewModel.selectedTaskViewModel.value == void 0) {
+ return /* @__PURE__ */ createElement("div", null);
+ } else {
+ return TaskSettingsModal(
+ boardViewModel.selectedTaskViewModel.value,
+ );
+ }
+ },
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "toolbar" },
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost board-close-button",
+ "aria-label":
+ translations.chatPage.task
+ .closeBoardButtonAudioLabel,
+ "on:click": boardViewModel.close,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_back",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost board-toggle-button inset-outline",
+ "aria-label":
+ translations.chatPage.task
+ .toggleBoardButtonAudioLabel,
+ "on:click":
+ boardViewModel.taskPageViewModel
+ .toggleBoardList,
+ "toggle:selected":
+ boardViewModel.taskPageViewModel
+ .isShowingBoadList,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "dock_to_right",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.task
+ .boardSettingsHeadline,
+ "on:click": boardViewModel.showSettings,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "settings",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "scroll-h ribbon" },
+ BoardViewToggleButton(
+ translations.chatPage.task.listViewButtonAudioLabel,
+ "view_list",
+ "list" /* List */,
+ boardViewModel,
+ ),
+ BoardViewToggleButton(
+ translations.chatPage.task.kanbanViewButtonAudioLabel,
+ "view_kanban",
+ "kanban" /* Kanban */,
+ boardViewModel,
+ ),
+ BoardViewToggleButton(
+ translations.chatPage.task.statusViewButtonAudioLabel,
+ "grid_view",
+ "status-grid" /* StatusGrid */,
+ boardViewModel,
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.task
+ .filterTasksButtonAudioLabel,
+ "on:click": boardViewModel.showFilterModal,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "filter_alt",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.task
+ .createTaskButtonAudioLabel,
+ "on:click": boardViewModel.createTask,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "add",
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "content main-content",
+ "children:set": mainContent,
+ }),
+ BoardSettingsModal(boardViewModel),
+ SearchModal(
+ boardViewModel.searchViewModel,
+ translations.chatPage.task.filterTasksHeadline,
+ TaskViewModelToEntry,
+ boardViewModel.isPresentingFilterModal,
+ ),
+ /* @__PURE__ */ createElement("div", {
+ "children:set": taskSettingsModal,
+ }),
+ );
+ }
+
+ // src/View/Components/boardEntry.tsx
+ function BoardEntry(boardViewModel) {
+ const view = /* @__PURE__ */ createElement(
+ "button",
+ {
+ "set:color": boardViewModel.color,
+ class: "tile colored-tile",
+ "toggle:selected": boardViewModel.isSelected,
+ "on:click": boardViewModel.select,
+ "on:dragover": allowDrop,
+ "on:drop": boardViewModel.handleDropBetweenBoards,
+ },
+ /* @__PURE__ */ createElement("span", {
+ class: "shadow",
+ "subscribe:innerText": boardViewModel.name,
+ }),
+ /* @__PURE__ */ createElement("b", {
+ "subscribe:innerText": boardViewModel.name,
+ }),
+ );
+ boardViewModel.index.subscribe((newIndex) => {
+ view.style.order = newIndex;
+ });
+ return view;
+ }
+ var BoardViewModelToEntry = (boardViewModel) => {
+ return BoardEntry(boardViewModel);
+ };
+
+ // src/View/ChatPages/taskPage.tsx
+ function TaskPage(taskPageViewModel) {
+ taskPageViewModel.loadData();
+ const isShowingBoard = createProxyState(
+ [taskPageViewModel.selectedBoardId],
+ () => taskPageViewModel.selectedBoardId.value != void 0,
+ );
+ const paneContent = createProxyState(
+ [taskPageViewModel.selectedBoardId],
+ () => {
+ const selectedBoardId = taskPageViewModel.selectedBoardId.value;
+ if (selectedBoardId == void 0) {
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane align-center justify-center" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.chatPage.task.noBoardSelected,
+ ),
+ );
+ }
+ const selectedBoard =
+ taskPageViewModel.boardViewModels.value.get(
+ selectedBoardId,
+ );
+ if (selectedBoard == void 0) {
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane align-center justify-center" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.chatPage.task.boardNotFound,
+ ),
+ );
+ }
+ return BoardPage(selectedBoard);
+ },
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ {
+ id: "task-page",
+ "toggle:isshowingboard": isShowingBoard,
+ "set:showingboardlist": taskPageViewModel.isShowingBoadList,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ {
+ id: "board-list",
+ class: "pane-wrapper side background",
+ "set:color": taskPageViewModel.chatViewModel.displayedColor,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "pane" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "toolbar" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-input" },
+ /* @__PURE__ */ createElement("input", {
+ class: "no-outline",
+ "bind:value":
+ taskPageViewModel.newBoardNameInput,
+ "on:enter": taskPageViewModel.createBoard,
+ placeholder:
+ translations.chatPage.task
+ .newBoardNamePlaceholder,
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "aria-label":
+ translations.chatPage.task
+ .createBoardButtonAudioLabel,
+ "on:click": taskPageViewModel.createBoard,
+ "toggle:disabled":
+ taskPageViewModel.cannotCreateBoard,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "add",
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "content" },
+ /* @__PURE__ */ createElement("div", {
+ class: "grid gap",
+ style: "grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr))",
+ "children:append": [
+ taskPageViewModel.boardViewModels,
+ BoardViewModelToEntry,
+ ],
+ }),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ id: "board-content",
+ class: "pane-wrapper",
+ "children:set": paneContent,
+ }),
+ );
+ }
+
+ // src/View/chatPage.tsx
+ function ChatPage(chatViewModel) {
+ const mainContent = createProxyState(
+ [chatViewModel.selectedPage],
+ () => {
+ chatViewModel.closeSubPages();
+ switch (chatViewModel.selectedPage.value) {
+ case "settings" /* Settings */: {
+ return SettingsPage(
+ chatViewModel.settingsPageViewModel,
+ );
+ }
+ case "tasks" /* Tasks */: {
+ return TaskPage(chatViewModel.taskPageViewModel);
+ }
+ case "calendar" /* Calendar */: {
+ return CalendarPage(chatViewModel.calendarViewModel);
+ }
+ default: {
+ return MessagePage(chatViewModel.messagePageViewModel);
+ }
+ }
+ },
+ );
+ return /* @__PURE__ */ createElement(
+ "article",
+ {
+ id: "chat-page",
+ "set:color": chatViewModel.displayedColor,
+ class: "subtle-background",
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "div",
+ { id: "ribbon" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "ghost",
+ "aria-label":
+ translations.chatPage.closeChatAudioLabe,
+ "on:click": chatViewModel.close,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "danger",
+ "aria-label":
+ translations.general.restoreConnection,
+ "on:click":
+ chatViewModel.connectionViewModel
+ .showConnectionModal,
+ "toggle:hidden":
+ chatViewModel.connectionViewModel.isConnected,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "signal_disconnected",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ ChatViewToggleButton(
+ translations.chatPage.pages.calendar,
+ "calendar_month",
+ "calendar" /* Calendar */,
+ chatViewModel,
+ ),
+ ChatViewToggleButton(
+ translations.chatPage.pages.tasks,
+ "task_alt",
+ "tasks" /* Tasks */,
+ chatViewModel,
+ ),
+ ChatViewToggleButton(
+ translations.chatPage.pages.messages,
+ "forum",
+ "messages" /* Messages */,
+ chatViewModel,
+ ),
+ ChatViewToggleButton(
+ translations.chatPage.pages.settings,
+ "settings",
+ "settings" /* Settings */,
+ chatViewModel,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ id: "main",
+ "children:set": mainContent,
+ }),
+ ),
+ );
+ }
+
+ // src/View/chatPageWrapper.tsx
+ function ChatPageWrapper(chatListViewModel2) {
+ const chatPageContent = createProxyState(
+ [chatListViewModel2.selectedChat],
+ () => {
+ if (chatListViewModel2.selectedChat.value == void 0) {
+ return /* @__PURE__ */ createElement("div", null);
+ } else {
+ return ChatPage(chatListViewModel2.selectedChat.value);
+ }
+ },
+ );
+ return /* @__PURE__ */ createElement("div", {
+ id: "chat-page-wrapper",
+ "children:set": chatPageContent,
+ });
+ }
+
+ // src/View/Modals/connectionModal.tsx
+ function ConnectionModal(connectionViewModel2) {
+ const previousAddressConverter = (address) => {
+ function connnect() {
+ connectionViewModel2.connectToAddress(address);
+ }
+ const cannotConnect = createProxyState(
+ [connectionViewModel2.isConnected],
+ () =>
+ connectionViewModel2.isConnected.value == true &&
+ connectionViewModel2.connectionModel.address == address,
+ );
+ return DeletableListItem(
+ address,
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "on:click": connnect,
+ "toggle:disabled": cannotConnect,
+ "aria-label":
+ translations.connectionModal
+ .connectButtonAudioLabel,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "link",
+ ),
+ ),
+ () => {
+ connectionViewModel2.removePreviousAddress(address);
+ },
+ );
+ };
+ return /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "modal",
+ "toggle:open": connectionViewModel2.isShowingConnectionModal,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.connectionModal.connectionModalHeadline,
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "flex-column gap",
+ "children:append": [
+ connectionViewModel2.previousAddresses,
+ previousAddressConverter,
+ ],
+ }),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": connectionViewModel2.hideConnectionModal },
+ translations.general.closeButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ ),
+ );
+ }
+
+ // ../ui-base/index.ts
+ var UDNFrontend = class {
+ ws;
+ // HANDLERS
+ connectionHandler = () => {};
+ disconnectionHandler = () => {};
+ messageHandler = (data) => {};
+ mailboxHandler = (mailboxId) => {};
+ mailboxConnectionHandler = (mailboxId) => {};
+ mailboxDeleteHandler = (mailboxId) => {};
+ // INIT
+ set onconnect(handler) {
+ this.connectionHandler = handler;
+ }
+ set ondisconnect(handler) {
+ this.disconnectionHandler = handler;
+ }
+ set onmessage(handler) {
+ this.messageHandler = handler;
+ }
+ set onmailboxcreate(handler) {
+ this.mailboxHandler = handler;
+ }
+ set onmailboxconnect(handler) {
+ this.mailboxConnectionHandler = handler;
+ }
+ set onmailboxdelete(handler) {
+ this.mailboxDeleteHandler = handler;
+ }
+ // UTILITY METHODS
+ send(messageObject) {
+ if (this.ws == void 0) return false;
+ if (this.ws.readyState != 1) return false;
+ const messageString = JSON.stringify(messageObject);
+ this.ws.send(messageString);
+ return true;
+ }
+ // PUBLIC METHODS
+ // connection
+ connect(address) {
+ try {
+ this.disconnect();
+ this.ws = new WebSocket(address);
+ this.ws.addEventListener("open", this.connectionHandler);
+ this.ws.addEventListener("close", this.disconnectionHandler);
+ this.ws.addEventListener("message", (message) => {
+ const dataString = message.data.toString();
+ const data = JSON.parse(dataString);
+ if (data.assignedMailboxId) {
+ return this.mailboxHandler(data.assignedMailboxId);
+ } else if (data.connectedMailboxId) {
+ return this.mailboxConnectionHandler(
+ data.connectedMailboxId,
+ );
+ } else if (data.deletedMailbox) {
+ return this.mailboxDeleteHandler(data.deletedMailbox);
+ } else {
+ this.send({
+ uuid: data.uuid,
+ confirmingMessageReceived: true,
+ });
+ this.messageHandler(data);
+ }
+ });
+ } catch (error) {
+ console.error(error);
+ }
+ }
+ disconnect() {
+ this.ws?.close();
+ }
+ // message
+ sendMessage(channel, body) {
+ const messageObject = {
+ messageChannel: channel,
+ messageBody: body,
+ };
+ return this.send(messageObject);
+ }
+ // subscription
+ subscribe(channel) {
+ const messageObject = {
+ subscribeChannel: channel,
+ };
+ return this.send(messageObject);
+ }
+ unsubscribe(channel) {
+ const messageObject = {
+ unsubscribeChannel: channel,
+ };
+ return this.send(messageObject);
+ }
+ // mailbox
+ requestMailbox() {
+ const messageObject = {
+ requestingMailboxSetup: true,
+ };
+ return this.send(messageObject);
+ }
+ connectMailbox(mailboxId) {
+ const messageObject = {
+ requestedMailbox: mailboxId,
+ };
+ return this.send(messageObject);
+ }
+ deleteMailbox(mailboxId) {
+ const messageObject = {
+ deletingMailbox: mailboxId,
+ };
+ return this.send(messageObject);
+ }
+ };
+
+ // src/Model/Global/connectionModel.ts
+ var ConnectionModel = class {
+ // init
+ constructor(storageModel2) {
+ this.reconnectInterval = void 0;
+ this.shouldAttemptReconnect = false;
+ this.connectionChangeHandlerManager = new HandlerManager();
+ this.messageHandlerManager = new HandlerManager();
+ this.messageSentHandlerManager = new HandlerManager();
+ this.channelsToSubscribe = /* @__PURE__ */ new Set();
+ // handlers
+ this.handleMessage = (data) => {
+ this.messageHandlerManager.trigger(data);
+ };
+ this.handleConnectionChange = () => {
+ console.log(
+ "connection status:",
+ this.isConnected,
+ this.address,
+ );
+ this.connectionChangeHandlerManager.trigger();
+ if (this.isConnected == false) {
+ if (this.shouldAttemptReconnect == false) return;
+ this.reconnectInterval = setInterval(() => {
+ if (this.isConnected == true) {
+ clearInterval(this.reconnectInterval);
+ return;
+ }
+ this.reconnect();
+ }, 5e3);
+ return;
+ }
+ if (this.address == void 0) return;
+ this.storeAddress(this.address);
+ this.sendSubscriptionRequest();
+ this.sendMessagesInOutbox();
+ this.shouldAttemptReconnect = true;
+ };
+ // connection
+ this.connect = (address) => {
+ this.udn.connect(address);
+ };
+ this.disconnect = () => {
+ this.udn.disconnect();
+ const reconnectAddressPath = StorageModel.getPath(
+ "connection" /* ConnectionModel */,
+ filePaths.connectionModel.reconnectAddress,
+ );
+ this.storageModel.remove(reconnectAddressPath);
+ this.shouldAttemptReconnect = false;
+ };
+ this.reconnect = () => {
+ const reconnectAddressPath = this.getReconnectAddressPath();
+ const reconnectAddress =
+ this.storageModel.read(reconnectAddressPath);
+ if (reconnectAddress == null) return;
+ console.log("reconnecting...");
+ this.connect(reconnectAddress);
+ };
+ // mailbox
+ this.getMailboxPath = (address) => {
+ const mailboxDirPath = StorageModel.getPath(
+ "connection" /* ConnectionModel */,
+ filePaths.connectionModel.mailboxes,
+ );
+ const mailboxFilePath = [...mailboxDirPath, address];
+ return mailboxFilePath;
+ };
+ this.requestNewMailbox = () => {
+ console.log("requesting new mailbox");
+ this.udn.requestMailbox();
+ };
+ this.connectMailbox = () => {
+ if (this.address == void 0) return;
+ const mailboxId = this.storageModel.read(
+ this.getMailboxPath(this.address),
+ );
+ console.log("connecting mailbox", mailboxId);
+ if (mailboxId == null) return this.requestNewMailbox();
+ this.udn.connectMailbox(mailboxId);
+ };
+ this.storeMailbox = (mailboxId) => {
+ if (this.address == void 0) return;
+ this.storageModel.write(
+ this.getMailboxPath(this.address),
+ mailboxId,
+ );
+ };
+ // subscription
+ this.addChannel = (channel) => {
+ this.channelsToSubscribe.add(channel);
+ this.sendSubscriptionRequest();
+ };
+ this.sendSubscriptionRequest = () => {
+ if (this.isConnected == false) return;
+ for (const channel of this.channelsToSubscribe) {
+ this.udn.subscribe(channel);
+ }
+ this.connectMailbox();
+ };
+ // outbox
+ this.getOutboxPath = () => {
+ return StorageModel.getPath(
+ "connection" /* ConnectionModel */,
+ filePaths.connectionModel.outbox,
+ );
+ };
+ this.getOutboxMessags = () => {
+ const outboxPath = this.getOutboxPath();
+ const messageIds = this.storageModel.list(outboxPath);
+ let chatMessages = [];
+ for (const messageId of messageIds) {
+ const chatMessage = this.storageModel.readStringifiable(
+ [...outboxPath, messageId],
+ ChatMessageReference,
+ );
+ if (chatMessage == null) continue;
+ chatMessages.push(chatMessage);
+ }
+ return chatMessages;
+ };
+ this.addToOutbox = (chatMessage) => {
+ const messagePath = [...this.getOutboxPath(), chatMessage.id];
+ this.storageModel.writeStringifiable(messagePath, chatMessage);
+ };
+ this.removeFromOutbox = (chatMessage) => {
+ const messagePath = [...this.getOutboxPath(), chatMessage.id];
+ this.storageModel.remove(messagePath);
+ };
+ this.sendMessagesInOutbox = () => {
+ const messages = this.getOutboxMessags();
+ for (const message of messages) {
+ const isSent = this.tryToSendMessage(message);
+ if (isSent == false) return;
+ this.removeFromOutbox(message);
+ }
+ };
+ // messaging
+ this.sendMessageOrStore = (chatMessage) => {
+ const isSent = this.tryToSendMessage(chatMessage);
+ if (isSent == true) return;
+ this.addToOutbox(chatMessage);
+ };
+ this.tryToSendMessage = (chatMessage) => {
+ const stringifiedBody = stringify(chatMessage);
+ const isSent = this.sendPlainMessage(
+ chatMessage.channel,
+ stringifiedBody,
+ );
+ if (isSent) this.messageSentHandlerManager.trigger(chatMessage);
+ return isSent;
+ };
+ this.sendPlainMessage = (channel, body) => {
+ return this.udn.sendMessage(channel, body);
+ };
+ // storage
+ this.getPreviousAddressPath = () => {
+ return StorageModel.getPath(
+ "connection" /* ConnectionModel */,
+ filePaths.connectionModel.previousAddresses,
+ );
+ };
+ this.getAddressPath = (address) => {
+ const dirPath = this.getPreviousAddressPath();
+ return [...dirPath, address];
+ };
+ this.getReconnectAddressPath = () => {
+ return StorageModel.getPath(
+ "connection" /* ConnectionModel */,
+ filePaths.connectionModel.reconnectAddress,
+ );
+ };
+ this.storeAddress = (address) => {
+ const addressPath = this.getAddressPath(address);
+ this.storageModel.write(addressPath, "");
+ const reconnectAddressPath = this.getReconnectAddressPath();
+ this.storageModel.write(reconnectAddressPath, address);
+ };
+ this.removeAddress = (address) => {
+ const addressPath = this.getAddressPath(address);
+ this.storageModel.remove(addressPath);
+ };
+ this.udn = new UDNFrontend();
+ this.storageModel = storageModel2;
+ this.udn.onmessage = (data) => {
+ this.handleMessage(data);
+ };
+ this.udn.onconnect = () => {
+ this.handleConnectionChange();
+ };
+ this.udn.ondisconnect = () => {
+ this.handleConnectionChange();
+ };
+ this.udn.onmailboxcreate = (mailboxId) => {
+ console.log("created mailbox", mailboxId);
+ this.storeMailbox(mailboxId);
+ this.connectMailbox();
+ };
+ this.udn.onmailboxdelete = (mailboxId) => {
+ console.log(`mailbox ${mailboxId} deleted`);
+ this.requestNewMailbox();
+ };
+ this.udn.onmailboxconnect = (mailboxId) => {
+ console.log(`using mailbox ${mailboxId}`);
+ };
+ this.reconnect();
+ }
+ // data
+ get isConnected() {
+ return this.udn.ws != void 0 && this.udn.ws.readyState == 1;
+ }
+ get address() {
+ return this.udn.ws?.url;
+ }
+ get addresses() {
+ const dirPath = this.getPreviousAddressPath();
+ return this.storageModel.list(dirPath);
+ }
+ };
+
+ // src/ViewModel/Global/connectionViewModel.ts
+ var ConnectionViewModel = class {
+ // init
+ constructor(coreViewModel, connectionModel2) {
+ this.coreViewModel = coreViewModel;
+ // state
+ this.serverAddressInput = new State("");
+ this.isConnected = new State(false);
+ this.isShowingConnectionModal = new State(false);
+ this.previousAddresses = new ListState();
+ // guards
+ this.cannotConnect = createProxyState(
+ [this.serverAddressInput, this.isConnected],
+ () =>
+ (this.isConnected.value == true &&
+ this.serverAddressInput.value ==
+ this.connectionModel.address) ||
+ this.serverAddressInput.value == "",
+ );
+ this.cannotDisonnect = createProxyState(
+ [this.isConnected],
+ () => this.isConnected.value == false,
+ );
+ this.hasNoPreviousConnections = createProxyState(
+ [this.previousAddresses],
+ () => this.previousAddresses.value.size == 0,
+ );
+ // handlers
+ this.connectionChangeHandler = () => {
+ this.isConnected.value = this.connectionModel.isConnected;
+ if (this.connectionModel.isConnected == false) return;
+ if (this.connectionModel.address == void 0) return;
+ this.serverAddressInput.value = this.connectionModel.address;
+ if (
+ !this.previousAddresses.value.has(
+ this.connectionModel.address,
+ )
+ ) {
+ this.previousAddresses.add(this.connectionModel.address);
+ }
+ };
+ // methods
+ this.connect = () => {
+ this.connectToAddress(this.serverAddressInput.value);
+ };
+ this.connectToAddress = (address) => {
+ this.connectionModel.connect(address);
+ };
+ this.disconnect = () => {
+ this.connectionModel.disconnect();
+ };
+ this.removePreviousAddress = (address) => {
+ this.connectionModel.removeAddress(address);
+ this.updatePreviousAddresses();
+ };
+ // view
+ this.showConnectionModal = () => {
+ this.isShowingConnectionModal.value = true;
+ };
+ this.hideConnectionModal = () => {
+ this.isShowingConnectionModal.value = false;
+ };
+ this.updatePreviousAddresses = () => {
+ this.previousAddresses.clear();
+ this.previousAddresses.add(...this.connectionModel.addresses);
+ };
+ this.connectionModel = connectionModel2;
+ this.updatePreviousAddresses();
+ connectionModel2.connectionChangeHandlerManager.addHandler(
+ this.connectionChangeHandler,
+ );
+ }
+ };
+
+ // src/ViewModel/Global/coreViewModel.ts
+ var CoreViewModel = class {
+ constructor() {
+ // DRAG & DROP
+ this.draggedObject = new State(void 0);
+ // SUGGESTIONS
+ // boards & tasks
+ this.boardSearchSuggestions = new ListState();
+ this.taskCategorySuggestions = new ListState();
+ this.taskStatusSuggestions = new ListState();
+ }
+ };
+
+ // src/ViewModel/Global/fileTransferViewModel.ts
+ var FileTransferViewModel = class {
+ // init
+ constructor(fileTransferModel2, chatListModel2) {
+ // state
+ this.presentedModal = new State(void 0);
+ this.generalFileOptions = new ListState();
+ this.chatFileOptions = new ListState();
+ this.selectedPaths = new ListState();
+ this.transferChannel = new State("");
+ this.transferKey = new State("");
+ this.receivingTransferChannel = new State("");
+ this.receivingTransferKey = new State("");
+ this.filePathsSent = new ListState();
+ this.filesSentCount = createProxyState(
+ [this.filePathsSent],
+ () => this.filePathsSent.value.size,
+ );
+ this.filesSentText = createProxyState([this.filesSentCount], () =>
+ translations.dataTransferModal.filesSentCount(
+ this.filesSentCount.value,
+ ),
+ );
+ this.filePathsReceived = new ListState();
+ this.filesReceivedCount = createProxyState(
+ [this.filePathsReceived],
+ () => this.filePathsReceived.value.size,
+ );
+ this.filesReceivedText = createProxyState(
+ [this.filesReceivedCount],
+ () =>
+ translations.dataTransferModal.filesReceivedCount(
+ this.filesReceivedCount.value,
+ ),
+ );
+ // guards
+ this.hasNoPathsSelected = createProxyState(
+ [this.selectedPaths],
+ () => this.selectedPaths.value.size == 0,
+ );
+ this.didNotFinishSending = new State(true);
+ this.cannotPrepareToReceive = createProxyState(
+ [this.receivingTransferChannel, this.receivingTransferKey],
+ () =>
+ this.receivingTransferChannel.value == "" ||
+ this.receivingTransferKey.value == "",
+ );
+ // handlers
+ this.handleReceivedFile = (path) => {
+ this.filePathsReceived.add(path);
+ };
+ // methods
+ this.getOptions = () => {
+ this.generalFileOptions.clear();
+ this.chatFileOptions.clear();
+ this.selectedPaths.clear();
+ this.generalFileOptions.add(
+ {
+ label: translations.dataTransferModal.connectionData,
+ path: StorageModel.getPath(
+ "connection" /* ConnectionModel */,
+ filePaths.connectionModel.previousAddresses,
+ ),
+ },
+ {
+ label: translations.dataTransferModal.settingsData,
+ path: StorageModel.getPath(
+ "settings" /* SettingsModel */,
+ [],
+ ),
+ },
+ );
+ const chatModels = this.chatListModel.chatModels;
+ for (const chatModel of chatModels) {
+ this.chatFileOptions.add({
+ label: chatModel.info.primaryChannel,
+ path: chatModel.getBasePath(),
+ });
+ }
+ };
+ this.getTransferData = () => {
+ const transferData =
+ this.fileTransferModel.generateTransferData();
+ this.transferChannel.value = transferData.channel;
+ this.transferKey.value = transferData.key;
+ };
+ // view
+ this.showDirectionSelectionModal = () => {
+ this.presentedModal.value = 0 /* DirectionSelection */;
+ this.getOptions();
+ };
+ this.showFileSelectionModal = () => {
+ this.presentedModal.value = 1 /* FileSelection */;
+ };
+ this.showTransferDataModal = () => {
+ this.presentedModal.value = 2 /* TransferDataDisplay */;
+ this.getTransferData();
+ };
+ this.initiateTransfer = () => {
+ this.presentedModal.value = 3 /* TransferDisplay */;
+ this.didNotFinishSending.value = true;
+ this.filePathsSent.clear();
+ this.fileTransferModel.sendFiles(
+ this.selectedPaths.value.values(),
+ (path) => {
+ console.log(path);
+ this.filePathsSent.add(path);
+ },
+ );
+ this.didNotFinishSending.value = false;
+ };
+ this.showTransferDataInputModal = () => {
+ this.presentedModal.value = 4 /* TransferDataInput */;
+ };
+ this.prepareReceivingData = () => {
+ this.presentedModal.value = 5 /* ReceptionDisplay */;
+ this.filePathsReceived.clear();
+ const transferData = {
+ channel: this.receivingTransferChannel.value,
+ key: this.receivingTransferKey.value,
+ };
+ this.fileTransferModel.prepareToReceive(transferData);
+ };
+ this.hideModal = () => {
+ this.presentedModal.value = void 0;
+ };
+ this.fileTransferModel = fileTransferModel2;
+ this.chatListModel = chatListModel2;
+ this.fileTransferModel.fileHandlerManager.addHandler(
+ this.handleReceivedFile,
+ );
+ }
+ };
+
+ // src/View/Components/textSpan.tsx
+ var StringToTextSpan = (string) => {
+ return /* @__PURE__ */ createElement(
+ "span",
+ { class: "ellipsis" },
+ string,
+ );
+ };
+
+ // src/View/Modals/dataTransferModal.tsx
+ function DataTransferModalWrapper(
+ connectionViewModel2,
+ fileTransferViewModel2,
+ ) {
+ return /* @__PURE__ */ createElement(
+ "div",
+ null,
+ DirectionSelectionModal(
+ connectionViewModel2,
+ fileTransferViewModel2,
+ ),
+ FileSelectionModal(fileTransferViewModel2),
+ TransferDataDisplayModal(fileTransferViewModel2),
+ TransferDisplayModal(fileTransferViewModel2),
+ TransferDataInputModal(fileTransferViewModel2),
+ DataReceptionModal(fileTransferViewModel2),
+ );
+ }
+ function DirectionSelectionModal(
+ connectionViewModel2,
+ fileTransferViewModel2,
+ ) {
+ const isPresented = createProxyState(
+ [fileTransferViewModel2.presentedModal],
+ () =>
+ fileTransferViewModel2.presentedModal.value ==
+ 0 /* DirectionSelection */,
+ );
+ const isDisconnected = createProxyState(
+ [connectionViewModel2.isConnected],
+ () => connectionViewModel2.isConnected.value == false,
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", "toggle:open": isPresented },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.dataTransferModal.transferDataHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "p",
+ {
+ class: "error",
+ "toggle:hidden": connectionViewModel2.isConnected,
+ },
+ translations.dataTransferModal.notConnectedError,
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "flex-column gap content-margin-bottom",
+ "toggle:hidden": isDisconnected,
+ },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "tile",
+ "on:click":
+ fileTransferViewModel2.showFileSelectionModal,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "upload",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "b",
+ null,
+ translations.dataTransferModal
+ .fromThisDeviceButton,
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "tile",
+ "on:click":
+ fileTransferViewModel2.showTransferDataInputModal,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "download",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "b",
+ null,
+ translations.dataTransferModal
+ .toThisDeviceButton,
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": fileTransferViewModel2.hideModal },
+ translations.general.closeButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ ),
+ );
+ }
+ function FileSelectionModal(fileTransferViewModel2) {
+ const OptionConverter = (fileOption) => {
+ return OptionEntry(fileOption, fileTransferViewModel2);
+ };
+ function OptionEntry(fileOption, fileTransferViewModel3) {
+ const isSelected = new State(false);
+ if (
+ fileTransferViewModel3.selectedPaths.value.has(fileOption.path)
+ ) {
+ isSelected.value = true;
+ }
+ isSelected.subscribeSilent((isSelected2) => {
+ if (isSelected2 == true) {
+ fileTransferViewModel3.selectedPaths.add(fileOption.path);
+ } else {
+ fileTransferViewModel3.selectedPaths.remove(
+ fileOption.path,
+ );
+ }
+ });
+ function toggle() {
+ isSelected.value = !isSelected.value;
+ }
+ return /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "tile",
+ "toggle:selected": isSelected,
+ "on:click": toggle,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "b",
+ { class: "ellipsis" },
+ fileOption.label,
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary ellipsis" },
+ StorageModel.pathComponentsToString(...fileOption.path),
+ ),
+ ),
+ );
+ }
+ const isPresented = createProxyState(
+ [fileTransferViewModel2.presentedModal],
+ () =>
+ fileTransferViewModel2.presentedModal.value ==
+ 1 /* FileSelection */,
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", "toggle:open": isPresented },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.dataTransferModal.transferDataHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.dataTransferModal.selectionDescription,
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "h3",
+ null,
+ translations.dataTransferModal.generalHeadline,
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "flex-column gap content-margin-bottom",
+ "children:append": [
+ fileTransferViewModel2.generalFileOptions,
+ OptionConverter,
+ ],
+ }),
+ /* @__PURE__ */ createElement(
+ "h3",
+ null,
+ translations.dataTransferModal.chatsHeadline,
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "flex-column gap",
+ "children:append": [
+ fileTransferViewModel2.chatFileOptions,
+ OptionConverter,
+ ],
+ }),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-100" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex",
+ "on:click":
+ fileTransferViewModel2.showDirectionSelectionModal,
+ },
+ translations.general.backButton,
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary flex",
+ "on:click":
+ fileTransferViewModel2.showTransferDataModal,
+ "toggle:disabled":
+ fileTransferViewModel2.hasNoPathsSelected,
+ },
+ translations.general.continueButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ function TransferDataDisplayModal(fileTransferViewModel2) {
+ const isPresented = createProxyState(
+ [fileTransferViewModel2.presentedModal],
+ () =>
+ fileTransferViewModel2.presentedModal.value ==
+ 2 /* TransferDataDisplay */,
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", "toggle:open": isPresented },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.dataTransferModal.transferDataHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.dataTransferModal.dataEntryDescription,
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-column gap content-margin-bottom" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "forum",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.dataTransferModal
+ .transferChannelHeadline,
+ ),
+ /* @__PURE__ */ createElement("b", {
+ "subscribe:innerText":
+ fileTransferViewModel2.transferChannel,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "key",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.dataTransferModal
+ .transferKeyHeadline,
+ ),
+ /* @__PURE__ */ createElement("b", {
+ "subscribe:innerText":
+ fileTransferViewModel2.transferKey,
+ }),
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-100" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex",
+ "on:click":
+ fileTransferViewModel2.showFileSelectionModal,
+ },
+ translations.general.backButton,
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary flex",
+ "on:click": fileTransferViewModel2.initiateTransfer,
+ },
+ translations.dataTransferModal.sendButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ function TransferDisplayModal(fileTransferViewModel2) {
+ const isPresented = createProxyState(
+ [fileTransferViewModel2.presentedModal],
+ () =>
+ fileTransferViewModel2.presentedModal.value ==
+ 3 /* TransferDisplay */,
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", "toggle:open": isPresented },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.dataTransferModal.transferDataHeadline,
+ ),
+ /* @__PURE__ */ createElement("p", {
+ class: "secondary",
+ "subscribe:innerText":
+ fileTransferViewModel2.filesSentText,
+ }),
+ /* @__PURE__ */ createElement(
+ "p",
+ {
+ class: "secondary",
+ "toggle:hidden":
+ fileTransferViewModel2.didNotFinishSending,
+ },
+ translations.dataTransferModal.allFilesSent,
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement("div", {
+ class: "tile flex-column align-start",
+ "children:append": [
+ fileTransferViewModel2.filePathsSent,
+ StringToTextSpan,
+ ],
+ }),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-100" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex",
+ "on:click": fileTransferViewModel2.initiateTransfer,
+ },
+ translations.dataTransferModal.sendAgainButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "restart_alt",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex",
+ "on:click": fileTransferViewModel2.hideModal,
+ "toggle:disabled":
+ fileTransferViewModel2.didNotFinishSending,
+ },
+ translations.general.closeButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ function TransferDataInputModal(fileTransferViewModel2) {
+ const isPresented = createProxyState(
+ [fileTransferViewModel2.presentedModal],
+ () =>
+ fileTransferViewModel2.presentedModal.value ==
+ 4 /* TransferDataInput */,
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", "toggle:open": isPresented },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.dataTransferModal.transferDataHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.dataTransferModal
+ .dataEntryInputDescription,
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-column gap content-margin-bottom" },
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "forum",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.dataTransferModal
+ .transferChannelHeadline,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "bind:value":
+ fileTransferViewModel2.receivingTransferChannel,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "key",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.dataTransferModal
+ .transferKeyHeadline,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ "bind:value":
+ fileTransferViewModel2.receivingTransferKey,
+ }),
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-100" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex",
+ "on:click":
+ fileTransferViewModel2.showDirectionSelectionModal,
+ },
+ translations.general.backButton,
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary flex",
+ "on:click":
+ fileTransferViewModel2.prepareReceivingData,
+ "toggle:disabled":
+ fileTransferViewModel2.cannotPrepareToReceive,
+ },
+ translations.general.continueButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ function DataReceptionModal(fileTransferViewModel2) {
+ const isPresented = createProxyState(
+ [fileTransferViewModel2.presentedModal],
+ () =>
+ fileTransferViewModel2.presentedModal.value ==
+ 5 /* ReceptionDisplay */,
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "modal", "toggle:open": isPresented },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "main",
+ null,
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.dataTransferModal.transferDataHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "p",
+ { class: "secondary" },
+ translations.dataTransferModal
+ .readyToReceiveDescription,
+ ),
+ /* @__PURE__ */ createElement("p", {
+ class: "secondary",
+ "subscribe:innerText":
+ fileTransferViewModel2.filesReceivedText,
+ }),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement("div", {
+ class: "tile flex-column align-start",
+ "children:append": [
+ fileTransferViewModel2.filePathsReceived,
+ StringToTextSpan,
+ ],
+ }),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": reload },
+ translations.general.reloadAppButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "refresh",
+ ),
+ ),
+ ),
+ );
+ }
+
+ // src/Model/Global/fileTransferModel.ts
+ var FileTransferModel = class {
+ // init
+ constructor(storageModel2, connectionModel2) {
+ this.fileHandlerManager = new HandlerManager();
+ // general
+ this.generateTransferData = () => {
+ const transferData = {
+ channel: generateRandomToken(2).substring(0, 4),
+ key: generateRandomToken(3).substring(0, 6),
+ };
+ this.transferData = transferData;
+ return transferData;
+ };
+ this.prepareToReceive = (transferData) => {
+ this.connectionModel.addChannel(transferData.channel);
+ this.transferData = transferData;
+ };
+ // handlers
+ this.handleMessage = (data) => {
+ if (this.transferData == void 0) return;
+ if (data.messageBody == void 0) return;
+ if (data.messageChannel != this.transferData.channel) return;
+ this.handleFile(data.messageBody);
+ };
+ this.handleFile = async (encryptedFileData) => {
+ if (this.transferData == void 0) return;
+ const decrypted = await decryptString(
+ encryptedFileData,
+ this.transferData.key,
+ );
+ console.log(decrypted);
+ const parsed = parse(decrypted);
+ const isFileData = checkMatchesObjectStructure(
+ parsed,
+ FileDataReference,
+ );
+ if (isFileData == false) return;
+ const fileData = parsed;
+ this.storageModel.write(fileData.path, fileData.body);
+ const pathString = StorageModel.pathComponentsToString(
+ ...fileData.path,
+ );
+ this.fileHandlerManager.trigger(pathString);
+ };
+ // sending
+ this.sendFiles = (directoryPaths, callback) => {
+ for (const directoryPath of directoryPaths) {
+ this.storageModel.recurse(directoryPath, (filePath) => {
+ this.sendFile(filePath);
+ const pathString = StorageModel.pathComponentsToString(
+ ...filePath,
+ );
+ callback(pathString);
+ });
+ }
+ };
+ this.sendFile = async (filePath) => {
+ if (this.transferData == void 0) return;
+ const fileContent = this.storageModel.read(filePath);
+ if (fileContent == null) return;
+ const fileData = {
+ path: filePath,
+ body: fileContent,
+ };
+ const stringifiedFileData = stringify(fileData);
+ const encryptedFileData = await encryptString(
+ stringifiedFileData,
+ this.transferData.key,
+ );
+ this.connectionModel.sendPlainMessage(
+ this.transferData.channel,
+ encryptedFileData,
+ );
+ };
+ this.storageModel = storageModel2;
+ this.connectionModel = connectionModel2;
+ this.connectionModel.messageHandlerManager.addHandler(
+ this.handleMessage,
+ );
+ }
+ };
+ var FileDataReference = {
+ path: [""],
+ body: "",
+ };
+
+ // src/View/Components/chatEntry.tsx
+ function ChatEntry(chatViewModel) {
+ const view = /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "tile colored-tile chat-entry",
+ "set:color": chatViewModel.settingsPageViewModel.color,
+ style: "height: 8rem",
+ "on:click": chatViewModel.open,
+ "toggle:unread": chatViewModel.hasUnreadMessages,
+ },
+ /* @__PURE__ */ createElement("span", {
+ class: "shadow",
+ "subscribe:innerText":
+ chatViewModel.settingsPageViewModel.primaryChannel,
+ }),
+ /* @__PURE__ */ createElement("h2", {
+ "subscribe:innerText":
+ chatViewModel.settingsPageViewModel.primaryChannel,
+ }),
+ );
+ chatViewModel.index.subscribe((newIndex) => {
+ view.style.order = newIndex;
+ });
+ return view;
+ }
+ var ChatViewModelToChatEntry = (chatViewModel) => {
+ return ChatEntry(chatViewModel);
+ };
+
+ // src/View/homePage.tsx
+ function HomePage(
+ storageViewModel2,
+ settingsViewModel2,
+ connectionViewModel2,
+ fileTransferViewModel2,
+ chatListViewModel2,
+ ) {
+ const overviewSection = /* @__PURE__ */ createElement(
+ "div",
+ { id: "overview-section" },
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.homePage.overviewHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "cell_tower",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.homePage.serverAddress,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ list: "previous-connection-list",
+ placeholder:
+ translations.homePage.serverAddressPlaceholder,
+ "bind:value": connectionViewModel2.serverAddressInput,
+ "on:enter": connectionViewModel2.connect,
+ }),
+ /* @__PURE__ */ createElement("datalist", {
+ hidden: true,
+ id: "previous-connection-list",
+ "children:append": [
+ connectionViewModel2.previousAddresses,
+ StringToOption,
+ ],
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "danger flex justify-center",
+ "aria-label":
+ translations.homePage.disconnectAudioLabel,
+ "on:click": connectionViewModel2.disconnect,
+ "toggle:disabled": connectionViewModel2.cannotDisonnect,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "link_off",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "flex justify-center",
+ "aria-label":
+ translations.homePage.manageConnectionsAudioLabel,
+ "on:click": connectionViewModel2.showConnectionModal,
+ "toggle:disabled":
+ connectionViewModel2.hasNoPreviousConnections,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "history",
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary flex justify-center",
+ "aria-label": translations.homePage.connectAudioLabel,
+ "on:click": connectionViewModel2.connect,
+ "toggle:disabled": connectionViewModel2.cannotConnect,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "link",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "account_circle",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.homePage.yourNameLabel,
+ ),
+ /* @__PURE__ */ createElement("input", {
+ placeholder: translations.homePage.yourNamePlaceholder,
+ "bind:value": settingsViewModel2.usernameInput,
+ "on:enter": settingsViewModel2.setName,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row justify-end" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "width-50",
+ "on:click": settingsViewModel2.setName,
+ "toggle:disabled": settingsViewModel2.cannotSetName,
+ "aria-label":
+ translations.homePage.setNameButtonAudioLabel,
+ },
+ translations.general.setButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "check",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "label",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "calendar_month",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.homePage.firstDayOfWeekLabel,
+ ),
+ /* @__PURE__ */ createElement(
+ "select",
+ {
+ "bind:value":
+ settingsViewModel2.firstDayOfWeekInput,
+ },
+ ...translations.regional.weekdays.full.map(
+ (weekdayName, i) =>
+ Option(
+ weekdayName,
+ i.toString(),
+ i.toString() ==
+ settingsViewModel2.firstDayOfWeekInput
+ .value,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_drop_down",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "tile flex-no",
+ "on:click":
+ fileTransferViewModel2.showDirectionSelectionModal,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "sync_alt",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.homePage.transferDataButton,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "tile flex-no",
+ "on:click": storageViewModel2.showStorageModal,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "hard_drive_2",
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ null,
+ translations.homePage.manageStorageButton,
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "mobile-only" },
+ /* @__PURE__ */ createElement("hr", null),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row justify-end" },
+ /* @__PURE__ */ createElement(
+ "button",
+ { class: "ghost width-50", "on:click": scrollToChat },
+ translations.homePage.scrollToChatButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ ),
+ ),
+ );
+ const chatSection = /* @__PURE__ */ createElement(
+ "div",
+ { id: "chat-section" },
+ /* @__PURE__ */ createElement(
+ "h2",
+ null,
+ translations.homePage.chatsHeadline,
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-row width-input" },
+ /* @__PURE__ */ createElement("input", {
+ placeholder: translations.homePage.addChatPlaceholder,
+ "aria-label": translations.homePage.addChatAudioLabel,
+ "bind:value": chatListViewModel2.newChatPrimaryChannel,
+ "on:enter": chatListViewModel2.createChat,
+ }),
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "primary",
+ "aria-label": translations.homePage.addChatButton,
+ "on:click": chatListViewModel2.createChat,
+ "toggle:disabled": chatListViewModel2.cannotCreateChat,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "add",
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ id: "chat-grid",
+ "children:append": [
+ chatListViewModel2.chatViewModels,
+ ChatViewModelToChatEntry,
+ ],
+ }),
+ );
+ function scrollToChat() {
+ chatSection.scrollIntoView();
+ }
+ return /* @__PURE__ */ createElement(
+ "article",
+ { id: "home-page" },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ overviewSection,
+ chatSection,
+ ),
+ );
+ }
+
+ // src/Model/Global/settingsModel.ts
+ var SettingsModel = class {
+ // storage
+ setName(newValue) {
+ this.username = newValue;
+ const path = StorageModel.getPath(
+ "settings" /* SettingsModel */,
+ filePaths.settingsModel.username,
+ );
+ this.storageModel.write(path, newValue);
+ }
+ setFirstDayOfWeek(newValue) {
+ this.firstDayOfWeek = newValue;
+ const path = StorageModel.getPath(
+ "settings" /* SettingsModel */,
+ filePaths.settingsModel.firstDayOfWeek,
+ );
+ this.storageModel.write(path, newValue);
+ }
+ // load
+ loadUsernam() {
+ const path = StorageModel.getPath(
+ "settings" /* SettingsModel */,
+ filePaths.settingsModel.username,
+ );
+ const content = this.storageModel.read(path);
+ this.username = content ?? "";
+ }
+ loadFirstDayofWeek() {
+ const path = StorageModel.getPath(
+ "settings" /* SettingsModel */,
+ filePaths.settingsModel.firstDayOfWeek,
+ );
+ const content = this.storageModel.read(path);
+ this.firstDayOfWeek = content ?? "0";
+ }
+ // init
+ constructor(storageModel2) {
+ this.storageModel = storageModel2;
+ this.loadUsernam();
+ this.loadFirstDayofWeek();
+ }
+ };
+
+ // src/ViewModel/Global/settingsViewModel.ts
+ var SettingsViewModel = class {
+ // init
+ constructor(coreViewModel, settingsModel2) {
+ this.coreViewModel = coreViewModel;
+ // state
+ this.username = new State("");
+ this.usernameInput = new State("");
+ this.firstDayOfWeekInput = new State("0");
+ // guards
+ this.cannotSetName = createProxyState(
+ [this.usernameInput],
+ () =>
+ this.usernameInput.value == "" ||
+ this.usernameInput.value == this.settingsModel.username,
+ );
+ // methods
+ this.setName = () => {
+ this.settingsModel.setName(this.usernameInput.value);
+ this.username.value = this.settingsModel.username;
+ this.usernameInput.callSubscriptions();
+ };
+ this.setFirstDayofWeek = () => {
+ this.settingsModel.setFirstDayOfWeek(
+ this.firstDayOfWeekInput.value,
+ );
+ };
+ this.settingsModel = settingsModel2;
+ this.username.value = settingsModel2.username;
+ this.usernameInput.value = settingsModel2.username;
+ this.firstDayOfWeekInput.value = settingsModel2.firstDayOfWeek;
+ this.firstDayOfWeekInput.subscribe(this.setFirstDayofWeek);
+ }
+ };
+
+ // src/View/Components/directoryItemList.tsx
+ function DirectoryItemList(
+ storageViewModel2,
+ pathString = PATH_COMPONENT_SEPARATOR,
+ ) {
+ const StringToDirectoryItemList = (pathString2) =>
+ DirectoryItemList(storageViewModel2, pathString2);
+ const path = StorageModel.stringToPathComponents(pathString);
+ const fileName = StorageModel.getFileName(path);
+ const items = new ListState();
+ const style = `text-indent: ${path.length}rem`;
+ function loadItems() {
+ items.clear();
+ const directoryItems = storageViewModel2.storageModel.list(path);
+ for (const directoryItem of directoryItems) {
+ const itemPath = [...path, directoryItem];
+ const pathString2 = StorageModel.pathComponentsToString(
+ ...itemPath,
+ );
+ items.add(pathString2);
+ }
+ }
+ function select() {
+ storageViewModel2.selectedPath.value = pathString;
+ }
+ storageViewModel2.lastDeletedItemPath.subscribe(
+ (lastDeletedItemPath) => {
+ if (!items.value.has(lastDeletedItemPath)) return;
+ select();
+ setTimeout(() => loadItems(), 50);
+ },
+ );
+ const isSelected = createProxyState(
+ [storageViewModel2.selectedPath],
+ () => storageViewModel2.selectedPath.value == pathString,
+ );
+ isSelected.subscribe(() => {
+ if (isSelected.value == false) return;
+ loadItems();
+ });
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-column" },
+ /* @__PURE__ */ createElement(
+ "button",
+ {
+ class: "width-100 flex-1 clip",
+ "toggle:selected": isSelected,
+ "on:click": select,
+ },
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "ellipsis width-100 flex-1", style },
+ fileName,
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "flex-column",
+ "children:append": [items, StringToDirectoryItemList],
+ }),
+ );
+ }
+
+ // src/View/Components/fileBrowser.tsx
+ function FileBrowser(storageViewModel2) {
+ const detailView = createProxyState(
+ [storageViewModel2.selectedPath],
+ () => {
+ if (
+ storageViewModel2.selectedPath.value ==
+ PATH_COMPONENT_SEPARATOR
+ )
+ return /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "secondary" },
+ translations.storage.noItemSelected,
+ ),
+ /* @__PURE__ */ createElement("hr", null),
+ DangerousActionButton(
+ translations.storage.removeJunkButton,
+ "delete_forever",
+ storageViewModel2.removeJunk,
+ ),
+ );
+ return /* @__PURE__ */ createElement(
+ "div",
+ { class: "flex-column gap" },
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "b",
+ null,
+ translations.storage.path,
+ ),
+ /* @__PURE__ */ createElement("span", {
+ class: "break-all",
+ "subscribe:innerText":
+ storageViewModel2.selectedPath,
+ }),
+ ),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "tile flex-no" },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "b",
+ null,
+ translations.storage.content,
+ ),
+ /* @__PURE__ */ createElement("code", {
+ "subscribe:innerText":
+ storageViewModel2.selectedFileContent,
+ }),
+ ),
+ ),
+ DangerousActionButton(
+ translations.storage.deleteItem,
+ "delete_forever",
+ storageViewModel2.deleteSelectedItem,
+ ),
+ );
+ },
+ );
+ const view = /* @__PURE__ */ createElement(
+ "div",
+ { class: "file-browser" },
+ /* @__PURE__ */ createElement(
+ "div",
+ null,
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "scroll-area" },
+ DirectoryItemList(storageViewModel2),
+ ),
+ /* @__PURE__ */ createElement(
+ "div",
+ { class: "detail-button-wrapper" },
+ /* @__PURE__ */ createElement(
+ "button",
+ { class: "ghost", "on:click": scrollToDetails },
+ /* @__PURE__ */ createElement("span", {
+ class: "ellipsis",
+ "subscribe:innerText":
+ storageViewModel2.selectedFileName,
+ }),
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "arrow_forward",
+ ),
+ ),
+ ),
+ ),
+ /* @__PURE__ */ createElement("div", {
+ class: "scroll-area",
+ "children:set": detailView,
+ }),
+ );
+ function scrollToDetails() {
+ view.scrollLeft = view.scrollWidth;
+ }
+ return view;
+ }
+
+ // src/View/Modals/storageModal.tsx
+ function StorageModal(storageViewModel2) {
+ return /* @__PURE__ */ createElement(
+ "div",
+ {
+ class: "modal",
+ "toggle:open": storageViewModel2.isShowingStorageModal,
+ },
+ /* @__PURE__ */ createElement(
+ "div",
+ { style: "max-width: 64rem" },
+ /* @__PURE__ */ createElement(
+ "main",
+ { class: "padding-0" },
+ FileBrowser(storageViewModel2),
+ ),
+ /* @__PURE__ */ createElement(
+ "button",
+ { "on:click": storageViewModel2.hideStorageModal },
+ translations.general.closeButton,
+ /* @__PURE__ */ createElement(
+ "span",
+ { class: "icon" },
+ "close",
+ ),
+ ),
+ ),
+ );
+ }
+
+ // src/ViewModel/Global/storageViewModel.ts
+ var StorageViewModel = class {
+ // init
+ constructor(coreViewModel, storageModel2) {
+ this.coreViewModel = coreViewModel;
+ // state
+ this.isShowingStorageModal = new State(false);
+ this.selectedPath = new State(PATH_COMPONENT_SEPARATOR);
+ this.didMakeChanges = new State(false);
+ this.lastDeletedItemPath = new State("");
+ // methods
+ this.getSelectedItemContent = () => {
+ const path = StorageModel.stringToPathComponents(
+ this.selectedPath.value,
+ );
+ const content = this.storageModel.read(path);
+ return (
+ (content ?? translations.storage.notAFile) ||
+ translations.storage.contentEmpty
+ );
+ };
+ this.deleteSelectedItem = () => {
+ const path = StorageModel.stringToPathComponents(
+ this.selectedPath.value,
+ );
+ this.lastDeletedItemPath.value = this.selectedPath.value;
+ this.storageModel.removeRecursively(path);
+ this.didMakeChanges.value = true;
+ };
+ this.removeJunk = () => {
+ this.storageModel.removeJunk();
+ this.selectedPath.value = PATH_COMPONENT_SEPARATOR;
+ };
+ // view
+ this.showStorageModal = () => {
+ this.isShowingStorageModal.value = true;
+ };
+ this.hideStorageModal = () => {
+ if (this.didMakeChanges.value == true) {
+ window.location.reload();
+ return;
+ }
+ this.isShowingStorageModal.value = false;
+ };
+ this.storageModel = storageModel2;
+ this.selectedFileName = createProxyState([this.selectedPath], () =>
+ StorageModel.getFileNameFromString(this.selectedPath.value),
+ );
+ this.selectedFileContent = createProxyState(
+ [this.selectedPath],
+ () => this.getSelectedItemContent(),
+ );
+ }
+ };
+
+ // src/Upgrader/v1.ts
+ var v1Upgrader = class {
+ // init
+ constructor(settingsModel2, connectionModel2, chatListModel2) {
+ this.settingsModel = settingsModel2;
+ this.connectionModel = connectionModel2;
+ this.chatListModel = chatListModel2;
+ // general
+ this.migrateSettings = () => {
+ const name = getLocalStorageItemAndClear("sender-name");
+ if (name != null) {
+ const parsedName = parseOrFallback(name);
+ this.settingsModel.setName(parsedName);
+ }
+ const firstDayOfWeek =
+ getLocalStorageItemAndClear("first-day-of-week");
+ if (firstDayOfWeek != null) {
+ const parsedFirstDayOfWeek =
+ parseOrFallback(firstDayOfWeek);
+ this.settingsModel.setFirstDayOfWeek(parsedFirstDayOfWeek);
+ }
+ };
+ this.migrateConnections = () => {
+ const previousAddressString =
+ getLocalStorageItemAndClear("previous-addresses");
+ if (previousAddressString == null) return;
+ const previousAddresses = parseArray(previousAddressString);
+ for (const address of previousAddresses) {
+ if (typeof address != "string") continue;
+ this.connectionModel.storeAddress(address);
+ }
+ };
+ // chats
+ this.migrateChats = () => {
+ const chatIdString = getLocalStorageItemAndClear("chat-ids");
+ if (chatIdString == null) return;
+ const chatIds = parseArray(chatIdString);
+ for (const chatId of chatIds) {
+ if (typeof chatId != "string") continue;
+ this.migrateChatById(chatId);
+ }
+ };
+ this.migrateChatById = (id) => {
+ const primaryChannel = getLocalStorageItemAndClear(
+ storageKeys.primaryChannel(id),
+ );
+ if (primaryChannel == null) return;
+ const parsedPriamryChannel = parseOrFallback(primaryChannel);
+ const chatModel =
+ this.chatListModel.createChat(parsedPriamryChannel);
+ const secondaryChannelString = getLocalStorageItemAndClear(
+ storageKeys.secondaryChannels(id),
+ );
+ if (secondaryChannelString != null) {
+ const potentialSecondaryChannels = parseArray(
+ secondaryChannelString,
+ );
+ const confirmedSecondaryChannels = [];
+ for (const secondaryChannel of potentialSecondaryChannels) {
+ if (typeof secondaryChannel != "string") continue;
+ confirmedSecondaryChannels.push(secondaryChannel);
+ }
+ chatModel.setSecondaryChannels(confirmedSecondaryChannels);
+ }
+ const encryptionKey = getLocalStorageItemAndClear(
+ storageKeys.encyptionKey(id),
+ );
+ if (encryptionKey != null) {
+ const parsedEncryptionKey = parseOrFallback(encryptionKey);
+ chatModel.setEncryptionKey(parsedEncryptionKey);
+ }
+ const messagesString =
+ getLocalStorageItemAndClear(storageKeys.messages(id)) ?? "";
+ const messageOutboxString =
+ getLocalStorageItemAndClear(storageKeys.outbox(id)) ?? "";
+ const potentialMessages = parseArray(messagesString);
+ const potentialMessagesInOutbox =
+ parseArray(messageOutboxString);
+ const addMessages = (potentialMessages2, status) => {
+ for (const potentialMessage of potentialMessages2) {
+ const isV1ChatMessage = checkMatchesObjectStructure(
+ potentialMessage,
+ V1ChatMessageReference,
+ );
+ if (isV1ChatMessage == false) continue;
+ const v1ChatMessage = potentialMessage;
+ const convertedChatMessage = {
+ dataVersion: "v2",
+ id: v4_default(),
+ channel: v1ChatMessage.channel,
+ sender: v1ChatMessage.sender,
+ body: v1ChatMessage.body,
+ dateSent: v1ChatMessage.isoDate,
+ status,
+ stringifiedFile: "",
+ };
+ chatModel.addMessage(convertedChatMessage);
+ if (status == "outbox" /* Outbox */) {
+ this.connectionModel.sendMessageOrStore(
+ convertedChatMessage,
+ );
+ }
+ }
+ };
+ addMessages(potentialMessages, "received" /* Received */);
+ addMessages(potentialMessagesInOutbox, "outbox" /* Outbox */);
+ const objectsString =
+ getLocalStorageItemAndClear(storageKeys.objects(id)) ?? "";
+ const objectOutboxString =
+ getLocalStorageItemAndClear(storageKeys.itemOutbox(id)) ??
+ "";
+ const potentialObjects = parseArray(objectsString);
+ const potentialObjectsInOutbox = parseArray(objectOutboxString);
+ const addObjects = (potentialObjects2) => {
+ const board =
+ chatModel.fileModel.boardsAndTasksModel.createBoard(
+ translations.updater.migrated,
+ );
+ chatModel.fileModel.boardsAndTasksModel.updateBoard(board);
+ for (const potentialObjectEntry of potentialObjects2) {
+ const potentialObject = potentialObjectEntry[1];
+ const isV1MessageObject =
+ checkIsV1MessageObject(potentialObject);
+ if (isV1MessageObject == false) continue;
+ const objectId = potentialObject.id;
+ const objectName = potentialObject.title;
+ const contentVersions = Object.values(
+ potentialObject.contentVersions,
+ );
+ for (const potentialVersion of contentVersions) {
+ const isV1MessageObjectContent =
+ checkIsV1MessageObjectContent(potentialVersion);
+ if (isV1MessageObjectContent == false) continue;
+ const convertedTaskFileContent = {
+ dataVersion: "v2",
+ fileId: objectId,
+ fileContentId: FileModel2.generateFileContentId(
+ potentialVersion.isoDateVersionCreated,
+ ),
+ creationDate:
+ potentialVersion.isoDateVersionCreated,
+ type: "task",
+ name: objectName ?? "",
+ boardId: board.fileId ?? "",
+ description: potentialVersion.noteContent ?? "",
+ category: potentialVersion.categoryName ?? "",
+ status: potentialVersion.status ?? "",
+ priority: potentialVersion.priority ?? "",
+ date: potentialVersion.date ?? "",
+ time: potentialVersion.time ?? "",
+ };
+ console.log(convertedTaskFileContent);
+ chatModel.fileModel.handleFileContent(
+ convertedTaskFileContent,
+ );
+ }
+ }
+ };
+ addObjects([...potentialObjects, ...potentialObjectsInOutbox]);
+ };
+ this.migrateSettings();
+ this.migrateConnections();
+ this.migrateChats();
+ }
+ };
+ var storageKeys = {
+ viewType(id) {
+ return id + "view-type";
+ },
+ hasUnread(id) {
+ return id + "has-unread-messages";
+ },
+ primaryChannel(id) {
+ return id + "primary-channel";
+ },
+ secondaryChannels(id) {
+ return id + "secondary-channels";
+ },
+ encyptionKey(id) {
+ return id + "encryption-key";
+ },
+ messages(id) {
+ return id + "messages";
+ },
+ objects(id) {
+ return id + "items";
+ },
+ outbox(id) {
+ return id + "outbox";
+ },
+ itemOutbox(id) {
+ return id + "item-outbox";
+ },
+ composingMessage(id) {
+ return id + "composing-message";
+ },
+ };
+ var V1ChatMessageReference = {
+ channel: "",
+ sender: "",
+ body: "",
+ isoDate: "",
+ };
+ function checkIsV1MessageObject(object) {
+ if (object.id == void 0) return false;
+ if (object.title == void 0) return false;
+ if (object.contentVersions == void 0) return false;
+ return true;
+ }
+ function checkIsV1MessageObjectContent(object) {
+ if (object.id == void 0) return false;
+ if (object.isoDateVersionCreated == void 0) return false;
+ return true;
+ }
+
+ // src/index.tsx
+ var storageModel = new StorageModel();
+ var settingsModel = new SettingsModel(storageModel);
+ var connectionModel = new ConnectionModel(storageModel);
+ var chatListModel = new ChatListModel(
+ storageModel,
+ settingsModel,
+ connectionModel,
);
- });
- document.body.append(
- /* @__PURE__ */ createElement("div", { id: "background-wrapper" }, /* @__PURE__ */ createElement("div", { id: "sky" }), /* @__PURE__ */ createElement("div", { id: "grass-1" }), /* @__PURE__ */ createElement("div", { id: "grass-2" }))
- );
- document.querySelector("main").append(
- HomePage(
- storageViewModel,
- settingsViewModel,
- connectionViewModel,
- fileTransferViewModel,
- chatListViewModel
- ),
- ChatPageWrapper(chatListViewModel),
- ConnectionModal(connectionViewModel),
- DataTransferModalWrapper(connectionViewModel, fileTransferViewModel),
- StorageModal(storageViewModel)
- );
+ var fileTransferModel = new FileTransferModel(
+ storageModel,
+ connectionModel,
+ );
+ new v1Upgrader(settingsModel, connectionModel, chatListModel);
+ var coreVieWModel = new CoreViewModel();
+ var storageViewModel = new StorageViewModel(coreVieWModel, storageModel);
+ var settingsViewModel = new SettingsViewModel(coreVieWModel, settingsModel);
+ var connectionViewModel = new ConnectionViewModel(
+ coreVieWModel,
+ connectionModel,
+ );
+ var chatListViewModel = new ChatListViewModel(
+ coreVieWModel,
+ storageModel,
+ chatListModel,
+ settingsViewModel,
+ connectionViewModel,
+ );
+ var fileTransferViewModel = new FileTransferViewModel(
+ fileTransferModel,
+ chatListModel,
+ );
+ chatListViewModel.selectedChat.subscribe(() => {
+ document.body.toggleAttribute(
+ "showing-chat",
+ chatListViewModel.selectedChat.value != void 0,
+ );
+ });
+ document.body.append(
+ /* @__PURE__ */ createElement(
+ "div",
+ { id: "background-wrapper" },
+ /* @__PURE__ */ createElement("div", { id: "sky" }),
+ /* @__PURE__ */ createElement("div", { id: "grass-1" }),
+ /* @__PURE__ */ createElement("div", { id: "grass-2" }),
+ ),
+ );
+ document
+ .querySelector("main")
+ .append(
+ HomePage(
+ storageViewModel,
+ settingsViewModel,
+ connectionViewModel,
+ fileTransferViewModel,
+ chatListViewModel,
+ ),
+ ChatPageWrapper(chatListViewModel),
+ ConnectionModal(connectionViewModel),
+ DataTransferModalWrapper(
+ connectionViewModel,
+ fileTransferViewModel,
+ ),
+ StorageModal(storageViewModel),
+ );
})();
diff --git a/dist/manifest.json b/dist/manifest.json
index 6bfe9035..a7ad1283 100644
--- a/dist/manifest.json
+++ b/dist/manifest.json
@@ -1,10 +1,10 @@
{
- "name": "Comms",
- "display": "fullscreen",
- "icons": [
- {
- "src": "icon.png",
- "sizes": "any"
- }
- ]
+ "name": "Comms",
+ "display": "fullscreen",
+ "icons": [
+ {
+ "src": "icon.png",
+ "sizes": "any"
+ }
+ ]
}
diff --git a/dist/styles.css b/dist/styles.css
index 14d1d412..9b64c708 100644
--- a/dist/styles.css
+++ b/dist/styles.css
@@ -2,34 +2,35 @@
BASIC
*/
* {
- box-sizing: border-box;
- text-align: left;
- border: none;
- outline: none;
- margin: 0px;
- padding: 0px;
+ box-sizing: border-box;
+ text-align: left;
+ border: none;
+ outline: none;
+ margin: 0px;
+ padding: 0px;
}
html {
- -webkit-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
}
/*
ANIMATION
*/
@keyframes slide-up {
- 0% {
- opacity: 0;
- transform: translateY(2rem);
- }
+ 0% {
+ opacity: 0;
+ transform: translateY(2rem);
+ }
}
:root {
- --animation-duration: 0.5s;
+ --animation-duration: 0.4s;
}
+.modal > div,
.slide-up {
- animation: slide-up var(--animation-duration);
+ animation: slide-up var(--animation-duration);
}
/*
@@ -37,128 +38,128 @@ COLORS
*/
/* definition */
:root {
- --background: hsla(0, 0%, 100%, 100%);
- --background-glass: hsla(0, 0%, 100%, 5%);
- --background-transparent: hsla(0, 0%, 0%, 5%);
- --background-hover: hsla(0, 0%, 55%, 12%);
- --background-active: hsla(0, 0%, 55%, 12%);
+ --background: hsla(0, 0%, 100%, 100%);
+ --background-glass: hsla(0, 0%, 100%, 5%);
+ --background-transparent: hsla(0, 0%, 0%, 5%);
+ --background-hover: hsla(0, 0%, 55%, 12%);
+ --background-active: hsla(0, 0%, 55%, 12%);
- --layer: hsla(0, 0%, 96%, 60%);
+ --layer: hsla(0, 0%, 96%, 60%);
- --surface: hsla(0, 0%, 0%, 5%);
- --surface-hover: hsla(0, 0%, 5%, 10%);
- --surface-active: hsla(0, 0%, 5%, 15%);
+ --surface: hsla(0, 0%, 0%, 5%);
+ --surface-hover: hsla(0, 0%, 5%, 10%);
+ --surface-active: hsla(0, 0%, 5%, 15%);
- --border: hsla(0, 0%, 0%, 10%);
- --surface-border: #8d8d8d;
+ --border: hsla(0, 0%, 0%, 10%);
+ --surface-border: #8d8d8d;
- --text-normal: #161616;
- --text-placeholder: hsla(0, 0%, 0%, 30%);
- --text-on-color: #ffffff;
+ --text-normal: #161616;
+ --text-placeholder: hsla(0, 0%, 0%, 30%);
+ --text-on-color: #ffffff;
- --text-success: #24a148;
- --text-warning: #f1c21b;
- --text-error: #da1e28;
- --text-info: #0043ce;
+ --text-success: #24a148;
+ --text-warning: #f1c21b;
+ --text-error: #da1e28;
+ --text-info: #0043ce;
- --link: #0f62fe;
- --link-hover: #0043ce;
+ --link: #0f62fe;
+ --link-hover: #0043ce;
- --primary: #0f62fe;
- --primary-hover: #0353e9;
- --primary-active: #002d9c;
+ --primary: #0f62fe;
+ --primary-hover: #0353e9;
+ --primary-active: #002d9c;
- --danger: #da1e28;
- --danger-hover: #ba1b23;
- --danger-active: #750e13;
+ --danger: #da1e28;
+ --danger-hover: #ba1b23;
+ --danger-active: #750e13;
}
@media (prefers-color-scheme: dark) {
- :root {
- --background: hsla(0, 0%, 9%, 100%);
- --background-glass: hsla(0, 0%, 9%, 20%);
- --background-transparent: hsla(0, 0%, 100%, 5%);
- --background-hover: hsla(0, 0%, 55%, 16%);
- --background-active: hsla(0, 0%, 55%, 40%);
-
- --layer: hsla(0, 0%, 15%, 60%);
-
- --surface: hsla(0, 0%, 100%, 4%);
- --surface-hover: hsla(0, 0%, 100%, 10%);
- --surface-active: hsla(0, 0%, 100%, 15%);
-
- --border: hsla(0, 0%, 100%, 10%);
- --surface-border: #6f6f6f;
-
- --text-normal: #f4f4f4;
- --text-placeholder: hsla(0, 0%, 100%, 30%);
-
- --text-success: #42be65;
- --text-warning: #f1c21b;
- --text-error: #fa4d56;
- --text-info: #4589ff;
-
- --link: #78a9ff;
- --link-hover: #a6c8ff;
- }
-
- @media (display-mode: standalone) {
:root {
- --background: hsla(0, 0%, 0%, 100%);
- --background-transparent: hsla(0, 0%, 0%, 60%);
- --layer: hsla(0, 0%, 0%, 60%);
+ --background: hsla(0, 0%, 9%, 100%);
+ --background-glass: hsla(0, 0%, 9%, 20%);
+ --background-transparent: hsla(0, 0%, 100%, 5%);
+ --background-hover: hsla(0, 0%, 55%, 16%);
+ --background-active: hsla(0, 0%, 55%, 40%);
+
+ --layer: hsla(0, 0%, 15%, 60%);
+
+ --surface: hsla(0, 0%, 100%, 4%);
+ --surface-hover: hsla(0, 0%, 100%, 10%);
+ --surface-active: hsla(0, 0%, 100%, 15%);
+
+ --border: hsla(0, 0%, 100%, 10%);
+ --surface-border: #6f6f6f;
+
+ --text-normal: #f4f4f4;
+ --text-placeholder: hsla(0, 0%, 100%, 30%);
+
+ --text-success: #42be65;
+ --text-warning: #f1c21b;
+ --text-error: #fa4d56;
+ --text-info: #4589ff;
+
+ --link: #78a9ff;
+ --link-hover: #a6c8ff;
+ }
+
+ @media (display-mode: standalone) {
+ :root {
+ --background: hsla(0, 0%, 0%, 100%);
+ --background-transparent: hsla(0, 0%, 0%, 60%);
+ --layer: hsla(0, 0%, 0%, 60%);
+ }
}
- }
}
/* basic */
html {
- background: var(--background);
- color: var(--text-normal);
+ background: var(--background);
+ color: var(--text-normal);
}
.tile {
- color: var(--text-normal);
+ color: var(--text-normal);
}
::placeholder {
- color: var(--text-placeholder);
+ color: var(--text-placeholder);
}
.modal > div {
- background: var(--background-glass);
+ background: var(--background-glass);
}
menu,
header {
- background: var(--background-transparent);
+ background: var(--background-transparent);
}
* {
- color: inherit;
- accent-color: var(--primary);
+ color: inherit;
+ accent-color: var(--primary);
}
a {
- color: var(--link);
+ color: var(--link);
}
/* text */
.success {
- color: var(--text-success);
+ color: var(--text-success);
}
.warning {
- color: var(--text-warning);
+ color: var(--text-warning);
}
.error {
- color: var(--text-error);
+ color: var(--text-error);
}
.info {
- color: var(--text-info);
+ color: var(--text-info);
}
/* controls */
@@ -169,129 +170,143 @@ textarea:not(.tile *),
select:not(.tile *),
.surface,
.tile {
- background: var(--surface);
-}
-[selected] {
- background: var(--background);
-}
-@media (hover: hover) {
- button:hover,
- select:hover,
- button.tile.ghost:hover {
- background: var(--surface-hover);
- }
- [selected]:hover {
background: var(--surface);
- }
}
+
+[selected] {
+ background: var(--background);
+}
+
+@media (hover: hover) {
+ button:hover,
+ select:hover,
+ button.tile.ghost:hover {
+ background: var(--surface-hover);
+ }
+
+ [selected]:hover {
+ background: var(--surface);
+ }
+}
+
button:active,
select:active,
button.tile.ghost:active {
- background: var(--surface-active);
+ background: var(--surface-active);
}
+
[selected]:active {
- background: var(--surface-hover);
+ background: var(--surface-hover);
}
+
.tile *,
header button {
- background: transparent;
+ background: transparent;
}
input:checked {
- background: var(--primary);
+ background: var(--primary);
}
/* primary button */
button.primary {
- background: var(--primary);
- color: var(--text-on-color);
+ background: var(--primary);
+ color: var(--text-on-color);
}
+
@media (hover: hover) {
- button.primary:hover {
- background: var(--primary-hover);
- }
+ button.primary:hover {
+ background: var(--primary-hover);
+ }
}
+
button.primary:active {
- background: var(--primary-active);
+ background: var(--primary-active);
}
/* ghost button */
button.ghost:not(.tile) {
- background: transparent;
+ background: transparent;
}
+
button.ghost {
- color: var(--link);
+ color: var(--link);
}
+
.tab-link[active] {
- color: var(--primary);
+ color: var(--primary);
}
+
@media (hover: hover) {
- button.ghost:hover {
- background: var(--background-hover);
- }
+ button.ghost:hover {
+ background: var(--background-hover);
+ }
}
+
button.ghost:active {
- background: var(--background-active);
+ background: var(--background-active);
}
/* danger button */
button.danger {
- background: var(--danger);
- color: white;
+ background: var(--danger);
+ color: white;
}
+
@media (hover: hover) {
- button.danger:hover {
- background: var(--danger-hover);
- }
+ button.danger:hover {
+ background: var(--danger-hover);
+ }
}
+
button.danger:active {
- background: var(--danger-active);
+ background: var(--danger-active);
}
/*
SIZES
*/
:root {
- /* gap */
- --gap-between-tiles: 4px;
- --gap-in-tab-links: 1rem;
- --gap-in-controls: 1.2rem;
- --large-gap: 2rem;
+ /* gap */
+ --gap-between-tiles: 4px;
+ --gap-in-tab-links: 1rem;
+ --gap-in-controls: 1.2rem;
+ --large-gap: 2rem;
- /* outline */
- --outline-width: var(--gap-between-tiles);
+ /* outline */
+ --outline-width: var(--gap-between-tiles);
- /* margin */
- --label-top-padding: 0.25rem;
- --label-tile-top-padding: 0.5rem;
- --label-margin-bottom: 1rem;
- --content-margin: 0.75rem;
+ /* margin */
+ --label-top-padding: 0.25rem;
+ --label-tile-top-padding: 0.5rem;
+ --label-margin-bottom: 1rem;
+ --content-margin: 0.75rem;
- /* padding */
- --control-padding: 1rem;
- --control-marker-padding: 0.7rem;
- --content-padding: 1rem;
- --modal-padding: 1.25rem;
- --horizontal-tab-link-padding: 0.8rem;
+ /* padding */
+ --control-padding: 1rem;
+ --control-marker-padding: 0.7rem;
+ --content-padding: 1rem;
+ --modal-padding: 1.25rem;
+ --horizontal-tab-link-padding: 0.8rem;
- /* size */
- --checkbox-size: 16px;
+ /* size */
+ --checkbox-size: 16px;
- --menu-height-mobile: calc(var(--header-height) + var(--safe-area-bottom));
- --menu-width-desktop: 200px;
- --split-pane-width-desktop: 300px;
+ --menu-height-mobile: calc(var(--header-height) + var(--safe-area-bottom));
+ --menu-width-desktop: 200px;
+ --split-pane-width-desktop: 300px;
- --control-height: 42px;
- --header-height: 48px;
+ --control-height: 42px;
+ --header-height: 48px;
- --max-input-width: 24rem;
- --max-content-width: 38rem;
+ --max-input-width: 24rem;
+ --max-content-width: 38rem;
- /* safe */
- --safe-area-top: env(safe-area-inset-top);
- --safe-area-left: env(safe-area-inset-left);
- --safe-area-right: env(safe-area-inset-right);
- --safe-area-bottom: env(safe-area-inset-bottom);
+ /* safe */
+ --safe-area-top: env(safe-area-inset-top);
+ --safe-area-left: env(safe-area-inset-left);
+ --safe-area-right: env(safe-area-inset-right);
+ --safe-area-bottom: env(safe-area-inset-bottom);
}
/*
@@ -299,51 +314,44 @@ TYPE
*/
@font-face {
- font-family: IBM Plex Sans;
- src: url("Fonts/IBMPlexSans-Regular.ttf");
+ font-family: IBM Plex Sans;
+ src: url("Fonts/IBMPlexSans-Regular.ttf");
}
@font-face {
- font-family: IBM Plex Sans;
- src: url("Fonts/IBMPlexSans-Bold.ttf");
- font-weight: bold;
+ font-family: IBM Plex Sans;
+ src: url("Fonts/IBMPlexSans-Bold.ttf");
+ font-weight: bold;
}
@font-face {
- font-family: IBM Plex Mono;
- src: url("Fonts/IBMPlexMono-Regular.ttf");
+ font-family: IBM Plex Mono;
+ src: url("Fonts/IBMPlexMono-Regular.ttf");
}
@font-face {
- font-family: IBM Plex Serif;
- src: url("Fonts/IBMPlexSerif-Regular.ttf");
+ font-family: IBM Plex Serif;
+ src: url("Fonts/IBMPlexSerif-Regular.ttf");
}
/*
MAIN
*/
-/* animation */
-@media (max-width: 640px) {
- .modal > div {
- animation: slide-up var(--animation-duration);
- }
-}
-
/* appearance */
input[type="checkbox"],
input[type="radio"],
select {
- appearance: none;
- --webkit-appearance: none;
- --mpz-appearance: none;
+ appearance: none;
+ --webkit-appearance: none;
+ --mpz-appearance: none;
}
/* backdrop-filter */
header,
menu,
.modal > div {
- backdrop-filter: blur(70px);
- -webkit-backdrop-filter: blur(70px);
+ backdrop-filter: blur(70px);
+ -webkit-backdrop-filter: blur(70px);
}
/* border, outline, etc */
@@ -351,63 +359,63 @@ button,
input,
select,
textarea {
- border: none;
+ border: none;
}
footer {
- border-top: 1px solid var(--surface-border);
+ border-top: 1px solid var(--surface-border);
}
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not(
- label.tile *
- ),
+ label.tile *
+ ),
label.tile,
select:not(label.tile *),
textarea:not(label.tile *) {
- border-bottom: 1px solid var(--surface-border);
+ border-bottom: 1px solid var(--surface-border);
}
hr {
- border-bottom: 1px solid var(--border);
+ border-bottom: 1px solid var(--border);
}
.border-bottom {
- border-bottom: 1px solid var(--border) !important;
+ border-bottom: 1px solid var(--border) !important;
}
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):focus:not(
- label.tile *
- ):not(.no-outline),
+ label.tile *
+ ):not(.no-outline),
label.tile:has(input:focus),
label.tile:has(textarea:focus),
textarea:focus:not(label.tile *),
[selected] {
- outline: var(--outline-width) solid var(--primary);
+ outline: var(--outline-width) solid var(--primary);
}
.inset-outline[selected] {
- outline: none;
- box-shadow: var(--primary) 0 0 0 var(--outline-width) inset;
+ outline: none;
+ box-shadow: var(--primary) 0 0 0 var(--outline-width) inset;
}
/* border-radius */
* {
- border-radius: 0px;
+ border-radius: 0px;
}
input[type="radio"] {
- border-radius: 100%;
+ border-radius: 100%;
}
/* cursor */
a,
.tab-link {
- cursor: pointer;
+ cursor: pointer;
}
/* display etc */
.align-start {
- align-items: start !important;
+ align-items: start !important;
}
button:not(.tile),
@@ -419,20 +427,20 @@ label.inline,
select,
textarea,
.tile:not(label) {
- align-items: center;
+ align-items: center;
}
.align-center {
- align-items: center !important;
+ align-items: center !important;
}
.align-end {
- align-items: end !important;
+ align-items: end !important;
}
label,
label * {
- display: block;
+ display: block;
}
footer,
@@ -445,97 +453,97 @@ main,
.modal > div,
.tile,
.tile > div {
- display: flex;
+ display: flex;
}
header > span,
.flex-column,
.flex-row {
- display: flex;
+ display: flex;
}
.grid {
- display: grid !important;
+ display: grid !important;
}
button:not(.tile),
input,
select,
textarea {
- display: inline-flex;
+ display: inline-flex;
}
.modal {
- display: none;
+ display: none;
}
[hidden] {
- display: none !important;
+ display: none !important;
}
main,
.tile,
.tile > div {
- flex: 1;
+ flex: 1;
}
.flex {
- flex: 1 !important;
+ flex: 1 !important;
}
.flex-no {
- flex: 0 !important;
+ flex: 0 !important;
}
.modal > div,
.modal main,
.tile > div {
- flex-direction: column;
+ flex-direction: column;
}
.flex-column {
- flex-direction: column !important;
+ flex-direction: column !important;
}
.flex-row {
- flex-direction: row !important;
+ flex-direction: row !important;
}
.flex-wrap {
- flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
}
button,
label.inline,
.tile,
.control-gap {
- gap: var(--gap-in-controls);
+ gap: var(--gap-in-controls);
}
header > span,
.gap {
- gap: var(--gap-between-tiles) !important;
+ gap: var(--gap-between-tiles) !important;
}
.large-gap {
- gap: var(--large-gap) !important;
+ gap: var(--large-gap) !important;
}
header,
button {
- justify-content: space-between;
+ justify-content: space-between;
}
.justify-apart {
- justify-content: space-between !important;
+ justify-content: space-between !important;
}
.tile {
- justify-content: start;
+ justify-content: start;
}
.justify-start {
- justify-content: start !important;
+ justify-content: start !important;
}
footer,
@@ -543,176 +551,177 @@ header button,
input[type="checkbox"],
input[type="radio"],
.modal {
- justify-content: center;
+ justify-content: center;
}
.justify-center {
- justify-content: safe center !important;
+ justify-content: safe center !important;
}
.justify-end {
- justify-content: end !important;
+ justify-content: end !important;
}
/* opacity */
[disabled] {
- opacity: 0.5;
+ opacity: 0.5;
}
label span,
.helper-text,
.secondary,
.tab-link:not([active]) {
- opacity: 0.6;
+ opacity: 0.6;
}
/* padding, margin, etc */
h1,
h2,
h3 {
- margin: var(--control-padding) 0px;
+ margin: var(--control-padding) 0px;
}
main,
p {
- margin: 0px;
+ margin: 0px;
}
.margin-0 {
- margin: 0px !important;
+ margin: 0px !important;
}
hr {
- margin: var(--content-margin) 0px;
+ margin: var(--content-margin) 0px;
}
:is(code, p):not(:last-child) {
- margin-bottom: var(--content-margin);
+ margin-bottom: var(--content-margin);
}
.content-margin-bottom {
- margin-bottom: var(--content-margin) !important;
+ margin-bottom: var(--content-margin) !important;
}
label:not(.tile) {
- margin-bottom: var(--label-margin-bottom);
+ margin-bottom: var(--label-margin-bottom);
}
.margin-bottom {
- margin-bottom: var(--gap-between-tiles) !important;
+ margin-bottom: var(--gap-between-tiles) !important;
}
label.tile {
- margin-bottom: var(--gap-between-tiles);
+ margin-bottom: var(--gap-between-tiles);
}
label:not(.tile) span:first-child {
- margin-top: var(--label-top-padding);
+ margin-top: var(--label-top-padding);
}
label.tile span:first-child {
- margin-top: var(--label-tile-top-padding);
+ margin-top: var(--label-tile-top-padding);
}
input[type="color"],
input[type="checkbox"],
input[type="radio"] {
- padding: 0px;
+ padding: 0px;
}
.padding-0 {
- padding: 0 !important;
+ padding: 0 !important;
}
article > div {
- padding: var(--content-padding);
+ padding: var(--content-padding);
}
button:not(.tile):not(header *),
input,
select {
- padding: 0px var(--control-padding);
+ padding: 0px var(--control-padding);
}
.padding-h {
- padding: 0px var(--control-padding) !important;
+ padding: 0px var(--control-padding) !important;
}
label.inline,
textarea,
.tile:not(label) {
- padding: var(--control-padding);
+ padding: var(--control-padding);
}
.padding {
- padding: var(--control-padding) !important;
+ padding: var(--control-padding) !important;
}
header > span {
- padding: var(--gap-between-tiles);
+ padding: var(--gap-between-tiles);
}
.padding-sm {
- padding: var(--gap-between-tiles) !important;
+ padding: var(--gap-between-tiles) !important;
}
.modal main {
- padding: var(--modal-padding);
+ padding: var(--modal-padding);
}
.padding-top {
- padding-top: var(--control-padding) !important;
+ padding-top: var(--control-padding) !important;
}
article > div {
- padding-top: calc(var(--header-height) + var(--content-margin));
+ padding-top: calc(var(--header-height) + var(--content-margin));
}
header {
- padding-left: var(--content-padding);
+ padding-left: var(--content-padding);
}
label.tile:has(> span.icon) {
- padding-left: var(--control-padding);
+ padding-left: var(--control-padding);
}
.padding-left {
- padding-left: var(--control-padding) !important;
+ padding-left: var(--control-padding) !important;
}
label.tile:has(> span.icon) :is(input, select, textarea) {
- padding-left: 0px;
+ padding-left: 0px;
}
select {
- padding-right: 2.5rem;
+ padding-right: 2.5rem;
}
.padding-right {
- padding-right: var(--control-padding) !important;
+ padding-right: var(--control-padding) !important;
}
.padding-bottom {
- padding-bottom: var(--control-padding) !important;
+ padding-bottom: var(--control-padding) !important;
}
article > div {
- padding-bottom: calc(
- var(--menu-height-mobile) + var(--control-height) + var(--content-padding)
- );
+ padding-bottom: calc(
+ var(--menu-height-mobile) + var(--control-height) +
+ var(--content-padding)
+ );
}
label:not(.inline) {
- text-indent: var(--control-padding);
+ text-indent: var(--control-padding);
}
label.tile:has(> span.icon) {
- text-indent: 0px;
+ text-indent: 0px;
}
/* pointer-events */
[disabled] {
- pointer-events: none;
+ pointer-events: none;
}
/* position etc */
@@ -720,88 +729,88 @@ article > div,
footer,
header,
select + .icon {
- position: absolute;
+ position: absolute;
}
.absolute {
- position: absolute !important;
+ position: absolute !important;
}
.modal {
- position: fixed;
+ position: fixed;
}
.fixed {
- position: fixed !important;
+ position: fixed !important;
}
article,
main,
input,
label {
- position: relative;
+ position: relative;
}
.relative {
- position: relative !important;
+ position: relative !important;
}
.sticky {
- position: sticky !important;
+ position: sticky !important;
}
article > div,
header,
.modal {
- top: 0;
+ top: 0;
}
.top-0 {
- top: 0 !important;
+ top: 0 !important;
}
article > div,
footer,
header,
.modal {
- left: 0px;
+ left: 0px;
}
.left-0 {
- left: 0px !important;
+ left: 0px !important;
}
article > div,
footer,
header,
.modal {
- right: 0px;
+ right: 0px;
}
.right-0 {
- right: 0px !important;
+ right: 0px !important;
}
select + .icon {
- right: var(--control-marker-padding);
+ right: var(--control-marker-padding);
}
footer {
- bottom: var(--safe-area-bottom);
+ bottom: var(--safe-area-bottom);
}
body:has(> menu) footer {
- bottom: var(--menu-height-mobile);
+ bottom: var(--menu-height-mobile);
}
article > div,
select + .icon,
.modal {
- bottom: 0px;
+ bottom: 0px;
}
.bottom-0 {
- bottom: 0px !important;
+ bottom: 0px !important;
}
/* size */
@@ -810,15 +819,15 @@ main,
select + .icon,
.modal > div,
.modal main {
- height: 100%;
+ height: 100%;
}
.height-100 {
- height: 100% !important;
+ height: 100% !important;
}
.height-auto {
- height: auto !important;
+ height: auto !important;
}
button:not(.tile):not(header *),
@@ -826,19 +835,19 @@ footer,
input:not([type="checkbox"]):not([type="radio"]),
select,
select + .icon {
- height: var(--control-height);
+ height: var(--control-height);
}
header {
- height: var(--header-height);
+ height: var(--header-height);
}
header button {
- height: calc(var(--header-height) - var(--gap-between-tiles) * 2);
+ height: calc(var(--header-height) - var(--gap-between-tiles) * 2);
}
.max-height-100 {
- max-height: 100% !important;
+ max-height: 100% !important;
}
button:not(.tile):not(header *),
@@ -846,19 +855,19 @@ footer,
input:not([type="checkbox"]):not([type="radio"]),
select,
.modal > div > :is(button, div.flex-row) {
- min-height: var(--control-height);
+ min-height: var(--control-height);
}
.ellipsis {
- max-width: 100%;
+ max-width: 100%;
}
article {
- min-width: 100%;
+ min-width: 100%;
}
.width-50 {
- width: 50% !important;
+ width: 50% !important;
}
article,
@@ -871,51 +880,51 @@ textarea,
.modal > div,
.tile,
.tile > div {
- width: 100%;
+ width: 100%;
}
.width-content,
.width-input,
.width-100 {
- width: 100% !important;
+ width: 100% !important;
}
header button {
- width: var(--header-height);
+ width: var(--header-height);
}
input[type="checkbox"],
input[type="radio"] {
- width: 16px;
- height: 16px;
+ width: 16px;
+ height: 16px;
}
label.tile textarea {
- resize: vertical;
+ resize: vertical;
}
/*
text
*/
* {
- font-family: IBM Plex Sans;
- font-size: 1rem;
+ font-family: IBM Plex Sans;
+ font-size: 1rem;
}
h1 {
- font-size: 2rem;
+ font-size: 2rem;
}
h2 {
- font-size: 1.6rem;
+ font-size: 1.6rem;
}
h3 {
- font-size: 1.4rem;
+ font-size: 1.4rem;
}
code {
- font-family: IBM Plex Mono;
+ font-family: IBM Plex Mono;
}
.secondary,
@@ -925,63 +934,63 @@ code {
.warning,
.success,
label.tile > div > span:not(.icon) {
- font-size: 0.9rem;
+ font-size: 0.9rem;
}
/* text-decoration */
a {
- text-decoration: none;
+ text-decoration: none;
}
/* z-index */
footer,
header,
menu {
- z-index: 1;
+ z-index: 1;
}
.modal {
- z-index: 2;
+ z-index: 2;
}
.modal > div {
- z-index: 2;
+ z-index: 2;
}
/*
PSEUDO
*/
input[type="checkbox"]:checked::before {
- content: "check";
- font-size: 70% !important;
- color: var(--text-on-color);
+ content: "check";
+ font-size: 70% !important;
+ color: var(--text-on-color);
}
input[type="radio"]:checked::before {
- content: "";
+ content: "";
- background: var(--text-on-color);
- border-radius: 100%;
+ background: var(--text-on-color);
+ border-radius: 100%;
- --inset: 4px;
- position: absolute;
- top: var(--inset);
- left: var(--inset);
- right: var(--inset);
- bottom: var(--inset);
+ --inset: 4px;
+ position: absolute;
+ top: var(--inset);
+ left: var(--inset);
+ right: var(--inset);
+ bottom: var(--inset);
}
.modal::before {
- content: "";
+ content: "";
- position: absolute;
- top: 0px;
- left: 0px;
- right: 0px;
- bottom: 0px;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
- background: var(--background);
- opacity: 0.6;
+ background: var(--background);
+ opacity: 0.6;
}
/*
@@ -990,65 +999,66 @@ LAYOUT
/* basic */
body,
html {
- display: flex;
- flex-direction: column-reverse;
+ display: flex;
+ flex-direction: column-reverse;
- margin: 0px;
- width: 100%;
- height: 100%;
+ margin: 0px;
+ width: 100%;
+ height: 100%;
}
body,
.modal {
- padding-left: var(--safe-area-left);
- padding-right: var(--safe-area-right);
- padding-top: var(--safe-area-top);
+ padding-left: var(--safe-area-left);
+ padding-right: var(--safe-area-right);
+ padding-top: var(--safe-area-top);
}
.modal {
- padding-bottom: var(--safe-area-bottom);
+ padding-bottom: var(--safe-area-bottom);
}
menu {
- width: 100%;
- height: var(--menu-height-mobile);
+ width: 100%;
+ height: var(--menu-height-mobile);
- position: absolute;
- bottom: 0px;
- left: 0px;
- right: 0px;
+ position: absolute;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
- display: flex;
- flex-direction: row;
- justify-content: space-around;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
- padding-top: 0px;
- padding-bottom: var(--safe-area-bottom);
+ padding-top: 0px;
+ padding-bottom: var(--safe-area-bottom);
}
.tab-link {
- width: 100%;
- height: var(--header-height);
+ width: 100%;
+ height: var(--header-height);
- padding: var(--horizontal-tab-link-padding) var(--control-padding);
+ padding: var(--horizontal-tab-link-padding) var(--control-padding);
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 0px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0px;
- font-size: 0.5rem;
+ font-size: 0.5rem;
}
article > div {
- padding-bottom: calc(
- var(--menu-height-mobile) + var(--header-height) + var(--content-padding)
- );
+ padding-bottom: calc(
+ var(--menu-height-mobile) + var(--header-height) +
+ var(--content-padding)
+ );
}
/* overflow */
menu {
- overflow: auto;
+ overflow: auto;
}
article,
@@ -1056,161 +1066,161 @@ body,
main,
.clip,
.ellipsis {
- overflow: hidden;
+ overflow: hidden;
}
.scroll-no {
- overflow: hidden !important;
+ overflow: hidden !important;
}
article > div,
.modal main {
- overflow: auto;
+ overflow: auto;
}
main {
- overflow-x: auto;
+ overflow-x: auto;
}
.scroll-h {
- overflow-x: auto !important;
+ overflow-x: auto !important;
}
.scroll-v {
- overflow-y: auto !important;
+ overflow-y: auto !important;
}
.ellipsis {
- text-overflow: ellipsis;
+ text-overflow: ellipsis;
}
.tile {
- word-break: normal;
+ word-break: normal;
}
.break-all {
- word-break: break-all !important;
+ word-break: break-all !important;
}
.break-word {
- word-break: break-word !important;
+ word-break: break-word !important;
}
.ellipsis {
- white-space: nowrap;
+ white-space: nowrap;
}
code {
- white-space: pre-wrap;
- word-break: break-all;
+ white-space: pre-wrap;
+ word-break: break-all;
}
/* scrolling */
main {
- scrollbar-width: none;
+ scrollbar-width: none;
}
main::-webkit-scrollbar {
- display: none;
+ display: none;
}
main,
article > div {
- scroll-behavior: smooth;
+ scroll-behavior: smooth;
}
article {
- scroll-snap-align: start;
+ scroll-snap-align: start;
}
main {
- scroll-snap-type: x mandatory;
+ scroll-snap-type: x mandatory;
}
/* tablet & up */
@media (min-width: 640px) {
- /* basic */
- .mobile-only {
- display: none;
- }
+ /* basic */
+ .mobile-only {
+ display: none;
+ }
- /* border, outline, etc */
- .modal > div {
- border: 1px solid var(--surface-border);
- }
+ /* border, outline, etc */
+ .modal > div {
+ border: 1px solid var(--surface-border);
+ }
- menu,
- main.split > article:first-of-type {
- border-right: 1px solid var(--border);
- }
+ menu,
+ main.split > article:first-of-type {
+ border-right: 1px solid var(--border);
+ }
- /* size */
- .modal > div {
- height: auto;
- max-height: 100%;
- }
+ /* size */
+ .modal > div {
+ height: auto;
+ max-height: 100%;
+ }
- p,
- .modal > div {
- max-width: var(--max-content-width);
- }
+ p,
+ .modal > div {
+ max-width: var(--max-content-width);
+ }
- .width-content {
- max-width: var(--max-content-width) !important;
- }
+ .width-content {
+ max-width: var(--max-content-width) !important;
+ }
- input,
- label,
- select,
- textarea {
- max-width: var(--max-input-width);
- }
+ input,
+ label,
+ select,
+ textarea {
+ max-width: var(--max-input-width);
+ }
- .width-input {
- max-width: var(--max-input-width) !important;
- }
+ .width-input {
+ max-width: var(--max-input-width) !important;
+ }
- /* layout */
- body {
- flex-direction: row;
- }
+ /* layout */
+ body {
+ flex-direction: row;
+ }
- main.split {
- display: grid;
- grid-template-columns: var(--split-pane-width-desktop) 1fr;
- }
+ main.split {
+ display: grid;
+ grid-template-columns: var(--split-pane-width-desktop) 1fr;
+ }
- article > div {
- padding-bottom: calc(var(--content-padding) + var(--control-height));
- }
+ article > div {
+ padding-bottom: calc(var(--content-padding) + var(--control-height));
+ }
- menu {
- position: static;
+ menu {
+ position: static;
- width: var(--menu-width-desktop);
- height: 100%;
+ width: var(--menu-width-desktop);
+ height: 100%;
- flex-direction: column;
- justify-content: start;
- padding-top: 20px;
- }
+ flex-direction: column;
+ justify-content: start;
+ padding-top: 20px;
+ }
- .tab-link {
- width: 100%;
- height: var(--control-height);
+ .tab-link {
+ width: 100%;
+ height: var(--control-height);
- flex-direction: row;
- gap: var(--gap-in-tab-links);
+ flex-direction: row;
+ gap: var(--gap-in-tab-links);
- font-size: 1rem;
- }
+ font-size: 1rem;
+ }
}
/* desktop */
@media (min-width: 1024px) {
- :root {
- --split-pane-width-desktop: 400px;
- }
+ :root {
+ --split-pane-width-desktop: 400px;
+ }
}
/*
@@ -1219,26 +1229,26 @@ ICONS
input[type="checkbox"],
.tile.arrow-right::after,
.icon {
- font-family: "Material Symbols Sharp";
- font-weight: normal;
- font-style: normal;
- font-size: 24px;
- line-height: 1;
- letter-spacing: normal;
- text-transform: none;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- white-space: nowrap;
- word-wrap: normal;
- direction: ltr;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
+ font-family: "Material Symbols Sharp";
+ font-weight: normal;
+ font-style: normal;
+ font-size: 24px;
+ line-height: 1;
+ letter-spacing: normal;
+ text-transform: none;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ white-space: nowrap;
+ word-wrap: normal;
+ direction: ltr;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
}
@font-face {
- font-family: "Material Symbols Sharp";
- font-style: normal;
- font-weight: 300;
- src: url("Icons/material-icons.woff") format("woff");
+ font-family: "Material Symbols Sharp";
+ font-style: normal;
+ font-weight: 300;
+ src: url("Icons/material-icons.woff") format("woff");
}
diff --git a/dist/support.js b/dist/support.js
index 39110646..8d839ed5 100644
--- a/dist/support.js
+++ b/dist/support.js
@@ -1,32 +1,35 @@
// TABS
document.querySelector("main").addEventListener("scroll", () => {
- const links = document.querySelectorAll(".tab-link");
- links.forEach((element) => element.removeAttribute("active"));
+ const links = document.querySelectorAll(".tab-link");
+ links.forEach((element) => element.removeAttribute("active"));
- const pageWidth = document.querySelector("main > article").offsetWidth;
- const scroll = document.querySelector("main").scrollLeft;
- const currentPage = Math.round(scroll / pageWidth);
+ const pageWidth = document.querySelector("main > article").offsetWidth;
+ const scroll = document.querySelector("main").scrollLeft;
+ const currentPage = Math.round(scroll / pageWidth);
- if (links[currentPage]) links[currentPage].setAttribute("active", "");
+ if (links[currentPage]) links[currentPage].setAttribute("active", "");
});
// SERVICE WORKER
async function registerServiceWorker() {
- if ("serviceWorker" in navigator) {
- try {
- const registration = await navigator.serviceWorker.register("/sw.js", {
- scope: "/",
- });
- if (registration.installing) {
- setTimeout(() => window.location.reload(), 500);
- } else if (registration.active) {
- console.log("Service worker active");
- registration.update();
- }
- } catch (error) {
- console.error(`Could not install service worker: ${error}`);
+ if ("serviceWorker" in navigator) {
+ try {
+ const registration = await navigator.serviceWorker.register(
+ "/sw.js",
+ {
+ scope: "/",
+ },
+ );
+ if (registration.installing) {
+ setTimeout(() => window.location.reload(), 500);
+ } else if (registration.active) {
+ console.log("Service worker active");
+ registration.update();
+ }
+ } catch (error) {
+ console.error(`Could not install service worker: ${error}`);
+ }
}
- }
}
registerServiceWorker();
diff --git a/dist/sw.js b/dist/sw.js
index fac4171d..ceefd51c 100644
--- a/dist/sw.js
+++ b/dist/sw.js
@@ -1,31 +1,32 @@
const CACHE_VERSION = "v2";
self.addEventListener("fetch", (event) => {
- event.respondWith(handleRequest(event));
+ event.respondWith(handleRequest(event));
});
async function handleRequest(event) {
- const response = await getFromCache(event);
- if (response) {
- fetchAndCache(event); // Call this but don't await, allowing the cache to update in the background
- return response;
- }
- return fetchAndCache(event);
+ const response = await getFromCache(event);
+ if (response) {
+ fetchAndCache(event); // Call this but don't await, allowing the cache to update in the background
+ return response;
+ }
+ return fetchAndCache(event);
}
async function getFromCache(event) {
- const request = event.request;
- const cache = await caches.open(CACHE_VERSION);
- const response = await cache.match(request);
- return response;
+ const request = event.request;
+ const cache = await caches.open(CACHE_VERSION);
+ const response = await cache.match(request);
+ return response;
}
async function fetchAndCache(event) {
- const request = event.request;
- const response = await fetch(request);
- if (response.status === 200) { // Only cache successful responses
- const cache = await caches.open(CACHE_VERSION);
- cache.put(request, response.clone());
- }
- return response;
+ const request = event.request;
+ const response = await fetch(request);
+ if (response.status === 200) {
+ // Only cache successful responses
+ const cache = await caches.open(CACHE_VERSION);
+ cache.put(request, response.clone());
+ }
+ return response;
}