mirror of
https://github.com/TxtDot/vigi.git
synced 2024-11-23 11:56:21 +03:00
feat: tabs, state management
This commit is contained in:
parent
9dfd66c14a
commit
107a8245e4
12 changed files with 414 additions and 51 deletions
13
README.md
13
README.md
|
@ -14,8 +14,21 @@ Browser is WIP.
|
|||
|
||||
See also: [Dalet](https://github.com/TxtDot/dalet).
|
||||
|
||||
Format support:
|
||||
|
||||
- [ ] Dalet support
|
||||
- [x] Text support
|
||||
- [ ] Gemtext support
|
||||
- [ ] Proxy support
|
||||
- [ ] Local txtdot engines support
|
||||
|
||||
Browser features:
|
||||
|
||||
- [x] input url processing
|
||||
- [x] tab window hiding
|
||||
- [x] tabs
|
||||
- [x] tab switching
|
||||
- [x] tab closing
|
||||
- [x] save and restore tabs
|
||||
- [ ] save and restore favorites
|
||||
- [ ] cache tab data
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
fs::{self},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
|
@ -13,30 +12,106 @@ mod types;
|
|||
mod utils;
|
||||
|
||||
use tauri::Manager;
|
||||
use types::{State, VigiError};
|
||||
use utils::{read_jsonl_tabs, read_or_create_current_tab_index, read_or_create_jsonl};
|
||||
use types::{TabType, VigiError, VigiState};
|
||||
use utils::{read_or_create_jsonl, read_or_create_number};
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||
#[tauri::command]
|
||||
async fn process_input(input: String) -> Result<Vec<Tag>, VigiError> {
|
||||
async fn process_input(
|
||||
input: String,
|
||||
state: tauri::State<'_, Mutex<VigiState>>,
|
||||
) -> Result<Vec<Tag>, VigiError> {
|
||||
// TODO: Implement mime type, language, protocol or search detection
|
||||
// TODO: Implement text links parsing
|
||||
|
||||
match reqwest::get(input).await {
|
||||
println!("Processing: {}", input);
|
||||
|
||||
match reqwest::get(input.clone()).await {
|
||||
Ok(res) => match res.text().await {
|
||||
Ok(res) => Ok(vec![Tag::new(0, Body::Text(res), Argument::Null)]),
|
||||
Err(_) => Err(VigiError::ParseError),
|
||||
Ok(res) => {
|
||||
update_tab(state, TabType::Text, res.clone(), input.clone())?;
|
||||
Ok(vec![Tag::new(0, Body::Text(res), Argument::Null)])
|
||||
}
|
||||
Err(_) => Err(VigiError::Parse),
|
||||
},
|
||||
Err(_) => Err(VigiError::NetworkError),
|
||||
Err(_) => Err(VigiError::Network),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_state(state: tauri::State<Mutex<VigiState>>) -> VigiState {
|
||||
(*state.lock().unwrap()).clone()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn select_tab(state: tauri::State<Mutex<VigiState>>, index: usize) -> Result<(), VigiError> {
|
||||
match state.lock() {
|
||||
Ok(mut state) => {
|
||||
state.update_current_tab_index(index)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(VigiError::StateLock),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn add_tab(state: tauri::State<Mutex<VigiState>>) -> Result<(), VigiError> {
|
||||
match state.lock() {
|
||||
Ok(mut state) => {
|
||||
state.add_tab()?;
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(VigiError::StateLock),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn remove_tab(state: tauri::State<Mutex<VigiState>>, index: usize) -> Result<(), VigiError> {
|
||||
match state.lock() {
|
||||
Ok(mut state) => {
|
||||
state.remove_tab(index)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(VigiError::StateLock),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_tab(
|
||||
state: tauri::State<Mutex<VigiState>>,
|
||||
tab_type: TabType,
|
||||
tab_title: String,
|
||||
tab_url: String,
|
||||
) -> Result<(), VigiError> {
|
||||
match state.lock() {
|
||||
Ok(mut state) => {
|
||||
state.update_tab(tab_type, tab_title, tab_url)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(VigiError::StateLock),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(Mutex::new(VigiState::null()))
|
||||
.setup(setup_handler)
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
process_input,
|
||||
get_state,
|
||||
select_tab,
|
||||
add_tab,
|
||||
remove_tab
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
fn setup_handler(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error + 'static>> {
|
||||
println!("---Setup---");
|
||||
|
||||
let app_handle = app.handle();
|
||||
|
||||
let state = app.state::<Mutex<State>>();
|
||||
let state = app.state::<Mutex<VigiState>>();
|
||||
let mut state = state.lock().unwrap();
|
||||
|
||||
let config_dir = app_handle
|
||||
|
@ -78,19 +153,13 @@ fn setup_handler(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error +
|
|||
state.local_tabs_path = local_data_dir.join("tabs.jsonl");
|
||||
state.tabs = read_or_create_jsonl(&state.local_tabs_path);
|
||||
|
||||
state.tabs_id_counter_path = local_data_dir.join("tabs_id_counter");
|
||||
state.tabs_id_counter = read_or_create_number(&state.tabs_id_counter_path);
|
||||
|
||||
state.current_tab_index_path = local_data_dir.join("current_tab_index");
|
||||
state.current_tab_index = read_or_create_current_tab_index(&state.current_tab_index_path);
|
||||
state.current_tab_index = read_or_create_number(&state.current_tab_index_path);
|
||||
|
||||
println!("---Setup done---");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(Mutex::new(State::null()))
|
||||
.setup(setup_handler)
|
||||
.invoke_handler(tauri::generate_handler![process_input])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
|
|
@ -1,47 +1,129 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
use crate::utils::write_tabs;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum VigiError {
|
||||
NetworkError,
|
||||
ParseError,
|
||||
Network,
|
||||
Parse,
|
||||
StateLock,
|
||||
StateUpdate,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct VigiState {
|
||||
pub tabs_id_counter_path: PathBuf,
|
||||
pub current_tab_index_path: PathBuf,
|
||||
pub local_tabs_path: PathBuf,
|
||||
pub favorites_tabs_path: PathBuf,
|
||||
|
||||
pub cache_dir: PathBuf,
|
||||
|
||||
pub tabs_id_counter: usize,
|
||||
pub current_tab_index: usize,
|
||||
pub tabs: Vec<Tab>,
|
||||
pub favorites_tabs: Vec<Tab>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
impl VigiState {
|
||||
pub fn null() -> Self {
|
||||
Self {
|
||||
tabs_id_counter_path: PathBuf::new(),
|
||||
current_tab_index_path: PathBuf::new(),
|
||||
local_tabs_path: PathBuf::new(),
|
||||
favorites_tabs_path: PathBuf::new(),
|
||||
cache_dir: PathBuf::new(),
|
||||
|
||||
tabs_id_counter: 0,
|
||||
current_tab_index: 0,
|
||||
tabs: Vec::new(),
|
||||
favorites_tabs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_current_tab_index(&mut self, new_index: usize) -> Result<(), VigiError> {
|
||||
self.current_tab_index = new_index;
|
||||
|
||||
self.write_current_tab_index()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_current_tab_index(&mut self) -> Result<(), VigiError> {
|
||||
fs::write(
|
||||
&self.current_tab_index_path,
|
||||
self.current_tab_index.to_string(),
|
||||
)
|
||||
.map_err(|_| VigiError::StateUpdate)
|
||||
}
|
||||
|
||||
fn write_id_counter(&mut self) -> Result<(), VigiError> {
|
||||
fs::write(&self.tabs_id_counter_path, self.tabs_id_counter.to_string())
|
||||
.map_err(|_| VigiError::StateUpdate)
|
||||
}
|
||||
|
||||
pub fn update_tab(
|
||||
&mut self,
|
||||
tab_type: TabType,
|
||||
tab_title: String,
|
||||
tab_url: String,
|
||||
) -> Result<(), VigiError> {
|
||||
match self.tabs.get_mut(self.current_tab_index) {
|
||||
Some(tab) => {
|
||||
*tab = Tab::new(tab_type, tab_title, tab_url, tab.id);
|
||||
|
||||
write_tabs(&self.local_tabs_path, &self.tabs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
None => Err(VigiError::StateUpdate),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tab(&mut self) -> Result<(), VigiError> {
|
||||
self.tabs_id_counter += 1;
|
||||
self.tabs.push(Tab::new(
|
||||
TabType::HomePage,
|
||||
"New tab".to_string(),
|
||||
"".to_string(),
|
||||
self.tabs_id_counter,
|
||||
));
|
||||
|
||||
self.write_id_counter()?;
|
||||
write_tabs(&self.local_tabs_path, &self.tabs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_tab(&mut self, index: usize) -> Result<(), VigiError> {
|
||||
if self.tabs.len() == 1 {
|
||||
self.current_tab_index = 0;
|
||||
} else {
|
||||
self.current_tab_index = self.current_tab_index + 1;
|
||||
}
|
||||
|
||||
self.tabs.remove(index);
|
||||
write_tabs(&self.local_tabs_path, &self.tabs)?;
|
||||
self.write_current_tab_index()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Tab {
|
||||
ty: TabType,
|
||||
name: String,
|
||||
title: String,
|
||||
url: String,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
impl Tab {
|
||||
pub fn new(ty: TabType, title: String, url: String, id: usize) -> Self {
|
||||
Self { ty, title, url, id }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum TabType {
|
||||
HomePage,
|
||||
Text,
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{
|
|||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::types::Tab;
|
||||
use crate::types::{Tab, VigiError};
|
||||
|
||||
pub fn read_jsonl_tabs(path: &PathBuf) -> Vec<Tab> {
|
||||
fs::read_to_string(&path)
|
||||
|
@ -24,11 +24,8 @@ pub fn read_or_create_jsonl(path: &PathBuf) -> Vec<Tab> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read_or_create_current_tab_index(path: &PathBuf) -> usize {
|
||||
println!(
|
||||
" Getting current tab index from {}",
|
||||
path.to_string_lossy()
|
||||
);
|
||||
pub fn read_or_create_number(path: &PathBuf) -> usize {
|
||||
println!(" Getting number from {}", path.to_string_lossy());
|
||||
if path.exists() {
|
||||
fs::read_to_string(path)
|
||||
.unwrap()
|
||||
|
@ -40,3 +37,14 @@ pub fn read_or_create_current_tab_index(path: &PathBuf) -> usize {
|
|||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_tabs(path: &PathBuf, tabs: &Vec<Tab>) -> Result<(), VigiError> {
|
||||
fs::write(
|
||||
path,
|
||||
tabs.iter()
|
||||
.map(|tab| serde_json::to_string(tab).unwrap())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n"),
|
||||
)
|
||||
.map_err(|_| VigiError::StateUpdate)
|
||||
}
|
||||
|
|
57
src/app.css
57
src/app.css
|
@ -43,24 +43,28 @@ body {
|
|||
@apply ease-out duration-150;
|
||||
|
||||
@apply hover:bg-vigi-90;
|
||||
@apply hover:px-2 hover:mx-1 hover:scale-125 hover:cursor-pointer;
|
||||
@apply hover:px-2 hover:mx-1 hover:scale-125 cursor-pointer;
|
||||
|
||||
@apply active:bg-vigi-100;
|
||||
}
|
||||
|
||||
.open-tabs {
|
||||
@apply flex justify-between mt-2 mx-2;
|
||||
}
|
||||
|
||||
.block {
|
||||
@apply p-2 rounded-xl bg-vigi-60;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
@apply basis-1/5 shrink-0;
|
||||
@apply shrink-0 grow-0 flex flex-col w-1/5;
|
||||
@apply ease-out duration-100;
|
||||
}
|
||||
|
||||
.sidebar.collapsed {
|
||||
@apply basis-0;
|
||||
|
||||
@apply p-0 m-0;
|
||||
@apply p-0 m-0 bg-transparent;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
|
@ -80,13 +84,54 @@ input::placeholder {
|
|||
@apply text-vigi-40 focus:text-vigi-20;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
@apply flex flex-col gap-1 mt-2 grow overflow-auto;
|
||||
}
|
||||
|
||||
.tab {
|
||||
@apply p-2 rounded-xl bg-vigi-50;
|
||||
@apply cursor-pointer;
|
||||
@apply ease-out duration-100;
|
||||
|
||||
@apply hover:bg-vigi-55;
|
||||
|
||||
@apply hover:px-4;
|
||||
|
||||
@apply flex items-center justify-between gap-2 w-full;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
@apply p-1 rounded-lg;
|
||||
@apply ease-out duration-100;
|
||||
|
||||
@apply hover:bg-vigi-90 active:bg-vigi-100;
|
||||
}
|
||||
|
||||
/* .tab .close-button {
|
||||
@apply text-transparent;
|
||||
}
|
||||
|
||||
.tab:hover .close-button {
|
||||
@apply text-vigi-0;
|
||||
} */
|
||||
|
||||
.tab.active {
|
||||
@apply bg-vigi-40 text-vigi-0 font-bold;
|
||||
|
||||
@apply hover:bg-vigi-45;
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
@apply truncate;
|
||||
}
|
||||
|
||||
::selection {
|
||||
@apply bg-vigi-60;
|
||||
}
|
||||
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 20px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
|
@ -96,12 +141,12 @@ input::placeholder {
|
|||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply rounded-xl bg-vigi-90 bg-clip-content;
|
||||
@apply rounded-xl bg-vigi-70 bg-clip-content;
|
||||
border: 6px solid transparent;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-vigi-100;
|
||||
@apply bg-vigi-75;
|
||||
border: 5px solid transparent;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,45 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import { slide } from "svelte/transition";
|
||||
|
||||
import Block from "./Block.svelte";
|
||||
import WindowControls from "./WindowControls.svelte";
|
||||
import type { StateTab } from "$lib/types";
|
||||
import { state } from "$lib/stores";
|
||||
import Tab from "./Tab.svelte";
|
||||
import { addTab } from "$lib/utils";
|
||||
import Button from "./Button.svelte";
|
||||
import Add from "$lib/icons/Add.svelte";
|
||||
|
||||
export let sidebarOpen = true;
|
||||
export let collapsed = true;
|
||||
|
||||
let tabs: StateTab[] = [];
|
||||
let currentTabIndex = 0;
|
||||
|
||||
state.subscribe(async (state) => {
|
||||
tabs = state.tabs;
|
||||
currentTabIndex = state.current_tab_index;
|
||||
|
||||
if (tabs.length === 0) {
|
||||
await addTab();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Block className={`sidebar${sidebarOpen ? "" : " collapsed"}`} draggable>
|
||||
{#if sidebarOpen}
|
||||
<div transition:slide={{ axis: "x", duration: 100 }}>
|
||||
<WindowControls />
|
||||
<Block className={`sidebar${collapsed ? "" : " collapsed"}`} draggable>
|
||||
{#if collapsed}
|
||||
<WindowControls />
|
||||
|
||||
<div class="open-tabs">
|
||||
Open tabs
|
||||
<Button onClick={addTab}>
|
||||
<Add />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
{#each tabs as tab, i (tab.id)}
|
||||
<Tab {tab} active={currentTabIndex === i} id={i} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</Block>
|
||||
|
|
46
src/lib/components/Tab.svelte
Normal file
46
src/lib/components/Tab.svelte
Normal file
|
@ -0,0 +1,46 @@
|
|||
<script lang="ts">
|
||||
import type { StateTab } from "$lib/types";
|
||||
import { removeTab, selectTab } from "$lib/utils";
|
||||
import { slide } from "svelte/transition";
|
||||
import Close from "$lib/icons/Close.svelte";
|
||||
import Button from "./Button.svelte";
|
||||
|
||||
export let active = false;
|
||||
export let tab: StateTab;
|
||||
|
||||
export let id: number;
|
||||
|
||||
let tabElement: HTMLButtonElement;
|
||||
|
||||
let hovered = false;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex gap-1 items-center"
|
||||
on:mouseenter={() => (hovered = true)}
|
||||
on:mouseleave={() => (hovered = false)}
|
||||
role="tab"
|
||||
tabindex={id}
|
||||
>
|
||||
<button
|
||||
class="tab"
|
||||
class:active
|
||||
transition:slide={{ duration: 100 }}
|
||||
bind:this={tabElement}
|
||||
on:click={() => {
|
||||
selectTab(id);
|
||||
}}
|
||||
>
|
||||
<div class="tab-title">
|
||||
{tab.title}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{#if hovered}
|
||||
<button
|
||||
class="close-button"
|
||||
transition:slide={{ duration: 100, axis: "x" }}
|
||||
on:click={() => removeTab(id)}><Close /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
17
src/lib/icons/Add.svelte
Normal file
17
src/lib/icons/Add.svelte
Normal file
|
@ -0,0 +1,17 @@
|
|||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...$$props}
|
||||
>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M12 4v16m-8-8h16"
|
||||
color="currentColor"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 300 B |
|
@ -1,3 +1,6 @@
|
|||
import { writable } from "svelte/store";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import type { VigiState } from "./types";
|
||||
|
||||
export const topBarInput = writable("");
|
||||
export const topBarInput: Writable<string> = writable("");
|
||||
|
||||
export const state: Writable<VigiState> = writable();
|
||||
|
|
14
src/lib/types.ts
Normal file
14
src/lib/types.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
export interface VigiState {
|
||||
current_tab_index: number;
|
||||
tabs: StateTab[];
|
||||
favorites_tabs: StateTab[];
|
||||
}
|
||||
|
||||
type TabType = "HomePage" | "Text";
|
||||
|
||||
export interface StateTab {
|
||||
ty: TabType;
|
||||
title: string;
|
||||
url: string;
|
||||
id: number;
|
||||
}
|
32
src/lib/utils.ts
Normal file
32
src/lib/utils.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { invoke } from "@tauri-apps/api";
|
||||
import { state, topBarInput } from "./stores";
|
||||
import type { StateTab, VigiState } from "./types";
|
||||
|
||||
export function updateVigiState() {
|
||||
invoke("get_state")
|
||||
.then((r) => {
|
||||
let st = r as VigiState;
|
||||
|
||||
state.set(st);
|
||||
|
||||
topBarInput.set(st.tabs[st.current_tab_index].url);
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
}
|
||||
|
||||
export async function addTab() {
|
||||
await invoke("add_tab");
|
||||
|
||||
updateVigiState();
|
||||
}
|
||||
|
||||
export async function selectTab(index: number) {
|
||||
await invoke("select_tab", { index });
|
||||
|
||||
updateVigiState();
|
||||
}
|
||||
|
||||
export async function removeTab(index: number) {
|
||||
await invoke("remove_tab", { index });
|
||||
updateVigiState();
|
||||
}
|
|
@ -8,14 +8,16 @@
|
|||
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import { topBarInput } from "$lib/stores";
|
||||
import { updateVigiState } from "$lib/utils";
|
||||
|
||||
let sidebarOpen = true;
|
||||
|
||||
let inputValue = "";
|
||||
let isLoading = false;
|
||||
|
||||
let data: Root = [];
|
||||
|
||||
updateVigiState();
|
||||
|
||||
document.addEventListener("keypress", (e: KeyboardEvent) => {
|
||||
const formElements = ["INPUT", "TEXTAREA", "SELECT", "OPTION"];
|
||||
if (formElements.includes((e.target as Element).tagName)) {
|
||||
|
@ -34,6 +36,9 @@
|
|||
.catch((err) => {
|
||||
data = [{ id: 0, body: "Error: " + err, argument: null }];
|
||||
isLoading = false;
|
||||
})
|
||||
.finally(() => {
|
||||
updateVigiState();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@ -42,7 +47,7 @@
|
|||
class={`common-window${sidebarOpen ? "" : " collapsed"}`}
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<Sidebar bind:sidebarOpen />
|
||||
<Sidebar bind:collapsed={sidebarOpen} />
|
||||
|
||||
<div class="main-window">
|
||||
<TopBar bind:sidebarOpen />
|
||||
|
|
Loading…
Add table
Reference in a new issue