mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-17 23:13:51 +01:00
Zoom, better sizes, color picker, sidebar footer
This commit is contained in:
131
package-lock.json
generated
131
package-lock.json
generated
@@ -34,6 +34,7 @@
|
||||
"parse-color": "^1.0.0",
|
||||
"parse-json": "^6.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react-router-dom": "^6.8.1"
|
||||
@@ -45,6 +46,7 @@
|
||||
"@types/parse-color": "^1.0.1",
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-color": "^3.0.6",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.52.0",
|
||||
"@typescript-eslint/parser": "^5.52.0",
|
||||
@@ -1049,6 +1051,14 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@icons/material": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
|
||||
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
@@ -2254,6 +2264,16 @@
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-color": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.6.tgz",
|
||||
"integrity": "sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/reactcss": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "18.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz",
|
||||
@@ -2263,6 +2283,15 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/reactcss": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
|
||||
"integrity": "sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||
@@ -5309,6 +5338,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -5356,6 +5390,11 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/material-colors": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -6061,6 +6100,23 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-color": {
|
||||
"version": "2.19.3",
|
||||
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
|
||||
"integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
|
||||
"dependencies": {
|
||||
"@icons/material": "^0.2.4",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash-es": "^4.17.15",
|
||||
"material-colors": "^1.2.1",
|
||||
"prop-types": "^15.5.10",
|
||||
"reactcss": "^1.2.0",
|
||||
"tinycolor2": "^1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
@@ -6220,6 +6276,14 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||
},
|
||||
"node_modules/reactcss": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
|
||||
"dependencies": {
|
||||
"lodash": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -6782,6 +6846,11 @@
|
||||
"integrity": "sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
|
||||
},
|
||||
"node_modules/tinypool": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.1.tgz",
|
||||
@@ -8138,6 +8207,12 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"@icons/material": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
|
||||
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
@@ -9001,6 +9076,16 @@
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@types/react-color": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.6.tgz",
|
||||
"integrity": "sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"@types/reactcss": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"version": "18.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz",
|
||||
@@ -9010,6 +9095,15 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/reactcss": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
|
||||
"integrity": "sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/scheduler": {
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||
@@ -11225,6 +11319,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -11266,6 +11365,11 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
||||
}
|
||||
},
|
||||
"material-colors": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||
},
|
||||
"merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -11739,6 +11843,20 @@
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"react-color": {
|
||||
"version": "2.19.3",
|
||||
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
|
||||
"integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
|
||||
"requires": {
|
||||
"@icons/material": "^0.2.4",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash-es": "^4.17.15",
|
||||
"material-colors": "^1.2.1",
|
||||
"prop-types": "^15.5.10",
|
||||
"reactcss": "^1.2.0",
|
||||
"tinycolor2": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
@@ -11845,6 +11963,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"reactcss": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
|
||||
"requires": {
|
||||
"lodash": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -12259,6 +12385,11 @@
|
||||
"integrity": "sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg==",
|
||||
"dev": true
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
|
||||
},
|
||||
"tinypool": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.1.tgz",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"parse-color": "^1.0.0",
|
||||
"parse-json": "^6.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react-router-dom": "^6.8.1"
|
||||
@@ -52,6 +53,7 @@
|
||||
"@types/parse-color": "^1.0.1",
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-color": "^3.0.6",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.52.0",
|
||||
"@typescript-eslint/parser": "^5.52.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -18,7 +18,7 @@ use sqlx::sqlite::SqlitePoolOptions;
|
||||
use sqlx::types::Json;
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use tauri::regex::Regex;
|
||||
use tauri::{AppHandle, State, Wry};
|
||||
use tauri::{AppHandle, Menu, MenuItem, State, Submenu, Wry};
|
||||
use tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowEvent};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@@ -224,8 +224,8 @@ async fn update_request(
|
||||
request.headers.0,
|
||||
pool,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to update request");
|
||||
.await
|
||||
.expect("Failed to update request");
|
||||
|
||||
app_handle
|
||||
.emit_all("updated_request", updated_request)
|
||||
@@ -317,12 +317,19 @@ fn greet(name: &str) -> String {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// here `"quit".to_string()` defines the menu item id, and the second parameter is the menu item label.
|
||||
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
|
||||
let tray_menu = SystemTrayMenu::new().add_item(quit);
|
||||
let system_tray = SystemTray::new().with_menu(tray_menu);
|
||||
|
||||
let submenu = Submenu::new("View", Menu::new()
|
||||
.add_item(CustomMenuItem::new("zoom_reset".to_string(), "Zoom to Actual Size").accelerator("CmdOrCtrl+0"))
|
||||
.add_item(CustomMenuItem::new("zoom_in".to_string(), "Zoom In").accelerator("CmdOrCtrl+Plus"))
|
||||
.add_item(CustomMenuItem::new("zoom_out".to_string(), "Zoom Out").accelerator("CmdOrCtrl+-")),
|
||||
);
|
||||
let menu = Menu::new().add_native_item(MenuItem::Quit).add_submenu(submenu);
|
||||
|
||||
tauri::Builder::default()
|
||||
.menu(menu)
|
||||
.system_tray(system_tray)
|
||||
.setup(|app| {
|
||||
let win = app.get_window("main").unwrap();
|
||||
@@ -360,9 +367,19 @@ fn main() {
|
||||
window.hide().unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
.on_menu_event(|event| {
|
||||
match event.menu_item_id() {
|
||||
"quit" => std::process::exit(0),
|
||||
"close" => event.window().close().unwrap(),
|
||||
"zoom_reset" => event.window().emit("zoom", 0).unwrap(),
|
||||
"zoom_in" => event.window().emit("zoom", 1).unwrap(),
|
||||
"zoom_out" => event.window().emit("zoom", -1).unwrap(),
|
||||
_ => {}
|
||||
};
|
||||
})
|
||||
.on_window_event(|e| {
|
||||
let apply_offset = || {
|
||||
let win = e.window();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use tauri::{Runtime, Window};
|
||||
|
||||
const TRAFFIC_LIGHT_OFFSET_X: f64 = 15.0;
|
||||
const TRAFFIC_LIGHT_OFFSET_Y: f64 = 18.0;
|
||||
const TRAFFIC_LIGHT_OFFSET_Y: f64 = 22.0;
|
||||
|
||||
pub trait WindowExt {
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -33,8 +33,8 @@ function App() {
|
||||
<div className="grid grid-rows-[auto_1fr] h-full overflow-hidden">
|
||||
<HStack
|
||||
as={WindowDragRegion}
|
||||
className="px-3 bg-background text-sm text-gray-900 border-b border-b-gray-200 pt-[1px]"
|
||||
items="center"
|
||||
className="px-3 bg-background text-gray-900 border-b border-b-gray-200 pt-[1px]"
|
||||
alignItems="center"
|
||||
>
|
||||
{request.name}
|
||||
</HStack>
|
||||
|
||||
@@ -1,65 +1,57 @@
|
||||
import classnames from 'classnames';
|
||||
import type {
|
||||
ButtonHTMLAttributes,
|
||||
ComponentPropsWithoutRef,
|
||||
ElementType,
|
||||
ForwardedRef,
|
||||
} from 'react';
|
||||
import type { ButtonHTMLAttributes, ForwardedRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
const colorStyles = {
|
||||
default: 'hover:bg-gray-700/10 text-gray-700 hover:text-gray-1000',
|
||||
gray: 'text-gray-800 bg-gray-100 hover:bg-gray-500/20 hover:text-gray-1000',
|
||||
primary: 'bg-blue-400 text-white',
|
||||
secondary: 'bg-violet-400 text-white',
|
||||
warning: 'bg-orange-400 text-white',
|
||||
danger: 'bg-red-400 text-white',
|
||||
custom: '',
|
||||
default: 'text-gray-700 enabled:hover:bg-gray-700/10 enabled:hover:text-gray-1000',
|
||||
gray: 'text-gray-800 bg-gray-100 enabled:hover:bg-gray-500/20 enabled:hover:text-gray-1000',
|
||||
primary: 'bg-blue-400 text-white hover:bg-blue-500',
|
||||
secondary: 'bg-violet-400 text-white hover:bg-violet-500',
|
||||
warning: 'bg-orange-400 text-white hover:bg-orange-500',
|
||||
danger: 'bg-red-400 text-white hover:bg-red-500',
|
||||
};
|
||||
|
||||
export type ButtonProps<T extends ElementType> = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
color?: keyof typeof colorStyles;
|
||||
size?: 'xs' | 'sm' | 'md';
|
||||
size?: 'sm' | 'md';
|
||||
justify?: 'start' | 'center';
|
||||
forDropdown?: boolean;
|
||||
as?: T;
|
||||
};
|
||||
|
||||
export const Button = forwardRef(function Button<T extends ElementType>(
|
||||
export const Button = forwardRef(function Button(
|
||||
{
|
||||
className,
|
||||
as,
|
||||
justify = 'center',
|
||||
children,
|
||||
size = 'md',
|
||||
forDropdown,
|
||||
color,
|
||||
justify = 'center',
|
||||
size = 'md',
|
||||
type = 'button',
|
||||
...props
|
||||
}: ButtonProps<T> & Omit<ComponentPropsWithoutRef<T>, keyof ButtonProps<T>>,
|
||||
}: ButtonProps,
|
||||
ref: ForwardedRef<HTMLButtonElement>,
|
||||
) {
|
||||
const Component = as || 'button';
|
||||
return (
|
||||
<Component
|
||||
<button
|
||||
ref={ref}
|
||||
type="button"
|
||||
type={type}
|
||||
className={classnames(
|
||||
className,
|
||||
'outline-none',
|
||||
'border border-transparent focus-visible:border-blue-300',
|
||||
'transition-all rounded-md flex items-center',
|
||||
'bg-opacity-90 hover:bg-opacity-100',
|
||||
colorStyles[color || 'default'],
|
||||
justify === 'start' && 'justify-start',
|
||||
justify === 'center' && 'justify-center',
|
||||
size === 'md' && 'h-10 px-4',
|
||||
size === 'sm' && 'h-8 px-3 text-sm',
|
||||
size === 'xs' && 'h-7 px-2.5 text-sm',
|
||||
size === 'md' && 'h-9 px-3',
|
||||
size === 'sm' && 'h-7 px-2.5 text-sm',
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{forDropdown && <Icon icon="triangleDown" className="ml-1 -mr-1" />}
|
||||
</Component>
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
import classnames from 'classnames';
|
||||
import type { LinkProps } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { ButtonProps } from './Button';
|
||||
import { Button } from './Button';
|
||||
|
||||
type Props = ButtonProps<typeof Link> & LinkProps;
|
||||
type Props = ButtonProps & LinkProps;
|
||||
|
||||
export function ButtonLink({ ...props }: Props) {
|
||||
return <Button as={Link} {...props} />;
|
||||
export function ButtonLink({
|
||||
reloadDocument,
|
||||
replace,
|
||||
state,
|
||||
preventScrollReset,
|
||||
relative,
|
||||
to,
|
||||
className,
|
||||
...buttonProps
|
||||
}: Props) {
|
||||
const linkProps = { reloadDocument, replace, state, preventScrollReset, relative, to };
|
||||
return (
|
||||
<Link {...linkProps}>
|
||||
<Button className={classnames(className, 'w-full')} {...buttonProps} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export function Dialog({
|
||||
<IconButton aria-label="Close" icon="x" size="sm" />
|
||||
</D.Close>
|
||||
<VStack space={3}>
|
||||
<HStack items="center" className="pb-3">
|
||||
<HStack alignItems="center" className="pb-3">
|
||||
<D.Title className="text-xl font-semibold">{title}</D.Title>
|
||||
</HStack>
|
||||
{description && <D.Description>{description}</D.Description>}
|
||||
|
||||
@@ -25,19 +25,15 @@
|
||||
}
|
||||
|
||||
.cm-gutters {
|
||||
@apply border-0 text-gray-500 text-opacity-30;
|
||||
@apply border-0 text-gray-500/60;
|
||||
|
||||
.cm-gutterElement {
|
||||
@apply cursor-default;
|
||||
}
|
||||
}
|
||||
|
||||
&.cm-focused .cm-gutters {
|
||||
@apply text-opacity-60;
|
||||
}
|
||||
|
||||
.placeholder-widget {
|
||||
@apply text-[0.9em] text-gray-800 dark:text-gray-1000 px-1 rounded cursor-default dark:shadow;
|
||||
@apply text-[0.9em] text-gray-800 dark:text-gray-900 px-1 rounded cursor-default dark:shadow;
|
||||
|
||||
/* NOTE: Background and border are translucent so we can see text selection through it */
|
||||
@apply bg-gray-300/40 border border-gray-300 border-opacity-40 hover:border-opacity-80;
|
||||
@@ -105,11 +101,12 @@
|
||||
}
|
||||
|
||||
.cm-editor .fold-gutter-icon:hover {
|
||||
@apply text-gray-400 bg-gray-100/20;
|
||||
@apply text-gray-900 bg-gray-300/50;
|
||||
}
|
||||
|
||||
.cm-editor .cm-foldPlaceholder {
|
||||
@apply px-2 border border-gray-200 bg-gray-100;
|
||||
@apply px-2 border border-gray-400/50 bg-gray-300/50 cursor-default;
|
||||
@apply hover:text-gray-800 hover:border-gray-400;
|
||||
}
|
||||
|
||||
.cm-editor .cm-activeLineGutter,
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
} from '@codemirror/view';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { debouncedAutocompletionDisplay } from './autocomplete';
|
||||
import { readOnlyTransactionFilter } from './readOnlyTransactionFilter';
|
||||
import { twig } from './twig/extension';
|
||||
import { url } from './url/extension';
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ function FormRow({
|
||||
defaultValue={value}
|
||||
onChange={onChangeValue}
|
||||
/>
|
||||
{onDelete && <IconButton size="sm" icon="trash" onClick={onDelete} />}
|
||||
{onDelete && <IconButton icon="trash" onClick={onDelete} />}
|
||||
</HStack>
|
||||
{addSubmit && <input type="submit" value="Add" className="sr-only" />}
|
||||
</div>
|
||||
|
||||
@@ -4,11 +4,13 @@ import {
|
||||
CheckIcon,
|
||||
ClockIcon,
|
||||
CodeIcon,
|
||||
ColorWheelIcon,
|
||||
Cross2Icon,
|
||||
EyeOpenIcon,
|
||||
GearIcon,
|
||||
HomeIcon,
|
||||
MoonIcon,
|
||||
ListBulletIcon,
|
||||
PaperPlaneIcon,
|
||||
PlusCircledIcon,
|
||||
PlusIcon,
|
||||
@@ -19,59 +21,40 @@ import {
|
||||
TriangleLeftIcon,
|
||||
TriangleRightIcon,
|
||||
UpdateIcon,
|
||||
RowsIcon,
|
||||
} from '@radix-ui/react-icons';
|
||||
import classnames from 'classnames';
|
||||
import type { NamedExoticComponent } from 'react';
|
||||
|
||||
type IconName =
|
||||
| 'archive'
|
||||
| 'home'
|
||||
| 'camera'
|
||||
| 'gear'
|
||||
| 'eye'
|
||||
| 'triangleDown'
|
||||
| 'triangleLeft'
|
||||
| 'triangleRight'
|
||||
| 'paperPlane'
|
||||
| 'update'
|
||||
| 'question'
|
||||
| 'check'
|
||||
| 'plus'
|
||||
| 'plusCircle'
|
||||
| 'clock'
|
||||
| 'sun'
|
||||
| 'code'
|
||||
| 'x'
|
||||
| 'trash'
|
||||
| 'moon';
|
||||
|
||||
const icons: Record<IconName, NamedExoticComponent<{ className: string }>> = {
|
||||
paperPlane: PaperPlaneIcon,
|
||||
triangleDown: TriangleDownIcon,
|
||||
plus: PlusIcon,
|
||||
plusCircle: PlusCircledIcon,
|
||||
clock: ClockIcon,
|
||||
const icons = {
|
||||
archive: ArchiveIcon,
|
||||
camera: CameraIcon,
|
||||
check: CheckIcon,
|
||||
triangleLeft: TriangleLeftIcon,
|
||||
triangleRight: TriangleRightIcon,
|
||||
clock: ClockIcon,
|
||||
code: CodeIcon,
|
||||
colorWheel: ColorWheelIcon,
|
||||
eye: EyeOpenIcon,
|
||||
gear: GearIcon,
|
||||
home: HomeIcon,
|
||||
update: UpdateIcon,
|
||||
sun: SunIcon,
|
||||
listBullet: ListBulletIcon,
|
||||
moon: MoonIcon,
|
||||
x: Cross2Icon,
|
||||
paperPlane: PaperPlaneIcon,
|
||||
plus: PlusIcon,
|
||||
plusCircle: PlusCircledIcon,
|
||||
question: QuestionMarkIcon,
|
||||
eye: EyeOpenIcon,
|
||||
code: CodeIcon,
|
||||
rows: RowsIcon,
|
||||
sun: SunIcon,
|
||||
trash: TrashIcon,
|
||||
triangleDown: TriangleDownIcon,
|
||||
triangleLeft: TriangleLeftIcon,
|
||||
triangleRight: TriangleRightIcon,
|
||||
update: UpdateIcon,
|
||||
x: Cross2Icon,
|
||||
};
|
||||
|
||||
export interface IconProps {
|
||||
icon: IconName;
|
||||
icon: keyof typeof icons;
|
||||
className?: string;
|
||||
size?: 'md';
|
||||
size?: 'xs' | 'sm' | 'md';
|
||||
spin?: boolean;
|
||||
}
|
||||
|
||||
@@ -83,6 +66,8 @@ export function Icon({ icon, spin, size = 'md', className }: IconProps) {
|
||||
className,
|
||||
'text-gray-800',
|
||||
size === 'md' && 'h-4 w-4',
|
||||
size === 'sm' && 'h-3 w-3',
|
||||
size === 'xs' && 'h-2 w-2',
|
||||
spin && 'animate-spin',
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import classnames from 'classnames';
|
||||
import { forwardRef } from 'react';
|
||||
import type { IconProps } from './Icon';
|
||||
import { Icon } from './Icon';
|
||||
import type { ButtonProps } from './Button';
|
||||
import { Button } from './Button';
|
||||
import classnames from 'classnames';
|
||||
import type { IconProps } from './Icon';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
type Props = Omit<IconProps, 'size'> &
|
||||
ButtonProps<typeof Button> & {
|
||||
iconClassName?: string;
|
||||
};
|
||||
type Props = IconProps & ButtonProps & { iconClassName?: string; iconSize?: IconProps['size'] };
|
||||
|
||||
export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButton(
|
||||
{ icon, spin, className, iconClassName, ...props }: Props,
|
||||
{ icon, spin, className, iconClassName, size, iconSize, ...props }: Props,
|
||||
ref,
|
||||
) {
|
||||
return (
|
||||
<Button ref={ref} className={classnames(className, 'group')} {...props}>
|
||||
<Button ref={ref} className={classnames(className, 'group')} size={size} {...props}>
|
||||
<Icon
|
||||
size={iconSize}
|
||||
icon={icon}
|
||||
spin={spin}
|
||||
className={classnames(
|
||||
|
||||
@@ -59,13 +59,13 @@ export function Input({
|
||||
{label}
|
||||
</label>
|
||||
<HStack
|
||||
items="center"
|
||||
alignItems="center"
|
||||
className={classnames(
|
||||
containerClassName,
|
||||
'relative w-full rounded-md text-gray-900',
|
||||
'border border-gray-200 focus-within:border-blue-400/40',
|
||||
size === 'md' && 'h-10',
|
||||
size === 'sm' && 'h-8',
|
||||
size === 'md' && 'h-9',
|
||||
size === 'sm' && 'h-7',
|
||||
)}
|
||||
>
|
||||
{leftSlot}
|
||||
|
||||
@@ -38,9 +38,9 @@ export function RequestPane({ fullHeight, request, className }: Props) {
|
||||
{['JSON', 'Params', 'Headers', 'Auth'].map((label, i) => (
|
||||
<Button
|
||||
key={label}
|
||||
size="xs"
|
||||
color={i === 0 && 'gray'}
|
||||
className={i !== 0 && 'opacity-80 hover:opacity-100'}
|
||||
size="sm"
|
||||
color={i === 0 ? 'gray' : undefined}
|
||||
className={i !== 0 ? 'opacity-80 hover:opacity-100' : undefined}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
|
||||
@@ -61,7 +61,7 @@ export function ResponsePane({ requestId, className }: Props) {
|
||||
{response && (
|
||||
<>
|
||||
<HStack
|
||||
items="center"
|
||||
alignItems="center"
|
||||
className="italic text-gray-600 text-sm w-full mb-1 flex-shrink-0 pl-2"
|
||||
>
|
||||
{response.status > 0 && (
|
||||
@@ -76,7 +76,7 @@ export function ResponsePane({ requestId, className }: Props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<HStack items="center" className="ml-auto h-8">
|
||||
<HStack alignItems="center" className="ml-auto h-8">
|
||||
{contentType.includes('html') && (
|
||||
<IconButton
|
||||
icon={viewMode === 'pretty' ? 'eye' : 'code'}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import classnames from 'classnames';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { SketchPicker } from 'react-color';
|
||||
import { useRequestCreate } from '../hooks/useRequest';
|
||||
import useTheme from '../hooks/useTheme';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import { Button } from './Button';
|
||||
import { ButtonLink } from './ButtonLink';
|
||||
import { Dialog } from './Dialog';
|
||||
import { HeaderEditor } from './HeaderEditor';
|
||||
import { IconButton } from './IconButton';
|
||||
@@ -20,28 +21,27 @@ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
||||
|
||||
export function Sidebar({ className, activeRequestId, workspaceId, requests, ...props }: Props) {
|
||||
const createRequest = useRequestCreate({ workspaceId, navigateAfter: true });
|
||||
const { appearance, toggleAppearance } = useTheme();
|
||||
const { appearance, toggleAppearance, forceSetTheme } = useTheme();
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const [color, setColor] = useState<string>('blue');
|
||||
const [showPicker, setShowPicker] = useState<boolean>(false);
|
||||
return (
|
||||
<div
|
||||
className={classnames(className, 'w-52 bg-gray-100 h-full border-r border-gray-200')}
|
||||
className={classnames(
|
||||
className,
|
||||
'min-w-[10rem] bg-gray-100 h-full border-r border-gray-200 relative',
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<HStack as={WindowDragRegion} items="center" justify="end">
|
||||
<HStack as={WindowDragRegion} alignItems="center" justifyContent="end">
|
||||
<Dialog wide open={open} onOpenChange={setOpen} title="Edit Headers">
|
||||
<HeaderEditor />
|
||||
<Button className="ml-auto mt-5" color="primary" onClick={() => setOpen(false)}>
|
||||
Save
|
||||
</Button>
|
||||
</Dialog>
|
||||
<IconButton size="sm" icon="camera" onClick={() => setOpen(true)} />
|
||||
<IconButton
|
||||
size="sm"
|
||||
icon={appearance === 'dark' ? 'moon' : 'sun'}
|
||||
onClick={toggleAppearance}
|
||||
/>
|
||||
<IconButton
|
||||
size="sm"
|
||||
className="mx-1"
|
||||
icon="plusCircle"
|
||||
onClick={async () => {
|
||||
await createRequest.mutate({ name: 'Test Request' });
|
||||
@@ -53,6 +53,27 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
|
||||
<SidebarItem key={r.id} request={r} active={r.id === activeRequestId} />
|
||||
))}
|
||||
{/*<Colors />*/}
|
||||
|
||||
<HStack
|
||||
className="absolute bottom-1 left-1 right-0 mx-1"
|
||||
alignItems="center"
|
||||
justifyContent="end"
|
||||
>
|
||||
<IconButton icon="colorWheel" onClick={() => setShowPicker((p) => !p)} />
|
||||
<IconButton icon={appearance === 'dark' ? 'moon' : 'sun'} onClick={toggleAppearance} />
|
||||
<IconButton icon="rows" onClick={() => setOpen(true)} />
|
||||
</HStack>
|
||||
|
||||
{showPicker && (
|
||||
<SketchPicker
|
||||
className="fixed z-10 bottom-2 right-2"
|
||||
color={color}
|
||||
onChange={(c) => {
|
||||
setColor(c.hex);
|
||||
forceSetTheme(c.hex);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</VStack>
|
||||
</div>
|
||||
);
|
||||
@@ -61,15 +82,16 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
|
||||
function SidebarItem({ request, active }: { request: HttpRequest; active: boolean }) {
|
||||
return (
|
||||
<li key={request.id}>
|
||||
<Button
|
||||
as={Link}
|
||||
<ButtonLink
|
||||
color="custom"
|
||||
to={`/workspaces/${request.workspaceId}/requests/${request.id}`}
|
||||
className={classnames('w-full', active ? 'bg-gray-200/70 text-gray-900' : 'text-gray-500')}
|
||||
size="xs"
|
||||
disabled={active}
|
||||
className={classnames('w-full', active ? 'bg-gray-200/70 text-gray-900' : 'text-gray-600')}
|
||||
size="sm"
|
||||
justify="start"
|
||||
>
|
||||
{request.name || request.url}
|
||||
</Button>
|
||||
</ButtonLink>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { HTMLAttributes, ReactNode } from 'react';
|
||||
import React, { Children, Fragment } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { Children, Fragment } from 'react';
|
||||
|
||||
const spaceClassesX = {
|
||||
0: 'pr-0',
|
||||
@@ -78,26 +78,35 @@ export function VStack({ className, space, children, ...props }: VStackProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface BaseStackProps extends HTMLAttributes<HTMLElement> {
|
||||
items?: 'start' | 'center';
|
||||
justify?: 'start' | 'center' | 'end';
|
||||
interface BaseStackProps {
|
||||
as?: React.ElementType;
|
||||
alignItems?: 'start' | 'center';
|
||||
justifyContent?: 'start' | 'center' | 'end';
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
function BaseStack({ className, items, justify, as = 'div', ...props }: BaseStackProps) {
|
||||
function BaseStack({
|
||||
className,
|
||||
alignItems,
|
||||
justifyContent,
|
||||
children,
|
||||
as = 'div',
|
||||
}: BaseStackProps) {
|
||||
const Component = as;
|
||||
return (
|
||||
<Component
|
||||
className={classnames(
|
||||
className,
|
||||
'flex flex-grow-0',
|
||||
items === 'center' && 'items-center',
|
||||
items === 'start' && 'items-start',
|
||||
justify === 'start' && 'justify-start',
|
||||
justify === 'center' && 'justify-center',
|
||||
justify === 'end' && 'justify-end',
|
||||
alignItems === 'center' && 'items-center',
|
||||
alignItems === 'start' && 'items-start',
|
||||
justifyContent === 'start' && 'justify-start',
|
||||
justifyContent === 'center' && 'justify-center',
|
||||
justifyContent === 'end' && 'justify-end',
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DropdownMenuRadio } from './Dropdown';
|
||||
import { Button } from './Button';
|
||||
import { Input } from './Input';
|
||||
import type { FormEvent } from 'react';
|
||||
import { Button } from './Button';
|
||||
import { DropdownMenuRadio } from './Dropdown';
|
||||
import { IconButton } from './IconButton';
|
||||
import { Input } from './Input';
|
||||
|
||||
interface Props {
|
||||
sendRequest: () => void;
|
||||
@@ -24,7 +24,6 @@ export function UrlBar({ sendRequest, loading, onMethodChange, method, onUrlChan
|
||||
<Input
|
||||
hideLabel
|
||||
useEditor={{ useTemplating: true, contentType: 'url' }}
|
||||
size="sm"
|
||||
className="font-mono px-0"
|
||||
name="url"
|
||||
label="Enter URL"
|
||||
@@ -46,7 +45,7 @@ export function UrlBar({ sendRequest, loading, onMethodChange, method, onUrlChan
|
||||
{ label: 'HEAD', value: 'HEAD' },
|
||||
]}
|
||||
>
|
||||
<Button type="button" disabled={loading} size="xs" className="mx-0.5" justify="start">
|
||||
<Button type="button" disabled={loading} size="sm" className="mx-0.5" justify="start">
|
||||
{method.toUpperCase()}
|
||||
</Button>
|
||||
</DropdownMenuRadio>
|
||||
@@ -55,7 +54,7 @@ export function UrlBar({ sendRequest, loading, onMethodChange, method, onUrlChan
|
||||
<IconButton
|
||||
type="submit"
|
||||
className="mr-0.5"
|
||||
size="xs"
|
||||
size="sm"
|
||||
icon={loading ? 'update' : 'paperPlane'}
|
||||
spin={loading}
|
||||
disabled={loading}
|
||||
|
||||
@@ -7,7 +7,7 @@ export function WindowDragRegion({ className, ...props }: Props) {
|
||||
return (
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className={classnames(className, 'w-full h-10 flex-shrink-0')}
|
||||
className={classnames(className, 'w-full h-12 flex-shrink-0')}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { debounce } from 'lodash';
|
||||
import { useEffect } from 'react';
|
||||
import type { Appearance } from '../lib/theme/window';
|
||||
import {
|
||||
@@ -10,13 +11,17 @@ import {
|
||||
|
||||
const appearanceQueryKey = ['theme', 'appearance'];
|
||||
|
||||
const forceSetTheme = debounce((gray: string) => {
|
||||
setAppearance(getAppearance(), gray);
|
||||
}, 200);
|
||||
|
||||
export default function useTheme() {
|
||||
const queryClient = useQueryClient();
|
||||
const appearance = useQuery({
|
||||
queryKey: appearanceQueryKey,
|
||||
queryFn: getAppearance,
|
||||
initialData: getAppearance(),
|
||||
});
|
||||
}).data;
|
||||
|
||||
const themeChange = (appearance: Appearance) => {
|
||||
setAppearance(appearance);
|
||||
@@ -32,7 +37,8 @@ export default function useTheme() {
|
||||
}, []);
|
||||
|
||||
return {
|
||||
appearance: appearance.data,
|
||||
appearance,
|
||||
forceSetTheme,
|
||||
toggleAppearance: handleToggleAppearance,
|
||||
};
|
||||
}
|
||||
|
||||
1
src-web/lib/constants.ts
Normal file
1
src-web/lib/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const DEFAULT_FONT_SIZE = 16;
|
||||
@@ -28,7 +28,7 @@ const lightTheme: AppTheme = {
|
||||
appearance: 'light',
|
||||
layers: {
|
||||
root: {
|
||||
whitePoint: 0.98,
|
||||
whitePoint: 0.95,
|
||||
colors: {
|
||||
gray: '#7f8fb0',
|
||||
red: '#da4545',
|
||||
@@ -59,9 +59,15 @@ export function toggleAppearance(): Appearance {
|
||||
return newAppearance;
|
||||
}
|
||||
|
||||
export function setAppearance(a?: Appearance) {
|
||||
export function setAppearance(a?: Appearance, gray?: string) {
|
||||
const appearance = a ?? getPreferredAppearance();
|
||||
const theme = appearance === 'dark' ? darkTheme : lightTheme;
|
||||
|
||||
// Hack to update the gray color for a demo
|
||||
if (theme.layers.root && gray) {
|
||||
theme.layers.root.colors.gray = gray;
|
||||
}
|
||||
|
||||
document.documentElement.setAttribute('data-appearance', appearance);
|
||||
document.documentElement.setAttribute('data-theme', theme.name);
|
||||
|
||||
|
||||
@@ -2,49 +2,30 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
--transition-duration: 100ms ease-in-out;
|
||||
}
|
||||
|
||||
:not(input):not(textarea),
|
||||
:not(input):not(textarea)::after,
|
||||
:not(input):not(textarea)::before {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
/* Default colors */
|
||||
background-color: hsl(var(--color-background));
|
||||
color: hsl(var(--color-gray-900));
|
||||
}
|
||||
|
||||
* {
|
||||
transition: background-color var(--transition-duration),
|
||||
border-color var(--transition-duration),
|
||||
box-shadow var(--transition-duration);
|
||||
}
|
||||
|
||||
/*.hide-scrollbar {*/
|
||||
/* &::-webkit-scrollbar-corner,*/
|
||||
/* &::-webkit-scrollbar {*/
|
||||
/* @apply w-[5px] h-[5px];*/
|
||||
/* background-color: transparent; !* or add it to the track *!*/
|
||||
/* }*/
|
||||
|
||||
/* &::-webkit-scrollbar-thumb {*/
|
||||
/* @apply bg-gray-100 bg-opacity-20 rounded-full;*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
|
||||
@layer base {
|
||||
html, body, #root {
|
||||
@apply w-full h-full overflow-hidden bg-gray-50 text-gray-900;
|
||||
}
|
||||
|
||||
/* Setup default transitions for elements */
|
||||
* {
|
||||
transition: background-color var(--transition-duration),
|
||||
border-color var(--transition-duration),
|
||||
box-shadow var(--transition-duration);
|
||||
}
|
||||
|
||||
/* Disable user selection to make it more "app-like" */
|
||||
:not(input):not(textarea),
|
||||
:not(input):not(textarea)::after,
|
||||
:not(input):not(textarea)::before {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
--transition-duration: 100ms ease-in-out;
|
||||
--color-white: 255 100% 100%;
|
||||
--color-black: 255 0% 0%;
|
||||
--color-background: var(--color-gray-50);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Layout } from './components/Layout';
|
||||
import { RouterError } from './components/RouterError';
|
||||
import { requestsQueryKey } from './hooks/useRequest';
|
||||
import { responsesQueryKey } from './hooks/useResponses';
|
||||
import { DEFAULT_FONT_SIZE } from './lib/constants';
|
||||
import type { HttpRequest, HttpResponse } from './lib/models';
|
||||
import { convertDates } from './lib/models';
|
||||
import { setAppearance } from './lib/theme/window';
|
||||
@@ -75,6 +76,21 @@ await listen('updated_response', ({ payload: response }: { payload: HttpResponse
|
||||
);
|
||||
});
|
||||
|
||||
await listen('zoom', ({ payload: zoomDelta }: { payload: number }) => {
|
||||
const fontSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
|
||||
|
||||
let newFontSize;
|
||||
if (zoomDelta === 0) {
|
||||
newFontSize = DEFAULT_FONT_SIZE;
|
||||
} else if (zoomDelta > 0) {
|
||||
newFontSize = Math.min(fontSize * 1.1, DEFAULT_FONT_SIZE * 5);
|
||||
} else if (zoomDelta < 0) {
|
||||
newFontSize = Math.max(fontSize * 0.9, DEFAULT_FONT_SIZE * 0.4);
|
||||
}
|
||||
|
||||
document.documentElement.style.fontSize = `${newFontSize}px`;
|
||||
});
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
|
||||
@@ -1,56 +1,65 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
/** @type {import("tailwindcss").Config} */
|
||||
module.exports = {
|
||||
darkMode: ['class', '[data-appearance="dark"]'],
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src-web/**/*.{html,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
fontFamily: {
|
||||
'mono': ['JetBrains Mono', "Menlo", 'monospace'],
|
||||
},
|
||||
borderRadius: {
|
||||
none: '0px',
|
||||
sm: 'var(--border-radius-sm)',
|
||||
DEFAULT: 'var(--border-radius)',
|
||||
md: 'var(--border-radius-md)',
|
||||
lg: 'var(--border-radius-lg)',
|
||||
full: '9999px',
|
||||
},
|
||||
colors: {
|
||||
transparent: 'transparent',
|
||||
white: 'hsl(0 100% 100% / <alpha-value>)',
|
||||
black: 'hsl(0 100% 0% / <alpha-value>)',
|
||||
background: 'hsl(var(--color-background) / <alpha-value>)',
|
||||
placeholder: 'hsl(var(--color-gray-300) / <alpha-value>)',
|
||||
red: color('red'),
|
||||
orange: color('orange'),
|
||||
yellow: color('yellow'),
|
||||
gray: color('gray'),
|
||||
blue: color('blue'),
|
||||
green: color('green'),
|
||||
pink: color('pink'),
|
||||
violet: color('violet'),
|
||||
}
|
||||
darkMode: ["class", "[data-appearance=\"dark\"]"],
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src-web/**/*.{html,tsx}"
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
fontFamily: {
|
||||
"mono": ["JetBrains Mono", "Menlo", "monospace"]
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
fontSize: {
|
||||
sm: "0.9rem",
|
||||
base: "1rem",
|
||||
xl: "1.25rem",
|
||||
"2xl": "1.563rem",
|
||||
"3xl": "1.953rem",
|
||||
"4xl": "2.441rem",
|
||||
"5xl": "3.052rem"
|
||||
},
|
||||
borderRadius: {
|
||||
none: "0px",
|
||||
sm: "var(--border-radius-sm)",
|
||||
DEFAULT: "var(--border-radius)",
|
||||
md: "var(--border-radius-md)",
|
||||
lg: "var(--border-radius-lg)",
|
||||
full: "9999px"
|
||||
},
|
||||
colors: {
|
||||
transparent: "transparent",
|
||||
white: "hsl(0 100% 100% / <alpha-value>)",
|
||||
black: "hsl(0 100% 0% / <alpha-value>)",
|
||||
background: "hsl(var(--color-background) / <alpha-value>)",
|
||||
placeholder: "hsl(var(--color-gray-400) / <alpha-value>)",
|
||||
red: color("red"),
|
||||
orange: color("orange"),
|
||||
yellow: color("yellow"),
|
||||
gray: color("gray"),
|
||||
blue: color("blue"),
|
||||
green: color("green"),
|
||||
pink: color("pink"),
|
||||
violet: color("violet")
|
||||
}
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
|
||||
function color(name) {
|
||||
return {
|
||||
0: `hsl(var(--color-${name}-0) / <alpha-value>)`,
|
||||
50: `hsl(var(--color-${name}-50) / <alpha-value>)`,
|
||||
100: `hsl(var(--color-${name}-100) / <alpha-value>)`,
|
||||
200: `hsl(var(--color-${name}-200) / <alpha-value>)`,
|
||||
300: `hsl(var(--color-${name}-300) / <alpha-value>)`,
|
||||
400: `hsl(var(--color-${name}-400) / <alpha-value>)`,
|
||||
500: `hsl(var(--color-${name}-500) / <alpha-value>)`,
|
||||
600: `hsl(var(--color-${name}-600) / <alpha-value>)`,
|
||||
700: `hsl(var(--color-${name}-700) / <alpha-value>)`,
|
||||
800: `hsl(var(--color-${name}-800) / <alpha-value>)`,
|
||||
900: `hsl(var(--color-${name}-900) / <alpha-value>)`,
|
||||
950: `hsl(var(--color-${name}-950) / <alpha-value>)`,
|
||||
1000: `hsl(var(--color-${name}-1000) / <alpha-value>)`,
|
||||
};
|
||||
return {
|
||||
0: `hsl(var(--color-${name}-0) / <alpha-value>)`,
|
||||
50: `hsl(var(--color-${name}-50) / <alpha-value>)`,
|
||||
100: `hsl(var(--color-${name}-100) / <alpha-value>)`,
|
||||
200: `hsl(var(--color-${name}-200) / <alpha-value>)`,
|
||||
300: `hsl(var(--color-${name}-300) / <alpha-value>)`,
|
||||
400: `hsl(var(--color-${name}-400) / <alpha-value>)`,
|
||||
500: `hsl(var(--color-${name}-500) / <alpha-value>)`,
|
||||
600: `hsl(var(--color-${name}-600) / <alpha-value>)`,
|
||||
700: `hsl(var(--color-${name}-700) / <alpha-value>)`,
|
||||
800: `hsl(var(--color-${name}-800) / <alpha-value>)`,
|
||||
900: `hsl(var(--color-${name}-900) / <alpha-value>)`,
|
||||
950: `hsl(var(--color-${name}-950) / <alpha-value>)`,
|
||||
1000: `hsl(var(--color-${name}-1000) / <alpha-value>)`
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user