mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-17 23:14:03 +01:00
Vim/emacs/vscode keybindings
This commit is contained in:
44
package-lock.json
generated
44
package-lock.json
generated
@@ -1782,6 +1782,47 @@
|
||||
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@replit/codemirror-emacs": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
|
||||
"integrity": "sha512-74DITnht6Cs6sHg02PQ169IKb1XgtyhI9sLD0JeOFco6Ds18PT+dkD8+DgXBDokne9UIFKsBbKPnpFRAz60/Lw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.2",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
"@codemirror/search": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.1",
|
||||
"@codemirror/view": "^6.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-vim": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.2.1.tgz",
|
||||
"integrity": "sha512-qDAcGSHBYU5RrdO//qCmD8K9t6vbP327iCj/iqrkVnjbrpFhrjOt92weGXGHmTNRh16cUtkUZ7Xq7rZf+8HVow==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
"@codemirror/language": "^6.1.0",
|
||||
"@codemirror/search": "^6.2.0",
|
||||
"@codemirror/state": "^6.0.1",
|
||||
"@codemirror/view": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-vscode-keymap": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-vscode-keymap/-/codemirror-vscode-keymap-6.0.2.tgz",
|
||||
"integrity": "sha512-j45qTwGxzpsv82lMD/NreGDORFKSctMDVkGRopaP+OrzSzv+pXDQuU3LnFvKpasyjVT0lf+PKG1v2DSCn/vxxg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/lint": "^6.0.0",
|
||||
"@codemirror/search": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-virtual": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz",
|
||||
@@ -15878,6 +15919,9 @@
|
||||
"@gilbarbara/deep-equal": "^0.3.1",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.3",
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.2.1",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.62.16",
|
||||
"@tanstack/react-router": "^1.95.1",
|
||||
|
||||
2
src-tauri/migrations/20250108035425_editor-keymap.sql
Normal file
2
src-tauri/migrations/20250108035425_editor-keymap.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE settings
|
||||
ADD COLUMN editor_keymap TEXT DEFAULT 'codemirror' NOT NULL;
|
||||
@@ -1,250 +1,61 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AnyModel =
|
||||
| CookieJar
|
||||
| Environment
|
||||
| Folder
|
||||
| GrpcConnection
|
||||
| GrpcEvent
|
||||
| GrpcRequest
|
||||
| HttpRequest
|
||||
| HttpResponse
|
||||
| Plugin
|
||||
| Settings
|
||||
| KeyValue
|
||||
| Workspace;
|
||||
export type AnyModel = CookieJar | Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | Plugin | Settings | KeyValue | Workspace;
|
||||
|
||||
export type Cookie = {
|
||||
raw_cookie: string;
|
||||
domain: CookieDomain;
|
||||
expires: CookieExpires;
|
||||
path: [string, boolean];
|
||||
};
|
||||
export type Cookie = { raw_cookie: string, domain: CookieDomain, expires: CookieExpires, path: [string, boolean], };
|
||||
|
||||
export type CookieDomain = { HostOnly: string } | { Suffix: string } | 'NotPresent' | 'Empty';
|
||||
export type CookieDomain = { "HostOnly": string } | { "Suffix": string } | "NotPresent" | "Empty";
|
||||
|
||||
export type CookieExpires = { AtUtc: string } | 'SessionEnd';
|
||||
export type CookieExpires = { "AtUtc": string } | "SessionEnd";
|
||||
|
||||
export type CookieJar = {
|
||||
model: 'cookie_jar';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
workspaceId: string;
|
||||
cookies: Array<Cookie>;
|
||||
name: string;
|
||||
};
|
||||
export type CookieJar = { model: "cookie_jar", id: string, createdAt: string, updatedAt: string, workspaceId: string, cookies: Array<Cookie>, name: string, };
|
||||
|
||||
export type Environment = {
|
||||
model: 'environment';
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
environmentId: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
name: string;
|
||||
variables: Array<EnvironmentVariable>;
|
||||
};
|
||||
export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean; name: string; value: string; id: string };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, environmentId: string | null, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
|
||||
|
||||
export type Folder = {
|
||||
model: 'folder';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
workspaceId: string;
|
||||
folderId: string | null;
|
||||
name: string;
|
||||
description: string;
|
||||
sortPriority: number;
|
||||
};
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type GrpcConnection = {
|
||||
model: 'grpc_connection';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
workspaceId: string;
|
||||
requestId: string;
|
||||
elapsed: number;
|
||||
error: string | null;
|
||||
method: string;
|
||||
service: string;
|
||||
status: number;
|
||||
state: GrpcConnectionState;
|
||||
trailers: { [key in string]?: string };
|
||||
url: string;
|
||||
};
|
||||
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
||||
|
||||
export type GrpcConnectionState = 'initialized' | 'connected' | 'closed';
|
||||
export type GrpcConnection = { model: "grpc_connection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, elapsed: number, error: string | null, method: string, service: string, status: number, state: GrpcConnectionState, trailers: { [key in string]?: string }, url: string, };
|
||||
|
||||
export type GrpcEvent = {
|
||||
model: 'grpc_event';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
workspaceId: string;
|
||||
requestId: string;
|
||||
connectionId: string;
|
||||
content: string;
|
||||
error: string | null;
|
||||
eventType: GrpcEventType;
|
||||
metadata: { [key in string]?: string };
|
||||
status: number | null;
|
||||
};
|
||||
export type GrpcConnectionState = "initialized" | "connected" | "closed";
|
||||
|
||||
export type GrpcEventType =
|
||||
| 'info'
|
||||
| 'error'
|
||||
| 'client_message'
|
||||
| 'server_message'
|
||||
| 'connection_start'
|
||||
| 'connection_end';
|
||||
export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, connectionId: string, content: string, error: string | null, eventType: GrpcEventType, metadata: { [key in string]?: string }, status: number | null, };
|
||||
|
||||
export type GrpcMetadataEntry = { enabled?: boolean; name: string; value: string; id: string };
|
||||
export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end";
|
||||
|
||||
export type GrpcRequest = {
|
||||
model: 'grpc_request';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
workspaceId: string;
|
||||
folderId: string | null;
|
||||
authenticationType: string | null;
|
||||
authentication: Record<string, any>;
|
||||
description: string;
|
||||
message: string;
|
||||
metadata: Array<GrpcMetadataEntry>;
|
||||
method: string | null;
|
||||
name: string;
|
||||
service: string | null;
|
||||
sortPriority: number;
|
||||
url: string;
|
||||
};
|
||||
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type HttpRequest = {
|
||||
model: 'http_request';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
workspaceId: string;
|
||||
folderId: string | null;
|
||||
authentication: Record<string, any>;
|
||||
authenticationType: string | null;
|
||||
body: Record<string, any>;
|
||||
bodyType: string | null;
|
||||
description: string;
|
||||
headers: Array<HttpRequestHeader>;
|
||||
method: string;
|
||||
name: string;
|
||||
sortPriority: number;
|
||||
url: string;
|
||||
urlParameters: Array<HttpUrlParameter>;
|
||||
};
|
||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
||||
|
||||
export type HttpRequestHeader = { enabled?: boolean; name: string; value: string; id: string };
|
||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||
|
||||
export type HttpResponse = {
|
||||
model: 'http_response';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
workspaceId: string;
|
||||
requestId: string;
|
||||
bodyPath: string | null;
|
||||
contentLength: number | null;
|
||||
elapsed: number;
|
||||
elapsedHeaders: number;
|
||||
error: string | null;
|
||||
headers: Array<HttpResponseHeader>;
|
||||
remoteAddr: string | null;
|
||||
status: number;
|
||||
statusReason: string | null;
|
||||
state: HttpResponseState;
|
||||
url: string;
|
||||
version: string | null;
|
||||
};
|
||||
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type HttpResponseHeader = { name: string; value: string };
|
||||
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
|
||||
|
||||
export type HttpResponseState = 'initialized' | 'connected' | 'closed';
|
||||
export type HttpResponseHeader = { name: string, value: string, };
|
||||
|
||||
export type HttpUrlParameter = { enabled?: boolean; name: string; value: string; id: string };
|
||||
export type HttpResponseState = "initialized" | "connected" | "closed";
|
||||
|
||||
export type KeyValue = {
|
||||
model: 'key_value';
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
key: string;
|
||||
namespace: string;
|
||||
value: string;
|
||||
};
|
||||
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type ModelPayload = { model: AnyModel; windowLabel: string; updateSource: UpdateSource };
|
||||
export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };
|
||||
|
||||
export type Plugin = {
|
||||
model: 'plugin';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
checkedAt: string | null;
|
||||
directory: string;
|
||||
enabled: boolean;
|
||||
url: string | null;
|
||||
};
|
||||
export type ModelPayload = { model: AnyModel, windowLabel: string, updateSource: UpdateSource, };
|
||||
|
||||
export type ProxySetting =
|
||||
| { type: 'enabled'; http: string; https: string; auth: ProxySettingAuth | null }
|
||||
| {
|
||||
type: 'disabled';
|
||||
};
|
||||
export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, enabled: boolean, url: string | null, };
|
||||
|
||||
export type ProxySettingAuth = { user: string; password: string };
|
||||
export type ProxySetting = { "type": "enabled", http: string, https: string, auth: ProxySettingAuth | null, } | { "type": "disabled" };
|
||||
|
||||
export type Settings = {
|
||||
model: 'settings';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
appearance: string;
|
||||
editorFontSize: number;
|
||||
editorSoftWrap: boolean;
|
||||
interfaceFontSize: number;
|
||||
interfaceScale: number;
|
||||
openWorkspaceNewWindow: boolean | null;
|
||||
telemetry: boolean;
|
||||
theme: string;
|
||||
themeDark: string;
|
||||
themeLight: string;
|
||||
updateChannel: string;
|
||||
proxy: ProxySetting | null;
|
||||
};
|
||||
export type ProxySettingAuth = { user: string, password: string, };
|
||||
|
||||
export type SyncState = {
|
||||
model: 'sync_state';
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
flushedAt: string;
|
||||
modelId: string;
|
||||
checksum: string;
|
||||
relPath: string;
|
||||
syncDir: string;
|
||||
};
|
||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, telemetry: boolean, theme: string, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, };
|
||||
|
||||
export type UpdateSource = 'sync' | 'window' | 'plugin' | 'background';
|
||||
export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, };
|
||||
|
||||
export type Workspace = {
|
||||
model: 'workspace';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
name: string;
|
||||
description: string;
|
||||
settingValidateCertificates: boolean;
|
||||
settingFollowRedirects: boolean;
|
||||
settingRequestTimeout: number;
|
||||
settingSyncDir: string | null;
|
||||
};
|
||||
export type UpdateSource = "sync" | "window" | "plugin" | "background";
|
||||
|
||||
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, settingSyncDir: string | null, };
|
||||
|
||||
@@ -4,6 +4,8 @@ use sea_query::Iden;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
@@ -26,6 +28,48 @@ pub struct ProxySettingAuth {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
pub enum EditorKeymap {
|
||||
Default,
|
||||
Vim,
|
||||
Vscode,
|
||||
Emacs,
|
||||
}
|
||||
|
||||
impl FromStr for EditorKeymap {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"default" => Ok(Self::Default),
|
||||
"vscode" => Ok(Self::Vscode),
|
||||
"vim" => Ok(Self::Vim),
|
||||
"emacs" => Ok(Self::Emacs),
|
||||
_ => Ok(Self::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EditorKeymap {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
EditorKeymap::Default => "default".to_string(),
|
||||
EditorKeymap::Vscode => "vscode".to_string(),
|
||||
EditorKeymap::Vim => "vim".to_string(),
|
||||
EditorKeymap::Emacs => "emacs".to_string(),
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EditorKeymap {
|
||||
fn default() -> Self {
|
||||
Self::Default
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
@@ -42,12 +86,13 @@ pub struct Settings {
|
||||
pub interface_font_size: i32,
|
||||
pub interface_scale: f32,
|
||||
pub open_workspace_new_window: Option<bool>,
|
||||
pub proxy: Option<ProxySetting>,
|
||||
pub telemetry: bool,
|
||||
pub theme: String,
|
||||
pub theme_dark: String,
|
||||
pub theme_light: String,
|
||||
pub update_channel: String,
|
||||
pub proxy: Option<ProxySetting>,
|
||||
pub editor_keymap: EditorKeymap,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
@@ -61,6 +106,7 @@ pub enum SettingsIden {
|
||||
|
||||
Appearance,
|
||||
EditorFontSize,
|
||||
EditorKeymap,
|
||||
EditorSoftWrap,
|
||||
InterfaceFontSize,
|
||||
InterfaceScale,
|
||||
@@ -78,6 +124,7 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
|
||||
|
||||
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||
let proxy: Option<String> = r.get("proxy")?;
|
||||
let editor_keymap: String = r.get("editor_keymap")?;
|
||||
Ok(Settings {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
@@ -85,6 +132,7 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
|
||||
updated_at: r.get("updated_at")?,
|
||||
appearance: r.get("appearance")?,
|
||||
editor_font_size: r.get("editor_font_size")?,
|
||||
editor_keymap: EditorKeymap::from_str(editor_keymap.as_str()).unwrap(),
|
||||
editor_soft_wrap: r.get("editor_soft_wrap")?,
|
||||
interface_font_size: r.get("interface_font_size")?,
|
||||
interface_scale: r.get("interface_scale")?,
|
||||
@@ -1043,9 +1091,7 @@ impl<'de> Deserialize<'de> for AnyModel {
|
||||
Some(m) if m == "environment" => {
|
||||
AnyModel::Environment(serde_json::from_value(value).unwrap())
|
||||
}
|
||||
Some(m) if m == "folder" => {
|
||||
AnyModel::Folder(serde_json::from_value(value).unwrap())
|
||||
}
|
||||
Some(m) if m == "folder" => AnyModel::Folder(serde_json::from_value(value).unwrap()),
|
||||
Some(m) if m == "key_value" => {
|
||||
AnyModel::KeyValue(serde_json::from_value(value).unwrap())
|
||||
}
|
||||
@@ -1058,9 +1104,7 @@ impl<'de> Deserialize<'de> for AnyModel {
|
||||
Some(m) if m == "cookie_jar" => {
|
||||
AnyModel::CookieJar(serde_json::from_value(value).unwrap())
|
||||
}
|
||||
Some(m) if m == "plugin" => {
|
||||
AnyModel::Plugin(serde_json::from_value(value).unwrap())
|
||||
}
|
||||
Some(m) if m == "plugin" => AnyModel::Plugin(serde_json::from_value(value).unwrap()),
|
||||
Some(m) => {
|
||||
return Err(serde::de::Error::custom(format!("Unknown model {}", m)));
|
||||
}
|
||||
|
||||
@@ -914,6 +914,7 @@ pub async fn update_settings<R: Runtime>(
|
||||
(SettingsIden::InterfaceFontSize, settings.interface_font_size.into()),
|
||||
(SettingsIden::InterfaceScale, settings.interface_scale.into()),
|
||||
(SettingsIden::EditorFontSize, settings.editor_font_size.into()),
|
||||
(SettingsIden::EditorKeymap, settings.editor_keymap.to_string().into()),
|
||||
(SettingsIden::EditorSoftWrap, settings.editor_soft_wrap.into()),
|
||||
(SettingsIden::Telemetry, settings.telemetry.into()),
|
||||
(SettingsIden::OpenWorkspaceNewWindow, settings.open_workspace_new_window.into()),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useFolders } from '../hooks/useFolders';
|
||||
import { useUpdateAnyFolder } from '../hooks/useUpdateAnyFolder';
|
||||
import { PlainInput } from './core/PlainInput';
|
||||
import { Input } from './core/Input';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { MarkdownEditor } from './MarkdownEditor';
|
||||
|
||||
@@ -17,13 +17,14 @@ export function FolderSettingsDialog({ folderId }: Props) {
|
||||
|
||||
return (
|
||||
<VStack space={3} className="pb-3">
|
||||
<PlainInput
|
||||
<Input
|
||||
label="Folder Name"
|
||||
defaultValue={folder.name}
|
||||
onChange={(name) => {
|
||||
if (folderId == null) return;
|
||||
updateFolder({ id: folderId, update: (folder) => ({ ...folder, name }) });
|
||||
}}
|
||||
stateKey={`name.${folder.id}`}
|
||||
/>
|
||||
|
||||
<MarkdownEditor
|
||||
|
||||
@@ -45,8 +45,8 @@ import type {
|
||||
GenericCompletionOption,
|
||||
} from './core/Editor/genericCompletion';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import { Input } from './core/Input';
|
||||
import type { Pair } from './core/PairEditor';
|
||||
import { PlainInput } from './core/PlainInput';
|
||||
import type { TabItem } from './core/Tabs/Tabs';
|
||||
import { TabContent, Tabs } from './core/Tabs/Tabs';
|
||||
import { EmptyStateText } from './EmptyStateText';
|
||||
@@ -481,7 +481,7 @@ export const RequestPane = memo(function RequestPane({
|
||||
</TabContent>
|
||||
<TabContent value={TAB_DESCRIPTION}>
|
||||
<div className="grid grid-rows-[auto_minmax(0,1fr)] h-full">
|
||||
<PlainInput
|
||||
<Input
|
||||
label="Request Name"
|
||||
hideLabel
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
@@ -490,6 +490,7 @@ export const RequestPane = memo(function RequestPane({
|
||||
containerClassName="border-0"
|
||||
placeholder="Request Name"
|
||||
onChange={(name) => updateRequest({ id: activeRequestId, update: { name } })}
|
||||
stateKey={`name.${activeRequest.id}`}
|
||||
/>
|
||||
<MarkdownEditor
|
||||
name="request-description"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { EditorKeymap } from '@yaakapp-internal/models';
|
||||
import React from 'react';
|
||||
import { useActiveWorkspace } from '../../hooks/useActiveWorkspace';
|
||||
import { useResolvedAppearance } from '../../hooks/useResolvedAppearance';
|
||||
@@ -9,7 +10,7 @@ import { getThemes } from '../../lib/theme/themes';
|
||||
import { isThemeDark } from '../../lib/theme/window';
|
||||
import type { ButtonProps } from '../core/Button';
|
||||
import { Checkbox } from '../core/Checkbox';
|
||||
import {Editor} from "../core/Editor/Editor";
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
import type { IconProps } from '../core/Icon';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
@@ -18,10 +19,13 @@ import { Select } from '../core/Select';
|
||||
import { Separator } from '../core/Separator';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
|
||||
const fontSizes = [
|
||||
const fontSizeOptions = [
|
||||
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
].map((n) => ({ label: `${n}`, value: `${n}` }));
|
||||
|
||||
const keymaps: EditorKeymap[] = ['default', 'vim', 'vscode', 'emacs'];
|
||||
const keymapOptions = keymaps.map((n) => ({ label: n, value: n }));
|
||||
|
||||
const buttonColors: ButtonProps['color'][] = [
|
||||
'primary',
|
||||
'info',
|
||||
@@ -86,9 +90,9 @@ export function SettingsAppearance() {
|
||||
label="Font Size"
|
||||
labelPosition="left"
|
||||
value={`${settings.interfaceFontSize}`}
|
||||
options={fontSizes}
|
||||
options={fontSizeOptions}
|
||||
onChange={(v) => updateSettings.mutate({ interfaceFontSize: parseInt(v) })}
|
||||
event="font-size.interface"
|
||||
event="ui-font-size"
|
||||
/>
|
||||
<Select
|
||||
size="sm"
|
||||
@@ -96,15 +100,25 @@ export function SettingsAppearance() {
|
||||
label="Editor Font Size"
|
||||
labelPosition="left"
|
||||
value={`${settings.editorFontSize}`}
|
||||
options={fontSizes}
|
||||
options={fontSizeOptions}
|
||||
onChange={(v) => updateSettings.mutate({ editorFontSize: clamp(parseInt(v) || 14, 8, 30) })}
|
||||
event="font-size.editor"
|
||||
event="editor-font-size"
|
||||
/>
|
||||
<Checkbox
|
||||
checked={settings.editorSoftWrap}
|
||||
title="Wrap Editor Lines"
|
||||
onChange={(editorSoftWrap) => updateSettings.mutate({ editorSoftWrap })}
|
||||
event="wrap-lines"
|
||||
event="editor-wrap-lines"
|
||||
/>
|
||||
<Select
|
||||
size="sm"
|
||||
name="editorKeymap"
|
||||
label="Editor Key Map"
|
||||
labelPosition="left"
|
||||
value={`${settings.editorKeymap}`}
|
||||
options={keymapOptions}
|
||||
onChange={(v) => updateSettings.mutate({ editorKeymap: v })}
|
||||
event="editor-keymap"
|
||||
/>
|
||||
|
||||
<Separator className="my-4" />
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useDeleteActiveWorkspace } from '../hooks/useDeleteActiveWorkspace';
|
||||
import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { Button } from './core/Button';
|
||||
import { PlainInput } from './core/PlainInput';
|
||||
import { Input } from './core/Input';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { MarkdownEditor } from './MarkdownEditor';
|
||||
import { SelectFile } from './SelectFile';
|
||||
@@ -22,10 +22,11 @@ export function WorkspaceSettingsDialog({ workspaceId, hide }: Props) {
|
||||
|
||||
return (
|
||||
<VStack space={3} alignItems="start" className="pb-3 max-h-[50vh]">
|
||||
<PlainInput
|
||||
<Input
|
||||
label="Workspace Name"
|
||||
defaultValue={workspace.name}
|
||||
onChange={(name) => updateWorkspace({ name })}
|
||||
stateKey={`name.${workspace.id}`}
|
||||
/>
|
||||
|
||||
<MarkdownEditor
|
||||
|
||||
@@ -4,16 +4,28 @@
|
||||
.cm-editor {
|
||||
@apply w-full block text-base;
|
||||
|
||||
/* Regular cursor */
|
||||
.cm-cursor {
|
||||
@apply border-text !important;
|
||||
/* Widen the cursor */
|
||||
/* Widen the cursor a bit */
|
||||
@apply border-l-[2px];
|
||||
}
|
||||
|
||||
/* Vim-mode cursor */
|
||||
.cm-fat-cursor {
|
||||
@apply bg-text opacity-60;
|
||||
}
|
||||
|
||||
&.cm-focused {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
&:not(.cm-focused) {
|
||||
.cm-cursor, .cm-fat-cursor {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cm-content {
|
||||
@apply py-0;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import { foldState, forceParsing } from '@codemirror/language';
|
||||
import type { EditorStateConfig, Extension } from '@codemirror/state';
|
||||
import { Compartment, EditorState } from '@codemirror/state';
|
||||
import { keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view';
|
||||
import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import { emacs } from '@replit/codemirror-emacs';
|
||||
import { vim } from '@replit/codemirror-vim';
|
||||
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
|
||||
import type { EditorKeymap, EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
import type { TemplateFunction } from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
import { EditorView } from 'codemirror';
|
||||
@@ -33,15 +36,17 @@ import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
||||
import { IconButton } from '../IconButton';
|
||||
import { HStack } from '../Stacks';
|
||||
import './Editor.css';
|
||||
import {
|
||||
baseExtensions,
|
||||
emptyExtension,
|
||||
getLanguageExtension,
|
||||
multiLineExtensions,
|
||||
} from './extensions';
|
||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
||||
import type { GenericCompletionConfig } from './genericCompletion';
|
||||
import { singleLineExtensions } from './singleLine';
|
||||
|
||||
const keymapExtensions: Record<EditorKeymap, Extension> = {
|
||||
vim: vim(),
|
||||
emacs: emacs(),
|
||||
vscode: keymap.of(vscodeKeymap),
|
||||
default: [],
|
||||
};
|
||||
|
||||
export interface EditorProps {
|
||||
id?: string;
|
||||
readOnly?: boolean;
|
||||
@@ -86,6 +91,7 @@ export interface EditorProps {
|
||||
const stateFields = { history: historyField, folds: foldState };
|
||||
|
||||
const emptyVariables: EnvironmentVariable[] = [];
|
||||
const emptyExtension: Extension = [];
|
||||
|
||||
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
||||
{
|
||||
@@ -119,6 +125,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
ref,
|
||||
) {
|
||||
const settings = useSettings();
|
||||
|
||||
const templateFunctions = useTemplateFunctions();
|
||||
const allEnvironmentVariables = useActiveEnvironmentVariables();
|
||||
const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables;
|
||||
@@ -178,6 +185,25 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
[placeholder],
|
||||
);
|
||||
|
||||
// Update vim
|
||||
const keymapCompartment = useRef(new Compartment());
|
||||
useEffect(
|
||||
function configureKeymap() {
|
||||
if (cm.current === null) return;
|
||||
const current = keymapCompartment.current.get(cm.current.view.state) ?? [];
|
||||
// PERF: This is expensive with hundreds of editors on screen, so only do it when necessary
|
||||
if (settings.editorKeymap === 'default' && current === keymapExtensions['default']) return; // Nothing to do
|
||||
if (settings.editorKeymap === 'vim' && current === keymapExtensions['vim']) return; // Nothing to do
|
||||
if (settings.editorKeymap === 'vscode' && current === keymapExtensions['vscode']) return; // Nothing to do
|
||||
if (settings.editorKeymap === 'emacs' && current === keymapExtensions['emacs']) return; // Nothing to do
|
||||
|
||||
const ext = keymapExtensions[settings.editorKeymap] ?? keymapExtensions['default'];
|
||||
const effect = keymapCompartment.current.reconfigure(ext);
|
||||
cm.current.view.dispatch({ effects: effect });
|
||||
},
|
||||
[settings.editorKeymap],
|
||||
);
|
||||
|
||||
// Update wrap lines
|
||||
const wrapLinesCompartment = useRef(new Compartment());
|
||||
useEffect(
|
||||
@@ -188,7 +214,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
if (wrapLines && current !== emptyExtension) return; // Nothing to do
|
||||
if (!wrapLines && current === emptyExtension) return; // Nothing to do
|
||||
|
||||
const ext = wrapLines ? EditorView.lineWrapping : emptyExtension;
|
||||
const ext = wrapLines ? EditorView.lineWrapping : [];
|
||||
const effect = wrapLinesCompartment.current.reconfigure(ext);
|
||||
cm.current?.view.dispatch({ effects: effect });
|
||||
},
|
||||
@@ -331,7 +357,10 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
placeholderCompartment.current.of(
|
||||
placeholderExt(placeholderElFromText(placeholder ?? '')),
|
||||
),
|
||||
wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : emptyExtension),
|
||||
wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : []),
|
||||
keymapCompartment.current.of(
|
||||
keymapExtensions[settings.editorKeymap] ?? keymapExtensions['default'],
|
||||
),
|
||||
...getExtensions({
|
||||
container,
|
||||
readOnly,
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
import { lintKeymap } from '@codemirror/lint';
|
||||
|
||||
import { searchKeymap } from '@codemirror/search';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import {
|
||||
crosshairCursor,
|
||||
@@ -86,8 +85,6 @@ const syntaxExtensions: Record<NonNullable<EditorProps['language']>, LanguageSup
|
||||
markdown: markdown(),
|
||||
};
|
||||
|
||||
export const emptyExtension: Extension = [];
|
||||
|
||||
export function getLanguageExtension({
|
||||
language,
|
||||
useTemplating = false,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { PromptTextRequest } from '@yaakapp-internal/plugins';
|
||||
import type { FormEvent, ReactNode } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import {PlainInput} from "./PlainInput";
|
||||
import { HStack } from './Stacks';
|
||||
import { Button } from './Button';
|
||||
import { Input } from './Input';
|
||||
import { HStack } from './Stacks';
|
||||
|
||||
export type PromptProps = Omit<PromptTextRequest, 'id' | 'title' | 'description'> & {
|
||||
description?: ReactNode;
|
||||
@@ -35,7 +35,7 @@ export function Prompt({
|
||||
className="grid grid-rows-[auto_auto] grid-cols-[minmax(0,1fr)] gap-4 mb-4"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<PlainInput
|
||||
<Input
|
||||
hideLabel
|
||||
autoSelect
|
||||
require={require}
|
||||
@@ -43,6 +43,7 @@ export function Prompt({
|
||||
label={label}
|
||||
defaultValue={defaultValue}
|
||||
onChange={setValue}
|
||||
stateKey={null}
|
||||
/>
|
||||
<HStack space={2} justifyContent="end">
|
||||
<Button onClick={onCancel} variant="border" color="secondary">
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
"@gilbarbara/deep-equal": "^0.3.1",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.3",
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.2.1",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.62.16",
|
||||
"@tanstack/react-router": "^1.95.1",
|
||||
|
||||
Reference in New Issue
Block a user