@setoelkahfi / svara / commits / dc9a2a8

chore: get time function

mellbacon committed May 21, 2024 at 13:10 UTC dc9a2a846843769e12e1cddf7bbf7e0280633a64
5 files changed +313 -11
src/config/config.ts
+5
@@ -119,3 +119,8 @@ export async function openLogFiles() {
119 let recent_log = (await fs.readDir(`${log_dir}logs`)).at(-1);
120 await invoke("open_in_explorer", {path: `${log_dir}logs${path.sep}${recent_log.name}`});
121 }
122 +
123 +export function getTime() {
124 + let time = new Intl.DateTimeFormat('en-US', {dateStyle: "short", timeStyle: "long"}).format();
125 + return time;
126 +}
src/lib/Notifications/Notification.svelte new
+162
@@ -0,0 +1,162 @@
1 +<script lang="ts">
2 + import { getTime } from "../../config/config";
3 + import { updateReadStatus } from "./notifications";
4 +
5 + export let id;
6 + export let type = "";
7 + export let message = "";
8 + export let read = false;
9 + export let actions = [];
10 +
11 + let notification: HTMLElement | null;
12 +</script>
13 +
14 +<div class="info">
15 + <span>{getTime()}</span>
16 + <!-- svelte-ignore a11y-click-events-have-key-events -->
17 + <span class="mark-read" on:click|preventDefault={() => {
18 + read = !read
19 + updateReadStatus(id, read);
20 + }
21 + }>
22 + {#if read}
23 + Mark as unread
24 + {:else}
25 + Mark as read
26 + {/if}
27 + </span>
28 +</div>
29 +<div bind:this={notification} class="notification" class:error={type === "Error"} class:warning={type === "Warning"} class:read>
30 + <div class="details">
31 + <div class="icon"></div>
32 + <div class="message">{type}: {message}</div>
33 + </div>
34 + <div class="actions">
35 + {#each actions as action}
36 + <!-- svelte-ignore a11y-click-events-have-key-events -->
37 + <div class="action-button" on:click={action.action}>{action.label}</div>
38 + {/each}
39 + </div>
40 +
41 + <!-- svelte-ignore a11y-click-events-have-key-events -->
42 + <span class="close-button" on:click={() => {}}></span>
43 +</div>
44 +
45 +<style lang="scss">
46 + $notification-height: 3rem;
47 + $notification-border-width: 1px;
48 + $error-color: #c02b2b;
49 + $warning-color: #ffeb10;
50 + $default-color: #4589ff;
51 + .info {
52 + color: #6d6d6d;
53 + font-weight: 200;
54 + font-size: 0.7rem;
55 + margin-bottom: 0.5rem;
56 + margin-top: 0.2rem;
57 + display: flex;
58 + align-items: center;
59 + span {
60 + margin: 0 5px;
61 + }
62 + .mark-read {
63 + text-decoration: underline;
64 + cursor: pointer;
65 + &:hover {
66 + color: #939393;
67 + }
68 + }
69 + }
70 + .notification {
71 + display: flex;
72 + min-height: $notification-height;
73 + background-color: #1f1f1f;
74 + margin-bottom: 0.8rem;
75 + position: relative;
76 + border-top: 3px solid $default-color;
77 + justify-content: space-between;
78 + &.warning {
79 + border-top: 3px solid $warning-color;
80 + .icon {
81 + background-color: $warning-color !important;
82 + }
83 + }
84 + &.error {
85 + border-top: 3px solid $error-color;
86 + .icon {
87 + background-color: $error-color !important;
88 + }
89 + }
90 + &.read {
91 + border-top-color: #333;
92 + color: #838383;
93 + .icon {
94 + background-color: #333 !important;
95 + }
96 + .action-button {
97 + color: #bdd4ff75 !important;
98 + }
99 + :global(.bx--toast-notification__close-button .bx--toast-notification__close-icon) {
100 + fill: #bdd4ff75 !important;
101 + }
102 + }
103 + &::before {
104 + position: absolute;
105 + height: 100%;
106 + width: 100%;
107 + border-width: 0 $notification-border-width $notification-border-width $notification-border-width;
108 + border-style: solid;
109 + box-sizing: border-box;
110 + border-color: #333;
111 + content: "";
112 + }
113 + .details {
114 + display: flex;
115 + margin: 0 0.5rem;
116 + position: relative;
117 + top: -1px;
118 + align-items: center;
119 + .icon {
120 + border-radius: 50%;
121 + background-color: $default-color;
122 + min-width: 20px;
123 + min-height: 20px;
124 + margin-right: 10px;
125 + }
126 + }
127 + .actions {
128 + display: flex;
129 + margin: 0 0.5rem;
130 + position: relative;
131 + top: -1px;
132 + align-items: center;
133 + .action-button {
134 + display: flex;
135 + justify-content: center;
136 + align-items: center;
137 + color: #BDD4FF;
138 + cursor: pointer;
139 + &:nth-child(2) {
140 + margin: 0 0.6rem;
141 + }
142 + &:hover {
143 + text-decoration: underline;
144 + }
145 + }
146 + }
147 + .close-button {
148 + font-size: 12px;
149 + padding: 0 7px;
150 + display: flex;
151 + align-items: center;
152 + justify-content: center;
153 + border-radius: 3px;
154 + margin: 0 0.5rem;
155 + cursor: pointer;
156 + &::before {
157 + content: "\2715";
158 + }
159 + z-index: 10;
160 + }
161 + }
162 +</style>
\ No newline at end of file
src/lib/Notifications/NotificationList.svelte new
+18
@@ -0,0 +1,18 @@
1 +<script lang="ts">
2 + import { notifications } from "./notifications";
3 + import Notification from "./Notification.svelte";
4 +</script>
5 +
6 +<div id="notifications">
7 +{#each $notifications as notification}
8 + <Notification id={notification.id} type={notification.type} message={notification.message} read={notification.read} actions={notification.actions}></Notification>
9 +{/each}
10 +</div>
11 +
12 +<style lang="scss">
13 +#notifications {
14 + height: calc(100% - 1.5rem);
15 + padding: 5px;
16 + overflow: auto;
17 +}
18 +</style>
\ No newline at end of file
src/lib/Notifications/notifications.ts new
+100
@@ -0,0 +1,100 @@
1 +import { writable } from "svelte/store";
2 +
3 +export enum NotifType {
4 + Message,
5 + Warning,
6 + Error
7 +}
8 +class Notification {
9 + id;
10 + type: string;
11 + message: string;
12 + read: boolean;
13 + actions;
14 + constructor(message, type, read = false, actions = []) {
15 + this.message = message;
16 + this.type = getType(type);
17 + this.read = read;
18 + this.actions = actions;
19 + }
20 +}
21 +export class Action {
22 + label: string;
23 + action: () => void | null;
24 + constructor(label, action: () => void | null = null) {
25 + this.label = label;
26 + this.action = action;
27 + }
28 +}
29 +
30 +const actions = [
31 + new Action(" Test Message", () => {
32 + addNotification(NotifType.Message, "Message notification success");
33 + }),
34 + new Action("Test Warning", () => {
35 + addNotification(NotifType.Warning, "Warning notification success");
36 + }),
37 + new Action("Test Error", () => {
38 + addNotification(NotifType.Error, "Failure or something idk");
39 + })
40 +]
41 +
42 +let id = 0;
43 +export const unreadnotifications = writable(false);
44 +export const notifications = writable([]);
45 +let notificationlist = [];
46 +
47 +export function addNotification(type: NotifType, message: string, actions = []) {
48 + let notification = new Notification(message, type, false, actions);
49 + notification.id = id;
50 + notificationlist = [...notificationlist, notification];
51 + notifications.set(notificationlist);
52 + updateNotificationStatus();
53 + id++;
54 +}
55 +
56 +export function updateReadStatus(id, read) {
57 + for (const notification of notificationlist) {
58 + if (notification.id === id) {
59 + notification.read = read;
60 + break;
61 + }
62 + }
63 + updateNotificationStatus();
64 +}
65 +
66 +export function clearNotifications() {
67 + notificationlist = [];
68 + notifications.set(notificationlist);
69 + updateNotificationStatus();
70 +}
71 +
72 +export function closeNotification(id) {
73 + notificationlist = notificationlist.filter(n => n.id !== id);
74 + notifications.set(notificationlist);
75 + updateNotificationStatus();
76 +}
77 +
78 +function getType(type: NotifType) {
79 + switch (type) {
80 + case NotifType.Message:
81 + return "Message";
82 + case NotifType.Warning:
83 + return "Warning";
84 + case NotifType.Error:
85 + return "Error";
86 + default:
87 + return "Message";
88 + }
89 +}
90 +
91 +function updateNotificationStatus() {
92 + let unread = false;
93 + for (const notification of notificationlist) {
94 + if (!notification.read) {
95 + unread = true;
96 + break;
97 + }
98 + }
99 + unreadnotifications.set(unread);
100 +}
src/lib/ToolView.svelte
+28 -11
@@ -1,23 +1,26 @@
1 <script lang="ts">
2 - import { closeBottomPanel, hideBottomPanel } from "./Statusbar.svelte";
2 + import { hideBottomPanel } from "./Statusbar.svelte";
3 import Ellipsis from "carbon-icons-svelte/lib/OverflowMenuHorizontal.svelte";
4 import Menu from "./utility/Menu.svelte";
5 - import { clearTerminal, closeTerminal } from "./Terminal.svelte";
5
6 export let content = null;
7 export let name = "Tool";
8 + export let options = [];
9 + export let buttons;
10 </script>
11 <div id="toolview">
12 <div class="header">{name}
13 <div class="header-tools">
14 <div class="tool-buttons">
14 - <Menu right menu={{icon: Ellipsis, children: [
15 - {name: "Clear Terminal", action: () => {
16 - clearTerminal();
17 - }}, {name: "Close Terminal", action: () => {
18 - closeBottomPanel();
19 - closeTerminal();
20 - }}]}}></Menu>
15 + {#each buttons as button}
16 + <!-- svelte-ignore a11y-click-events-have-key-events -->
17 + <div class="tool-button" title={button.title} on:click={button.action}>
18 + <svelte:component this={button.icon}></svelte:component>
19 + </div>
20 + {/each}
21 + {#if options}
22 + <Menu right menu={{icon: Ellipsis, children: options}}></Menu>
23 + {/if}
24 </div>
25 <!-- svelte-ignore a11y-click-events-have-key-events -->
26 <span class="close" title="Hide Panel" on:click={hideBottomPanel}></span>
@@ -36,7 +39,6 @@
39 .toolview-container {
40 padding: 8px;
41 height: 100%;
39 -
42 }
43 .header {
44 height: 35px;
@@ -53,12 +55,27 @@
55 flex-direction: row;
56 .tool-buttons {
57 height: 24px;
58 + display: flex;
59 + flex-direction: row;
60 + align-items: center;
61 + justify-content: center;
62 }
57 - :global(.menu-button) {
63 + .tool-button {
64 + display: flex;
65 + align-items: center;
66 + justify-content: center;
67 + height: 100%;
68 + cursor: pointer;
69 + }
70 + :global(.menu-button), .tool-button {
71 min-width: unset;
72 padding: 0 7px;
73 border-radius: 3px;
74 }
75 + :global(.tool-button svg) {
76 + width: 18px;
77 + height: 18px;
78 + }
79 }
80 .close {
81 font-size: 12px;