Merge remote-tracking branch 'origin/main' into vim-indent
This commit is contained in:
commit
29b2639b4c
34 changed files with 1694 additions and 666 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -109,6 +109,8 @@ dependencies = [
|
||||||
"isahc",
|
"isahc",
|
||||||
"language",
|
"language",
|
||||||
"menu",
|
"menu",
|
||||||
|
"project",
|
||||||
|
"regex",
|
||||||
"schemars",
|
"schemars",
|
||||||
"search",
|
"search",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
1
assets/icons/assist_15.svg
Normal file
1
assets/icons/assist_15.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.9 0.499976C13.9 0.279062 13.7209 0.0999756 13.5 0.0999756C13.2791 0.0999756 13.1 0.279062 13.1 0.499976V1.09998H12.5C12.2791 1.09998 12.1 1.27906 12.1 1.49998C12.1 1.72089 12.2791 1.89998 12.5 1.89998H13.1V2.49998C13.1 2.72089 13.2791 2.89998 13.5 2.89998C13.7209 2.89998 13.9 2.72089 13.9 2.49998V1.89998H14.5C14.7209 1.89998 14.9 1.72089 14.9 1.49998C14.9 1.27906 14.7209 1.09998 14.5 1.09998H13.9V0.499976ZM11.8536 3.14642C12.0488 3.34168 12.0488 3.65826 11.8536 3.85353L10.8536 4.85353C10.6583 5.04879 10.3417 5.04879 10.1465 4.85353C9.9512 4.65827 9.9512 4.34169 10.1465 4.14642L11.1464 3.14643C11.3417 2.95116 11.6583 2.95116 11.8536 3.14642ZM9.85357 5.14642C10.0488 5.34168 10.0488 5.65827 9.85357 5.85353L2.85355 12.8535C2.65829 13.0488 2.34171 13.0488 2.14645 12.8535C1.95118 12.6583 1.95118 12.3417 2.14645 12.1464L9.14646 5.14642C9.34172 4.95116 9.65831 4.95116 9.85357 5.14642ZM13.5 5.09998C13.7209 5.09998 13.9 5.27906 13.9 5.49998V6.09998H14.5C14.7209 6.09998 14.9 6.27906 14.9 6.49998C14.9 6.72089 14.7209 6.89998 14.5 6.89998H13.9V7.49998C13.9 7.72089 13.7209 7.89998 13.5 7.89998C13.2791 7.89998 13.1 7.72089 13.1 7.49998V6.89998H12.5C12.2791 6.89998 12.1 6.72089 12.1 6.49998C12.1 6.27906 12.2791 6.09998 12.5 6.09998H13.1V5.49998C13.1 5.27906 13.2791 5.09998 13.5 5.09998ZM8.90002 0.499976C8.90002 0.279062 8.72093 0.0999756 8.50002 0.0999756C8.2791 0.0999756 8.10002 0.279062 8.10002 0.499976V1.09998H7.50002C7.2791 1.09998 7.10002 1.27906 7.10002 1.49998C7.10002 1.72089 7.2791 1.89998 7.50002 1.89998H8.10002V2.49998C8.10002 2.72089 8.2791 2.89998 8.50002 2.89998C8.72093 2.89998 8.90002 2.72089 8.90002 2.49998V1.89998H9.50002C9.72093 1.89998 9.90002 1.72089 9.90002 1.49998C9.90002 1.27906 9.72093 1.09998 9.50002 1.09998H8.90002V0.499976Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 1.9 KiB |
3
assets/icons/hamburger_15.svg
Normal file
3
assets/icons/hamburger_15.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 3C1.22386 3 1 3.22386 1 3.5C1 3.77614 1.22386 4 1.5 4H13.5C13.7761 4 14 3.77614 14 3.5C14 3.22386 13.7761 3 13.5 3H1.5ZM1 7.5C1 7.22386 1.22386 7 1.5 7H13.5C13.7761 7 14 7.22386 14 7.5C14 7.77614 13.7761 8 13.5 8H1.5C1.22386 8 1 7.77614 1 7.5ZM1 11.5C1 11.2239 1.22386 11 1.5 11H13.5C13.7761 11 14 11.2239 14 11.5C14 11.7761 13.7761 12 13.5 12H1.5C1.22386 12 1 11.7761 1 11.5Z" fill="#CCCAC2"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 552 B |
1
assets/icons/quote_15.svg
Normal file
1
assets/icons/quote_15.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.42503 3.44136C10.0561 3.23654 10.7837 3.2402 11.3792 3.54623C12.7532 4.25224 13.3477 6.07191 12.7946 8C12.5465 8.8649 12.1102 9.70472 11.1861 10.5524C10.262 11.4 8.98034 11.9 8.38571 11.9C8.17269 11.9 8 11.7321 8 11.525C8 11.3179 8.17644 11.15 8.38571 11.15C9.06497 11.15 9.67189 10.7804 10.3906 10.236C10.9406 9.8193 11.3701 9.28633 11.608 8.82191C12.0628 7.93367 12.0782 6.68174 11.3433 6.34901C10.9904 6.73455 10.5295 6.95946 9.97725 6.95946C8.7773 6.95946 8.0701 5.99412 8.10051 5.12009C8.12957 4.28474 8.66032 3.68954 9.42503 3.44136ZM3.42503 3.44136C4.05614 3.23654 4.78366 3.2402 5.37923 3.54623C6.7532 4.25224 7.34766 6.07191 6.79462 8C6.54654 8.8649 6.11019 9.70472 5.1861 10.5524C4.26201 11.4 2.98034 11.9 2.38571 11.9C2.17269 11.9 2 11.7321 2 11.525C2 11.3179 2.17644 11.15 2.38571 11.15C3.06497 11.15 3.67189 10.7804 4.39058 10.236C4.94065 9.8193 5.37014 9.28633 5.60797 8.82191C6.06282 7.93367 6.07821 6.68174 5.3433 6.34901C4.99037 6.73455 4.52948 6.95946 3.97725 6.95946C2.7773 6.95946 2.0701 5.99412 2.10051 5.12009C2.12957 4.28474 2.66032 3.68954 3.42503 3.44136Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
assets/icons/split_message_15.svg
Normal file
1
assets/icons/split_message_15.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.81832 0.68179C7.64258 0.506054 7.35766 0.506054 7.18192 0.68179L5.18192 2.68179C5.00619 2.85753 5.00619 3.14245 5.18192 3.31819C5.35766 3.49392 5.64258 3.49392 5.81832 3.31819L7.05012 2.08638L7.05012 5.50023C7.05012 5.74876 7.25159 5.95023 7.50012 5.95023C7.74865 5.95023 7.95012 5.74876 7.95012 5.50023L7.95012 2.08638L9.18192 3.31819C9.35766 3.49392 9.64258 3.49392 9.81832 3.31819C9.99406 3.14245 9.99406 2.85753 9.81832 2.68179L7.81832 0.68179ZM7.95012 12.9136V9.50023C7.95012 9.2517 7.74865 9.05023 7.50012 9.05023C7.25159 9.05023 7.05012 9.2517 7.05012 9.50023V12.9136L5.81832 11.6818C5.64258 11.5061 5.35766 11.5061 5.18192 11.6818C5.00619 11.8575 5.00619 12.1424 5.18192 12.3182L7.18192 14.3182C7.26632 14.4026 7.38077 14.45 7.50012 14.45C7.61947 14.45 7.73393 14.4026 7.81832 14.3182L9.81832 12.3182C9.99406 12.1424 9.99406 11.8575 9.81832 11.6818C9.64258 11.5061 9.35766 11.5061 9.18192 11.6818L7.95012 12.9136ZM1.49994 7.00017C1.2238 7.00017 0.999939 7.22403 0.999939 7.50017C0.999939 7.77631 1.2238 8.00017 1.49994 8.00017L13.4999 8.00017C13.7761 8.00017 13.9999 7.77631 13.9999 7.50017C13.9999 7.22403 13.7761 7.00017 13.4999 7.00017L1.49994 7.00017Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -40,7 +40,8 @@
|
||||||
"cmd-o": "workspace::Open",
|
"cmd-o": "workspace::Open",
|
||||||
"alt-cmd-o": "projects::OpenRecent",
|
"alt-cmd-o": "projects::OpenRecent",
|
||||||
"ctrl-~": "workspace::NewTerminal",
|
"ctrl-~": "workspace::NewTerminal",
|
||||||
"ctrl-`": "terminal_panel::ToggleFocus"
|
"ctrl-`": "terminal_panel::ToggleFocus",
|
||||||
|
"shift-escape": "workspace::ToggleZoom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -197,9 +198,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AssistantEditor > Editor",
|
"context": "AssistantPanel",
|
||||||
|
"bindings": {
|
||||||
|
"cmd-g": "search::SelectNextMatch",
|
||||||
|
"cmd-shift-g": "search::SelectPrevMatch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "ConversationEditor > Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-enter": "assistant::Assist",
|
"cmd-enter": "assistant::Assist",
|
||||||
|
"cmd-s": "workspace::Save",
|
||||||
"cmd->": "assistant::QuoteSelection",
|
"cmd->": "assistant::QuoteSelection",
|
||||||
"shift-enter": "assistant::Split",
|
"shift-enter": "assistant::Split",
|
||||||
"ctrl-r": "assistant::CycleMessageRole"
|
"ctrl-r": "assistant::CycleMessageRole"
|
||||||
|
@ -234,8 +243,7 @@
|
||||||
"cmd-shift-g": "search::SelectPrevMatch",
|
"cmd-shift-g": "search::SelectPrevMatch",
|
||||||
"alt-cmd-c": "search::ToggleCaseSensitive",
|
"alt-cmd-c": "search::ToggleCaseSensitive",
|
||||||
"alt-cmd-w": "search::ToggleWholeWord",
|
"alt-cmd-w": "search::ToggleWholeWord",
|
||||||
"alt-cmd-r": "search::ToggleRegex",
|
"alt-cmd-r": "search::ToggleRegex"
|
||||||
"shift-escape": "workspace::ToggleZoom"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Bindings from VS Code
|
// Bindings from VS Code
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"context": "Editor && VimControl && !VimWaiting",
|
"context": "Editor && VimControl && !VimWaiting && !menu",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"g": [
|
"g": [
|
||||||
"vim::PushOperator",
|
"vim::PushOperator",
|
||||||
|
@ -58,10 +58,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"%": "vim::Matching",
|
"%": "vim::Matching",
|
||||||
"ctrl-y": [
|
|
||||||
"vim::Scroll",
|
|
||||||
"LineUp"
|
|
||||||
],
|
|
||||||
"f": [
|
"f": [
|
||||||
"vim::PushOperator",
|
"vim::PushOperator",
|
||||||
{
|
{
|
||||||
|
@ -197,33 +193,23 @@
|
||||||
"focus": true
|
"focus": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ctrl-f": [
|
"ctrl-f": "vim::PageDown",
|
||||||
"vim::Scroll",
|
"pagedown": "vim::PageDown",
|
||||||
"PageDown"
|
"ctrl-b": "vim::PageUp",
|
||||||
],
|
"pageup": "vim::PageUp",
|
||||||
"ctrl-b": [
|
"ctrl-d": "vim::ScrollDown",
|
||||||
"vim::Scroll",
|
"ctrl-u": "vim::ScrollUp",
|
||||||
"PageUp"
|
"ctrl-e": "vim::LineDown",
|
||||||
],
|
"ctrl-y": "vim::LineUp",
|
||||||
"ctrl-d": [
|
|
||||||
"vim::Scroll",
|
|
||||||
"HalfPageDown"
|
|
||||||
],
|
|
||||||
"ctrl-u": [
|
|
||||||
"vim::Scroll",
|
|
||||||
"HalfPageUp"
|
|
||||||
],
|
|
||||||
"ctrl-e": [
|
|
||||||
"vim::Scroll",
|
|
||||||
"LineDown"
|
|
||||||
],
|
|
||||||
"r": [
|
"r": [
|
||||||
"vim::PushOperator",
|
"vim::PushOperator",
|
||||||
"Replace"
|
"Replace"
|
||||||
],
|
],
|
||||||
"s": "vim::Substitute",
|
"s": "vim::Substitute",
|
||||||
"> >": "editor::Indent",
|
"> >": "editor::Indent",
|
||||||
"< <": "editor::Outdent"
|
"< <": "editor::Outdent",
|
||||||
|
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||||
|
"ctrl-pageup": "pane::ActivatePrevItem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -240,6 +226,8 @@
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"g": "vim::StartOfDocument",
|
"g": "vim::StartOfDocument",
|
||||||
"h": "editor::Hover",
|
"h": "editor::Hover",
|
||||||
|
"t": "pane::ActivateNextItem",
|
||||||
|
"shift-t": "pane::ActivatePrevItem",
|
||||||
"escape": [
|
"escape": [
|
||||||
"vim::SwitchMode",
|
"vim::SwitchMode",
|
||||||
"Normal"
|
"Normal"
|
||||||
|
|
|
@ -57,37 +57,37 @@
|
||||||
"show_whitespaces": "selection",
|
"show_whitespaces": "selection",
|
||||||
// Scrollbar related settings
|
// Scrollbar related settings
|
||||||
"scrollbar": {
|
"scrollbar": {
|
||||||
// When to show the scrollbar in the editor.
|
// When to show the scrollbar in the editor.
|
||||||
// This setting can take four values:
|
// This setting can take four values:
|
||||||
//
|
//
|
||||||
// 1. Show the scrollbar if there's important information or
|
// 1. Show the scrollbar if there's important information or
|
||||||
// follow the system's configured behavior (default):
|
// follow the system's configured behavior (default):
|
||||||
// "auto"
|
// "auto"
|
||||||
// 2. Match the system's configured behavior:
|
// 2. Match the system's configured behavior:
|
||||||
// "system"
|
// "system"
|
||||||
// 3. Always show the scrollbar:
|
// 3. Always show the scrollbar:
|
||||||
// "always"
|
// "always"
|
||||||
// 4. Never show the scrollbar:
|
// 4. Never show the scrollbar:
|
||||||
// "never"
|
// "never"
|
||||||
"show": "auto",
|
"show": "auto",
|
||||||
// Whether to show git diff indicators in the scrollbar.
|
// Whether to show git diff indicators in the scrollbar.
|
||||||
"git_diff": true
|
"git_diff": true
|
||||||
},
|
},
|
||||||
"project_panel": {
|
"project_panel": {
|
||||||
// Whether to show the git status in the project panel.
|
// Whether to show the git status in the project panel.
|
||||||
"git_status": true,
|
"git_status": true,
|
||||||
// Where to dock project panel. Can be 'left' or 'right'.
|
// Where to dock project panel. Can be 'left' or 'right'.
|
||||||
"dock": "left",
|
"dock": "left",
|
||||||
// Default width of the project panel.
|
// Default width of the project panel.
|
||||||
"default_width": 240
|
"default_width": 240
|
||||||
},
|
},
|
||||||
"assistant": {
|
"assistant": {
|
||||||
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
|
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
|
||||||
"dock": "right",
|
"dock": "right",
|
||||||
// Default width when the assistant is docked to the left or right.
|
// Default width when the assistant is docked to the left or right.
|
||||||
"default_width": 450,
|
"default_width": 640,
|
||||||
// Default height when the assistant is docked to the bottom.
|
// Default height when the assistant is docked to the bottom.
|
||||||
"default_height": 320
|
"default_height": 320
|
||||||
},
|
},
|
||||||
// Whether the screen sharing icon is shown in the os status bar.
|
// Whether the screen sharing icon is shown in the os status bar.
|
||||||
"show_call_status_icon": true,
|
"show_call_status_icon": true,
|
||||||
|
|
|
@ -22,9 +22,10 @@ util = { path = "../util" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
chrono = "0.4"
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
isahc.workspace = true
|
isahc.workspace = true
|
||||||
|
regex.workspace = true
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
@ -33,3 +34,4 @@ tiktoken-rs = "0.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
editor = { path = "../editor", features = ["test-support"] }
|
editor = { path = "../editor", features = ["test-support"] }
|
||||||
|
project = { path = "../project", features = ["test-support"] }
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
pub mod assistant;
|
pub mod assistant;
|
||||||
mod assistant_settings;
|
mod assistant_settings;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
pub use assistant::AssistantPanel;
|
pub use assistant::AssistantPanel;
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
use collections::HashMap;
|
||||||
|
use fs::Fs;
|
||||||
|
use futures::StreamExt;
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display};
|
use std::{
|
||||||
|
cmp::Reverse,
|
||||||
|
fmt::{self, Display},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
use util::paths::CONVERSATIONS_DIR;
|
||||||
|
|
||||||
// Data types for chat completion requests
|
// Data types for chat completion requests
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -14,6 +26,84 @@ struct OpenAIRequest {
|
||||||
stream: bool,
|
stream: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
struct MessageId(usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
struct MessageMetadata {
|
||||||
|
role: Role,
|
||||||
|
sent_at: DateTime<Local>,
|
||||||
|
status: MessageStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
enum MessageStatus {
|
||||||
|
Pending,
|
||||||
|
Done,
|
||||||
|
Error(Arc<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct SavedMessage {
|
||||||
|
id: MessageId,
|
||||||
|
start: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct SavedConversation {
|
||||||
|
zed: String,
|
||||||
|
version: String,
|
||||||
|
text: String,
|
||||||
|
messages: Vec<SavedMessage>,
|
||||||
|
message_metadata: HashMap<MessageId, MessageMetadata>,
|
||||||
|
summary: String,
|
||||||
|
model: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SavedConversation {
|
||||||
|
const VERSION: &'static str = "0.1.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SavedConversationMetadata {
|
||||||
|
title: String,
|
||||||
|
path: PathBuf,
|
||||||
|
mtime: chrono::DateTime<chrono::Local>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SavedConversationMetadata {
|
||||||
|
pub async fn list(fs: Arc<dyn Fs>) -> Result<Vec<Self>> {
|
||||||
|
fs.create_dir(&CONVERSATIONS_DIR).await?;
|
||||||
|
|
||||||
|
let mut paths = fs.read_dir(&CONVERSATIONS_DIR).await?;
|
||||||
|
let mut conversations = Vec::<SavedConversationMetadata>::new();
|
||||||
|
while let Some(path) = paths.next().await {
|
||||||
|
let path = path?;
|
||||||
|
|
||||||
|
let pattern = r" - \d+.zed.json$";
|
||||||
|
let re = Regex::new(pattern).unwrap();
|
||||||
|
|
||||||
|
let metadata = fs.metadata(&path).await?;
|
||||||
|
if let Some((file_name, metadata)) = path
|
||||||
|
.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.zip(metadata)
|
||||||
|
{
|
||||||
|
let title = re.replace(file_name, "");
|
||||||
|
conversations.push(Self {
|
||||||
|
title: title.into_owned(),
|
||||||
|
path,
|
||||||
|
mtime: metadata.mtime.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conversations.sort_unstable_by_key(|conversation| Reverse(conversation.mtime));
|
||||||
|
|
||||||
|
Ok(conversations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
struct RequestMessage {
|
struct RequestMessage {
|
||||||
role: Role,
|
role: Role,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,4 @@
|
||||||
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
|
||||||
use gpui::{executor::Background, serde_json, AppContext, Task};
|
use gpui::{executor::Background, serde_json, AppContext, Task};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -8,7 +7,6 @@ use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use util::http::HttpClient;
|
use util::http::HttpClient;
|
||||||
use util::{channel::ReleaseChannel, TryFutureExt};
|
use util::{channel::ReleaseChannel, TryFutureExt};
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub struct Telemetry {
|
pub struct Telemetry {
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<dyn HttpClient>,
|
||||||
|
@ -120,39 +118,15 @@ impl Telemetry {
|
||||||
Some(self.state.lock().log_file.as_ref()?.path().to_path_buf())
|
Some(self.state.lock().log_file.as_ref()?.path().to_path_buf())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(self: &Arc<Self>) {
|
pub fn start(self: &Arc<Self>, installation_id: Option<String>) {
|
||||||
let this = self.clone();
|
let mut state = self.state.lock();
|
||||||
self.executor
|
state.installation_id = installation_id.map(|id| id.into());
|
||||||
.spawn(
|
let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
|
||||||
async move {
|
drop(state);
|
||||||
let installation_id =
|
|
||||||
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp("device_id") {
|
|
||||||
installation_id
|
|
||||||
} else {
|
|
||||||
let installation_id = Uuid::new_v4().to_string();
|
|
||||||
KEY_VALUE_STORE
|
|
||||||
.write_kvp("device_id".to_string(), installation_id.clone())
|
|
||||||
.await?;
|
|
||||||
installation_id
|
|
||||||
};
|
|
||||||
|
|
||||||
let installation_id: Arc<str> = installation_id.into();
|
if has_clickhouse_events {
|
||||||
let mut state = this.state.lock();
|
self.flush_clickhouse_events();
|
||||||
state.installation_id = Some(installation_id.clone());
|
}
|
||||||
|
|
||||||
let has_clickhouse_events = !state.clickhouse_events_queue.is_empty();
|
|
||||||
|
|
||||||
drop(state);
|
|
||||||
|
|
||||||
if has_clickhouse_events {
|
|
||||||
this.flush_clickhouse_events();
|
|
||||||
}
|
|
||||||
|
|
||||||
anyhow::Ok(())
|
|
||||||
}
|
|
||||||
.log_err(),
|
|
||||||
)
|
|
||||||
.detach();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method takes the entire TelemetrySettings struct in order to force client code
|
/// This method takes the entire TelemetrySettings struct in order to force client code
|
||||||
|
|
|
@ -7641,8 +7641,14 @@ impl View for Editor {
|
||||||
keymap.add_identifier("renaming");
|
keymap.add_identifier("renaming");
|
||||||
}
|
}
|
||||||
match self.context_menu.as_ref() {
|
match self.context_menu.as_ref() {
|
||||||
Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
|
Some(ContextMenu::Completions(_)) => {
|
||||||
Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
|
keymap.add_identifier("menu");
|
||||||
|
keymap.add_identifier("showing_completions")
|
||||||
|
}
|
||||||
|
Some(ContextMenu::CodeActions(_)) => {
|
||||||
|
keymap.add_identifier("menu");
|
||||||
|
keymap.add_identifier("showing_code_actions")
|
||||||
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
for layer in self.keymap_context_layers.values() {
|
for layer in self.keymap_context_layers.values() {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
scroll::scroll_amount::ScrollAmount,
|
||||||
test::{
|
test::{
|
||||||
assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
|
assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
|
||||||
editor_test_context::EditorTestContext, select_ranges,
|
editor_test_context::EditorTestContext, select_ranges,
|
||||||
|
@ -1359,6 +1360,43 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppCon
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
|
||||||
|
cx.simulate_window_resize(cx.window_id, vec2f(1000., 4. * line_height + 0.5));
|
||||||
|
|
||||||
|
cx.set_state(
|
||||||
|
&r#"ˇone
|
||||||
|
two
|
||||||
|
three
|
||||||
|
four
|
||||||
|
five
|
||||||
|
six
|
||||||
|
seven
|
||||||
|
eight
|
||||||
|
nine
|
||||||
|
ten
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.));
|
||||||
|
editor.scroll_screen(&ScrollAmount::Page(1.), cx);
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
|
||||||
|
editor.scroll_screen(&ScrollAmount::Page(1.), cx);
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 6.));
|
||||||
|
editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
|
||||||
|
|
||||||
|
editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 2.));
|
||||||
|
editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||||
init_test(cx, |_| {});
|
init_test(cx, |_| {});
|
||||||
|
|
|
@ -368,7 +368,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let cur_position = self.scroll_position(cx);
|
let cur_position = self.scroll_position(cx);
|
||||||
let new_pos = cur_position + vec2f(0., amount.lines(self) - 1.);
|
let new_pos = cur_position + vec2f(0., amount.lines(self));
|
||||||
self.set_scroll_position(new_pos, cx);
|
self.set_scroll_position(new_pos, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,22 +27,22 @@ pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(Editor::scroll_cursor_center);
|
cx.add_action(Editor::scroll_cursor_center);
|
||||||
cx.add_action(Editor::scroll_cursor_bottom);
|
cx.add_action(Editor::scroll_cursor_bottom);
|
||||||
cx.add_action(|this: &mut Editor, _: &LineDown, cx| {
|
cx.add_action(|this: &mut Editor, _: &LineDown, cx| {
|
||||||
this.scroll_screen(&ScrollAmount::LineDown, cx)
|
this.scroll_screen(&ScrollAmount::Line(1.), cx)
|
||||||
});
|
});
|
||||||
cx.add_action(|this: &mut Editor, _: &LineUp, cx| {
|
cx.add_action(|this: &mut Editor, _: &LineUp, cx| {
|
||||||
this.scroll_screen(&ScrollAmount::LineUp, cx)
|
this.scroll_screen(&ScrollAmount::Line(-1.), cx)
|
||||||
});
|
});
|
||||||
cx.add_action(|this: &mut Editor, _: &HalfPageDown, cx| {
|
cx.add_action(|this: &mut Editor, _: &HalfPageDown, cx| {
|
||||||
this.scroll_screen(&ScrollAmount::HalfPageDown, cx)
|
this.scroll_screen(&ScrollAmount::Page(0.5), cx)
|
||||||
});
|
});
|
||||||
cx.add_action(|this: &mut Editor, _: &HalfPageUp, cx| {
|
cx.add_action(|this: &mut Editor, _: &HalfPageUp, cx| {
|
||||||
this.scroll_screen(&ScrollAmount::HalfPageUp, cx)
|
this.scroll_screen(&ScrollAmount::Page(-0.5), cx)
|
||||||
});
|
});
|
||||||
cx.add_action(|this: &mut Editor, _: &PageDown, cx| {
|
cx.add_action(|this: &mut Editor, _: &PageDown, cx| {
|
||||||
this.scroll_screen(&ScrollAmount::PageDown, cx)
|
this.scroll_screen(&ScrollAmount::Page(1.), cx)
|
||||||
});
|
});
|
||||||
cx.add_action(|this: &mut Editor, _: &PageUp, cx| {
|
cx.add_action(|this: &mut Editor, _: &PageUp, cx| {
|
||||||
this.scroll_screen(&ScrollAmount::PageUp, cx)
|
this.scroll_screen(&ScrollAmount::Page(-1.), cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,10 @@ use crate::Editor;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Deserialize)]
|
#[derive(Clone, PartialEq, Deserialize)]
|
||||||
pub enum ScrollAmount {
|
pub enum ScrollAmount {
|
||||||
LineUp,
|
// Scroll N lines (positive is towards the end of the document)
|
||||||
LineDown,
|
Line(f32),
|
||||||
HalfPageUp,
|
// Scroll N pages (positive is towards the end of the document)
|
||||||
HalfPageDown,
|
Page(f32),
|
||||||
PageUp,
|
|
||||||
PageDown,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollAmount {
|
impl ScrollAmount {
|
||||||
|
@ -24,10 +22,10 @@ impl ScrollAmount {
|
||||||
let context_menu = editor.context_menu.as_mut()?;
|
let context_menu = editor.context_menu.as_mut()?;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::LineDown | Self::HalfPageDown => context_menu.select_next(cx),
|
Self::Line(c) if *c > 0. => context_menu.select_next(cx),
|
||||||
Self::LineUp | Self::HalfPageUp => context_menu.select_prev(cx),
|
Self::Line(_) => context_menu.select_prev(cx),
|
||||||
Self::PageDown => context_menu.select_last(cx),
|
Self::Page(c) if *c > 0. => context_menu.select_last(cx),
|
||||||
Self::PageUp => context_menu.select_first(cx),
|
Self::Page(_) => context_menu.select_first(cx),
|
||||||
}
|
}
|
||||||
.then_some(())
|
.then_some(())
|
||||||
})
|
})
|
||||||
|
@ -36,13 +34,13 @@ impl ScrollAmount {
|
||||||
|
|
||||||
pub fn lines(&self, editor: &mut Editor) -> f32 {
|
pub fn lines(&self, editor: &mut Editor) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Self::LineDown => 1.,
|
Self::Line(count) => *count,
|
||||||
Self::LineUp => -1.,
|
Self::Page(count) => editor
|
||||||
Self::HalfPageDown => editor.visible_line_count().map(|l| l / 2.).unwrap_or(1.),
|
.visible_line_count()
|
||||||
Self::HalfPageUp => -editor.visible_line_count().map(|l| l / 2.).unwrap_or(1.),
|
// subtract one to leave an anchor line
|
||||||
// Minus 1. here so that there is a pivot line that stays on the screen
|
// round towards zero (so page-up and page-down are symmetric)
|
||||||
Self::PageDown => editor.visible_line_count().unwrap_or(1.) - 1.,
|
.map(|l| ((l - 1.) * count).trunc())
|
||||||
Self::PageUp => -editor.visible_line_count().unwrap_or(1.) - 1.,
|
.unwrap_or(0.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,6 +165,7 @@ impl<V: View> Element<V> for Label {
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut ViewContext<V>,
|
||||||
) -> Self::PaintState {
|
) -> Self::PaintState {
|
||||||
|
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||||
line.paint(
|
line.paint(
|
||||||
scene,
|
scene,
|
||||||
bounds.origin(),
|
bounds.origin(),
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use std::{borrow::Cow, ops::Range};
|
use super::constrain_size_preserving_aspect_ratio;
|
||||||
|
use crate::json::ToJson;
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
geometry::{
|
geometry::{
|
||||||
|
@ -10,6 +8,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use serde_json::json;
|
||||||
|
use std::{borrow::Cow, ops::Range};
|
||||||
|
|
||||||
pub struct Svg {
|
pub struct Svg {
|
||||||
path: Cow<'static, str>,
|
path: Cow<'static, str>,
|
||||||
|
@ -24,6 +26,14 @@ impl Svg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_style<V: View>(style: SvgStyle) -> impl Element<V> {
|
||||||
|
Self::new(style.asset)
|
||||||
|
.with_color(style.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.dimensions.width)
|
||||||
|
.with_height(style.dimensions.height)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_color(mut self, color: Color) -> Self {
|
pub fn with_color(mut self, color: Color) -> Self {
|
||||||
self.color = color;
|
self.color = color;
|
||||||
self
|
self
|
||||||
|
@ -105,9 +115,24 @@ impl<V: View> Element<V> for Svg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::json::ToJson;
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
|
pub struct SvgStyle {
|
||||||
|
pub color: Color,
|
||||||
|
pub asset: String,
|
||||||
|
pub dimensions: Dimensions,
|
||||||
|
}
|
||||||
|
|
||||||
use super::constrain_size_preserving_aspect_ratio;
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
|
pub struct Dimensions {
|
||||||
|
pub width: f32,
|
||||||
|
pub height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dimensions {
|
||||||
|
pub fn to_vec(&self) -> Vector2F {
|
||||||
|
vec2f(self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_usvg_rect(rect: usvg::Rect) -> RectF {
|
fn from_usvg_rect(rect: usvg::Rect) -> RectF {
|
||||||
RectF::new(
|
RectF::new(
|
||||||
|
|
|
@ -153,6 +153,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
OpenedEntry {
|
OpenedEntry {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
|
|
|
@ -259,7 +259,11 @@ impl BufferSearchBar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
|
pub fn is_dismissed(&self) -> bool {
|
||||||
|
self.dismissed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext<Self>) {
|
||||||
self.dismissed = true;
|
self.dismissed = true;
|
||||||
for searchable_item in self.seachable_items_with_matches.keys() {
|
for searchable_item in self.seachable_items_with_matches.keys() {
|
||||||
if let Some(searchable_item) =
|
if let Some(searchable_item) =
|
||||||
|
@ -275,7 +279,7 @@ impl BufferSearchBar {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
|
pub fn show(&mut self, focus: bool, suggest_query: bool, cx: &mut ViewContext<Self>) -> bool {
|
||||||
let searchable_item = if let Some(searchable_item) = &self.active_searchable_item {
|
let searchable_item = if let Some(searchable_item) = &self.active_searchable_item {
|
||||||
SearchableItemHandle::boxed_clone(searchable_item.as_ref())
|
SearchableItemHandle::boxed_clone(searchable_item.as_ref())
|
||||||
} else {
|
} else {
|
||||||
|
@ -484,7 +488,7 @@ impl BufferSearchBar {
|
||||||
self.select_match(Direction::Prev, cx);
|
self.select_match(Direction::Prev, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
pub fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(index) = self.active_match_index {
|
if let Some(index) = self.active_match_index {
|
||||||
if let Some(searchable_item) = self.active_searchable_item.as_ref() {
|
if let Some(searchable_item) = self.active_searchable_item.as_ref() {
|
||||||
if let Some(matches) = self
|
if let Some(matches) = self
|
||||||
|
|
|
@ -25,6 +25,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(TerminalPanel::new_terminal);
|
cx.add_action(TerminalPanel::new_terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Close,
|
Close,
|
||||||
DockPositionChanged,
|
DockPositionChanged,
|
||||||
|
|
|
@ -4,7 +4,7 @@ pub mod ui;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
|
elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, SvgStyle, TooltipStyle},
|
||||||
fonts::{HighlightStyle, TextStyle},
|
fonts::{HighlightStyle, TextStyle},
|
||||||
platform, AppContext, AssetSource, Border, MouseState,
|
platform, AppContext, AssetSource, Border, MouseState,
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@ use serde::{de::DeserializeOwned, Deserialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle, SvgStyle};
|
use ui::{ButtonStyle, CheckboxStyle, IconStyle, ModalStyle};
|
||||||
|
|
||||||
pub use theme_registry::*;
|
pub use theme_registry::*;
|
||||||
pub use theme_settings::*;
|
pub use theme_settings::*;
|
||||||
|
@ -993,18 +993,33 @@ pub struct TerminalStyle {
|
||||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct AssistantStyle {
|
pub struct AssistantStyle {
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub header: ContainerStyle,
|
pub hamburger_button: Interactive<IconStyle>,
|
||||||
|
pub split_button: Interactive<IconStyle>,
|
||||||
|
pub assist_button: Interactive<IconStyle>,
|
||||||
|
pub quote_button: Interactive<IconStyle>,
|
||||||
|
pub zoom_in_button: Interactive<IconStyle>,
|
||||||
|
pub zoom_out_button: Interactive<IconStyle>,
|
||||||
|
pub plus_button: Interactive<IconStyle>,
|
||||||
|
pub title: ContainedText,
|
||||||
|
pub message_header: ContainerStyle,
|
||||||
pub sent_at: ContainedText,
|
pub sent_at: ContainedText,
|
||||||
pub user_sender: Interactive<ContainedText>,
|
pub user_sender: Interactive<ContainedText>,
|
||||||
pub assistant_sender: Interactive<ContainedText>,
|
pub assistant_sender: Interactive<ContainedText>,
|
||||||
pub system_sender: Interactive<ContainedText>,
|
pub system_sender: Interactive<ContainedText>,
|
||||||
pub model_info_container: ContainerStyle,
|
|
||||||
pub model: Interactive<ContainedText>,
|
pub model: Interactive<ContainedText>,
|
||||||
pub remaining_tokens: ContainedText,
|
pub remaining_tokens: ContainedText,
|
||||||
pub no_remaining_tokens: ContainedText,
|
pub no_remaining_tokens: ContainedText,
|
||||||
pub error_icon: Icon,
|
pub error_icon: Icon,
|
||||||
pub api_key_editor: FieldEditor,
|
pub api_key_editor: FieldEditor,
|
||||||
pub api_key_prompt: ContainedText,
|
pub api_key_prompt: ContainedText,
|
||||||
|
pub saved_conversation: SavedConversation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
|
pub struct SavedConversation {
|
||||||
|
pub container: Interactive<ContainerStyle>,
|
||||||
|
pub saved_at: ContainedText,
|
||||||
|
pub title: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
|
||||||
elements::{
|
elements::{
|
||||||
ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label,
|
ConstrainedBox, Container, ContainerStyle, Dimensions, Empty, Flex, KeystrokeLabel, Label,
|
||||||
MouseEventHandler, ParentElement, Stack, Svg,
|
MouseEventHandler, ParentElement, Stack, Svg, SvgStyle,
|
||||||
},
|
},
|
||||||
fonts::TextStyle,
|
fonts::TextStyle,
|
||||||
geometry::vector::{vec2f, Vector2F},
|
geometry::vector::Vector2F,
|
||||||
platform,
|
platform,
|
||||||
platform::MouseButton,
|
platform::MouseButton,
|
||||||
scene::MouseClick,
|
scene::MouseClick,
|
||||||
|
@ -94,25 +93,6 @@ where
|
||||||
.with_cursor_style(platform::CursorStyle::PointingHand)
|
.with_cursor_style(platform::CursorStyle::PointingHand)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
|
||||||
pub struct SvgStyle {
|
|
||||||
pub color: Color,
|
|
||||||
pub asset: String,
|
|
||||||
pub dimensions: Dimensions,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
|
||||||
pub struct Dimensions {
|
|
||||||
pub width: f32,
|
|
||||||
pub height: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dimensions {
|
|
||||||
pub fn to_vec(&self) -> Vector2F {
|
|
||||||
vec2f(self.width, self.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
||||||
Svg::new(style.asset.clone())
|
Svg::new(style.asset.clone())
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
|
@ -123,8 +103,8 @@ pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
#[derive(Clone, Deserialize, Default, JsonSchema)]
|
||||||
pub struct IconStyle {
|
pub struct IconStyle {
|
||||||
icon: SvgStyle,
|
pub icon: SvgStyle,
|
||||||
container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
|
pub fn icon<V: View>(style: &IconStyle) -> Container<V> {
|
||||||
|
|
|
@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory");
|
pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory");
|
||||||
pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed");
|
pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed");
|
||||||
|
pub static ref CONVERSATIONS_DIR: PathBuf = HOME.join(".config/zed/conversations");
|
||||||
pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
|
pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
|
||||||
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
|
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
|
||||||
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");
|
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
mod change;
|
mod change;
|
||||||
mod delete;
|
mod delete;
|
||||||
|
mod scroll;
|
||||||
mod substitute;
|
mod substitute;
|
||||||
mod yank;
|
mod yank;
|
||||||
|
|
||||||
use std::{borrow::Cow, cmp::Ordering, sync::Arc};
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
motion::Motion,
|
motion::Motion,
|
||||||
|
@ -13,14 +14,12 @@ use crate::{
|
||||||
};
|
};
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use editor::{
|
use editor::{
|
||||||
display_map::ToDisplayPoint,
|
display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Anchor, Bias, ClipboardSelection,
|
||||||
scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
|
DisplayPoint,
|
||||||
Anchor, Bias, ClipboardSelection, DisplayPoint, Editor,
|
|
||||||
};
|
};
|
||||||
use gpui::{actions, impl_actions, AppContext, ViewContext, WindowContext};
|
use gpui::{actions, AppContext, ViewContext, WindowContext};
|
||||||
use language::{AutoindentMode, Point, SelectionGoal};
|
use language::{AutoindentMode, Point, SelectionGoal};
|
||||||
use log::error;
|
use log::error;
|
||||||
use serde::Deserialize;
|
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
|
@ -30,9 +29,6 @@ use self::{
|
||||||
yank::{yank_motion, yank_object},
|
yank::{yank_motion, yank_object},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Deserialize)]
|
|
||||||
struct Scroll(ScrollAmount);
|
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
vim,
|
vim,
|
||||||
[
|
[
|
||||||
|
@ -51,8 +47,6 @@ actions!(
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
impl_actions!(vim, [Scroll]);
|
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(insert_after);
|
cx.add_action(insert_after);
|
||||||
cx.add_action(insert_first_non_whitespace);
|
cx.add_action(insert_first_non_whitespace);
|
||||||
|
@ -90,13 +84,8 @@ pub fn init(cx: &mut AppContext) {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
cx.add_action(paste);
|
cx.add_action(paste);
|
||||||
cx.add_action(|_: &mut Workspace, Scroll(amount): &Scroll, cx| {
|
|
||||||
Vim::update(cx, |vim, cx| {
|
scroll::init(cx);
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
|
||||||
scroll(editor, amount, cx);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normal_motion(
|
pub fn normal_motion(
|
||||||
|
@ -393,46 +382,6 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext<Workspace>) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll(editor: &mut Editor, amount: &ScrollAmount, cx: &mut ViewContext<Editor>) {
|
|
||||||
let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
|
|
||||||
editor.scroll_screen(amount, cx);
|
|
||||||
if should_move_cursor {
|
|
||||||
let selection_ordering = editor.newest_selection_on_screen(cx);
|
|
||||||
if selection_ordering.is_eq() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
|
|
||||||
visible_rows as u32
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
|
|
||||||
let top_anchor = editor.scroll_manager.anchor().anchor;
|
|
||||||
|
|
||||||
editor.change_selections(None, cx, |s| {
|
|
||||||
s.replace_cursors_with(|snapshot| {
|
|
||||||
let mut new_point = top_anchor.to_display_point(&snapshot);
|
|
||||||
|
|
||||||
match selection_ordering {
|
|
||||||
Ordering::Less => {
|
|
||||||
*new_point.row_mut() += scroll_margin_rows;
|
|
||||||
new_point = snapshot.clip_point(new_point, Bias::Right);
|
|
||||||
}
|
|
||||||
Ordering::Greater => {
|
|
||||||
*new_point.row_mut() += visible_rows - scroll_margin_rows as u32;
|
|
||||||
new_point = snapshot.clip_point(new_point, Bias::Left);
|
|
||||||
}
|
|
||||||
Ordering::Equal => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
vec![new_point]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
|
pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
|
||||||
Vim::update(cx, |vim, cx| {
|
Vim::update(cx, |vim, cx| {
|
||||||
vim.update_active_editor(cx, |editor, cx| {
|
vim.update_active_editor(cx, |editor, cx| {
|
||||||
|
|
120
crates/vim/src/normal/scroll.rs
Normal file
120
crates/vim/src/normal/scroll.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use crate::Vim;
|
||||||
|
use editor::{display_map::ToDisplayPoint, scroll::scroll_amount::ScrollAmount, Editor};
|
||||||
|
use gpui::{actions, AppContext, ViewContext};
|
||||||
|
use language::Bias;
|
||||||
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
actions!(
|
||||||
|
vim,
|
||||||
|
[LineUp, LineDown, ScrollUp, ScrollDown, PageUp, PageDown,]
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn init(cx: &mut AppContext) {
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &LineDown, cx| {
|
||||||
|
scroll(cx, |c| ScrollAmount::Line(c.unwrap_or(1.)))
|
||||||
|
});
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &LineUp, cx| {
|
||||||
|
scroll(cx, |c| ScrollAmount::Line(-c.unwrap_or(1.)))
|
||||||
|
});
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &PageDown, cx| {
|
||||||
|
scroll(cx, |c| ScrollAmount::Page(c.unwrap_or(1.)))
|
||||||
|
});
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &PageUp, cx| {
|
||||||
|
scroll(cx, |c| ScrollAmount::Page(-c.unwrap_or(1.)))
|
||||||
|
});
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &ScrollDown, cx| {
|
||||||
|
scroll(cx, |c| {
|
||||||
|
if let Some(c) = c {
|
||||||
|
ScrollAmount::Line(c)
|
||||||
|
} else {
|
||||||
|
ScrollAmount::Page(0.5)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
cx.add_action(|_: &mut Workspace, _: &ScrollUp, cx| {
|
||||||
|
scroll(cx, |c| {
|
||||||
|
if let Some(c) = c {
|
||||||
|
ScrollAmount::Line(-c)
|
||||||
|
} else {
|
||||||
|
ScrollAmount::Page(-0.5)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll(cx: &mut ViewContext<Workspace>, by: fn(c: Option<f32>) -> ScrollAmount) {
|
||||||
|
Vim::update(cx, |vim, cx| {
|
||||||
|
let amount = by(vim.pop_number_operator(cx).map(|c| c as f32));
|
||||||
|
vim.update_active_editor(cx, |editor, cx| scroll_editor(editor, &amount, cx));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_editor(editor: &mut Editor, amount: &ScrollAmount, cx: &mut ViewContext<Editor>) {
|
||||||
|
let should_move_cursor = editor.newest_selection_on_screen(cx).is_eq();
|
||||||
|
editor.scroll_screen(amount, cx);
|
||||||
|
if should_move_cursor {
|
||||||
|
let selection_ordering = editor.newest_selection_on_screen(cx);
|
||||||
|
if selection_ordering.is_eq() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
|
||||||
|
visible_rows as u32
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let top_anchor = editor.scroll_manager.anchor().anchor;
|
||||||
|
|
||||||
|
editor.change_selections(None, cx, |s| {
|
||||||
|
s.replace_cursors_with(|snapshot| {
|
||||||
|
let mut new_point = top_anchor.to_display_point(&snapshot);
|
||||||
|
|
||||||
|
match selection_ordering {
|
||||||
|
Ordering::Less => {
|
||||||
|
new_point = snapshot.clip_point(new_point, Bias::Right);
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
*new_point.row_mut() += visible_rows - 1;
|
||||||
|
new_point = snapshot.clip_point(new_point, Bias::Left);
|
||||||
|
}
|
||||||
|
Ordering::Equal => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![new_point]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{state::Mode, test::VimTestContext};
|
||||||
|
use gpui::geometry::vector::vec2f;
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_scroll(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = VimTestContext::new(cx, true).await;
|
||||||
|
|
||||||
|
cx.set_state(indoc! {"ˇa\nb\nc\nd\ne\n"}, Mode::Normal);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 0.))
|
||||||
|
});
|
||||||
|
cx.simulate_keystrokes(["ctrl-e"]);
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 1.))
|
||||||
|
});
|
||||||
|
cx.simulate_keystrokes(["2", "ctrl-e"]);
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 3.))
|
||||||
|
});
|
||||||
|
cx.simulate_keystrokes(["ctrl-y"]);
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
assert_eq!(editor.snapshot(cx).scroll_position(), vec2f(0., 2.))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -249,7 +249,7 @@ impl Dock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
|
pub(crate) fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
|
||||||
let subscriptions = [
|
let subscriptions = [
|
||||||
cx.observe(&panel, |_, _, cx| cx.notify()),
|
cx.observe(&panel, |_, _, cx| cx.notify()),
|
||||||
cx.subscribe(&panel, |this, panel, event, cx| {
|
cx.subscribe(&panel, |this, panel, event, cx| {
|
||||||
|
@ -605,6 +605,7 @@ pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui::{ViewContext, WindowContext};
|
use gpui::{ViewContext, WindowContext};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TestPanelEvent {
|
pub enum TestPanelEvent {
|
||||||
PositionChanged,
|
PositionChanged,
|
||||||
Activated,
|
Activated,
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
mod dragged_item_receiver;
|
mod dragged_item_receiver;
|
||||||
|
|
||||||
use super::{ItemHandle, SplitDirection};
|
use super::{ItemHandle, SplitDirection};
|
||||||
|
pub use crate::toolbar::Toolbar;
|
||||||
use crate::{
|
use crate::{
|
||||||
item::WeakItemHandle, notify_of_new_dock, toolbar::Toolbar, AutosaveSetting, Item,
|
item::WeakItemHandle, notify_of_new_dock, AutosaveSetting, Item, NewCenterTerminal, NewFile,
|
||||||
NewCenterTerminal, NewFile, NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
|
NewSearch, ToggleZoom, Workspace, WorkspaceSettings,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
|
@ -250,7 +251,7 @@ impl Pane {
|
||||||
pane: handle.clone(),
|
pane: handle.clone(),
|
||||||
next_timestamp,
|
next_timestamp,
|
||||||
}))),
|
}))),
|
||||||
toolbar: cx.add_view(|_| Toolbar::new(handle)),
|
toolbar: cx.add_view(|_| Toolbar::new(Some(handle))),
|
||||||
tab_bar_context_menu: TabBarContextMenu {
|
tab_bar_context_menu: TabBarContextMenu {
|
||||||
kind: TabBarContextMenuKind::New,
|
kind: TabBarContextMenuKind::New,
|
||||||
handle: context_menu,
|
handle: context_menu,
|
||||||
|
@ -1112,7 +1113,7 @@ impl Pane {
|
||||||
.get(self.active_item_index)
|
.get(self.active_item_index)
|
||||||
.map(|item| item.as_ref());
|
.map(|item| item.as_ref());
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.set_active_pane_item(active_item, cx);
|
toolbar.set_active_item(active_item, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1602,7 +1603,7 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.pane_focus_update(true, cx);
|
toolbar.focus_changed(true, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(active_item) = self.active_item() {
|
if let Some(active_item) = self.active_item() {
|
||||||
|
@ -1631,7 +1632,7 @@ impl View for Pane {
|
||||||
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
self.has_focus = false;
|
self.has_focus = false;
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.pane_focus_update(false, cx);
|
toolbar.focus_changed(false, cx);
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ trait ToolbarItemViewHandle {
|
||||||
active_pane_item: Option<&dyn ItemHandle>,
|
active_pane_item: Option<&dyn ItemHandle>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> ToolbarItemLocation;
|
) -> ToolbarItemLocation;
|
||||||
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext);
|
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext);
|
||||||
fn row_count(&self, cx: &WindowContext) -> usize;
|
fn row_count(&self, cx: &WindowContext) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,10 +51,10 @@ pub enum ToolbarItemLocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Toolbar {
|
pub struct Toolbar {
|
||||||
active_pane_item: Option<Box<dyn ItemHandle>>,
|
active_item: Option<Box<dyn ItemHandle>>,
|
||||||
hidden: bool,
|
hidden: bool,
|
||||||
can_navigate: bool,
|
can_navigate: bool,
|
||||||
pane: WeakViewHandle<Pane>,
|
pane: Option<WeakViewHandle<Pane>>,
|
||||||
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
|
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ impl View for Toolbar {
|
||||||
let pane = self.pane.clone();
|
let pane = self.pane.clone();
|
||||||
let mut enable_go_backward = false;
|
let mut enable_go_backward = false;
|
||||||
let mut enable_go_forward = false;
|
let mut enable_go_forward = false;
|
||||||
if let Some(pane) = pane.upgrade(cx) {
|
if let Some(pane) = pane.and_then(|pane| pane.upgrade(cx)) {
|
||||||
let pane = pane.read(cx);
|
let pane = pane.read(cx);
|
||||||
enable_go_backward = pane.can_navigate_backward();
|
enable_go_backward = pane.can_navigate_backward();
|
||||||
enable_go_forward = pane.can_navigate_forward();
|
enable_go_forward = pane.can_navigate_forward();
|
||||||
|
@ -143,19 +143,17 @@ impl View for Toolbar {
|
||||||
enable_go_backward,
|
enable_go_backward,
|
||||||
spacing,
|
spacing,
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
|
||||||
move |toolbar, cx| {
|
move |toolbar, cx| {
|
||||||
if let Some(workspace) = toolbar
|
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
|
||||||
.pane
|
|
||||||
.upgrade(cx)
|
|
||||||
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
|
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
|
||||||
cx.window_context().defer(move |cx| {
|
let pane = pane.downgrade();
|
||||||
workspace.update(cx, |workspace, cx| {
|
cx.window_context().defer(move |cx| {
|
||||||
workspace.go_back(pane.clone(), cx).detach_and_log_err(cx);
|
workspace.update(cx, |workspace, cx| {
|
||||||
});
|
workspace.go_back(pane, cx).detach_and_log_err(cx);
|
||||||
})
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -171,21 +169,17 @@ impl View for Toolbar {
|
||||||
enable_go_forward,
|
enable_go_forward,
|
||||||
spacing,
|
spacing,
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
|
||||||
move |toolbar, cx| {
|
move |toolbar, cx| {
|
||||||
if let Some(workspace) = toolbar
|
if let Some(pane) = toolbar.pane.as_ref().and_then(|pane| pane.upgrade(cx))
|
||||||
.pane
|
|
||||||
.upgrade(cx)
|
|
||||||
.and_then(|pane| pane.read(cx).workspace().upgrade(cx))
|
|
||||||
{
|
{
|
||||||
let pane = pane.clone();
|
if let Some(workspace) = pane.read(cx).workspace().upgrade(cx) {
|
||||||
cx.window_context().defer(move |cx| {
|
let pane = pane.downgrade();
|
||||||
workspace.update(cx, |workspace, cx| {
|
cx.window_context().defer(move |cx| {
|
||||||
workspace
|
workspace.update(cx, |workspace, cx| {
|
||||||
.go_forward(pane.clone(), cx)
|
workspace.go_forward(pane, cx).detach_and_log_err(cx);
|
||||||
.detach_and_log_err(cx);
|
});
|
||||||
});
|
})
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -269,9 +263,9 @@ fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Toolbar {
|
impl Toolbar {
|
||||||
pub fn new(pane: WeakViewHandle<Pane>) -> Self {
|
pub fn new(pane: Option<WeakViewHandle<Pane>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
active_pane_item: None,
|
active_item: None,
|
||||||
pane,
|
pane,
|
||||||
items: Default::default(),
|
items: Default::default(),
|
||||||
hidden: false,
|
hidden: false,
|
||||||
|
@ -288,7 +282,7 @@ impl Toolbar {
|
||||||
where
|
where
|
||||||
T: 'static + ToolbarItemView,
|
T: 'static + ToolbarItemView,
|
||||||
{
|
{
|
||||||
let location = item.set_active_pane_item(self.active_pane_item.as_deref(), cx);
|
let location = item.set_active_pane_item(self.active_item.as_deref(), cx);
|
||||||
cx.subscribe(&item, |this, item, event, cx| {
|
cx.subscribe(&item, |this, item, event, cx| {
|
||||||
if let Some((_, current_location)) =
|
if let Some((_, current_location)) =
|
||||||
this.items.iter_mut().find(|(i, _)| i.id() == item.id())
|
this.items.iter_mut().find(|(i, _)| i.id() == item.id())
|
||||||
|
@ -307,20 +301,16 @@ impl Toolbar {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_active_pane_item(
|
pub fn set_active_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||||
&mut self,
|
self.active_item = item.map(|item| item.boxed_clone());
|
||||||
pane_item: Option<&dyn ItemHandle>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) {
|
|
||||||
self.active_pane_item = pane_item.map(|item| item.boxed_clone());
|
|
||||||
self.hidden = self
|
self.hidden = self
|
||||||
.active_pane_item
|
.active_item
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|item| !item.show_toolbar(cx))
|
.map(|item| !item.show_toolbar(cx))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
for (toolbar_item, current_location) in self.items.iter_mut() {
|
for (toolbar_item, current_location) in self.items.iter_mut() {
|
||||||
let new_location = toolbar_item.set_active_pane_item(pane_item, cx);
|
let new_location = toolbar_item.set_active_pane_item(item, cx);
|
||||||
if new_location != *current_location {
|
if new_location != *current_location {
|
||||||
*current_location = new_location;
|
*current_location = new_location;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -328,9 +318,9 @@ impl Toolbar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut ViewContext<Self>) {
|
pub fn focus_changed(&mut self, focused: bool, cx: &mut ViewContext<Self>) {
|
||||||
for (toolbar_item, _) in self.items.iter_mut() {
|
for (toolbar_item, _) in self.items.iter_mut() {
|
||||||
toolbar_item.pane_focus_update(pane_focused, cx);
|
toolbar_item.focus_changed(focused, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +354,7 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext) {
|
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
|
||||||
self.update(cx, |this, cx| {
|
self.update(cx, |this, cx| {
|
||||||
this.pane_focus_update(pane_focused, cx);
|
this.pane_focus_update(pane_focused, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
|
@ -861,7 +861,10 @@ impl Workspace {
|
||||||
&self.right_dock
|
&self.right_dock
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
|
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>)
|
||||||
|
where
|
||||||
|
T::Event: std::fmt::Debug,
|
||||||
|
{
|
||||||
let dock = match panel.position(cx) {
|
let dock = match panel.position(cx) {
|
||||||
DockPosition::Left => &self.left_dock,
|
DockPosition::Left => &self.left_dock,
|
||||||
DockPosition::Bottom => &self.bottom_dock,
|
DockPosition::Bottom => &self.bottom_dock,
|
||||||
|
@ -904,10 +907,11 @@ impl Workspace {
|
||||||
});
|
});
|
||||||
} else if T::should_zoom_in_on_event(event) {
|
} else if T::should_zoom_in_on_event(event) {
|
||||||
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
|
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
|
||||||
if panel.has_focus(cx) {
|
if !panel.has_focus(cx) {
|
||||||
this.zoomed = Some(panel.downgrade().into_any());
|
cx.focus(&panel);
|
||||||
this.zoomed_position = Some(panel.read(cx).position(cx));
|
|
||||||
}
|
}
|
||||||
|
this.zoomed = Some(panel.downgrade().into_any());
|
||||||
|
this.zoomed_position = Some(panel.read(cx).position(cx));
|
||||||
} else if T::should_zoom_out_on_event(event) {
|
} else if T::should_zoom_out_on_event(event) {
|
||||||
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
|
dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
|
||||||
if this.zoomed_position == Some(prev_position) {
|
if this.zoomed_position == Some(prev_position) {
|
||||||
|
@ -1702,6 +1706,11 @@ impl Workspace {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub fn zoomed_view(&self, cx: &AppContext) -> Option<AnyViewHandle> {
|
||||||
|
self.zoomed.and_then(|view| view.upgrade(cx))
|
||||||
|
}
|
||||||
|
|
||||||
fn dismiss_zoomed_items_to_reveal(
|
fn dismiss_zoomed_items_to_reveal(
|
||||||
&mut self,
|
&mut self,
|
||||||
dock_to_reveal: Option<DockPosition>,
|
dock_to_reveal: Option<DockPosition>,
|
||||||
|
|
|
@ -48,6 +48,7 @@ use util::{
|
||||||
http::{self, HttpClient},
|
http::{self, HttpClient},
|
||||||
paths::PathLikeWithPosition,
|
paths::PathLikeWithPosition,
|
||||||
};
|
};
|
||||||
|
use uuid::Uuid;
|
||||||
use welcome::{show_welcome_experience, FIRST_OPEN};
|
use welcome::{show_welcome_experience, FIRST_OPEN};
|
||||||
|
|
||||||
use fs::RealFs;
|
use fs::RealFs;
|
||||||
|
@ -68,7 +69,8 @@ fn main() {
|
||||||
log::info!("========== starting zed ==========");
|
log::info!("========== starting zed ==========");
|
||||||
let mut app = gpui::App::new(Assets).unwrap();
|
let mut app = gpui::App::new(Assets).unwrap();
|
||||||
|
|
||||||
init_panic_hook(&app);
|
let installation_id = app.background().block(installation_id()).ok();
|
||||||
|
init_panic_hook(&app, installation_id.clone());
|
||||||
|
|
||||||
app.background();
|
app.background();
|
||||||
|
|
||||||
|
@ -168,7 +170,7 @@ fn main() {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
client.telemetry().start();
|
client.telemetry().start(installation_id);
|
||||||
|
|
||||||
let app_state = Arc::new(AppState {
|
let app_state = Arc::new(AppState {
|
||||||
languages,
|
languages,
|
||||||
|
@ -268,6 +270,22 @@ fn main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn installation_id() -> Result<String> {
|
||||||
|
let legacy_key_name = "device_id";
|
||||||
|
|
||||||
|
if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
|
||||||
|
Ok(installation_id)
|
||||||
|
} else {
|
||||||
|
let installation_id = Uuid::new_v4().to_string();
|
||||||
|
|
||||||
|
KEY_VALUE_STORE
|
||||||
|
.write_kvp(legacy_key_name.to_string(), installation_id.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(installation_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn open_urls(
|
fn open_urls(
|
||||||
urls: Vec<String>,
|
urls: Vec<String>,
|
||||||
cli_connections_tx: &mpsc::UnboundedSender<(
|
cli_connections_tx: &mpsc::UnboundedSender<(
|
||||||
|
@ -371,6 +389,8 @@ struct Panic {
|
||||||
os_version: Option<String>,
|
os_version: Option<String>,
|
||||||
architecture: String,
|
architecture: String,
|
||||||
panicked_on: u128,
|
panicked_on: u128,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
installation_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -379,7 +399,7 @@ struct PanicRequest {
|
||||||
token: String,
|
token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_panic_hook(app: &App) {
|
fn init_panic_hook(app: &App, installation_id: Option<String>) {
|
||||||
let is_pty = stdout_is_a_pty();
|
let is_pty = stdout_is_a_pty();
|
||||||
let platform = app.platform();
|
let platform = app.platform();
|
||||||
|
|
||||||
|
@ -432,6 +452,7 @@ fn init_panic_hook(app: &App) {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_millis(),
|
.as_millis(),
|
||||||
backtrace,
|
backtrace,
|
||||||
|
installation_id: installation_id.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_pty {
|
if is_pty {
|
||||||
|
|
|
@ -10,11 +10,190 @@ export default function assistant(colorScheme: ColorScheme) {
|
||||||
background: editor(colorScheme).background,
|
background: editor(colorScheme).background,
|
||||||
padding: { left: 12 },
|
padding: { left: 12 },
|
||||||
},
|
},
|
||||||
header: {
|
messageHeader: {
|
||||||
border: border(layer, "default", { bottom: true, top: true }),
|
border: border(layer, "default", { bottom: true, top: true }),
|
||||||
margin: { bottom: 6, top: 6 },
|
margin: { bottom: 6, top: 6 },
|
||||||
background: editor(colorScheme).background,
|
background: editor(colorScheme).background,
|
||||||
},
|
},
|
||||||
|
hamburgerButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/hamburger_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
splitButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/split_message_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
quoteButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/quote_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
assistButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/assist_15.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 15,
|
||||||
|
height: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { left: 12, right: 24 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
zoomInButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/maximize_8.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { right: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
zoomOutButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/minimize_8.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { right: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
plusButton: interactive({
|
||||||
|
base: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
asset: "icons/plus_12.svg",
|
||||||
|
dimensions: {
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
margin: { right: 12 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
title: {
|
||||||
|
margin: { left: 12 },
|
||||||
|
...text(layer, "sans", "default", { size: "sm" })
|
||||||
|
},
|
||||||
|
savedConversation: {
|
||||||
|
container: interactive({
|
||||||
|
base: {
|
||||||
|
background: background(layer, "on"),
|
||||||
|
padding: { top: 4, bottom: 4 }
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
savedAt: {
|
||||||
|
margin: { left: 8 },
|
||||||
|
...text(layer, "sans", "default", { size: "xs" }),
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
margin: { left: 16 },
|
||||||
|
...text(layer, "sans", "default", { size: "sm", weight: "bold" }),
|
||||||
|
}
|
||||||
|
},
|
||||||
userSender: {
|
userSender: {
|
||||||
default: {
|
default: {
|
||||||
...text(layer, "sans", "default", {
|
...text(layer, "sans", "default", {
|
||||||
|
@ -43,13 +222,10 @@ export default function assistant(colorScheme: ColorScheme) {
|
||||||
margin: { top: 2, left: 8 },
|
margin: { top: 2, left: 8 },
|
||||||
...text(layer, "sans", "default", { size: "2xs" }),
|
...text(layer, "sans", "default", { size: "2xs" }),
|
||||||
},
|
},
|
||||||
modelInfoContainer: {
|
|
||||||
margin: { right: 16, top: 4 },
|
|
||||||
},
|
|
||||||
model: interactive({
|
model: interactive({
|
||||||
base: {
|
base: {
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
border: border(layer, "on", { overlay: true }),
|
margin: { left: 12, right: 12, top: 12 },
|
||||||
padding: 4,
|
padding: 4,
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
...text(layer, "sans", "default", { size: "xs" }),
|
...text(layer, "sans", "default", { size: "xs" }),
|
||||||
|
@ -57,22 +233,21 @@ export default function assistant(colorScheme: ColorScheme) {
|
||||||
state: {
|
state: {
|
||||||
hovered: {
|
hovered: {
|
||||||
background: background(layer, "on", "hovered"),
|
background: background(layer, "on", "hovered"),
|
||||||
|
border: border(layer, "on", { overlay: true }),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
remainingTokens: {
|
remainingTokens: {
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
border: border(layer, "on", { overlay: true }),
|
margin: { top: 12, right: 12 },
|
||||||
padding: 4,
|
padding: 4,
|
||||||
margin: { left: 4 },
|
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
...text(layer, "sans", "positive", { size: "xs" }),
|
...text(layer, "sans", "positive", { size: "xs" }),
|
||||||
},
|
},
|
||||||
noRemainingTokens: {
|
noRemainingTokens: {
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
border: border(layer, "on", { overlay: true }),
|
margin: { top: 12, right: 12 },
|
||||||
padding: 4,
|
padding: 4,
|
||||||
margin: { left: 4 },
|
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
...text(layer, "sans", "negative", { size: "xs" }),
|
...text(layer, "sans", "negative", { size: "xs" }),
|
||||||
},
|
},
|
||||||
|
|
|
@ -30,7 +30,7 @@ const getTheme = (variant: Variant): ThemeConfig => {
|
||||||
return {
|
return {
|
||||||
name: `${meta.name} Forest Light`,
|
name: `${meta.name} Forest Light`,
|
||||||
author: meta.author,
|
author: meta.author,
|
||||||
appearance: ThemeAppearance.Dark,
|
appearance: ThemeAppearance.Light,
|
||||||
licenseType: meta.licenseType,
|
licenseType: meta.licenseType,
|
||||||
licenseUrl: meta.licenseUrl,
|
licenseUrl: meta.licenseUrl,
|
||||||
licenseFile: `${__dirname}/LICENSE`,
|
licenseFile: `${__dirname}/LICENSE`,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue