Compare commits
15 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2c56602657 | ||
![]() |
d9c1558411 | ||
![]() |
8900c18f98 | ||
![]() |
a5fb428839 | ||
![]() |
a21287d9f8 | ||
![]() |
4f9ec8148b | ||
![]() |
55e9d39596 | ||
![]() |
2ee3ed1fdc | ||
![]() |
d15663393a | ||
![]() |
4f116da026 | ||
![]() |
88bf8872c1 | ||
![]() |
644d32db2a | ||
![]() |
a26744a96c | ||
![]() |
02a426b657 | ||
![]() |
c1bb4e7808 |
108 changed files with 6443 additions and 1948 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -109,6 +109,8 @@ dependencies = [
|
||||||
"isahc",
|
"isahc",
|
||||||
"language",
|
"language",
|
||||||
"menu",
|
"menu",
|
||||||
|
"project",
|
||||||
|
"regex",
|
||||||
"schemars",
|
"schemars",
|
||||||
"search",
|
"search",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -6916,18 +6918,6 @@ dependencies = [
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "theme_testbench"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"gpui",
|
|
||||||
"project",
|
|
||||||
"settings",
|
|
||||||
"smallvec",
|
|
||||||
"theme",
|
|
||||||
"workspace",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
|
@ -8809,7 +8799,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.92.0"
|
version = "0.92.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"ai",
|
"ai",
|
||||||
|
@ -8888,7 +8878,6 @@ dependencies = [
|
||||||
"text",
|
"text",
|
||||||
"theme",
|
"theme",
|
||||||
"theme_selector",
|
"theme_selector",
|
||||||
"theme_testbench",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tiny_http",
|
"tiny_http",
|
||||||
"toml",
|
"toml",
|
||||||
|
|
|
@ -61,7 +61,6 @@ members = [
|
||||||
"crates/text",
|
"crates/text",
|
||||||
"crates/theme",
|
"crates/theme",
|
||||||
"crates/theme_selector",
|
"crates/theme_selector",
|
||||||
"crates/theme_testbench",
|
|
||||||
"crates/util",
|
"crates/util",
|
||||||
"crates/vim",
|
"crates/vim",
|
||||||
"crates/workspace",
|
"crates/workspace",
|
||||||
|
|
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
|
||||||
|
@ -400,6 +408,7 @@
|
||||||
"cmd-shift-p": "command_palette::Toggle",
|
"cmd-shift-p": "command_palette::Toggle",
|
||||||
"cmd-shift-m": "diagnostics::Deploy",
|
"cmd-shift-m": "diagnostics::Deploy",
|
||||||
"cmd-shift-e": "project_panel::ToggleFocus",
|
"cmd-shift-e": "project_panel::ToggleFocus",
|
||||||
|
"cmd-?": "assistant::ToggleFocus",
|
||||||
"cmd-alt-s": "workspace::SaveAll",
|
"cmd-alt-s": "workspace::SaveAll",
|
||||||
"cmd-k m": "language_selector::Toggle"
|
"cmd-k m": "language_selector::Toggle"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -326,7 +326,7 @@ impl View for ActivityIndicator {
|
||||||
let mut element = MouseEventHandler::<Self, _>::new(0, cx, |state, cx| {
|
let mut element = MouseEventHandler::<Self, _>::new(0, cx, |state, cx| {
|
||||||
let theme = &theme::current(cx).workspace.status_bar.lsp_status;
|
let theme = &theme::current(cx).workspace.status_bar.lsp_status;
|
||||||
let style = if state.hovered() && on_click.is_some() {
|
let style = if state.hovered() && on_click.is_some() {
|
||||||
theme.hover.as_ref().unwrap_or(&theme.default)
|
theme.hovered.as_ref().unwrap_or(&theme.default)
|
||||||
} else {
|
} else {
|
||||||
&theme.default
|
&theme.default
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
@ -49,7 +49,7 @@ impl View for UpdateNotification {
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -74,7 +74,7 @@ impl View for UpdateNotification {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.with_child({
|
.with_child({
|
||||||
let style = theme.action_message.style_for(state, false);
|
let style = theme.action_message.style_for(state);
|
||||||
Text::new("View the release notes", style.text.clone())
|
Text::new("View the release notes", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -83,7 +83,7 @@ impl View for Breadcrumbs {
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
|
MouseEventHandler::<Breadcrumbs, Breadcrumbs>::new(0, cx, |state, _| {
|
||||||
let style = style.style_for(state, false);
|
let style = style.style_for(state);
|
||||||
crumbs.with_style(style.container)
|
crumbs.with_style(style.container)
|
||||||
})
|
})
|
||||||
.on_click(MouseButton::Left, |_, this, cx| {
|
.on_click(MouseButton::Left, |_, this, cx| {
|
||||||
|
|
|
@ -299,7 +299,12 @@ impl CollabTitlebarItem {
|
||||||
pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext<Self>) {
|
pub fn toggle_user_menu(&mut self, _: &ToggleUserMenu, cx: &mut ViewContext<Self>) {
|
||||||
let theme = theme::current(cx).clone();
|
let theme = theme::current(cx).clone();
|
||||||
let avatar_style = theme.workspace.titlebar.leader_avatar.clone();
|
let avatar_style = theme.workspace.titlebar.leader_avatar.clone();
|
||||||
let item_style = theme.context_menu.item.disabled_style().clone();
|
let item_style = theme
|
||||||
|
.context_menu
|
||||||
|
.item
|
||||||
|
.inactive_state()
|
||||||
|
.disabled_style()
|
||||||
|
.clone();
|
||||||
self.user_menu.update(cx, |user_menu, cx| {
|
self.user_menu.update(cx, |user_menu, cx| {
|
||||||
let items = if let Some(user) = self.user_store.read(cx).current_user() {
|
let items = if let Some(user) = self.user_store.read(cx).current_user() {
|
||||||
vec![
|
vec![
|
||||||
|
@ -361,8 +366,20 @@ impl CollabTitlebarItem {
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(titlebar.toggle_contacts_badge)
|
.with_style(titlebar.toggle_contacts_badge)
|
||||||
.contained()
|
.contained()
|
||||||
.with_margin_left(titlebar.toggle_contacts_button.default.icon_width)
|
.with_margin_left(
|
||||||
.with_margin_top(titlebar.toggle_contacts_button.default.icon_width)
|
titlebar
|
||||||
|
.toggle_contacts_button
|
||||||
|
.inactive_state()
|
||||||
|
.default
|
||||||
|
.icon_width,
|
||||||
|
)
|
||||||
|
.with_margin_top(
|
||||||
|
titlebar
|
||||||
|
.toggle_contacts_button
|
||||||
|
.inactive_state()
|
||||||
|
.default
|
||||||
|
.icon_width,
|
||||||
|
)
|
||||||
.aligned(),
|
.aligned(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -372,7 +389,8 @@ impl CollabTitlebarItem {
|
||||||
MouseEventHandler::<ToggleContactsMenu, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ToggleContactsMenu, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar
|
let style = titlebar
|
||||||
.toggle_contacts_button
|
.toggle_contacts_button
|
||||||
.style_for(state, self.contacts_popover.is_some());
|
.in_state(self.contacts_popover.is_some())
|
||||||
|
.style_for(state);
|
||||||
Svg::new("icons/user_plus_16.svg")
|
Svg::new("icons/user_plus_16.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -419,7 +437,7 @@ impl CollabTitlebarItem {
|
||||||
|
|
||||||
let titlebar = &theme.workspace.titlebar;
|
let titlebar = &theme.workspace.titlebar;
|
||||||
MouseEventHandler::<ToggleScreenSharing, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ToggleScreenSharing, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar.call_control.style_for(state, false);
|
let style = titlebar.call_control.style_for(state);
|
||||||
Svg::new(icon)
|
Svg::new(icon)
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -473,7 +491,7 @@ impl CollabTitlebarItem {
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<ShareUnshare, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ShareUnshare, Self>::new(0, cx, |state, _| {
|
||||||
//TODO: Ensure this button has consistent width for both text variations
|
//TODO: Ensure this button has consistent width for both text variations
|
||||||
let style = titlebar.share_button.style_for(state, false);
|
let style = titlebar.share_button.inactive_state().style_for(state);
|
||||||
Label::new(label, style.text.clone())
|
Label::new(label, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -511,7 +529,7 @@ impl CollabTitlebarItem {
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<ToggleUserMenu, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<ToggleUserMenu, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar.call_control.style_for(state, false);
|
let style = titlebar.call_control.style_for(state);
|
||||||
Svg::new("icons/ellipsis_14.svg")
|
Svg::new("icons/ellipsis_14.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -549,7 +567,7 @@ impl CollabTitlebarItem {
|
||||||
fn render_sign_in_button(&self, theme: &Theme, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
fn render_sign_in_button(&self, theme: &Theme, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||||
let titlebar = &theme.workspace.titlebar;
|
let titlebar = &theme.workspace.titlebar;
|
||||||
MouseEventHandler::<SignIn, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<SignIn, Self>::new(0, cx, |state, _| {
|
||||||
let style = titlebar.sign_in_prompt.style_for(state, false);
|
let style = titlebar.sign_in_prompt.inactive_state().style_for(state);
|
||||||
Label::new("Sign In", style.text.clone())
|
Label::new("Sign In", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -117,7 +117,8 @@ impl PickerDelegate for ContactFinderDelegate {
|
||||||
.contact_finder
|
.contact_finder
|
||||||
.picker
|
.picker
|
||||||
.item
|
.item
|
||||||
.style_for(mouse_state, selected);
|
.in_state(selected)
|
||||||
|
.style_for(mouse_state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_children(user.avatar.clone().map(|avatar| {
|
.with_children(user.avatar.clone().map(|avatar| {
|
||||||
Image::from_data(avatar)
|
Image::from_data(avatar)
|
||||||
|
|
|
@ -774,7 +774,8 @@ impl ContactList {
|
||||||
.with_style(
|
.with_style(
|
||||||
*theme
|
*theme
|
||||||
.contact_row
|
.contact_row
|
||||||
.style_for(&mut Default::default(), is_selected),
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default()),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
@ -797,7 +798,7 @@ impl ContactList {
|
||||||
.width
|
.width
|
||||||
.or(theme.contact_avatar.height)
|
.or(theme.contact_avatar.height)
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
let row = &theme.project_row.default;
|
let row = &theme.project_row.inactive_state().default;
|
||||||
let tree_branch = theme.tree_branch;
|
let tree_branch = theme.tree_branch;
|
||||||
let line_height = row.name.text.line_height(font_cache);
|
let line_height = row.name.text.line_height(font_cache);
|
||||||
let cap_height = row.name.text.cap_height(font_cache);
|
let cap_height = row.name.text.cap_height(font_cache);
|
||||||
|
@ -810,8 +811,11 @@ impl ContactList {
|
||||||
};
|
};
|
||||||
|
|
||||||
MouseEventHandler::<JoinProject, Self>::new(project_id as usize, cx, |mouse_state, _| {
|
MouseEventHandler::<JoinProject, Self>::new(project_id as usize, cx, |mouse_state, _| {
|
||||||
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
|
let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
|
||||||
let row = theme.project_row.style_for(mouse_state, is_selected);
|
let row = theme
|
||||||
|
.project_row
|
||||||
|
.in_state(is_selected)
|
||||||
|
.style_for(mouse_state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -893,7 +897,7 @@ impl ContactList {
|
||||||
.width
|
.width
|
||||||
.or(theme.contact_avatar.height)
|
.or(theme.contact_avatar.height)
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
let row = &theme.project_row.default;
|
let row = &theme.project_row.inactive_state().default;
|
||||||
let tree_branch = theme.tree_branch;
|
let tree_branch = theme.tree_branch;
|
||||||
let line_height = row.name.text.line_height(font_cache);
|
let line_height = row.name.text.line_height(font_cache);
|
||||||
let cap_height = row.name.text.cap_height(font_cache);
|
let cap_height = row.name.text.cap_height(font_cache);
|
||||||
|
@ -904,8 +908,11 @@ impl ContactList {
|
||||||
peer_id.as_u64() as usize,
|
peer_id.as_u64() as usize,
|
||||||
cx,
|
cx,
|
||||||
|mouse_state, _| {
|
|mouse_state, _| {
|
||||||
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
|
let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
|
||||||
let row = theme.project_row.style_for(mouse_state, is_selected);
|
let row = theme
|
||||||
|
.project_row
|
||||||
|
.in_state(is_selected)
|
||||||
|
.style_for(mouse_state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -989,7 +996,8 @@ impl ContactList {
|
||||||
|
|
||||||
let header_style = theme
|
let header_style = theme
|
||||||
.header_row
|
.header_row
|
||||||
.style_for(&mut Default::default(), is_selected);
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default());
|
||||||
let text = match section {
|
let text = match section {
|
||||||
Section::ActiveCall => "Collaborators",
|
Section::ActiveCall => "Collaborators",
|
||||||
Section::Requests => "Contact Requests",
|
Section::Requests => "Contact Requests",
|
||||||
|
@ -999,7 +1007,7 @@ impl ContactList {
|
||||||
let leave_call = if section == Section::ActiveCall {
|
let leave_call = if section == Section::ActiveCall {
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |state, _| {
|
||||||
let style = theme.leave_call.style_for(state, false);
|
let style = theme.leave_call.style_for(state);
|
||||||
Label::new("Leave Call", style.text.clone())
|
Label::new("Leave Call", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -1110,8 +1118,7 @@ impl ContactList {
|
||||||
contact.user.id as usize,
|
contact.user.id as usize,
|
||||||
cx,
|
cx,
|
||||||
|mouse_state, _| {
|
|mouse_state, _| {
|
||||||
let button_style =
|
let button_style = theme.contact_button.style_for(mouse_state);
|
||||||
theme.contact_button.style_for(mouse_state, false);
|
|
||||||
render_icon_button(button_style, "icons/x_mark_8.svg")
|
render_icon_button(button_style, "icons/x_mark_8.svg")
|
||||||
.aligned()
|
.aligned()
|
||||||
.flex_float()
|
.flex_float()
|
||||||
|
@ -1146,7 +1153,8 @@ impl ContactList {
|
||||||
.with_style(
|
.with_style(
|
||||||
*theme
|
*theme
|
||||||
.contact_row
|
.contact_row
|
||||||
.style_for(&mut Default::default(), is_selected),
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.on_click(MouseButton::Left, move |_, this, cx| {
|
.on_click(MouseButton::Left, move |_, this, cx| {
|
||||||
|
@ -1204,7 +1212,7 @@ impl ContactList {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
theme.contact_button.style_for(mouse_state, false)
|
theme.contact_button.style_for(mouse_state)
|
||||||
};
|
};
|
||||||
render_icon_button(button_style, "icons/x_mark_8.svg").aligned()
|
render_icon_button(button_style, "icons/x_mark_8.svg").aligned()
|
||||||
})
|
})
|
||||||
|
@ -1227,7 +1235,7 @@ impl ContactList {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
theme.contact_button.style_for(mouse_state, false)
|
theme.contact_button.style_for(mouse_state)
|
||||||
};
|
};
|
||||||
render_icon_button(button_style, "icons/check_8.svg")
|
render_icon_button(button_style, "icons/check_8.svg")
|
||||||
.aligned()
|
.aligned()
|
||||||
|
@ -1250,7 +1258,7 @@ impl ContactList {
|
||||||
let button_style = if is_contact_request_pending {
|
let button_style = if is_contact_request_pending {
|
||||||
&theme.disabled_button
|
&theme.disabled_button
|
||||||
} else {
|
} else {
|
||||||
theme.contact_button.style_for(mouse_state, false)
|
theme.contact_button.style_for(mouse_state)
|
||||||
};
|
};
|
||||||
render_icon_button(button_style, "icons/x_mark_8.svg")
|
render_icon_button(button_style, "icons/x_mark_8.svg")
|
||||||
.aligned()
|
.aligned()
|
||||||
|
@ -1277,7 +1285,8 @@ impl ContactList {
|
||||||
.with_style(
|
.with_style(
|
||||||
*theme
|
*theme
|
||||||
.contact_row
|
.contact_row
|
||||||
.style_for(&mut Default::default(), is_selected),
|
.in_state(is_selected)
|
||||||
|
.style_for(&mut Default::default()),
|
||||||
)
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ where
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Dismiss, V>::new(user.id as usize, cx, |state, _| {
|
MouseEventHandler::<Dismiss, V>::new(user.id as usize, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -93,7 +93,7 @@ where
|
||||||
.with_children(buttons.into_iter().enumerate().map(
|
.with_children(buttons.into_iter().enumerate().map(
|
||||||
|(ix, (message, handler))| {
|
|(ix, (message, handler))| {
|
||||||
MouseEventHandler::<Button, V>::new(ix, cx, |state, _| {
|
MouseEventHandler::<Button, V>::new(ix, cx, |state, _| {
|
||||||
let button = theme.button.style_for(state, false);
|
let button = theme.button.style_for(state);
|
||||||
Label::new(message, button.text.clone())
|
Label::new(message, button.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(button.container)
|
.with_style(button.container)
|
||||||
|
|
|
@ -185,8 +185,8 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
let mat = &self.matches[ix];
|
let mat = &self.matches[ix];
|
||||||
let command = &self.actions[mat.candidate_id];
|
let command = &self.actions[mat.candidate_id];
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let key_style = &theme.command_palette.key.style_for(mouse_state, selected);
|
let key_style = &theme.command_palette.key.in_state(selected);
|
||||||
let keystroke_spacing = theme.command_palette.keystroke_spacing;
|
let keystroke_spacing = theme.command_palette.keystroke_spacing;
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
|
|
|
@ -328,10 +328,8 @@ impl ContextMenu {
|
||||||
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
Flex::column().with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
match item {
|
match item {
|
||||||
ContextMenuItem::Item { label, .. } => {
|
ContextMenuItem::Item { label, .. } => {
|
||||||
let style = style.item.style_for(
|
let style = style.item.in_state(self.selected_index == Some(ix));
|
||||||
&mut Default::default(),
|
let style = style.style_for(&mut Default::default());
|
||||||
Some(ix) == self.selected_index,
|
|
||||||
);
|
|
||||||
|
|
||||||
match label {
|
match label {
|
||||||
ContextMenuItemLabel::String(label) => {
|
ContextMenuItemLabel::String(label) => {
|
||||||
|
@ -363,10 +361,8 @@ impl ContextMenu {
|
||||||
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
match item {
|
match item {
|
||||||
ContextMenuItem::Item { action, .. } => {
|
ContextMenuItem::Item { action, .. } => {
|
||||||
let style = style.item.style_for(
|
let style = style.item.in_state(self.selected_index == Some(ix));
|
||||||
&mut Default::default(),
|
let style = style.style_for(&mut Default::default());
|
||||||
Some(ix) == self.selected_index,
|
|
||||||
);
|
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
ContextMenuItemAction::Action(action) => KeystrokeLabel::new(
|
ContextMenuItemAction::Action(action) => KeystrokeLabel::new(
|
||||||
|
@ -412,8 +408,8 @@ impl ContextMenu {
|
||||||
let action = action.clone();
|
let action = action.clone();
|
||||||
let view_id = self.parent_view_id;
|
let view_id = self.parent_view_id;
|
||||||
MouseEventHandler::<MenuItem, ContextMenu>::new(ix, cx, |state, _| {
|
MouseEventHandler::<MenuItem, ContextMenu>::new(ix, cx, |state, _| {
|
||||||
let style =
|
let style = style.item.in_state(self.selected_index == Some(ix));
|
||||||
style.item.style_for(state, Some(ix) == self.selected_index);
|
let style = style.style_for(state);
|
||||||
let keystroke = match &action {
|
let keystroke = match &action {
|
||||||
ContextMenuItemAction::Action(action) => Some(
|
ContextMenuItemAction::Action(action) => Some(
|
||||||
KeystrokeLabel::new(
|
KeystrokeLabel::new(
|
||||||
|
|
|
@ -127,16 +127,16 @@ impl CopilotCodeVerification {
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(
|
Label::new(
|
||||||
if copied { "Copied!" } else { "Copy" },
|
if copied { "Copied!" } else { "Copy" },
|
||||||
device_code_style.cta.style_for(state, false).text.clone(),
|
device_code_style.cta.style_for(state).text.clone(),
|
||||||
)
|
)
|
||||||
.aligned()
|
.aligned()
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(*device_code_style.right_container.style_for(state, false))
|
.with_style(*device_code_style.right_container.style_for(state))
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_width(device_code_style.right),
|
.with_width(device_code_style.right),
|
||||||
)
|
)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(device_code_style.cta.style_for(state, false).container)
|
.with_style(device_code_style.cta.style_for(state).container)
|
||||||
})
|
})
|
||||||
.on_click(gpui::platform::MouseButton::Left, {
|
.on_click(gpui::platform::MouseButton::Left, {
|
||||||
let user_code = data.user_code.clone();
|
let user_code = data.user_code.clone();
|
||||||
|
|
|
@ -71,7 +71,8 @@ impl View for CopilotButton {
|
||||||
.status_bar
|
.status_bar
|
||||||
.panel_buttons
|
.panel_buttons
|
||||||
.button
|
.button
|
||||||
.style_for(state, active);
|
.in_state(active)
|
||||||
|
.style_for(state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -255,7 +256,7 @@ impl CopilotButton {
|
||||||
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
|
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(Label::new("Copilot Settings", style.label.clone()))
|
.with_child(Label::new("Copilot Settings", style.label.clone()))
|
||||||
.with_child(theme::ui::icon(icon_style.style_for(state, false)))
|
.with_child(theme::ui::icon(icon_style.style_for(state)))
|
||||||
.align_children_center()
|
.align_children_center()
|
||||||
.into_any()
|
.into_any()
|
||||||
},
|
},
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl View for DiagnosticIndicator {
|
||||||
.workspace
|
.workspace
|
||||||
.status_bar
|
.status_bar
|
||||||
.diagnostic_summary
|
.diagnostic_summary
|
||||||
.style_for(state, false);
|
.style_for(state);
|
||||||
|
|
||||||
let mut summary_row = Flex::row();
|
let mut summary_row = Flex::row();
|
||||||
if self.summary.error_count > 0 {
|
if self.summary.error_count > 0 {
|
||||||
|
@ -198,7 +198,7 @@ impl View for DiagnosticIndicator {
|
||||||
MouseEventHandler::<Message, _>::new(1, cx, |state, _| {
|
MouseEventHandler::<Message, _>::new(1, cx, |state, _| {
|
||||||
Label::new(
|
Label::new(
|
||||||
diagnostic.message.split('\n').next().unwrap().to_string(),
|
diagnostic.message.split('\n').next().unwrap().to_string(),
|
||||||
message_style.style_for(state, false).text.clone(),
|
message_style.style_for(state).text.clone(),
|
||||||
)
|
)
|
||||||
.aligned()
|
.aligned()
|
||||||
.contained()
|
.contained()
|
||||||
|
|
|
@ -3320,15 +3320,21 @@ impl Editor {
|
||||||
pub fn render_code_actions_indicator(
|
pub fn render_code_actions_indicator(
|
||||||
&self,
|
&self,
|
||||||
style: &EditorStyle,
|
style: &EditorStyle,
|
||||||
active: bool,
|
is_active: bool,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<AnyElement<Self>> {
|
) -> Option<AnyElement<Self>> {
|
||||||
if self.available_code_actions.is_some() {
|
if self.available_code_actions.is_some() {
|
||||||
enum CodeActions {}
|
enum CodeActions {}
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
|
||||||
Svg::new("icons/bolt_8.svg")
|
Svg::new("icons/bolt_8.svg").with_color(
|
||||||
.with_color(style.code_actions.indicator.style_for(state, active).color)
|
style
|
||||||
|
.code_actions
|
||||||
|
.indicator
|
||||||
|
.in_state(is_active)
|
||||||
|
.style_for(state)
|
||||||
|
.color,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.with_padding(Padding::uniform(3.))
|
.with_padding(Padding::uniform(3.))
|
||||||
|
@ -3378,10 +3384,8 @@ impl Editor {
|
||||||
.with_color(
|
.with_color(
|
||||||
style
|
style
|
||||||
.indicator
|
.indicator
|
||||||
.style_for(
|
.in_state(fold_status == FoldStatus::Folded)
|
||||||
mouse_state,
|
.style_for(mouse_state)
|
||||||
fold_status == FoldStatus::Folded,
|
|
||||||
)
|
|
||||||
.color,
|
.color,
|
||||||
)
|
)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
|
|
@ -1529,7 +1529,7 @@ impl EditorElement {
|
||||||
|
|
||||||
enum JumpIcon {}
|
enum JumpIcon {}
|
||||||
MouseEventHandler::<JumpIcon, _>::new((*id).into(), cx, |state, _| {
|
MouseEventHandler::<JumpIcon, _>::new((*id).into(), cx, |state, _| {
|
||||||
let style = style.jump_icon.style_for(state, false);
|
let style = style.jump_icon.style_for(state);
|
||||||
Svg::new("icons/arrow_up_right_8.svg")
|
Svg::new("icons/arrow_up_right_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -2094,7 +2094,7 @@ impl Element<Editor> for EditorElement {
|
||||||
.folds
|
.folds
|
||||||
.ellipses
|
.ellipses
|
||||||
.background
|
.background
|
||||||
.style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize), false)
|
.style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
|
||||||
.color;
|
.color;
|
||||||
|
|
||||||
(id, fold, color)
|
(id, fold, color)
|
||||||
|
|
|
@ -41,7 +41,8 @@ impl View for DeployFeedbackButton {
|
||||||
.status_bar
|
.status_bar
|
||||||
.panel_buttons
|
.panel_buttons
|
||||||
.button
|
.button
|
||||||
.style_for(state, active);
|
.in_state(active)
|
||||||
|
.style_for(state);
|
||||||
|
|
||||||
Svg::new("icons/feedback_16.svg")
|
Svg::new("icons/feedback_16.svg")
|
||||||
.with_color(style.icon_color)
|
.with_color(style.icon_color)
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl View for SubmitFeedbackButton {
|
||||||
let theme = theme::current(cx).clone();
|
let theme = theme::current(cx).clone();
|
||||||
enum SubmitFeedbackButton {}
|
enum SubmitFeedbackButton {}
|
||||||
MouseEventHandler::<SubmitFeedbackButton, Self>::new(0, cx, |state, _| {
|
MouseEventHandler::<SubmitFeedbackButton, Self>::new(0, cx, |state, _| {
|
||||||
let style = theme.feedback.submit_button.style_for(state, false);
|
let style = theme.feedback.submit_button.style_for(state);
|
||||||
Label::new("Submit as Markdown", style.text.clone())
|
Label::new("Submit as Markdown", style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -546,7 +546,7 @@ impl PickerDelegate for FileFinderDelegate {
|
||||||
.get(ix)
|
.get(ix)
|
||||||
.expect("Invalid matches state: no element for index {ix}");
|
.expect("Invalid matches state: no element for index {ix}");
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let (file_name, file_name_positions, full_path, full_path_positions) =
|
let (file_name, file_name_positions, full_path, full_path_positions) =
|
||||||
self.labels_for_match(path_match, cx, ix);
|
self.labels_for_match(path_match, cx, ix);
|
||||||
Flex::column()
|
Flex::column()
|
||||||
|
|
|
@ -152,6 +152,29 @@ impl App {
|
||||||
asset_source,
|
asset_source,
|
||||||
))));
|
))));
|
||||||
|
|
||||||
|
foreground_platform.on_event(Box::new({
|
||||||
|
let cx = app.0.clone();
|
||||||
|
move |event| {
|
||||||
|
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
|
||||||
|
// Allow system menu "cmd-?" shortcut to be overridden
|
||||||
|
if keystroke.cmd
|
||||||
|
&& !keystroke.shift
|
||||||
|
&& !keystroke.alt
|
||||||
|
&& !keystroke.function
|
||||||
|
&& keystroke.key == "?"
|
||||||
|
{
|
||||||
|
if cx
|
||||||
|
.borrow_mut()
|
||||||
|
.update_active_window(|cx| cx.dispatch_keystroke(keystroke))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}));
|
||||||
foreground_platform.on_quit(Box::new({
|
foreground_platform.on_quit(Box::new({
|
||||||
let cx = app.0.clone();
|
let cx = app.0.clone();
|
||||||
move || {
|
move || {
|
||||||
|
|
|
@ -164,6 +164,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,9 @@ use crate::{
|
||||||
},
|
},
|
||||||
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
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 +25,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 +114,24 @@ impl<V: View> Element<V> for Svg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::json::ToJson;
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
pub struct SvgStyle {
|
||||||
|
pub color: Color,
|
||||||
|
pub asset: String,
|
||||||
|
pub dimensions: Dimensions,
|
||||||
|
}
|
||||||
|
|
||||||
use super::constrain_size_preserving_aspect_ratio;
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
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(
|
||||||
|
|
|
@ -939,7 +939,6 @@ extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_send![super(this, class!(NSApplication)), sendEvent: native_event]
|
msg_send![super(this, class!(NSApplication)), sendEvent: native_event]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
cmp::{self, Ordering, Reverse},
|
cmp::{self, Ordering, Reverse},
|
||||||
collections::BinaryHeap,
|
collections::BinaryHeap,
|
||||||
iter,
|
fmt, iter,
|
||||||
ops::{Deref, DerefMut, Range},
|
ops::{Deref, DerefMut, Range},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
@ -428,6 +428,8 @@ impl SyntaxSnapshot {
|
||||||
invalidated_ranges: Vec<Range<usize>>,
|
invalidated_ranges: Vec<Range<usize>>,
|
||||||
registry: Option<&Arc<LanguageRegistry>>,
|
registry: Option<&Arc<LanguageRegistry>>,
|
||||||
) {
|
) {
|
||||||
|
log::trace!("reparse. invalidated ranges:{:?}", invalidated_ranges);
|
||||||
|
|
||||||
let max_depth = self.layers.summary().max_depth;
|
let max_depth = self.layers.summary().max_depth;
|
||||||
let mut cursor = self.layers.cursor::<SyntaxLayerSummary>();
|
let mut cursor = self.layers.cursor::<SyntaxLayerSummary>();
|
||||||
cursor.next(&text);
|
cursor.next(&text);
|
||||||
|
@ -489,6 +491,15 @@ impl SyntaxSnapshot {
|
||||||
let Some(layer) = cursor.item() else { break };
|
let Some(layer) = cursor.item() else { break };
|
||||||
|
|
||||||
if changed_regions.intersects(&layer, text) {
|
if changed_regions.intersects(&layer, text) {
|
||||||
|
if let SyntaxLayerContent::Parsed { language, .. } = &layer.content {
|
||||||
|
log::trace!(
|
||||||
|
"discard layer. language:{}, range:{:?}. changed_regions:{:?}",
|
||||||
|
language.name(),
|
||||||
|
LogAnchorRange(&layer.range, text),
|
||||||
|
LogChangedRegions(&changed_regions, text),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
changed_regions.insert(
|
changed_regions.insert(
|
||||||
ChangedRegion {
|
ChangedRegion {
|
||||||
depth: layer.depth + 1,
|
depth: layer.depth + 1,
|
||||||
|
@ -541,26 +552,24 @@ impl SyntaxSnapshot {
|
||||||
.to_ts_point();
|
.to_ts_point();
|
||||||
}
|
}
|
||||||
|
|
||||||
if included_ranges.is_empty() {
|
if let Some((SyntaxLayerContent::Parsed { tree: old_tree, .. }, layer_start)) =
|
||||||
included_ranges.push(tree_sitter::Range {
|
old_layer.map(|layer| (&layer.content, layer.range.start))
|
||||||
start_byte: 0,
|
|
||||||
end_byte: 0,
|
|
||||||
start_point: Default::default(),
|
|
||||||
end_point: Default::default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(SyntaxLayerContent::Parsed { tree: old_tree, .. }) =
|
|
||||||
old_layer.map(|layer| &layer.content)
|
|
||||||
{
|
{
|
||||||
|
log::trace!(
|
||||||
|
"existing layer. language:{}, start:{:?}, ranges:{:?}",
|
||||||
|
language.name(),
|
||||||
|
LogPoint(layer_start.to_point(&text)),
|
||||||
|
LogIncludedRanges(&old_tree.included_ranges())
|
||||||
|
);
|
||||||
|
|
||||||
if let ParseMode::Combined {
|
if let ParseMode::Combined {
|
||||||
mut parent_layer_changed_ranges,
|
mut parent_layer_changed_ranges,
|
||||||
..
|
..
|
||||||
} = step.mode
|
} = step.mode
|
||||||
{
|
{
|
||||||
for range in &mut parent_layer_changed_ranges {
|
for range in &mut parent_layer_changed_ranges {
|
||||||
range.start -= step_start_byte;
|
range.start = range.start.saturating_sub(step_start_byte);
|
||||||
range.end -= step_start_byte;
|
range.end = range.end.saturating_sub(step_start_byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
included_ranges = splice_included_ranges(
|
included_ranges = splice_included_ranges(
|
||||||
|
@ -570,6 +579,22 @@ impl SyntaxSnapshot {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if included_ranges.is_empty() {
|
||||||
|
included_ranges.push(tree_sitter::Range {
|
||||||
|
start_byte: 0,
|
||||||
|
end_byte: 0,
|
||||||
|
start_point: Default::default(),
|
||||||
|
end_point: Default::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"update layer. language:{}, start:{:?}, ranges:{:?}",
|
||||||
|
language.name(),
|
||||||
|
LogAnchorRange(&step.range, text),
|
||||||
|
LogIncludedRanges(&included_ranges),
|
||||||
|
);
|
||||||
|
|
||||||
tree = parse_text(
|
tree = parse_text(
|
||||||
grammar,
|
grammar,
|
||||||
text.as_rope(),
|
text.as_rope(),
|
||||||
|
@ -586,6 +611,22 @@ impl SyntaxSnapshot {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
if included_ranges.is_empty() {
|
||||||
|
included_ranges.push(tree_sitter::Range {
|
||||||
|
start_byte: 0,
|
||||||
|
end_byte: 0,
|
||||||
|
start_point: Default::default(),
|
||||||
|
end_point: Default::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"create layer. language:{}, range:{:?}, included_ranges:{:?}",
|
||||||
|
language.name(),
|
||||||
|
LogAnchorRange(&step.range, text),
|
||||||
|
LogIncludedRanges(&included_ranges),
|
||||||
|
);
|
||||||
|
|
||||||
tree = parse_text(
|
tree = parse_text(
|
||||||
grammar,
|
grammar,
|
||||||
text.as_rope(),
|
text.as_rope(),
|
||||||
|
@ -613,6 +654,7 @@ impl SyntaxSnapshot {
|
||||||
get_injections(
|
get_injections(
|
||||||
config,
|
config,
|
||||||
text,
|
text,
|
||||||
|
step.range.clone(),
|
||||||
tree.root_node_with_offset(
|
tree.root_node_with_offset(
|
||||||
step_start_byte,
|
step_start_byte,
|
||||||
step_start_point.to_ts_point(),
|
step_start_point.to_ts_point(),
|
||||||
|
@ -1117,6 +1159,7 @@ fn parse_text(
|
||||||
fn get_injections(
|
fn get_injections(
|
||||||
config: &InjectionConfig,
|
config: &InjectionConfig,
|
||||||
text: &BufferSnapshot,
|
text: &BufferSnapshot,
|
||||||
|
outer_range: Range<Anchor>,
|
||||||
node: Node,
|
node: Node,
|
||||||
language_registry: &Arc<LanguageRegistry>,
|
language_registry: &Arc<LanguageRegistry>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
@ -1153,16 +1196,17 @@ fn get_injections(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid duplicate matches if two changed ranges intersect the same injection.
|
|
||||||
let content_range =
|
let content_range =
|
||||||
content_ranges.first().unwrap().start_byte..content_ranges.last().unwrap().end_byte;
|
content_ranges.first().unwrap().start_byte..content_ranges.last().unwrap().end_byte;
|
||||||
if let Some((last_pattern_ix, last_range)) = &prev_match {
|
|
||||||
if mat.pattern_index == *last_pattern_ix && content_range == *last_range {
|
// Avoid duplicate matches if two changed ranges intersect the same injection.
|
||||||
|
if let Some((prev_pattern_ix, prev_range)) = &prev_match {
|
||||||
|
if mat.pattern_index == *prev_pattern_ix && content_range == *prev_range {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prev_match = Some((mat.pattern_index, content_range.clone()));
|
|
||||||
|
|
||||||
|
prev_match = Some((mat.pattern_index, content_range.clone()));
|
||||||
let combined = config.patterns[mat.pattern_index].combined;
|
let combined = config.patterns[mat.pattern_index].combined;
|
||||||
|
|
||||||
let mut language_name = None;
|
let mut language_name = None;
|
||||||
|
@ -1218,11 +1262,10 @@ fn get_injections(
|
||||||
|
|
||||||
for (language, mut included_ranges) in combined_injection_ranges.drain() {
|
for (language, mut included_ranges) in combined_injection_ranges.drain() {
|
||||||
included_ranges.sort_unstable();
|
included_ranges.sort_unstable();
|
||||||
let range = text.anchor_before(node.start_byte())..text.anchor_after(node.end_byte());
|
|
||||||
queue.push(ParseStep {
|
queue.push(ParseStep {
|
||||||
depth,
|
depth,
|
||||||
language: ParseStepLanguage::Loaded { language },
|
language: ParseStepLanguage::Loaded { language },
|
||||||
range,
|
range: outer_range.clone(),
|
||||||
included_ranges,
|
included_ranges,
|
||||||
mode: ParseMode::Combined {
|
mode: ParseMode::Combined {
|
||||||
parent_layer_range: node.start_byte()..node.end_byte(),
|
parent_layer_range: node.start_byte()..node.end_byte(),
|
||||||
|
@ -1234,72 +1277,77 @@ fn get_injections(
|
||||||
|
|
||||||
pub(crate) fn splice_included_ranges(
|
pub(crate) fn splice_included_ranges(
|
||||||
mut ranges: Vec<tree_sitter::Range>,
|
mut ranges: Vec<tree_sitter::Range>,
|
||||||
changed_ranges: &[Range<usize>],
|
removed_ranges: &[Range<usize>],
|
||||||
new_ranges: &[tree_sitter::Range],
|
new_ranges: &[tree_sitter::Range],
|
||||||
) -> Vec<tree_sitter::Range> {
|
) -> Vec<tree_sitter::Range> {
|
||||||
let mut changed_ranges = changed_ranges.into_iter().peekable();
|
let mut removed_ranges = removed_ranges.iter().cloned().peekable();
|
||||||
let mut new_ranges = new_ranges.into_iter().peekable();
|
let mut new_ranges = new_ranges.into_iter().cloned().peekable();
|
||||||
let mut ranges_ix = 0;
|
let mut ranges_ix = 0;
|
||||||
loop {
|
loop {
|
||||||
let new_range = new_ranges.peek();
|
let next_new_range = new_ranges.peek();
|
||||||
let mut changed_range = changed_ranges.peek();
|
let next_removed_range = removed_ranges.peek();
|
||||||
|
|
||||||
// Remove ranges that have changed before inserting any new ranges
|
let (remove, insert) = match (next_removed_range, next_new_range) {
|
||||||
// into those ranges.
|
(None, None) => break,
|
||||||
if let Some((changed, new)) = changed_range.zip(new_range) {
|
(Some(_), None) => (removed_ranges.next().unwrap(), None),
|
||||||
if new.end_byte < changed.start {
|
(Some(next_removed_range), Some(next_new_range)) => {
|
||||||
changed_range = None;
|
if next_removed_range.end < next_new_range.start_byte {
|
||||||
|
(removed_ranges.next().unwrap(), None)
|
||||||
|
} else {
|
||||||
|
let mut start = next_new_range.start_byte;
|
||||||
|
let mut end = next_new_range.end_byte;
|
||||||
|
|
||||||
|
while let Some(next_removed_range) = removed_ranges.peek() {
|
||||||
|
if next_removed_range.start > next_new_range.end_byte {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let next_removed_range = removed_ranges.next().unwrap();
|
||||||
|
start = cmp::min(start, next_removed_range.start);
|
||||||
|
end = cmp::max(end, next_removed_range.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
(start..end, Some(new_ranges.next().unwrap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, Some(next_new_range)) => (
|
||||||
|
next_new_range.start_byte..next_new_range.end_byte,
|
||||||
|
Some(new_ranges.next().unwrap()),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut start_ix = ranges_ix
|
||||||
|
+ match ranges[ranges_ix..].binary_search_by_key(&remove.start, |r| r.end_byte) {
|
||||||
|
Ok(ix) => ix,
|
||||||
|
Err(ix) => ix,
|
||||||
|
};
|
||||||
|
let mut end_ix = ranges_ix
|
||||||
|
+ match ranges[ranges_ix..].binary_search_by_key(&remove.end, |r| r.start_byte) {
|
||||||
|
Ok(ix) => ix + 1,
|
||||||
|
Err(ix) => ix,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If there are empty ranges, then there may be multiple ranges with the same
|
||||||
|
// start or end. Expand the splice to include any adjacent ranges that touch
|
||||||
|
// the changed range.
|
||||||
|
while start_ix > 0 {
|
||||||
|
if ranges[start_ix - 1].end_byte == remove.start {
|
||||||
|
start_ix -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while let Some(range) = ranges.get(end_ix) {
|
||||||
|
if range.start_byte == remove.end {
|
||||||
|
end_ix += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(changed) = changed_range {
|
ranges.splice(start_ix..end_ix, insert);
|
||||||
let mut start_ix = ranges_ix
|
ranges_ix = start_ix;
|
||||||
+ match ranges[ranges_ix..].binary_search_by_key(&changed.start, |r| r.end_byte) {
|
|
||||||
Ok(ix) | Err(ix) => ix,
|
|
||||||
};
|
|
||||||
let mut end_ix = ranges_ix
|
|
||||||
+ match ranges[ranges_ix..].binary_search_by_key(&changed.end, |r| r.start_byte) {
|
|
||||||
Ok(ix) => ix + 1,
|
|
||||||
Err(ix) => ix,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If there are empty ranges, then there may be multiple ranges with the same
|
|
||||||
// start or end. Expand the splice to include any adjacent ranges that touch
|
|
||||||
// the changed range.
|
|
||||||
while start_ix > 0 {
|
|
||||||
if ranges[start_ix - 1].end_byte == changed.start {
|
|
||||||
start_ix -= 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while let Some(range) = ranges.get(end_ix) {
|
|
||||||
if range.start_byte == changed.end {
|
|
||||||
end_ix += 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if end_ix > start_ix {
|
|
||||||
ranges.splice(start_ix..end_ix, []);
|
|
||||||
}
|
|
||||||
changed_ranges.next();
|
|
||||||
ranges_ix = start_ix;
|
|
||||||
} else if let Some(new_range) = new_range {
|
|
||||||
let ix = ranges_ix
|
|
||||||
+ match ranges[ranges_ix..]
|
|
||||||
.binary_search_by_key(&new_range.start_byte, |r| r.start_byte)
|
|
||||||
{
|
|
||||||
Ok(ix) | Err(ix) => ix,
|
|
||||||
};
|
|
||||||
ranges.insert(ix, **new_range);
|
|
||||||
new_ranges.next();
|
|
||||||
ranges_ix = ix + 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ranges
|
ranges
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1628,3 +1676,46 @@ impl ToTreeSitterPoint for Point {
|
||||||
Point::new(point.row as u32, point.column as u32)
|
Point::new(point.row as u32, point.column as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LogIncludedRanges<'a>(&'a [tree_sitter::Range]);
|
||||||
|
struct LogPoint(Point);
|
||||||
|
struct LogAnchorRange<'a>(&'a Range<Anchor>, &'a text::BufferSnapshot);
|
||||||
|
struct LogChangedRegions<'a>(&'a ChangeRegionSet, &'a text::BufferSnapshot);
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for LogIncludedRanges<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_list()
|
||||||
|
.entries(self.0.iter().map(|range| {
|
||||||
|
let start = range.start_point;
|
||||||
|
let end = range.end_point;
|
||||||
|
(start.row, start.column)..(end.row, end.column)
|
||||||
|
}))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for LogAnchorRange<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let range = self.0.to_point(self.1);
|
||||||
|
(LogPoint(range.start)..LogPoint(range.end)).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for LogChangedRegions<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_list()
|
||||||
|
.entries(
|
||||||
|
self.0
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|region| LogAnchorRange(®ion.range, self.1)),
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for LogPoint {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
(self.0.row, self.0.column).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,13 @@ fn test_splice_included_ranges() {
|
||||||
let new_ranges = splice_included_ranges(ranges.clone(), &[30..50], &[ts_range(25..55)]);
|
let new_ranges = splice_included_ranges(ranges.clone(), &[30..50], &[ts_range(25..55)]);
|
||||||
assert_eq!(new_ranges, &[ts_range(25..55), ts_range(80..90)]);
|
assert_eq!(new_ranges, &[ts_range(25..55), ts_range(80..90)]);
|
||||||
|
|
||||||
|
// does not create overlapping ranges
|
||||||
|
let new_ranges = splice_included_ranges(ranges.clone(), &[0..18], &[ts_range(20..32)]);
|
||||||
|
assert_eq!(
|
||||||
|
new_ranges,
|
||||||
|
&[ts_range(20..32), ts_range(50..60), ts_range(80..90)]
|
||||||
|
);
|
||||||
|
|
||||||
fn ts_range(range: Range<usize>) -> tree_sitter::Range {
|
fn ts_range(range: Range<usize>) -> tree_sitter::Range {
|
||||||
tree_sitter::Range {
|
tree_sitter::Range {
|
||||||
start_byte: range.start,
|
start_byte: range.start,
|
||||||
|
@ -624,6 +631,26 @@ fn test_combined_injections_splitting_some_injections() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_combined_injections_editing_after_last_injection() {
|
||||||
|
test_edit_sequence(
|
||||||
|
"ERB",
|
||||||
|
&[
|
||||||
|
r#"
|
||||||
|
<% foo %>
|
||||||
|
<div></div>
|
||||||
|
<% bar %>
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
<% foo %>
|
||||||
|
<div></div>
|
||||||
|
<% bar %>«
|
||||||
|
more text»
|
||||||
|
"#,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_combined_injections_inside_injections() {
|
fn test_combined_injections_inside_injections() {
|
||||||
let (_buffer, _syntax_map) = test_edit_sequence(
|
let (_buffer, _syntax_map) = test_edit_sequence(
|
||||||
|
@ -974,13 +1001,16 @@ fn test_edit_sequence(language_name: &str, steps: &[&str]) -> (Buffer, SyntaxMap
|
||||||
mutated_syntax_map.reparse(language.clone(), &buffer);
|
mutated_syntax_map.reparse(language.clone(), &buffer);
|
||||||
|
|
||||||
for (i, marked_string) in steps.into_iter().enumerate() {
|
for (i, marked_string) in steps.into_iter().enumerate() {
|
||||||
buffer.edit_via_marked_text(&marked_string.unindent());
|
let marked_string = marked_string.unindent();
|
||||||
|
log::info!("incremental parse {i}: {marked_string:?}");
|
||||||
|
buffer.edit_via_marked_text(&marked_string);
|
||||||
|
|
||||||
// Reparse the syntax map
|
// Reparse the syntax map
|
||||||
mutated_syntax_map.interpolate(&buffer);
|
mutated_syntax_map.interpolate(&buffer);
|
||||||
mutated_syntax_map.reparse(language.clone(), &buffer);
|
mutated_syntax_map.reparse(language.clone(), &buffer);
|
||||||
|
|
||||||
// Create a second syntax map from scratch
|
// Create a second syntax map from scratch
|
||||||
|
log::info!("fresh parse {i}: {marked_string:?}");
|
||||||
let mut reference_syntax_map = SyntaxMap::new();
|
let mut reference_syntax_map = SyntaxMap::new();
|
||||||
reference_syntax_map.set_language_registry(registry.clone());
|
reference_syntax_map.set_language_registry(registry.clone());
|
||||||
reference_syntax_map.reparse(language.clone(), &buffer);
|
reference_syntax_map.reparse(language.clone(), &buffer);
|
||||||
|
@ -1133,6 +1163,7 @@ fn range_for_text(buffer: &Buffer, text: &str) -> Range<usize> {
|
||||||
start..start + text.len()
|
start..start + text.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn assert_layers_for_range(
|
fn assert_layers_for_range(
|
||||||
syntax_map: &SyntaxMap,
|
syntax_map: &SyntaxMap,
|
||||||
buffer: &BufferSnapshot,
|
buffer: &BufferSnapshot,
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl View for ActiveBufferLanguage {
|
||||||
|
|
||||||
MouseEventHandler::<Self, Self>::new(0, cx, |state, cx| {
|
MouseEventHandler::<Self, Self>::new(0, cx, |state, cx| {
|
||||||
let theme = &theme::current(cx).workspace.status_bar;
|
let theme = &theme::current(cx).workspace.status_bar;
|
||||||
let style = theme.active_language.style_for(state, false);
|
let style = theme.active_language.style_for(state);
|
||||||
Label::new(active_language_text, style.text.clone())
|
Label::new(active_language_text, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -180,7 +180,7 @@ impl PickerDelegate for LanguageSelectorDelegate {
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let mat = &self.matches[ix];
|
let mat = &self.matches[ix];
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name());
|
let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name());
|
||||||
let mut label = mat.string.clone();
|
let mut label = mat.string.clone();
|
||||||
if buffer_language_name.as_deref() == Some(mat.string.as_str()) {
|
if buffer_language_name.as_deref() == Some(mat.string.as_str()) {
|
||||||
|
|
|
@ -681,7 +681,7 @@ impl LspLogToolbarItemView {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| "No server selected".into());
|
.unwrap_or_else(|| "No server selected".into());
|
||||||
let style = theme.toolbar_dropdown_menu.header.style_for(state, false);
|
let style = theme.toolbar_dropdown_menu.header.style_for(state);
|
||||||
Label::new(label, style.text.clone())
|
Label::new(label, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -722,7 +722,8 @@ impl LspLogToolbarItemView {
|
||||||
let style = theme
|
let style = theme
|
||||||
.toolbar_dropdown_menu
|
.toolbar_dropdown_menu
|
||||||
.item
|
.item
|
||||||
.style_for(state, logs_selected);
|
.in_state(logs_selected)
|
||||||
|
.style_for(state);
|
||||||
Label::new(SERVER_LOGS, style.text.clone())
|
Label::new(SERVER_LOGS, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -739,7 +740,8 @@ impl LspLogToolbarItemView {
|
||||||
let style = theme
|
let style = theme
|
||||||
.toolbar_dropdown_menu
|
.toolbar_dropdown_menu
|
||||||
.item
|
.item
|
||||||
.style_for(state, rpc_trace_selected);
|
.in_state(rpc_trace_selected)
|
||||||
|
.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(RPC_MESSAGES, style.text.clone())
|
Label::new(RPC_MESSAGES, style.text.clone())
|
||||||
|
|
|
@ -565,7 +565,7 @@ impl SyntaxTreeToolbarItemView {
|
||||||
) -> impl Element<Self> {
|
) -> impl Element<Self> {
|
||||||
enum ToggleMenu {}
|
enum ToggleMenu {}
|
||||||
MouseEventHandler::<ToggleMenu, Self>::new(0, cx, move |state, _| {
|
MouseEventHandler::<ToggleMenu, Self>::new(0, cx, move |state, _| {
|
||||||
let style = theme.toolbar_dropdown_menu.header.style_for(state, false);
|
let style = theme.toolbar_dropdown_menu.header.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(active_layer.language.name().to_string(), style.text.clone())
|
Label::new(active_layer.language.name().to_string(), style.text.clone())
|
||||||
|
@ -601,7 +601,8 @@ impl SyntaxTreeToolbarItemView {
|
||||||
let style = theme
|
let style = theme
|
||||||
.toolbar_dropdown_menu
|
.toolbar_dropdown_menu
|
||||||
.item
|
.item
|
||||||
.style_for(state, is_selected);
|
.in_state(is_selected)
|
||||||
|
.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(layer.language.name().to_string(), style.text.clone())
|
Label::new(layer.language.name().to_string(), style.text.clone())
|
||||||
|
|
|
@ -204,7 +204,7 @@ impl PickerDelegate for OutlineViewDelegate {
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
let string_match = &self.matches[ix];
|
let string_match = &self.matches[ix];
|
||||||
let outline_item = &self.outline.items[string_match.candidate_id];
|
let outline_item = &self.outline.items[string_match.candidate_id];
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -1253,7 +1254,10 @@ impl ProjectPanel {
|
||||||
let show_editor = details.is_editing && !details.is_processing;
|
let show_editor = details.is_editing && !details.is_processing;
|
||||||
|
|
||||||
MouseEventHandler::<Self, _>::new(entry_id.to_usize(), cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(entry_id.to_usize(), cx, |state, cx| {
|
||||||
let mut style = entry_style.style_for(state, details.is_selected).clone();
|
let mut style = entry_style
|
||||||
|
.in_state(details.is_selected)
|
||||||
|
.style_for(state)
|
||||||
|
.clone();
|
||||||
|
|
||||||
if cx
|
if cx
|
||||||
.global::<DragAndDrop<Workspace>>()
|
.global::<DragAndDrop<Workspace>>()
|
||||||
|
@ -1264,7 +1268,7 @@ impl ProjectPanel {
|
||||||
.filter(|destination| details.path.starts_with(destination))
|
.filter(|destination| details.path.starts_with(destination))
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
style = entry_style.active.clone().unwrap();
|
style = entry_style.active_state().default.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let row_container_style = if show_editor {
|
let row_container_style = if show_editor {
|
||||||
|
@ -1405,9 +1409,11 @@ impl View for ProjectPanel {
|
||||||
let button_style = theme.open_project_button.clone();
|
let button_style = theme.open_project_button.clone();
|
||||||
let context_menu_item_style = theme::current(cx).context_menu.item.clone();
|
let context_menu_item_style = theme::current(cx).context_menu.item.clone();
|
||||||
move |state, cx| {
|
move |state, cx| {
|
||||||
let button_style = button_style.style_for(state, false).clone();
|
let button_style = button_style.style_for(state).clone();
|
||||||
let context_menu_item =
|
let context_menu_item = context_menu_item_style
|
||||||
context_menu_item_style.style_for(state, true).clone();
|
.active_state()
|
||||||
|
.style_for(state)
|
||||||
|
.clone();
|
||||||
|
|
||||||
theme::ui::keystroke_label(
|
theme::ui::keystroke_label(
|
||||||
"Open a project",
|
"Open a project",
|
||||||
|
|
|
@ -196,7 +196,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = &theme.picker.item;
|
let style = &theme.picker.item;
|
||||||
let current_style = style.style_for(mouse_state, selected);
|
let current_style = style.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
let string_match = &self.matches[ix];
|
let string_match = &self.matches[ix];
|
||||||
let symbol = &self.symbols[string_match.candidate_id];
|
let symbol = &self.symbols[string_match.candidate_id];
|
||||||
|
@ -229,7 +229,10 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
||||||
.with_child(
|
.with_child(
|
||||||
// Avoid styling the path differently when it is selected, since
|
// Avoid styling the path differently when it is selected, since
|
||||||
// the symbol's syntax highlighting doesn't change when selected.
|
// the symbol's syntax highlighting doesn't change when selected.
|
||||||
Label::new(path.to_string(), style.default.label.clone()),
|
Label::new(
|
||||||
|
path.to_string(),
|
||||||
|
style.inactive_state().default.label.clone(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(current_style.container)
|
.with_style(current_style.container)
|
||||||
|
|
|
@ -173,7 +173,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||||
cx: &gpui::AppContext,
|
cx: &gpui::AppContext,
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
let string_match = &self.matches[ix];
|
let string_match = &self.matches[ix];
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -328,7 +332,11 @@ impl BufferSearchBar {
|
||||||
Some(
|
Some(
|
||||||
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, is_active);
|
let style = theme
|
||||||
|
.search
|
||||||
|
.option_button
|
||||||
|
.in_state(is_active)
|
||||||
|
.style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -371,7 +379,7 @@ impl BufferSearchBar {
|
||||||
enum NavButton {}
|
enum NavButton {}
|
||||||
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, false);
|
let style = theme.search.option_button.inactive_state().style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -403,7 +411,7 @@ impl BufferSearchBar {
|
||||||
|
|
||||||
enum CloseButton {}
|
enum CloseButton {}
|
||||||
MouseEventHandler::<CloseButton, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<CloseButton, _>::new(0, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -480,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
|
||||||
|
|
|
@ -896,7 +896,7 @@ impl ProjectSearchBar {
|
||||||
enum NavButton {}
|
enum NavButton {}
|
||||||
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
MouseEventHandler::<NavButton, _>::new(direction as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, false);
|
let style = theme.search.option_button.inactive_state().style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
@ -927,7 +927,11 @@ impl ProjectSearchBar {
|
||||||
let is_active = self.is_option_enabled(option, cx);
|
let is_active = self.is_option_enabled(option, cx);
|
||||||
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(option as usize, cx, |state, cx| {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.search.option_button.style_for(state, is_active);
|
let style = theme
|
||||||
|
.search
|
||||||
|
.option_button
|
||||||
|
.in_state(is_active)
|
||||||
|
.style_for(state);
|
||||||
Label::new(icon, style.text.clone())
|
Label::new(icon, style.text.clone())
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,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::*;
|
||||||
|
@ -128,12 +128,12 @@ pub struct Titlebar {
|
||||||
pub leader_avatar: AvatarStyle,
|
pub leader_avatar: AvatarStyle,
|
||||||
pub follower_avatar: AvatarStyle,
|
pub follower_avatar: AvatarStyle,
|
||||||
pub inactive_avatar_grayscale: bool,
|
pub inactive_avatar_grayscale: bool,
|
||||||
pub sign_in_prompt: Interactive<ContainedText>,
|
pub sign_in_prompt: Toggleable<Interactive<ContainedText>>,
|
||||||
pub outdated_warning: ContainedText,
|
pub outdated_warning: ContainedText,
|
||||||
pub share_button: Interactive<ContainedText>,
|
pub share_button: Toggleable<Interactive<ContainedText>>,
|
||||||
pub call_control: Interactive<IconButton>,
|
pub call_control: Interactive<IconButton>,
|
||||||
pub toggle_contacts_button: Interactive<IconButton>,
|
pub toggle_contacts_button: Toggleable<Interactive<IconButton>>,
|
||||||
pub user_menu_button: Interactive<IconButton>,
|
pub user_menu_button: Toggleable<Interactive<IconButton>>,
|
||||||
pub toggle_contacts_badge: ContainerStyle,
|
pub toggle_contacts_badge: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,12 +204,12 @@ pub struct ContactList {
|
||||||
pub user_query_editor: FieldEditor,
|
pub user_query_editor: FieldEditor,
|
||||||
pub user_query_editor_height: f32,
|
pub user_query_editor_height: f32,
|
||||||
pub add_contact_button: IconButton,
|
pub add_contact_button: IconButton,
|
||||||
pub header_row: Interactive<ContainedText>,
|
pub header_row: Toggleable<Interactive<ContainedText>>,
|
||||||
pub leave_call: Interactive<ContainedText>,
|
pub leave_call: Interactive<ContainedText>,
|
||||||
pub contact_row: Interactive<ContainerStyle>,
|
pub contact_row: Toggleable<Interactive<ContainerStyle>>,
|
||||||
pub row_height: f32,
|
pub row_height: f32,
|
||||||
pub project_row: Interactive<ProjectRow>,
|
pub project_row: Toggleable<Interactive<ProjectRow>>,
|
||||||
pub tree_branch: Interactive<TreeBranch>,
|
pub tree_branch: Toggleable<Interactive<TreeBranch>>,
|
||||||
pub contact_avatar: ImageStyle,
|
pub contact_avatar: ImageStyle,
|
||||||
pub contact_status_free: ContainerStyle,
|
pub contact_status_free: ContainerStyle,
|
||||||
pub contact_status_busy: ContainerStyle,
|
pub contact_status_busy: ContainerStyle,
|
||||||
|
@ -251,7 +251,7 @@ pub struct DropdownMenu {
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub header: Interactive<DropdownMenuItem>,
|
pub header: Interactive<DropdownMenuItem>,
|
||||||
pub section_header: ContainedText,
|
pub section_header: ContainedText,
|
||||||
pub item: Interactive<DropdownMenuItem>,
|
pub item: Toggleable<Interactive<DropdownMenuItem>>,
|
||||||
pub row_height: f32,
|
pub row_height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ pub struct DropdownMenuItem {
|
||||||
pub struct TabBar {
|
pub struct TabBar {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub pane_button: Interactive<IconButton>,
|
pub pane_button: Toggleable<Interactive<IconButton>>,
|
||||||
pub pane_button_container: ContainerStyle,
|
pub pane_button_container: ContainerStyle,
|
||||||
pub active_pane: TabStyles,
|
pub active_pane: TabStyles,
|
||||||
pub inactive_pane: TabStyles,
|
pub inactive_pane: TabStyles,
|
||||||
|
@ -359,7 +359,7 @@ pub struct Search {
|
||||||
pub include_exclude_editor: FindEditor,
|
pub include_exclude_editor: FindEditor,
|
||||||
pub invalid_include_exclude_editor: ContainerStyle,
|
pub invalid_include_exclude_editor: ContainerStyle,
|
||||||
pub include_exclude_inputs: ContainedText,
|
pub include_exclude_inputs: ContainedText,
|
||||||
pub option_button: Interactive<ContainedText>,
|
pub option_button: Toggleable<Interactive<ContainedText>>,
|
||||||
pub match_background: Color,
|
pub match_background: Color,
|
||||||
pub match_index: ContainedText,
|
pub match_index: ContainedText,
|
||||||
pub results_status: TextStyle,
|
pub results_status: TextStyle,
|
||||||
|
@ -395,7 +395,7 @@ pub struct StatusBarPanelButtons {
|
||||||
pub group_left: ContainerStyle,
|
pub group_left: ContainerStyle,
|
||||||
pub group_bottom: ContainerStyle,
|
pub group_bottom: ContainerStyle,
|
||||||
pub group_right: ContainerStyle,
|
pub group_right: ContainerStyle,
|
||||||
pub button: Interactive<PanelButton>,
|
pub button: Toggleable<Interactive<PanelButton>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default)]
|
||||||
|
@ -444,10 +444,10 @@ pub struct PanelButton {
|
||||||
pub struct ProjectPanel {
|
pub struct ProjectPanel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub entry: Interactive<ProjectPanelEntry>,
|
pub entry: Toggleable<Interactive<ProjectPanelEntry>>,
|
||||||
pub dragged_entry: ProjectPanelEntry,
|
pub dragged_entry: ProjectPanelEntry,
|
||||||
pub ignored_entry: Interactive<ProjectPanelEntry>,
|
pub ignored_entry: Toggleable<Interactive<ProjectPanelEntry>>,
|
||||||
pub cut_entry: Interactive<ProjectPanelEntry>,
|
pub cut_entry: Toggleable<Interactive<ProjectPanelEntry>>,
|
||||||
pub filename_editor: FieldEditor,
|
pub filename_editor: FieldEditor,
|
||||||
pub indent_width: f32,
|
pub indent_width: f32,
|
||||||
pub open_project_button: Interactive<ContainedText>,
|
pub open_project_button: Interactive<ContainedText>,
|
||||||
|
@ -481,7 +481,7 @@ pub struct GitProjectStatus {
|
||||||
pub struct ContextMenu {
|
pub struct ContextMenu {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub item: Interactive<ContextMenuItem>,
|
pub item: Toggleable<Interactive<ContextMenuItem>>,
|
||||||
pub keystroke_margin: f32,
|
pub keystroke_margin: f32,
|
||||||
pub separator: ContainerStyle,
|
pub separator: ContainerStyle,
|
||||||
}
|
}
|
||||||
|
@ -498,7 +498,7 @@ pub struct ContextMenuItem {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
#[derive(Debug, Deserialize, Default)]
|
||||||
pub struct CommandPalette {
|
pub struct CommandPalette {
|
||||||
pub key: Interactive<ContainedLabel>,
|
pub key: Toggleable<ContainedLabel>,
|
||||||
pub keystroke_spacing: f32,
|
pub keystroke_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,7 +565,7 @@ pub struct Picker {
|
||||||
pub input_editor: FieldEditor,
|
pub input_editor: FieldEditor,
|
||||||
pub empty_input_editor: FieldEditor,
|
pub empty_input_editor: FieldEditor,
|
||||||
pub no_matches: ContainedLabel,
|
pub no_matches: ContainedLabel,
|
||||||
pub item: Interactive<ContainedLabel>,
|
pub item: Toggleable<Interactive<ContainedLabel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Default)]
|
#[derive(Clone, Debug, Deserialize, Default)]
|
||||||
|
@ -771,13 +771,13 @@ pub struct InteractiveColor {
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
pub struct CodeActions {
|
pub struct CodeActions {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub indicator: Interactive<InteractiveColor>,
|
pub indicator: Toggleable<Interactive<InteractiveColor>>,
|
||||||
pub vertical_scale: f32,
|
pub vertical_scale: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
pub struct Folds {
|
pub struct Folds {
|
||||||
pub indicator: Interactive<InteractiveColor>,
|
pub indicator: Toggleable<Interactive<InteractiveColor>>,
|
||||||
pub ellipses: FoldEllipses,
|
pub ellipses: FoldEllipses,
|
||||||
pub fold_background: Color,
|
pub fold_background: Color,
|
||||||
pub icon_margin_scale: f32,
|
pub icon_margin_scale: f32,
|
||||||
|
@ -805,38 +805,46 @@ pub struct DiffStyle {
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub struct Interactive<T> {
|
pub struct Interactive<T> {
|
||||||
pub default: T,
|
pub default: T,
|
||||||
pub hover: Option<T>,
|
pub hovered: Option<T>,
|
||||||
pub hover_and_active: Option<T>,
|
|
||||||
pub clicked: Option<T>,
|
pub clicked: Option<T>,
|
||||||
pub click_and_active: Option<T>,
|
|
||||||
pub active: Option<T>,
|
|
||||||
pub disabled: Option<T>,
|
pub disabled: Option<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Interactive<T> {
|
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
||||||
pub fn style_for(&self, state: &mut MouseState, active: bool) -> &T {
|
pub struct Toggleable<T> {
|
||||||
|
active: T,
|
||||||
|
inactive: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Toggleable<T> {
|
||||||
|
pub fn new(active: T, inactive: T) -> Self {
|
||||||
|
Self { active, inactive }
|
||||||
|
}
|
||||||
|
pub fn in_state(&self, active: bool) -> &T {
|
||||||
if active {
|
if active {
|
||||||
if state.hovered() {
|
&self.active
|
||||||
self.hover_and_active
|
} else {
|
||||||
.as_ref()
|
&self.inactive
|
||||||
.unwrap_or(self.active.as_ref().unwrap_or(&self.default))
|
}
|
||||||
} else if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some()
|
}
|
||||||
{
|
pub fn active_state(&self) -> &T {
|
||||||
self.click_and_active
|
self.in_state(true)
|
||||||
.as_ref()
|
}
|
||||||
.unwrap_or(self.active.as_ref().unwrap_or(&self.default))
|
pub fn inactive_state(&self) -> &T {
|
||||||
} else {
|
self.in_state(false)
|
||||||
self.active.as_ref().unwrap_or(&self.default)
|
}
|
||||||
}
|
}
|
||||||
} else if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some() {
|
|
||||||
|
impl<T> Interactive<T> {
|
||||||
|
pub fn style_for(&self, state: &mut MouseState) -> &T {
|
||||||
|
if state.clicked() == Some(platform::MouseButton::Left) && self.clicked.is_some() {
|
||||||
self.clicked.as_ref().unwrap()
|
self.clicked.as_ref().unwrap()
|
||||||
} else if state.hovered() {
|
} else if state.hovered() {
|
||||||
self.hover.as_ref().unwrap_or(&self.default)
|
self.hovered.as_ref().unwrap_or(&self.default)
|
||||||
} else {
|
} else {
|
||||||
&self.default
|
&self.default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disabled_style(&self) -> &T {
|
pub fn disabled_style(&self) -> &T {
|
||||||
self.disabled.as_ref().unwrap_or(&self.default)
|
self.disabled.as_ref().unwrap_or(&self.default)
|
||||||
}
|
}
|
||||||
|
@ -849,13 +857,9 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
|
||||||
{
|
{
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Helper {
|
struct Helper {
|
||||||
#[serde(flatten)]
|
|
||||||
default: Value,
|
default: Value,
|
||||||
hover: Option<Value>,
|
hovered: Option<Value>,
|
||||||
hover_and_active: Option<Value>,
|
|
||||||
clicked: Option<Value>,
|
clicked: Option<Value>,
|
||||||
click_and_active: Option<Value>,
|
|
||||||
active: Option<Value>,
|
|
||||||
disabled: Option<Value>,
|
disabled: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -880,21 +884,15 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let hover = deserialize_state(json.hover)?;
|
let hovered = deserialize_state(json.hovered)?;
|
||||||
let hover_and_active = deserialize_state(json.hover_and_active)?;
|
|
||||||
let clicked = deserialize_state(json.clicked)?;
|
let clicked = deserialize_state(json.clicked)?;
|
||||||
let click_and_active = deserialize_state(json.click_and_active)?;
|
|
||||||
let active = deserialize_state(json.active)?;
|
|
||||||
let disabled = deserialize_state(json.disabled)?;
|
let disabled = deserialize_state(json.disabled)?;
|
||||||
let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
|
let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
|
||||||
|
|
||||||
Ok(Interactive {
|
Ok(Interactive {
|
||||||
default,
|
default,
|
||||||
hover,
|
hovered,
|
||||||
hover_and_active,
|
|
||||||
clicked,
|
clicked,
|
||||||
click_and_active,
|
|
||||||
active,
|
|
||||||
disabled,
|
disabled,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -994,18 +992,33 @@ pub struct TerminalStyle {
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
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)]
|
||||||
|
pub struct SavedConversation {
|
||||||
|
pub container: Interactive<ContainerStyle>,
|
||||||
|
pub saved_at: ContainedText,
|
||||||
|
pub title: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -93,25 +92,6 @@ where
|
||||||
.with_cursor_style(platform::CursorStyle::PointingHand)
|
.with_cursor_style(platform::CursorStyle::PointingHand)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
|
||||||
pub struct SvgStyle {
|
|
||||||
pub color: Color,
|
|
||||||
pub asset: String,
|
|
||||||
pub dimensions: Dimensions,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
|
||||||
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)
|
||||||
|
@ -122,8 +102,8 @@ pub fn svg<V: View>(style: &SvgStyle) -> ConstrainedBox<V> {
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
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> {
|
||||||
|
@ -170,7 +150,7 @@ where
|
||||||
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
||||||
{
|
{
|
||||||
MouseEventHandler::<Tag, V>::new(0, cx, |state, _| {
|
MouseEventHandler::<Tag, V>::new(0, cx, |state, _| {
|
||||||
let style = style.style_for(state, false);
|
let style = style.style_for(state);
|
||||||
Label::new(label, style.text.to_owned())
|
Label::new(label, style.text.to_owned())
|
||||||
.aligned()
|
.aligned()
|
||||||
.contained()
|
.contained()
|
||||||
|
@ -220,13 +200,13 @@ where
|
||||||
title,
|
title,
|
||||||
style
|
style
|
||||||
.title_text
|
.title_text
|
||||||
.style_for(&mut MouseState::default(), false)
|
.style_for(&mut MouseState::default())
|
||||||
.clone(),
|
.clone(),
|
||||||
))
|
))
|
||||||
.with_child(
|
.with_child(
|
||||||
// FIXME: Get a better tag type
|
// FIXME: Get a better tag type
|
||||||
MouseEventHandler::<Tag, V>::new(999999, cx, |state, _cx| {
|
MouseEventHandler::<Tag, V>::new(999999, cx, |state, _cx| {
|
||||||
let style = style.close_icon.style_for(state, false);
|
let style = style.close_icon.style_for(state);
|
||||||
icon(style)
|
icon(style)
|
||||||
})
|
})
|
||||||
.on_click(platform::MouseButton::Left, move |_, _, cx| {
|
.on_click(platform::MouseButton::Left, move |_, _, cx| {
|
||||||
|
|
|
@ -208,7 +208,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> AnyElement<Picker<Self>> {
|
) -> AnyElement<Picker<Self>> {
|
||||||
let theme = theme::current(cx);
|
let theme = theme::current(cx);
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
let theme_match = &self.matches[ix];
|
let theme_match = &self.matches[ix];
|
||||||
Label::new(theme_match.string.clone(), style.label.clone())
|
Label::new(theme_match.string.clone(), style.label.clone())
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "theme_testbench"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/theme_testbench.rs"
|
|
||||||
doctest = false
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gpui = { path = "../gpui" }
|
|
||||||
theme = { path = "../theme" }
|
|
||||||
settings = { path = "../settings" }
|
|
||||||
workspace = { path = "../workspace" }
|
|
||||||
project = { path = "../project" }
|
|
||||||
|
|
||||||
smallvec.workspace = true
|
|
|
@ -1,300 +0,0 @@
|
||||||
use gpui::{
|
|
||||||
actions,
|
|
||||||
color::Color,
|
|
||||||
elements::{
|
|
||||||
AnyElement, Canvas, Container, ContainerStyle, Flex, Label, Margin, MouseEventHandler,
|
|
||||||
Padding, ParentElement,
|
|
||||||
},
|
|
||||||
fonts::TextStyle,
|
|
||||||
AppContext, Border, Element, Entity, ModelHandle, Quad, Task, View, ViewContext, ViewHandle,
|
|
||||||
WeakViewHandle,
|
|
||||||
};
|
|
||||||
use project::Project;
|
|
||||||
use theme::{ColorScheme, Layer, Style, StyleSet, ThemeSettings};
|
|
||||||
use workspace::{item::Item, register_deserializable_item, Pane, Workspace};
|
|
||||||
|
|
||||||
actions!(theme, [DeployThemeTestbench]);
|
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
|
||||||
cx.add_action(ThemeTestbench::deploy);
|
|
||||||
|
|
||||||
register_deserializable_item::<ThemeTestbench>(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ThemeTestbench {}
|
|
||||||
|
|
||||||
impl ThemeTestbench {
|
|
||||||
pub fn deploy(
|
|
||||||
workspace: &mut Workspace,
|
|
||||||
_: &DeployThemeTestbench,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) {
|
|
||||||
let view = cx.add_view(|_| ThemeTestbench {});
|
|
||||||
workspace.add_item(Box::new(view), cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_ramps(color_scheme: &ColorScheme) -> Flex<Self> {
|
|
||||||
fn display_ramp(ramp: &Vec<Color>) -> AnyElement<ThemeTestbench> {
|
|
||||||
Flex::row()
|
|
||||||
.with_children(ramp.iter().cloned().map(|color| {
|
|
||||||
Canvas::new(move |scene, bounds, _, _, _| {
|
|
||||||
scene.push_quad(Quad {
|
|
||||||
bounds,
|
|
||||||
background: Some(color),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.flex(1.0, false)
|
|
||||||
}))
|
|
||||||
.flex(1.0, false)
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
|
|
||||||
Flex::column()
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.neutral))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.red))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.orange))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.yellow))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.green))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.cyan))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.blue))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.violet))
|
|
||||||
.with_child(display_ramp(&color_scheme.ramps.magenta))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_layer(
|
|
||||||
layer_index: usize,
|
|
||||||
layer: &Layer,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Container<Self> {
|
|
||||||
Flex::column()
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(0, layer_index, "base", &layer.base, cx).flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(1, layer_index, "variant", &layer.variant, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(2, layer_index, "on", &layer.on, cx).flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(3, layer_index, "accent", &layer.accent, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(4, layer_index, "positive", &layer.positive, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(5, layer_index, "warning", &layer.warning, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Self::render_button_set(6, layer_index, "negative", &layer.negative, cx)
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.contained()
|
|
||||||
.with_style(ContainerStyle {
|
|
||||||
margin: Margin {
|
|
||||||
top: 10.,
|
|
||||||
bottom: 10.,
|
|
||||||
left: 10.,
|
|
||||||
right: 10.,
|
|
||||||
},
|
|
||||||
background_color: Some(layer.base.default.background),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_button_set(
|
|
||||||
set_index: usize,
|
|
||||||
layer_index: usize,
|
|
||||||
set_name: &'static str,
|
|
||||||
style_set: &StyleSet,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> Flex<Self> {
|
|
||||||
Flex::row()
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6,
|
|
||||||
layer_index,
|
|
||||||
set_name,
|
|
||||||
&style_set,
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 1,
|
|
||||||
layer_index,
|
|
||||||
"hovered",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.hovered),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 2,
|
|
||||||
layer_index,
|
|
||||||
"pressed",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.pressed),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 3,
|
|
||||||
layer_index,
|
|
||||||
"active",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.active),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 4,
|
|
||||||
layer_index,
|
|
||||||
"disabled",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.disabled),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.with_child(Self::render_button(
|
|
||||||
set_index * 6 + 5,
|
|
||||||
layer_index,
|
|
||||||
"inverted",
|
|
||||||
&style_set,
|
|
||||||
Some(|style_set| &style_set.inverted),
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_button(
|
|
||||||
button_index: usize,
|
|
||||||
layer_index: usize,
|
|
||||||
text: &'static str,
|
|
||||||
style_set: &StyleSet,
|
|
||||||
style_override: Option<fn(&StyleSet) -> &Style>,
|
|
||||||
cx: &mut ViewContext<Self>,
|
|
||||||
) -> AnyElement<Self> {
|
|
||||||
enum TestBenchButton {}
|
|
||||||
MouseEventHandler::<TestBenchButton, _>::new(layer_index + button_index, cx, |state, cx| {
|
|
||||||
let style = if let Some(style_override) = style_override {
|
|
||||||
style_override(&style_set)
|
|
||||||
} else if state.clicked().is_some() {
|
|
||||||
&style_set.pressed
|
|
||||||
} else if state.hovered() {
|
|
||||||
&style_set.hovered
|
|
||||||
} else {
|
|
||||||
&style_set.default
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::render_label(text.to_string(), style, cx)
|
|
||||||
.contained()
|
|
||||||
.with_style(ContainerStyle {
|
|
||||||
margin: Margin {
|
|
||||||
top: 4.,
|
|
||||||
bottom: 4.,
|
|
||||||
left: 4.,
|
|
||||||
right: 4.,
|
|
||||||
},
|
|
||||||
padding: Padding {
|
|
||||||
top: 4.,
|
|
||||||
bottom: 4.,
|
|
||||||
left: 4.,
|
|
||||||
right: 4.,
|
|
||||||
},
|
|
||||||
background_color: Some(style.background),
|
|
||||||
border: Border {
|
|
||||||
width: 1.,
|
|
||||||
color: style.border,
|
|
||||||
overlay: false,
|
|
||||||
top: true,
|
|
||||||
bottom: true,
|
|
||||||
left: true,
|
|
||||||
right: true,
|
|
||||||
},
|
|
||||||
corner_radius: 2.,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.flex(1., true)
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_label(text: String, style: &Style, cx: &mut ViewContext<Self>) -> Label {
|
|
||||||
let settings = settings::get::<ThemeSettings>(cx);
|
|
||||||
let font_cache = cx.font_cache();
|
|
||||||
let family_id = settings.buffer_font_family;
|
|
||||||
let font_size = settings.buffer_font_size(cx);
|
|
||||||
let font_id = font_cache
|
|
||||||
.select_font(family_id, &Default::default())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let text_style = TextStyle {
|
|
||||||
color: style.foreground,
|
|
||||||
font_family_id: family_id,
|
|
||||||
font_family_name: font_cache.family_name(family_id).unwrap(),
|
|
||||||
font_id,
|
|
||||||
font_size,
|
|
||||||
font_properties: Default::default(),
|
|
||||||
underline: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Label::new(text, text_style)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entity for ThemeTestbench {
|
|
||||||
type Event = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for ThemeTestbench {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"ThemeTestbench"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> AnyElement<Self> {
|
|
||||||
let color_scheme = &theme::current(cx).clone().color_scheme;
|
|
||||||
|
|
||||||
Flex::row()
|
|
||||||
.with_child(
|
|
||||||
Self::render_ramps(color_scheme)
|
|
||||||
.contained()
|
|
||||||
.with_margin_right(10.)
|
|
||||||
.flex(0.1, false),
|
|
||||||
)
|
|
||||||
.with_child(
|
|
||||||
Flex::column()
|
|
||||||
.with_child(Self::render_layer(100, &color_scheme.lowest, cx).flex(1., true))
|
|
||||||
.with_child(Self::render_layer(200, &color_scheme.middle, cx).flex(1., true))
|
|
||||||
.with_child(Self::render_layer(300, &color_scheme.highest, cx).flex(1., true))
|
|
||||||
.flex(1., false),
|
|
||||||
)
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Item for ThemeTestbench {
|
|
||||||
fn tab_content<T: View>(
|
|
||||||
&self,
|
|
||||||
_: Option<usize>,
|
|
||||||
style: &theme::Tab,
|
|
||||||
_: &AppContext,
|
|
||||||
) -> AnyElement<T> {
|
|
||||||
Label::new("Theme Testbench", style.label.clone())
|
|
||||||
.aligned()
|
|
||||||
.contained()
|
|
||||||
.into_any()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialized_item_kind() -> Option<&'static str> {
|
|
||||||
Some("ThemeTestBench")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize(
|
|
||||||
_project: ModelHandle<Project>,
|
|
||||||
_workspace: WeakViewHandle<Workspace>,
|
|
||||||
_workspace_id: workspace::WorkspaceId,
|
|
||||||
_item_id: workspace::ItemId,
|
|
||||||
cx: &mut ViewContext<Pane>,
|
|
||||||
) -> Task<gpui::anyhow::Result<ViewHandle<Self>>> {
|
|
||||||
Task::ready(Ok(cx.add_view(|_| Self {})))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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");
|
||||||
|
|
|
@ -141,7 +141,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
|
||||||
) -> gpui::AnyElement<Picker<Self>> {
|
) -> gpui::AnyElement<Picker<Self>> {
|
||||||
let theme = &theme::current(cx);
|
let theme = &theme::current(cx);
|
||||||
let keymap_match = &self.matches[ix];
|
let keymap_match = &self.matches[ix];
|
||||||
let style = theme.picker.item.style_for(mouse_state, selected);
|
let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||||
|
|
||||||
Label::new(keymap_match.string.clone(), style.label.clone())
|
Label::new(keymap_match.string.clone(), style.label.clone())
|
||||||
.with_highlights(keymap_match.positions.clone())
|
.with_highlights(keymap_match.positions.clone())
|
||||||
|
|
|
@ -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| {
|
||||||
|
@ -498,7 +498,9 @@ impl View for PanelButtons {
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
|
MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
|
||||||
let style = button_style.style_for(state, is_active);
|
let style = button_style.in_state(is_active);
|
||||||
|
|
||||||
|
let style = style.style_for(state);
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
Svg::new(view.icon_path(cx))
|
Svg::new(view.icon_path(cx))
|
||||||
|
@ -603,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,
|
||||||
|
|
|
@ -291,7 +291,7 @@ pub mod simple_message_notification {
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<Cancel, _>::new(0, cx, |state, _| {
|
||||||
let style = theme.dismiss_button.style_for(state, false);
|
let style = theme.dismiss_button.style_for(state);
|
||||||
Svg::new("icons/x_mark_8.svg")
|
Svg::new("icons/x_mark_8.svg")
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -323,7 +323,7 @@ pub mod simple_message_notification {
|
||||||
0,
|
0,
|
||||||
cx,
|
cx,
|
||||||
|state, _| {
|
|state, _| {
|
||||||
let style = theme.action_message.style_for(state, false);
|
let style = theme.action_message.style_for(state);
|
||||||
|
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -285,19 +286,27 @@ impl Pane {
|
||||||
pane.tab_bar_context_menu
|
pane.tab_bar_context_menu
|
||||||
.handle_if_kind(TabBarContextMenuKind::Split),
|
.handle_if_kind(TabBarContextMenuKind::Split),
|
||||||
))
|
))
|
||||||
.with_child(Pane::render_tab_bar_button(
|
.with_child({
|
||||||
2,
|
let icon_path;
|
||||||
|
let tooltip_label;
|
||||||
if pane.is_zoomed() {
|
if pane.is_zoomed() {
|
||||||
"icons/minimize_8.svg"
|
icon_path = "icons/minimize_8.svg";
|
||||||
|
tooltip_label = "Zoom In".into();
|
||||||
} else {
|
} else {
|
||||||
"icons/maximize_8.svg"
|
icon_path = "icons/maximize_8.svg";
|
||||||
},
|
tooltip_label = "Zoom In".into();
|
||||||
pane.is_zoomed(),
|
}
|
||||||
Some(("Toggle Zoom".into(), Some(Box::new(ToggleZoom)))),
|
|
||||||
cx,
|
Pane::render_tab_bar_button(
|
||||||
move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
|
2,
|
||||||
None,
|
icon_path,
|
||||||
))
|
pane.is_zoomed(),
|
||||||
|
Some((tooltip_label, Some(Box::new(ToggleZoom)))),
|
||||||
|
cx,
|
||||||
|
move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
.into_any()
|
.into_any()
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -1112,7 +1121,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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1410,7 +1419,7 @@ impl Pane {
|
||||||
pub fn render_tab_bar_button<F: 'static + Fn(&mut Pane, &mut EventContext<Pane>)>(
|
pub fn render_tab_bar_button<F: 'static + Fn(&mut Pane, &mut EventContext<Pane>)>(
|
||||||
index: usize,
|
index: usize,
|
||||||
icon: &'static str,
|
icon: &'static str,
|
||||||
active: bool,
|
is_active: bool,
|
||||||
tooltip: Option<(String, Option<Box<dyn Action>>)>,
|
tooltip: Option<(String, Option<Box<dyn Action>>)>,
|
||||||
cx: &mut ViewContext<Pane>,
|
cx: &mut ViewContext<Pane>,
|
||||||
on_click: F,
|
on_click: F,
|
||||||
|
@ -1420,7 +1429,7 @@ impl Pane {
|
||||||
|
|
||||||
let mut button = MouseEventHandler::<TabBarButton, _>::new(index, cx, |mouse_state, cx| {
|
let mut button = MouseEventHandler::<TabBarButton, _>::new(index, cx, |mouse_state, cx| {
|
||||||
let theme = &settings::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
|
let theme = &settings::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
|
||||||
let style = theme.pane_button.style_for(mouse_state, active);
|
let style = theme.pane_button.in_state(is_active).style_for(mouse_state);
|
||||||
Svg::new(icon)
|
Svg::new(icon)
|
||||||
.with_color(style.color)
|
.with_color(style.color)
|
||||||
.constrained()
|
.constrained()
|
||||||
|
@ -1602,7 +1611,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 +1640,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);
|
});
|
||||||
});
|
})
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -231,7 +225,7 @@ fn nav_button<A: Action, F: 'static + Fn(&mut Toolbar, &mut ViewContext<Toolbar>
|
||||||
) -> AnyElement<Toolbar> {
|
) -> AnyElement<Toolbar> {
|
||||||
MouseEventHandler::<A, _>::new(0, cx, |state, _| {
|
MouseEventHandler::<A, _>::new(0, cx, |state, _| {
|
||||||
let style = if enabled {
|
let style = if enabled {
|
||||||
style.style_for(state, false)
|
style.style_for(state)
|
||||||
} else {
|
} else {
|
||||||
style.disabled_style()
|
style.disabled_style()
|
||||||
};
|
};
|
||||||
|
@ -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>,
|
||||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||||
description = "The fast, collaborative code editor."
|
description = "The fast, collaborative code editor."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.92.0"
|
version = "0.92.4"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -62,7 +62,6 @@ text = { path = "../text" }
|
||||||
terminal_view = { path = "../terminal_view" }
|
terminal_view = { path = "../terminal_view" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
theme_selector = { path = "../theme_selector" }
|
theme_selector = { path = "../theme_selector" }
|
||||||
theme_testbench = { path = "../theme_testbench" }
|
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
vim = { path = "../vim" }
|
vim = { path = "../vim" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
dev
|
stable
|
|
@ -62,16 +62,23 @@ impl LspAdapter for ElixirLspAdapter {
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||||
let release =
|
let http = delegate.http_client();
|
||||||
latest_github_release("elixir-lsp/elixir-ls", false, delegate.http_client()).await?;
|
let release = latest_github_release("elixir-lsp/elixir-ls", false, http).await?;
|
||||||
let asset_name = "elixir-ls.zip";
|
let version_name = release
|
||||||
|
.name
|
||||||
|
.strip_prefix("Release ")
|
||||||
|
.context("Elixir-ls release name does not start with prefix")?
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let asset_name = format!("elixir-ls-{}.zip", &version_name);
|
||||||
let asset = release
|
let asset = release
|
||||||
.assets
|
.assets
|
||||||
.iter()
|
.iter()
|
||||||
.find(|asset| asset.name == asset_name)
|
.find(|asset| asset.name == asset_name)
|
||||||
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||||
|
|
||||||
let version = GitHubLspBinaryVersion {
|
let version = GitHubLspBinaryVersion {
|
||||||
name: release.name,
|
name: version_name,
|
||||||
url: asset.browser_download_url.clone(),
|
url: asset.browser_download_url.clone(),
|
||||||
};
|
};
|
||||||
Ok(Box::new(version) as Box<_>)
|
Ok(Box::new(version) as Box<_>)
|
||||||
|
@ -116,7 +123,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||||
.await?
|
.await?
|
||||||
.status;
|
.status;
|
||||||
if !unzip_status.success() {
|
if !unzip_status.success() {
|
||||||
Err(anyhow!("failed to unzip clangd archive"))?;
|
Err(anyhow!("failed to unzip elixir-ls archive"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||||
|
|
|
@ -9,7 +9,5 @@
|
||||||
(#set! combined)
|
(#set! combined)
|
||||||
)
|
)
|
||||||
|
|
||||||
; expressions live within HTML tags, and do not need to be combined
|
|
||||||
; <link href={ Routes.static_path(..) } />
|
|
||||||
((expression (expression_value) @content)
|
((expression (expression_value) @content)
|
||||||
(#set! language "elixir"))
|
(#set! language "elixir"))
|
||||||
|
|
|
@ -70,8 +70,6 @@ fn main() {
|
||||||
|
|
||||||
init_panic_hook(&app);
|
init_panic_hook(&app);
|
||||||
|
|
||||||
app.background();
|
|
||||||
|
|
||||||
load_embedded_fonts(&app);
|
load_embedded_fonts(&app);
|
||||||
|
|
||||||
let fs = Arc::new(RealFs);
|
let fs = Arc::new(RealFs);
|
||||||
|
@ -154,7 +152,6 @@ fn main() {
|
||||||
search::init(cx);
|
search::init(cx);
|
||||||
vim::init(cx);
|
vim::init(cx);
|
||||||
terminal_view::init(cx);
|
terminal_view::init(cx);
|
||||||
theme_testbench::init(cx);
|
|
||||||
copilot::init(http.clone(), node_runtime, cx);
|
copilot::init(http.clone(), node_runtime, cx);
|
||||||
ai::init(cx);
|
ai::init(cx);
|
||||||
|
|
||||||
|
|
|
@ -361,15 +361,15 @@ pub fn initialize_workspace(
|
||||||
|
|
||||||
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
|
let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
|
let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
let assistant_panel = if *util::channel::RELEASE_CHANNEL == ReleaseChannel::Stable {
|
let assistant_panel = AssistantPanel::load(workspace_handle.clone(), cx.clone());
|
||||||
None
|
let (project_panel, terminal_panel, assistant_panel) =
|
||||||
} else {
|
futures::try_join!(project_panel, terminal_panel, assistant_panel)?;
|
||||||
Some(AssistantPanel::load(workspace_handle.clone(), cx.clone()).await?)
|
|
||||||
};
|
|
||||||
let (project_panel, terminal_panel) = futures::try_join!(project_panel, terminal_panel)?;
|
|
||||||
workspace_handle.update(&mut cx, |workspace, cx| {
|
workspace_handle.update(&mut cx, |workspace, cx| {
|
||||||
let project_panel_position = project_panel.position(cx);
|
let project_panel_position = project_panel.position(cx);
|
||||||
workspace.add_panel(project_panel, cx);
|
workspace.add_panel(project_panel, cx);
|
||||||
|
workspace.add_panel(terminal_panel, cx);
|
||||||
|
workspace.add_panel(assistant_panel, cx);
|
||||||
|
|
||||||
if !was_deserialized
|
if !was_deserialized
|
||||||
&& workspace
|
&& workspace
|
||||||
.project()
|
.project()
|
||||||
|
@ -383,13 +383,7 @@ pub fn initialize_workspace(
|
||||||
{
|
{
|
||||||
workspace.toggle_dock(project_panel_position, cx);
|
workspace.toggle_dock(project_panel_position, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.focus_self();
|
cx.focus_self();
|
||||||
|
|
||||||
workspace.add_panel(terminal_panel, cx);
|
|
||||||
if let Some(assistant_panel) = assistant_panel {
|
|
||||||
workspace.add_panel(assistant_panel, cx);
|
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
1
styles/.gitignore
vendored
1
styles/.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
|
coverage/
|
||||||
|
|
20
styles/.zed/settings.json
Normal file
20
styles/.zed/settings.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Folder-specific settings
|
||||||
|
//
|
||||||
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
|
// see the documentation: https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings
|
||||||
|
{
|
||||||
|
"languages": {
|
||||||
|
"TypeScript": {
|
||||||
|
"tab_size": 4
|
||||||
|
},
|
||||||
|
"TSX": {
|
||||||
|
"tab_size": 4
|
||||||
|
},
|
||||||
|
"JavaScript": {
|
||||||
|
"tab_size": 4
|
||||||
|
},
|
||||||
|
"JSON": {
|
||||||
|
"tab_size": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2334
styles/package-lock.json
generated
2334
styles/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,8 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ts-node ./src/buildThemes.ts",
|
"build": "ts-node ./src/buildThemes.ts",
|
||||||
"build-licenses": "ts-node ./src/buildLicenses.ts",
|
"build-licenses": "ts-node ./src/buildLicenses.ts",
|
||||||
"build-tokens": "ts-node ./src/buildTokens.ts"
|
"build-tokens": "ts-node ./src/buildTokens.ts",
|
||||||
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -20,12 +21,18 @@
|
||||||
"chroma-js": "^2.4.2",
|
"chroma-js": "^2.4.2",
|
||||||
"deepmerge": "^4.3.0",
|
"deepmerge": "^4.3.0",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"ts-node": "^10.9.1"
|
"ts-deepmerge": "^6.0.3",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"utility-types": "^3.10.0",
|
||||||
|
"vitest": "^0.32.0"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"printWidth": 80,
|
"printWidth": 80,
|
||||||
"htmlWhitespaceSensitivity": "strict",
|
"htmlWhitespaceSensitivity": "strict",
|
||||||
"tabWidth": 4
|
"tabWidth": 4
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitest/coverage-v8": "^0.32.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import * as fs from "fs";
|
import * as fs from "fs"
|
||||||
import * as path from "path";
|
import * as path from "path"
|
||||||
import { ColorScheme, createColorScheme } from "./common";
|
import { ColorScheme, createColorScheme } from "./common"
|
||||||
import { themes } from "./themes";
|
import { themes } from "./themes"
|
||||||
import { slugify } from "./utils/slugify";
|
import { slugify } from "./utils/slugify"
|
||||||
import { colorSchemeTokens } from "./theme/tokens/colorScheme";
|
import { colorSchemeTokens } from "./theme/tokens/colorScheme"
|
||||||
|
|
||||||
const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens");
|
const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens")
|
||||||
const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json");
|
const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json")
|
||||||
const METADATA_FILE = path.join(TOKENS_DIRECTORY, "$metadata.json");
|
const METADATA_FILE = path.join(TOKENS_DIRECTORY, "$metadata.json")
|
||||||
|
|
||||||
function clearTokens(tokensDirectory: string) {
|
function clearTokens(tokensDirectory: string) {
|
||||||
if (!fs.existsSync(tokensDirectory)) {
|
if (!fs.existsSync(tokensDirectory)) {
|
||||||
|
@ -22,64 +22,66 @@ function clearTokens(tokensDirectory: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenSet = {
|
type TokenSet = {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
selectedTokenSets: { [key: string]: "enabled" };
|
selectedTokenSets: { [key: string]: "enabled" }
|
||||||
};
|
}
|
||||||
|
|
||||||
function buildTokenSetOrder(colorSchemes: ColorScheme[]): { tokenSetOrder: string[] } {
|
function buildTokenSetOrder(colorSchemes: ColorScheme[]): {
|
||||||
const tokenSetOrder: string[] = colorSchemes.map(
|
tokenSetOrder: string[]
|
||||||
(scheme) => scheme.name.toLowerCase().replace(/\s+/g, "_")
|
} {
|
||||||
);
|
const tokenSetOrder: string[] = colorSchemes.map((scheme) =>
|
||||||
return { tokenSetOrder };
|
scheme.name.toLowerCase().replace(/\s+/g, "_")
|
||||||
|
)
|
||||||
|
return { tokenSetOrder }
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildThemesIndex(colorSchemes: ColorScheme[]): TokenSet[] {
|
function buildThemesIndex(colorSchemes: ColorScheme[]): TokenSet[] {
|
||||||
const themesIndex: TokenSet[] = colorSchemes.map((scheme, index) => {
|
const themesIndex: TokenSet[] = colorSchemes.map((scheme, index) => {
|
||||||
const id = `${scheme.isLight ? "light" : "dark"}_${scheme.name
|
const id = `${scheme.isLight ? "light" : "dark"}_${scheme.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/\s+/g, "_")}_${index}`;
|
.replace(/\s+/g, "_")}_${index}`
|
||||||
const selectedTokenSets: { [key: string]: "enabled" } = {};
|
const selectedTokenSets: { [key: string]: "enabled" } = {}
|
||||||
const tokenSet = scheme.name.toLowerCase().replace(/\s+/g, "_");
|
const tokenSet = scheme.name.toLowerCase().replace(/\s+/g, "_")
|
||||||
selectedTokenSets[tokenSet] = "enabled";
|
selectedTokenSets[tokenSet] = "enabled"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name: `${scheme.name} - ${scheme.isLight ? "Light" : "Dark"}`,
|
name: `${scheme.name} - ${scheme.isLight ? "Light" : "Dark"}`,
|
||||||
selectedTokenSets,
|
selectedTokenSets,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
return themesIndex;
|
return themesIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeTokens(colorSchemes: ColorScheme[], tokensDirectory: string) {
|
function writeTokens(colorSchemes: ColorScheme[], tokensDirectory: string) {
|
||||||
clearTokens(tokensDirectory);
|
clearTokens(tokensDirectory)
|
||||||
|
|
||||||
for (const colorScheme of colorSchemes) {
|
for (const colorScheme of colorSchemes) {
|
||||||
const fileName = slugify(colorScheme.name) + ".json";
|
const fileName = slugify(colorScheme.name) + ".json"
|
||||||
const tokens = colorSchemeTokens(colorScheme);
|
const tokens = colorSchemeTokens(colorScheme)
|
||||||
const tokensJSON = JSON.stringify(tokens, null, 2);
|
const tokensJSON = JSON.stringify(tokens, null, 2)
|
||||||
const outPath = path.join(tokensDirectory, fileName);
|
const outPath = path.join(tokensDirectory, fileName)
|
||||||
fs.writeFileSync(outPath, tokensJSON, { mode: 0o644 });
|
fs.writeFileSync(outPath, tokensJSON, { mode: 0o644 })
|
||||||
console.log(`- ${outPath} created`);
|
console.log(`- ${outPath} created`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const themeIndexData = buildThemesIndex(colorSchemes);
|
const themeIndexData = buildThemesIndex(colorSchemes)
|
||||||
|
|
||||||
const themesJSON = JSON.stringify(themeIndexData, null, 2);
|
const themesJSON = JSON.stringify(themeIndexData, null, 2)
|
||||||
fs.writeFileSync(TOKENS_FILE, themesJSON, { mode: 0o644 });
|
fs.writeFileSync(TOKENS_FILE, themesJSON, { mode: 0o644 })
|
||||||
console.log(`- ${TOKENS_FILE} created`);
|
console.log(`- ${TOKENS_FILE} created`)
|
||||||
|
|
||||||
const tokenSetOrderData = buildTokenSetOrder(colorSchemes);
|
const tokenSetOrderData = buildTokenSetOrder(colorSchemes)
|
||||||
|
|
||||||
const metadataJSON = JSON.stringify(tokenSetOrderData, null, 2);
|
const metadataJSON = JSON.stringify(tokenSetOrderData, null, 2)
|
||||||
fs.writeFileSync(METADATA_FILE, metadataJSON, { mode: 0o644 });
|
fs.writeFileSync(METADATA_FILE, metadataJSON, { mode: 0o644 })
|
||||||
console.log(`- ${METADATA_FILE} created`);
|
console.log(`- ${METADATA_FILE} created`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const colorSchemes: ColorScheme[] = themes.map((theme) =>
|
const colorSchemes: ColorScheme[] = themes.map((theme) =>
|
||||||
createColorScheme(theme)
|
createColorScheme(theme)
|
||||||
);
|
)
|
||||||
|
|
||||||
writeTokens(colorSchemes, TOKENS_DIRECTORY);
|
writeTokens(colorSchemes, TOKENS_DIRECTORY)
|
||||||
|
|
86
styles/src/component/icon_button.ts
Normal file
86
styles/src/component/icon_button.ts
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { ColorScheme } from "../common"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
import { background, foreground } from "../styleTree/components"
|
||||||
|
|
||||||
|
export type Margin = {
|
||||||
|
top: number
|
||||||
|
bottom: number
|
||||||
|
left: number
|
||||||
|
right: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IconButtonOptions {
|
||||||
|
layer?:
|
||||||
|
| ColorScheme["lowest"]
|
||||||
|
| ColorScheme["middle"]
|
||||||
|
| ColorScheme["highest"]
|
||||||
|
color?: keyof ColorScheme["lowest"]
|
||||||
|
margin?: Partial<Margin>
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToggleableIconButtonOptions = IconButtonOptions & {
|
||||||
|
active_color?: keyof ColorScheme["lowest"]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function icon_button(
|
||||||
|
theme: ColorScheme,
|
||||||
|
{ color, margin, layer }: IconButtonOptions
|
||||||
|
) {
|
||||||
|
if (!color) color = "base"
|
||||||
|
|
||||||
|
const m = {
|
||||||
|
top: margin?.top ?? 0,
|
||||||
|
bottom: margin?.bottom ?? 0,
|
||||||
|
left: margin?.left ?? 0,
|
||||||
|
right: margin?.right ?? 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return interactive({
|
||||||
|
base: {
|
||||||
|
corner_radius: 6,
|
||||||
|
padding: {
|
||||||
|
top: 2,
|
||||||
|
bottom: 2,
|
||||||
|
left: 4,
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
margin: m,
|
||||||
|
icon_width: 14,
|
||||||
|
icon_height: 14,
|
||||||
|
button_width: 20,
|
||||||
|
button_height: 16,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
default: {
|
||||||
|
background: background(layer ?? theme.lowest, color),
|
||||||
|
color: foreground(layer ?? theme.lowest, color),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: background(layer ?? theme.lowest, color, "hovered"),
|
||||||
|
color: foreground(layer ?? theme.lowest, color, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer ?? theme.lowest, color, "pressed"),
|
||||||
|
color: foreground(layer ?? theme.lowest, color, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleable_icon_button(
|
||||||
|
theme: ColorScheme,
|
||||||
|
{ color, active_color, margin }: ToggleableIconButtonOptions
|
||||||
|
) {
|
||||||
|
if (!color) color = "base"
|
||||||
|
|
||||||
|
return toggleable({
|
||||||
|
state: {
|
||||||
|
inactive: icon_button(theme, { color, margin }),
|
||||||
|
active: icon_button(theme, {
|
||||||
|
color: active_color ? active_color : color,
|
||||||
|
margin,
|
||||||
|
layer: theme.middle,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
90
styles/src/component/text_button.ts
Normal file
90
styles/src/component/text_button.ts
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import { ColorScheme } from "../common"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
import {
|
||||||
|
TextProperties,
|
||||||
|
background,
|
||||||
|
foreground,
|
||||||
|
text,
|
||||||
|
} from "../styleTree/components"
|
||||||
|
import { Margin } from "./icon_button"
|
||||||
|
|
||||||
|
interface TextButtonOptions {
|
||||||
|
layer?:
|
||||||
|
| ColorScheme["lowest"]
|
||||||
|
| ColorScheme["middle"]
|
||||||
|
| ColorScheme["highest"]
|
||||||
|
color?: keyof ColorScheme["lowest"]
|
||||||
|
margin?: Partial<Margin>
|
||||||
|
text_properties?: TextProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToggleableTextButtonOptions = TextButtonOptions & {
|
||||||
|
active_color?: keyof ColorScheme["lowest"]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function text_button(
|
||||||
|
theme: ColorScheme,
|
||||||
|
{ color, layer, margin, text_properties }: TextButtonOptions
|
||||||
|
) {
|
||||||
|
if (!color) color = "base"
|
||||||
|
|
||||||
|
const text_options: TextProperties = {
|
||||||
|
size: "xs",
|
||||||
|
weight: "normal",
|
||||||
|
...text_properties,
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = {
|
||||||
|
top: margin?.top ?? 0,
|
||||||
|
bottom: margin?.bottom ?? 0,
|
||||||
|
left: margin?.left ?? 0,
|
||||||
|
right: margin?.right ?? 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return interactive({
|
||||||
|
base: {
|
||||||
|
corner_radius: 6,
|
||||||
|
padding: {
|
||||||
|
top: 1,
|
||||||
|
bottom: 1,
|
||||||
|
left: 6,
|
||||||
|
right: 6,
|
||||||
|
},
|
||||||
|
margin: m,
|
||||||
|
button_height: 22,
|
||||||
|
...text(layer ?? theme.lowest, "sans", color, text_options),
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
default: {
|
||||||
|
background: background(layer ?? theme.lowest, color),
|
||||||
|
color: foreground(layer ?? theme.lowest, color),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: background(layer ?? theme.lowest, color, "hovered"),
|
||||||
|
color: foreground(layer ?? theme.lowest, color, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer ?? theme.lowest, color, "pressed"),
|
||||||
|
color: foreground(layer ?? theme.lowest, color, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleable_text_button(
|
||||||
|
theme: ColorScheme,
|
||||||
|
{ color, active_color, margin }: ToggleableTextButtonOptions
|
||||||
|
) {
|
||||||
|
if (!color) color = "base"
|
||||||
|
|
||||||
|
return toggleable({
|
||||||
|
state: {
|
||||||
|
inactive: text_button(theme, { color, margin }),
|
||||||
|
active: text_button(theme, {
|
||||||
|
color: active_color ? active_color : color,
|
||||||
|
margin,
|
||||||
|
layer: theme.middle,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
4
styles/src/element/index.ts
Normal file
4
styles/src/element/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { interactive } from "./interactive"
|
||||||
|
import { toggleable } from "./toggle"
|
||||||
|
|
||||||
|
export { interactive, toggleable }
|
56
styles/src/element/interactive.test.ts
Normal file
56
styles/src/element/interactive.test.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import {
|
||||||
|
NOT_ENOUGH_STATES_ERROR,
|
||||||
|
NO_DEFAULT_OR_BASE_ERROR,
|
||||||
|
interactive,
|
||||||
|
} from "./interactive"
|
||||||
|
import { describe, it, expect } from "vitest"
|
||||||
|
|
||||||
|
describe("interactive", () => {
|
||||||
|
it("creates an Interactive<Element> with base properties and states", () => {
|
||||||
|
const result = interactive({
|
||||||
|
base: { fontSize: 10, color: "#FFFFFF" },
|
||||||
|
state: {
|
||||||
|
hovered: { color: "#EEEEEE" },
|
||||||
|
clicked: { color: "#CCCCCC" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
default: { color: "#FFFFFF", fontSize: 10 },
|
||||||
|
hovered: { color: "#EEEEEE", fontSize: 10 },
|
||||||
|
clicked: { color: "#CCCCCC", fontSize: 10 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates an Interactive<Element> with no base properties", () => {
|
||||||
|
const result = interactive({
|
||||||
|
state: {
|
||||||
|
default: { color: "#FFFFFF", fontSize: 10 },
|
||||||
|
hovered: { color: "#EEEEEE" },
|
||||||
|
clicked: { color: "#CCCCCC" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
default: { color: "#FFFFFF", fontSize: 10 },
|
||||||
|
hovered: { color: "#EEEEEE", fontSize: 10 },
|
||||||
|
clicked: { color: "#CCCCCC", fontSize: 10 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when both default and base are missing", () => {
|
||||||
|
const state = {
|
||||||
|
hovered: { color: "blue" },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => interactive({ state })).toThrow(NO_DEFAULT_OR_BASE_ERROR)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when no other state besides default is present", () => {
|
||||||
|
const state = {
|
||||||
|
default: { fontSize: 10 },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => interactive({ state })).toThrow(NOT_ENOUGH_STATES_ERROR)
|
||||||
|
})
|
||||||
|
})
|
97
styles/src/element/interactive.ts
Normal file
97
styles/src/element/interactive.ts
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import merge from "ts-deepmerge"
|
||||||
|
import { DeepPartial } from "utility-types"
|
||||||
|
|
||||||
|
export type InteractiveState =
|
||||||
|
| "default"
|
||||||
|
| "hovered"
|
||||||
|
| "clicked"
|
||||||
|
| "selected"
|
||||||
|
| "disabled"
|
||||||
|
|
||||||
|
type Interactive<T> = {
|
||||||
|
default: T
|
||||||
|
hovered?: T
|
||||||
|
clicked?: T
|
||||||
|
selected?: T
|
||||||
|
disabled?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NO_DEFAULT_OR_BASE_ERROR =
|
||||||
|
"An interactive object must have a default state, or a base property."
|
||||||
|
export const NOT_ENOUGH_STATES_ERROR =
|
||||||
|
"An interactive object must have a default and at least one other state."
|
||||||
|
|
||||||
|
interface InteractiveProps<T> {
|
||||||
|
base?: T
|
||||||
|
state: Partial<Record<InteractiveState, DeepPartial<T>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for creating Interactive<T> objects that works with Toggle<T>-like behavior.
|
||||||
|
* It takes a default object to be used as the value for `default` field and fills out other fields
|
||||||
|
* with fields from either `base` or from the `state` object which contains values for specific states.
|
||||||
|
* Notably, it does not touch `hover`, `clicked`, `selected` and `disabled` states if there are no modifications for them.
|
||||||
|
*
|
||||||
|
* @param defaultObj Object to be used as the value for the `default` field.
|
||||||
|
* @param base Optional object containing base fields to be included in the resulting object.
|
||||||
|
* @param state Object containing optional modified fields to be included in the resulting object for each state.
|
||||||
|
* @returns Interactive<T> object with fields from `base` and `state`.
|
||||||
|
*/
|
||||||
|
export function interactive<T extends Object>({
|
||||||
|
base,
|
||||||
|
state,
|
||||||
|
}: InteractiveProps<T>): Interactive<T> {
|
||||||
|
if (!base && !state.default) throw new Error(NO_DEFAULT_OR_BASE_ERROR)
|
||||||
|
|
||||||
|
let defaultState: T
|
||||||
|
|
||||||
|
if (state.default && base) {
|
||||||
|
defaultState = merge(base, state.default) as T
|
||||||
|
} else {
|
||||||
|
defaultState = base ? base : (state.default as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
let interactiveObj: Interactive<T> = {
|
||||||
|
default: defaultState,
|
||||||
|
}
|
||||||
|
|
||||||
|
let stateCount = 0
|
||||||
|
|
||||||
|
if (state.hovered !== undefined) {
|
||||||
|
interactiveObj.hovered = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.hovered
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.clicked !== undefined) {
|
||||||
|
interactiveObj.clicked = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.clicked
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.selected !== undefined) {
|
||||||
|
interactiveObj.selected = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.selected
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.disabled !== undefined) {
|
||||||
|
interactiveObj.disabled = merge(
|
||||||
|
interactiveObj.default,
|
||||||
|
state.disabled
|
||||||
|
) as T
|
||||||
|
stateCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateCount < 1) {
|
||||||
|
throw new Error(NOT_ENOUGH_STATES_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
return interactiveObj
|
||||||
|
}
|
52
styles/src/element/toggle.test.ts
Normal file
52
styles/src/element/toggle.test.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import {
|
||||||
|
NO_ACTIVE_ERROR,
|
||||||
|
NO_INACTIVE_OR_BASE_ERROR,
|
||||||
|
toggleable,
|
||||||
|
} from "./toggle"
|
||||||
|
import { describe, it, expect } from "vitest"
|
||||||
|
|
||||||
|
describe("toggleable", () => {
|
||||||
|
it("creates a Toggleable<Element> with base properties and states", () => {
|
||||||
|
const result = toggleable({
|
||||||
|
base: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
state: {
|
||||||
|
active: { color: "#FFFFFF" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates a Toggleable<Element> with no base properties", () => {
|
||||||
|
const result = toggleable({
|
||||||
|
state: {
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when both inactive and base are missing", () => {
|
||||||
|
const state = {
|
||||||
|
active: { background: "#000000", color: "#FFFFFF" },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => toggleable({ state })).toThrow(NO_INACTIVE_OR_BASE_ERROR)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("throws error when no active state is present", () => {
|
||||||
|
const state = {
|
||||||
|
inactive: { background: "#000000", color: "#CCCCCC" },
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => toggleable({ state })).toThrow(NO_ACTIVE_ERROR)
|
||||||
|
})
|
||||||
|
})
|
47
styles/src/element/toggle.ts
Normal file
47
styles/src/element/toggle.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import merge from "ts-deepmerge"
|
||||||
|
import { DeepPartial } from "utility-types"
|
||||||
|
|
||||||
|
type ToggleState = "inactive" | "active"
|
||||||
|
|
||||||
|
type Toggleable<T> = Record<ToggleState, T>
|
||||||
|
|
||||||
|
export const NO_INACTIVE_OR_BASE_ERROR =
|
||||||
|
"A toggleable object must have an inactive state, or a base property."
|
||||||
|
export const NO_ACTIVE_ERROR = "A toggleable object must have an active state."
|
||||||
|
|
||||||
|
interface ToggleableProps<T> {
|
||||||
|
base?: T
|
||||||
|
state: Partial<Record<ToggleState, DeepPartial<T>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for creating Toggleable objects.
|
||||||
|
* @template T The type of the object being toggled.
|
||||||
|
* @param props Object containing the base (inactive) state and state modifications to create the active state.
|
||||||
|
* @returns A Toggleable object containing both the inactive and active states.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* toggleable({
|
||||||
|
* base: { background: "#000000", text: "#CCCCCC" },
|
||||||
|
* state: { active: { text: "#CCCCCC" } },
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function toggleable<T extends object>(
|
||||||
|
props: ToggleableProps<T>
|
||||||
|
): Toggleable<T> {
|
||||||
|
const { base, state } = props
|
||||||
|
|
||||||
|
if (!base && !state.inactive) throw new Error(NO_INACTIVE_OR_BASE_ERROR)
|
||||||
|
if (!state.active) throw new Error(NO_ACTIVE_ERROR)
|
||||||
|
|
||||||
|
const inactiveState = base
|
||||||
|
? ((state.inactive ? merge(base, state.inactive) : base) as T)
|
||||||
|
: (state.inactive as T)
|
||||||
|
|
||||||
|
const toggleObj: Toggleable<T> = {
|
||||||
|
inactive: inactiveState,
|
||||||
|
active: merge(base ?? {}, state.active) as T,
|
||||||
|
}
|
||||||
|
return toggleObj
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import { text } from "./components"
|
|
||||||
import contactFinder from "./contactFinder"
|
import contactFinder from "./contactFinder"
|
||||||
import contactsPopover from "./contactsPopover"
|
import contactsPopover from "./contactsPopover"
|
||||||
import commandPalette from "./commandPalette"
|
import commandPalette from "./commandPalette"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { text, border, background, foreground } from "./components"
|
import { text, border, background, foreground } from "./components"
|
||||||
import editor from "./editor"
|
import editor from "./editor"
|
||||||
|
import { interactive } from "../element"
|
||||||
|
|
||||||
export default function assistant(colorScheme: ColorScheme) {
|
export default function assistant(colorScheme: ColorScheme) {
|
||||||
const layer = colorScheme.highest
|
const layer = colorScheme.highest
|
||||||
|
@ -9,50 +10,245 @@ 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 }),
|
|
||||||
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: {
|
||||||
|
padding: { left: 12, right: 8.5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
padding: { left: 8.5, right: 8.5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
padding: { left: 8.5, right: 8.5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
padding: { left: 8.5, right: 8.5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
padding: { left: 10, right: 10 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
padding: { left: 10, right: 10 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
padding: { left: 10, right: 10 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
title: {
|
||||||
|
...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: {
|
||||||
...text(layer, "sans", "default", { size: "sm", weight: "bold" }),
|
default: {
|
||||||
|
...text(layer, "sans", "default", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
assistantSender: {
|
assistantSender: {
|
||||||
...text(layer, "sans", "accent", { size: "sm", weight: "bold" }),
|
default: {
|
||||||
|
...text(layer, "sans", "accent", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
systemSender: {
|
systemSender: {
|
||||||
...text(layer, "sans", "variant", { size: "sm", weight: "bold" }),
|
default: {
|
||||||
|
...text(layer, "sans", "variant", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
sentAt: {
|
sentAt: {
|
||||||
margin: { top: 2, left: 8 },
|
margin: { top: 2, left: 8 },
|
||||||
...text(layer, "sans", "default", { size: "2xs" }),
|
...text(layer, "sans", "default", { size: "2xs" }),
|
||||||
},
|
},
|
||||||
modelInfoContainer: {
|
model: interactive({
|
||||||
margin: { right: 16, top: 4 },
|
base: {
|
||||||
},
|
background: background(layer, "on"),
|
||||||
model: {
|
margin: { left: 12, right: 12, top: 12 },
|
||||||
background: background(layer, "on"),
|
padding: 4,
|
||||||
border: border(layer, "on", { overlay: true }),
|
cornerRadius: 4,
|
||||||
padding: 4,
|
...text(layer, "sans", "default", { size: "xs" }),
|
||||||
cornerRadius: 4,
|
|
||||||
...text(layer, "sans", "default", { size: "xs" }),
|
|
||||||
hover: {
|
|
||||||
background: background(layer, "on", "hovered"),
|
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
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: 24 },
|
||||||
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: 24 },
|
||||||
padding: 4,
|
padding: 4,
|
||||||
margin: { left: 4 },
|
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
...text(layer, "sans", "negative", { size: "xs" }),
|
...text(layer, "sans", "negative", { size: "xs" }),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { withOpacity } from "../theme/color"
|
import { withOpacity } from "../theme/color"
|
||||||
import { text, background } from "./components"
|
import { text, background } from "./components"
|
||||||
|
import { toggleable } from "../element"
|
||||||
|
|
||||||
export default function commandPalette(colorScheme: ColorScheme) {
|
export default function commandPalette(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.highest
|
let layer = colorScheme.highest
|
||||||
return {
|
|
||||||
keystrokeSpacing: 8,
|
const key = toggleable({
|
||||||
key: {
|
base: {
|
||||||
text: text(layer, "mono", "variant", "default", { size: "xs" }),
|
text: text(layer, "mono", "variant", "default", { size: "xs" }),
|
||||||
cornerRadius: 2,
|
cornerRadius: 2,
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
|
@ -21,10 +22,21 @@ export default function commandPalette(colorScheme: ColorScheme) {
|
||||||
bottom: 1,
|
bottom: 1,
|
||||||
left: 2,
|
left: 2,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
active: {
|
active: {
|
||||||
text: text(layer, "mono", "on", "default", { size: "xs" }),
|
text: text(layer, "mono", "on", "default", { size: "xs" }),
|
||||||
background: withOpacity(background(layer, "on"), 0.2),
|
background: withOpacity(background(layer, "on"), 0.2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
keystrokeSpacing: 8,
|
||||||
|
// TODO: This should be a Toggle<ContainedText> on the rust side so we don't have to do this
|
||||||
|
key: {
|
||||||
|
inactive: { ...key.inactive },
|
||||||
|
active: key.active,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,15 @@ export function foreground(
|
||||||
return getStyle(layer, styleSetOrStyles, style).foreground
|
return getStyle(layer, styleSetOrStyles, style).foreground
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Text {
|
interface Text extends Object {
|
||||||
|
family: keyof typeof fontFamilies
|
||||||
|
color: string
|
||||||
|
size: number
|
||||||
|
weight?: FontWeight
|
||||||
|
underline?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextStyle extends Object {
|
||||||
family: keyof typeof fontFamilies
|
family: keyof typeof fontFamilies
|
||||||
color: string
|
color: string
|
||||||
size: number
|
size: number
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, border, borderColor, foreground, text } from "./components"
|
import { background, border, borderColor, foreground, text } from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
export default function contactsPanel(colorScheme: ColorScheme) {
|
export default function contactsPanel(colorScheme: ColorScheme) {
|
||||||
const nameMargin = 8
|
const nameMargin = 8
|
||||||
const sidePadding = 12
|
const sidePadding = 12
|
||||||
|
@ -71,47 +71,85 @@ export default function contactsPanel(colorScheme: ColorScheme) {
|
||||||
},
|
},
|
||||||
rowHeight: 28,
|
rowHeight: 28,
|
||||||
sectionIconSize: 8,
|
sectionIconSize: 8,
|
||||||
headerRow: {
|
headerRow: toggleable({
|
||||||
...text(layer, "mono", { size: "sm" }),
|
base: interactive({
|
||||||
margin: { top: 14 },
|
base: {
|
||||||
padding: {
|
...text(layer, "mono", { size: "sm" }),
|
||||||
left: sidePadding,
|
margin: { top: 14 },
|
||||||
right: sidePadding,
|
padding: {
|
||||||
|
left: sidePadding,
|
||||||
|
right: sidePadding,
|
||||||
|
},
|
||||||
|
background: background(layer, "default"), // posiewic: breaking change
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer, "pressed"),
|
||||||
|
},
|
||||||
|
}, // hack, we want headerRow to be interactive for whatever reason. It probably shouldn't be interactive in the first place.
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
...text(layer, "mono", "active", { size: "sm" }),
|
||||||
|
background: background(layer, "active"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
active: {
|
}),
|
||||||
...text(layer, "mono", "active", { size: "sm" }),
|
leaveCall: interactive({
|
||||||
background: background(layer, "active"),
|
base: {
|
||||||
|
background: background(layer),
|
||||||
|
border: border(layer),
|
||||||
|
cornerRadius: 6,
|
||||||
|
margin: {
|
||||||
|
top: 1,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
top: 1,
|
||||||
|
bottom: 1,
|
||||||
|
left: 7,
|
||||||
|
right: 7,
|
||||||
|
},
|
||||||
|
...text(layer, "sans", "variant", { size: "xs" }),
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
leaveCall: {
|
hovered: {
|
||||||
background: background(layer),
|
...text(layer, "sans", "hovered", { size: "xs" }),
|
||||||
border: border(layer),
|
background: background(layer, "hovered"),
|
||||||
cornerRadius: 6,
|
border: border(layer, "hovered"),
|
||||||
margin: {
|
},
|
||||||
top: 1,
|
|
||||||
},
|
},
|
||||||
padding: {
|
}),
|
||||||
top: 1,
|
|
||||||
bottom: 1,
|
|
||||||
left: 7,
|
|
||||||
right: 7,
|
|
||||||
},
|
|
||||||
...text(layer, "sans", "variant", { size: "xs" }),
|
|
||||||
hover: {
|
|
||||||
...text(layer, "sans", "hovered", { size: "xs" }),
|
|
||||||
background: background(layer, "hovered"),
|
|
||||||
border: border(layer, "hovered"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
contactRow: {
|
contactRow: {
|
||||||
padding: {
|
inactive: {
|
||||||
left: sidePadding,
|
default: {
|
||||||
right: sidePadding,
|
padding: {
|
||||||
|
left: sidePadding,
|
||||||
|
right: sidePadding,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
background: background(layer, "active"),
|
default: {
|
||||||
|
background: background(layer, "active"),
|
||||||
|
padding: {
|
||||||
|
left: sidePadding,
|
||||||
|
right: sidePadding,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
contactAvatar: {
|
contactAvatar: {
|
||||||
cornerRadius: 10,
|
cornerRadius: 10,
|
||||||
width: 18,
|
width: 18,
|
||||||
|
@ -135,12 +173,14 @@ export default function contactsPanel(colorScheme: ColorScheme) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
contactButtonSpacing: nameMargin,
|
contactButtonSpacing: nameMargin,
|
||||||
contactButton: {
|
contactButton: interactive({
|
||||||
...contactButton,
|
base: { ...contactButton },
|
||||||
hover: {
|
state: {
|
||||||
background: background(layer, "hovered"),
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
disabledButton: {
|
disabledButton: {
|
||||||
...contactButton,
|
...contactButton,
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
|
@ -149,34 +189,52 @@ export default function contactsPanel(colorScheme: ColorScheme) {
|
||||||
callingIndicator: {
|
callingIndicator: {
|
||||||
...text(layer, "mono", "variant", { size: "xs" }),
|
...text(layer, "mono", "variant", { size: "xs" }),
|
||||||
},
|
},
|
||||||
treeBranch: {
|
treeBranch: toggleable({
|
||||||
color: borderColor(layer),
|
base: interactive({
|
||||||
width: 1,
|
base: {
|
||||||
hover: {
|
color: borderColor(layer),
|
||||||
color: borderColor(layer),
|
width: 1,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
color: borderColor(layer),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
color: borderColor(layer),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
active: {
|
}),
|
||||||
color: borderColor(layer),
|
projectRow: toggleable({
|
||||||
|
base: interactive({
|
||||||
|
base: {
|
||||||
|
...projectRow,
|
||||||
|
background: background(layer),
|
||||||
|
icon: {
|
||||||
|
margin: { left: nameMargin },
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
width: 12,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
...projectRow.name,
|
||||||
|
...text(layer, "mono", { size: "sm" }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: { background: background(layer, "active") },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
projectRow: {
|
|
||||||
...projectRow,
|
|
||||||
background: background(layer),
|
|
||||||
icon: {
|
|
||||||
margin: { left: nameMargin },
|
|
||||||
color: foreground(layer, "variant"),
|
|
||||||
width: 12,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
...projectRow.name,
|
|
||||||
...text(layer, "mono", { size: "sm" }),
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
background: background(layer, "hovered"),
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
background: background(layer, "active"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, foreground, text } from "./components"
|
import { background, foreground, text } from "./components"
|
||||||
|
import { interactive } from "../element"
|
||||||
const avatarSize = 12
|
const avatarSize = 12
|
||||||
const headerPadding = 8
|
const headerPadding = 8
|
||||||
|
|
||||||
|
@ -21,24 +21,32 @@ export default function contactNotification(colorScheme: ColorScheme): Object {
|
||||||
...text(layer, "sans", { size: "xs" }),
|
...text(layer, "sans", { size: "xs" }),
|
||||||
margin: { left: avatarSize + headerPadding, top: 6, bottom: 6 },
|
margin: { left: avatarSize + headerPadding, top: 6, bottom: 6 },
|
||||||
},
|
},
|
||||||
button: {
|
button: interactive({
|
||||||
...text(layer, "sans", "on", { size: "xs" }),
|
base: {
|
||||||
background: background(layer, "on"),
|
...text(layer, "sans", "on", { size: "xs" }),
|
||||||
padding: 4,
|
background: background(layer, "on"),
|
||||||
cornerRadius: 6,
|
padding: 4,
|
||||||
margin: { left: 6 },
|
cornerRadius: 6,
|
||||||
hover: {
|
margin: { left: 6 },
|
||||||
background: background(layer, "on", "hovered"),
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
dismissButton: {
|
dismissButton: {
|
||||||
color: foreground(layer, "variant"),
|
default: {
|
||||||
iconWidth: 8,
|
color: foreground(layer, "variant"),
|
||||||
iconHeight: 8,
|
iconWidth: 8,
|
||||||
buttonWidth: 8,
|
iconHeight: 8,
|
||||||
buttonHeight: 8,
|
buttonWidth: 8,
|
||||||
hover: {
|
buttonHeight: 8,
|
||||||
color: foreground(layer, "hovered"),
|
hover: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, border, borderColor, text } from "./components"
|
import { background, border, borderColor, text } from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
|
||||||
export default function contextMenu(colorScheme: ColorScheme) {
|
export default function contextMenu(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.middle
|
let layer = colorScheme.middle
|
||||||
|
@ -10,37 +11,54 @@ export default function contextMenu(colorScheme: ColorScheme) {
|
||||||
shadow: colorScheme.popoverShadow,
|
shadow: colorScheme.popoverShadow,
|
||||||
border: border(layer),
|
border: border(layer),
|
||||||
keystrokeMargin: 30,
|
keystrokeMargin: 30,
|
||||||
item: {
|
item: toggleable({
|
||||||
iconSpacing: 8,
|
base: interactive({
|
||||||
iconWidth: 14,
|
base: {
|
||||||
padding: { left: 6, right: 6, top: 2, bottom: 2 },
|
iconSpacing: 8,
|
||||||
cornerRadius: 6,
|
iconWidth: 14,
|
||||||
label: text(layer, "sans", { size: "sm" }),
|
padding: { left: 6, right: 6, top: 2, bottom: 2 },
|
||||||
keystroke: {
|
cornerRadius: 6,
|
||||||
...text(layer, "sans", "variant", {
|
label: text(layer, "sans", { size: "sm" }),
|
||||||
size: "sm",
|
keystroke: {
|
||||||
weight: "bold",
|
...text(layer, "sans", "variant", {
|
||||||
}),
|
size: "sm",
|
||||||
padding: { left: 3, right: 3 },
|
weight: "bold",
|
||||||
},
|
}),
|
||||||
hover: {
|
padding: { left: 3, right: 3 },
|
||||||
background: background(layer, "hovered"),
|
},
|
||||||
label: text(layer, "sans", "hovered", { size: "sm" }),
|
},
|
||||||
keystroke: {
|
state: {
|
||||||
...text(layer, "sans", "hovered", {
|
hovered: {
|
||||||
size: "sm",
|
background: background(layer, "hovered"),
|
||||||
weight: "bold",
|
label: text(layer, "sans", "hovered", { size: "sm" }),
|
||||||
}),
|
keystroke: {
|
||||||
padding: { left: 3, right: 3 },
|
...text(layer, "sans", "hovered", {
|
||||||
|
size: "sm",
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
padding: { left: 3, right: 3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
background: background(layer, "active"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer, "pressed"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
active: {
|
}),
|
||||||
background: background(layer, "active"),
|
|
||||||
},
|
|
||||||
activeHover: {
|
|
||||||
background: background(layer, "active"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
separator: {
|
separator: {
|
||||||
background: borderColor(layer),
|
background: borderColor(layer),
|
||||||
margin: { top: 2, bottom: 2 },
|
margin: { top: 2, bottom: 2 },
|
||||||
|
|
|
@ -1,60 +1,69 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, border, foreground, svg, text } from "./components"
|
import { background, border, foreground, svg, text } from "./components"
|
||||||
|
import { interactive } from "../element"
|
||||||
export default function copilot(colorScheme: ColorScheme) {
|
export default function copilot(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.middle
|
let layer = colorScheme.middle
|
||||||
|
|
||||||
let content_width = 264
|
let content_width = 264
|
||||||
|
|
||||||
let ctaButton = {
|
let ctaButton =
|
||||||
// Copied from welcome screen. FIXME: Move this into a ZDS component
|
// Copied from welcome screen. FIXME: Move this into a ZDS component
|
||||||
background: background(layer),
|
interactive({
|
||||||
border: border(layer, "default"),
|
base: {
|
||||||
cornerRadius: 4,
|
background: background(layer),
|
||||||
margin: {
|
border: border(layer, "default"),
|
||||||
top: 4,
|
cornerRadius: 4,
|
||||||
bottom: 4,
|
margin: {
|
||||||
left: 8,
|
top: 4,
|
||||||
right: 8,
|
bottom: 4,
|
||||||
},
|
left: 8,
|
||||||
padding: {
|
right: 8,
|
||||||
top: 3,
|
},
|
||||||
bottom: 3,
|
padding: {
|
||||||
left: 7,
|
top: 3,
|
||||||
right: 7,
|
bottom: 3,
|
||||||
},
|
left: 7,
|
||||||
...text(layer, "sans", "default", { size: "sm" }),
|
right: 7,
|
||||||
hover: {
|
},
|
||||||
...text(layer, "sans", "default", { size: "sm" }),
|
...text(layer, "sans", "default", { size: "sm" }),
|
||||||
background: background(layer, "hovered"),
|
},
|
||||||
border: border(layer, "active"),
|
state: {
|
||||||
},
|
hovered: {
|
||||||
}
|
...text(layer, "sans", "default", { size: "sm" }),
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
border: border(layer, "active"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
outLinkIcon: {
|
outLinkIcon: interactive({
|
||||||
icon: svg(
|
base: {
|
||||||
foreground(layer, "variant"),
|
|
||||||
"icons/link_out_12.svg",
|
|
||||||
12,
|
|
||||||
12
|
|
||||||
),
|
|
||||||
container: {
|
|
||||||
cornerRadius: 6,
|
|
||||||
padding: { left: 6 },
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
icon: svg(
|
icon: svg(
|
||||||
foreground(layer, "hovered"),
|
foreground(layer, "variant"),
|
||||||
"icons/link_out_12.svg",
|
"icons/link_out_12.svg",
|
||||||
12,
|
12,
|
||||||
12
|
12
|
||||||
),
|
),
|
||||||
|
container: {
|
||||||
|
cornerRadius: 6,
|
||||||
|
padding: { left: 6 },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
icon: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
modal: {
|
modal: {
|
||||||
titleText: {
|
titleText: {
|
||||||
...text(layer, "sans", { size: "xs", weight: "bold" }),
|
default: {
|
||||||
|
...text(layer, "sans", { size: "xs", weight: "bold" }),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
titlebar: {
|
titlebar: {
|
||||||
background: background(colorScheme.lowest),
|
background: background(colorScheme.lowest),
|
||||||
|
@ -75,42 +84,46 @@ export default function copilot(colorScheme: ColorScheme) {
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
closeIcon: {
|
closeIcon: interactive({
|
||||||
icon: svg(
|
base: {
|
||||||
foreground(layer, "variant"),
|
|
||||||
"icons/x_mark_8.svg",
|
|
||||||
8,
|
|
||||||
8
|
|
||||||
),
|
|
||||||
container: {
|
|
||||||
cornerRadius: 2,
|
|
||||||
padding: {
|
|
||||||
top: 4,
|
|
||||||
bottom: 4,
|
|
||||||
left: 4,
|
|
||||||
right: 4,
|
|
||||||
},
|
|
||||||
margin: {
|
|
||||||
right: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
icon: svg(
|
icon: svg(
|
||||||
foreground(layer, "on"),
|
foreground(layer, "variant"),
|
||||||
"icons/x_mark_8.svg",
|
"icons/x_mark_8.svg",
|
||||||
8,
|
8,
|
||||||
8
|
8
|
||||||
),
|
),
|
||||||
|
container: {
|
||||||
|
cornerRadius: 2,
|
||||||
|
padding: {
|
||||||
|
top: 4,
|
||||||
|
bottom: 4,
|
||||||
|
left: 4,
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
clicked: {
|
state: {
|
||||||
icon: svg(
|
hovered: {
|
||||||
foreground(layer, "base"),
|
icon: svg(
|
||||||
"icons/x_mark_8.svg",
|
foreground(layer, "on"),
|
||||||
8,
|
"icons/x_mark_8.svg",
|
||||||
8
|
8,
|
||||||
),
|
8
|
||||||
|
),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
icon: svg(
|
||||||
|
foreground(layer, "base"),
|
||||||
|
"icons/x_mark_8.svg",
|
||||||
|
8,
|
||||||
|
8
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
dimensions: {
|
dimensions: {
|
||||||
width: 280,
|
width: 280,
|
||||||
height: 280,
|
height: 280,
|
||||||
|
@ -185,28 +198,32 @@ export default function copilot(colorScheme: ColorScheme) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
right: (content_width * 1) / 3,
|
right: (content_width * 1) / 3,
|
||||||
rightContainer: {
|
rightContainer: interactive({
|
||||||
border: border(colorScheme.lowest, "inverted", {
|
base: {
|
||||||
bottom: false,
|
border: border(colorScheme.lowest, "inverted", {
|
||||||
right: false,
|
|
||||||
top: false,
|
|
||||||
left: true,
|
|
||||||
}),
|
|
||||||
padding: {
|
|
||||||
top: 3,
|
|
||||||
bottom: 5,
|
|
||||||
left: 8,
|
|
||||||
right: 0,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
border: border(layer, "active", {
|
|
||||||
bottom: false,
|
bottom: false,
|
||||||
right: false,
|
right: false,
|
||||||
top: false,
|
top: false,
|
||||||
left: true,
|
left: true,
|
||||||
}),
|
}),
|
||||||
|
padding: {
|
||||||
|
top: 3,
|
||||||
|
bottom: 5,
|
||||||
|
left: 8,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
border: border(layer, "active", {
|
||||||
|
bottom: false,
|
||||||
|
right: false,
|
||||||
|
top: false,
|
||||||
|
left: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { background, border, borderColor, foreground, text } from "./components"
|
||||||
import hoverPopover from "./hoverPopover"
|
import hoverPopover from "./hoverPopover"
|
||||||
|
|
||||||
import { buildSyntax } from "../theme/syntax"
|
import { buildSyntax } from "../theme/syntax"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
|
||||||
export default function editor(colorScheme: ColorScheme) {
|
export default function editor(colorScheme: ColorScheme) {
|
||||||
const { isLight } = colorScheme
|
const { isLight } = colorScheme
|
||||||
|
@ -48,46 +49,76 @@ export default function editor(colorScheme: ColorScheme) {
|
||||||
// Inline autocomplete suggestions, Co-pilot suggestions, etc.
|
// Inline autocomplete suggestions, Co-pilot suggestions, etc.
|
||||||
suggestion: syntax.predictive,
|
suggestion: syntax.predictive,
|
||||||
codeActions: {
|
codeActions: {
|
||||||
indicator: {
|
indicator: toggleable({
|
||||||
color: foreground(layer, "variant"),
|
base: interactive({
|
||||||
|
base: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "variant", "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
color: foreground(layer, "variant", "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
color: foreground(layer, "accent"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "accent", "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
color: foreground(layer, "accent", "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
clicked: {
|
|
||||||
color: foreground(layer, "base"),
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
color: foreground(layer, "on"),
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
color: foreground(layer, "on"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
verticalScale: 0.55,
|
verticalScale: 0.55,
|
||||||
},
|
},
|
||||||
folds: {
|
folds: {
|
||||||
iconMarginScale: 2.5,
|
iconMarginScale: 2.5,
|
||||||
foldedIcon: "icons/chevron_right_8.svg",
|
foldedIcon: "icons/chevron_right_8.svg",
|
||||||
foldableIcon: "icons/chevron_down_8.svg",
|
foldableIcon: "icons/chevron_down_8.svg",
|
||||||
indicator: {
|
indicator: toggleable({
|
||||||
color: foreground(layer, "variant"),
|
base: interactive({
|
||||||
|
base: {
|
||||||
clicked: {
|
color: foreground(layer, "variant"),
|
||||||
color: foreground(layer, "base"),
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "on"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
color: foreground(layer, "base"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
color: foreground(layer, "default"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hover: {
|
}),
|
||||||
color: foreground(layer, "on"),
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
color: foreground(layer, "on"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ellipses: {
|
ellipses: {
|
||||||
textColor: colorScheme.ramps.neutral(0.71).hex(),
|
textColor: colorScheme.ramps.neutral(0.71).hex(),
|
||||||
cornerRadiusFactor: 0.15,
|
cornerRadiusFactor: 0.15,
|
||||||
background: {
|
background: {
|
||||||
// Copied from hover_popover highlight
|
// Copied from hover_popover highlight
|
||||||
color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex(),
|
default: {
|
||||||
|
color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex(),
|
||||||
|
},
|
||||||
|
|
||||||
hover: {
|
hovered: {
|
||||||
color: colorScheme.ramps.neutral(0.5).alpha(0.5).hex(),
|
color: colorScheme.ramps.neutral(0.5).alpha(0.5).hex(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -223,21 +254,26 @@ export default function editor(colorScheme: ColorScheme) {
|
||||||
color: syntax.linkUri.color,
|
color: syntax.linkUri.color,
|
||||||
underline: syntax.linkUri.underline,
|
underline: syntax.linkUri.underline,
|
||||||
},
|
},
|
||||||
jumpIcon: {
|
jumpIcon: interactive({
|
||||||
color: foreground(layer, "on"),
|
base: {
|
||||||
iconWidth: 20,
|
color: foreground(layer, "on"),
|
||||||
buttonWidth: 20,
|
iconWidth: 20,
|
||||||
cornerRadius: 6,
|
buttonWidth: 20,
|
||||||
padding: {
|
cornerRadius: 6,
|
||||||
top: 6,
|
padding: {
|
||||||
bottom: 6,
|
top: 6,
|
||||||
left: 6,
|
bottom: 6,
|
||||||
right: 6,
|
left: 6,
|
||||||
|
right: 6,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hover: {
|
state: {
|
||||||
background: background(layer, "on", "hovered"),
|
hovered: {
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
|
|
||||||
scrollbar: {
|
scrollbar: {
|
||||||
width: 12,
|
width: 12,
|
||||||
minHeightFactor: 1.0,
|
minHeightFactor: 1.0,
|
||||||
|
|
|
@ -1,35 +1,40 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, border, text } from "./components"
|
import { background, border, text } from "./components"
|
||||||
|
import { interactive } from "../element"
|
||||||
|
|
||||||
export default function feedback(colorScheme: ColorScheme) {
|
export default function feedback(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.highest
|
let layer = colorScheme.highest
|
||||||
|
|
||||||
return {
|
return {
|
||||||
submit_button: {
|
submit_button: interactive({
|
||||||
...text(layer, "mono", "on"),
|
base: {
|
||||||
background: background(layer, "on"),
|
...text(layer, "mono", "on"),
|
||||||
cornerRadius: 6,
|
background: background(layer, "on"),
|
||||||
border: border(layer, "on"),
|
cornerRadius: 6,
|
||||||
margin: {
|
border: border(layer, "on"),
|
||||||
right: 4,
|
margin: {
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
bottom: 2,
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
top: 2,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
padding: {
|
state: {
|
||||||
bottom: 2,
|
clicked: {
|
||||||
left: 10,
|
...text(layer, "mono", "on", "pressed"),
|
||||||
right: 10,
|
background: background(layer, "on", "pressed"),
|
||||||
top: 2,
|
border: border(layer, "on", "pressed"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
...text(layer, "mono", "on", "hovered"),
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
border: border(layer, "on", "hovered"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
clicked: {
|
}),
|
||||||
...text(layer, "mono", "on", "pressed"),
|
|
||||||
background: background(layer, "on", "pressed"),
|
|
||||||
border: border(layer, "on", "pressed"),
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
...text(layer, "mono", "on", "hovered"),
|
|
||||||
background: background(layer, "on", "hovered"),
|
|
||||||
border: border(layer, "on", "hovered"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
button_margin: 8,
|
button_margin: 8,
|
||||||
info_text_default: text(layer, "sans", "default", { size: "xs" }),
|
info_text_default: text(layer, "sans", "default", { size: "xs" }),
|
||||||
link_text_default: text(layer, "sans", "default", {
|
link_text_default: text(layer, "sans", "default", {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { withOpacity } from "../theme/color"
|
import { withOpacity } from "../theme/color"
|
||||||
import { background, border, text } from "./components"
|
import { background, border, text } from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
|
||||||
export default function picker(colorScheme: ColorScheme): any {
|
export default function picker(colorScheme: ColorScheme): any {
|
||||||
let layer = colorScheme.lowest
|
let layer = colorScheme.lowest
|
||||||
|
@ -38,35 +39,65 @@ export default function picker(colorScheme: ColorScheme): any {
|
||||||
...container,
|
...container,
|
||||||
padding: {},
|
padding: {},
|
||||||
},
|
},
|
||||||
item: {
|
item: toggleable({
|
||||||
padding: {
|
base: interactive({
|
||||||
bottom: 4,
|
base: {
|
||||||
left: 12,
|
padding: {
|
||||||
right: 12,
|
bottom: 4,
|
||||||
top: 4,
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
top: 4,
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
top: 1,
|
||||||
|
left: 4,
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
cornerRadius: 8,
|
||||||
|
text: text(layer, "sans", "variant"),
|
||||||
|
highlightText: text(layer, "sans", "accent", {
|
||||||
|
weight: "bold",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: withOpacity(
|
||||||
|
background(layer, "hovered"),
|
||||||
|
0.5
|
||||||
|
),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: withOpacity(
|
||||||
|
background(layer, "pressed"),
|
||||||
|
0.5
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
background: withOpacity(
|
||||||
|
background(layer, "base", "active"),
|
||||||
|
0.5
|
||||||
|
),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: withOpacity(
|
||||||
|
background(layer, "hovered"),
|
||||||
|
0.5
|
||||||
|
),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: withOpacity(
|
||||||
|
background(layer, "pressed"),
|
||||||
|
0.5
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
margin: {
|
}),
|
||||||
top: 1,
|
|
||||||
left: 4,
|
|
||||||
right: 4,
|
|
||||||
},
|
|
||||||
cornerRadius: 8,
|
|
||||||
text: text(layer, "sans", "variant"),
|
|
||||||
highlightText: text(layer, "sans", "accent", { weight: "bold" }),
|
|
||||||
active: {
|
|
||||||
background: withOpacity(
|
|
||||||
background(layer, "base", "active"),
|
|
||||||
0.5
|
|
||||||
),
|
|
||||||
text: text(layer, "sans", "base", "active"),
|
|
||||||
highlightText: text(layer, "sans", "accent", {
|
|
||||||
weight: "bold",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
background: withOpacity(background(layer, "hovered"), 0.5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
inputEditor,
|
inputEditor,
|
||||||
emptyInputEditor,
|
emptyInputEditor,
|
||||||
noMatches: {
|
noMatches: {
|
||||||
|
|
|
@ -1,106 +1,196 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { withOpacity } from "../theme/color"
|
import { withOpacity } from "../theme/color"
|
||||||
import { background, border, foreground, text } from "./components"
|
import {
|
||||||
|
Border,
|
||||||
|
TextStyle,
|
||||||
|
background,
|
||||||
|
border,
|
||||||
|
foreground,
|
||||||
|
text,
|
||||||
|
} from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
import merge from "ts-deepmerge"
|
||||||
export default function projectPanel(colorScheme: ColorScheme) {
|
export default function projectPanel(colorScheme: ColorScheme) {
|
||||||
const { isLight } = colorScheme
|
const { isLight } = colorScheme
|
||||||
|
|
||||||
let layer = colorScheme.middle
|
let layer = colorScheme.middle
|
||||||
|
|
||||||
let baseEntry = {
|
type EntryStateProps = {
|
||||||
height: 22,
|
background?: string
|
||||||
iconColor: foreground(layer, "variant"),
|
border?: Border
|
||||||
iconSize: 7,
|
text?: TextStyle
|
||||||
iconSpacing: 5,
|
iconColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = {
|
type EntryState = {
|
||||||
git: {
|
default: EntryStateProps
|
||||||
modified: isLight
|
hovered?: EntryStateProps
|
||||||
? colorScheme.ramps.yellow(0.6).hex()
|
clicked?: EntryStateProps
|
||||||
: colorScheme.ramps.yellow(0.5).hex(),
|
|
||||||
inserted: isLight
|
|
||||||
? colorScheme.ramps.green(0.45).hex()
|
|
||||||
: colorScheme.ramps.green(0.5).hex(),
|
|
||||||
conflict: isLight
|
|
||||||
? colorScheme.ramps.red(0.6).hex()
|
|
||||||
: colorScheme.ramps.red(0.5).hex(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = {
|
const entry = (unselected?: EntryState, selected?: EntryState) => {
|
||||||
...baseEntry,
|
const git_status = {
|
||||||
text: text(layer, "mono", "variant", { size: "sm" }),
|
git: {
|
||||||
hover: {
|
modified: isLight
|
||||||
background: background(layer, "variant", "hovered"),
|
? colorScheme.ramps.yellow(0.6).hex()
|
||||||
},
|
: colorScheme.ramps.yellow(0.5).hex(),
|
||||||
active: {
|
inserted: isLight
|
||||||
background: colorScheme.isLight
|
? colorScheme.ramps.green(0.45).hex()
|
||||||
? withOpacity(background(layer, "active"), 0.5)
|
: colorScheme.ramps.green(0.5).hex(),
|
||||||
: background(layer, "active"),
|
conflict: isLight
|
||||||
text: text(layer, "mono", "active", { size: "sm" }),
|
? colorScheme.ramps.red(0.6).hex()
|
||||||
},
|
: colorScheme.ramps.red(0.5).hex(),
|
||||||
activeHover: {
|
},
|
||||||
background: background(layer, "active"),
|
}
|
||||||
text: text(layer, "mono", "active", { size: "sm" }),
|
|
||||||
},
|
const base_properties = {
|
||||||
status,
|
height: 22,
|
||||||
|
background: background(layer),
|
||||||
|
iconColor: foreground(layer, "variant"),
|
||||||
|
iconSize: 7,
|
||||||
|
iconSpacing: 5,
|
||||||
|
text: text(layer, "sans", "variant", { size: "sm" }),
|
||||||
|
status: {
|
||||||
|
...git_status,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedStyle: EntryState | undefined = selected
|
||||||
|
? selected
|
||||||
|
: unselected
|
||||||
|
|
||||||
|
const unselected_default_style = merge(
|
||||||
|
base_properties,
|
||||||
|
unselected?.default ?? {},
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
const unselected_hovered_style = merge(
|
||||||
|
base_properties,
|
||||||
|
{ background: background(layer, "hovered") },
|
||||||
|
unselected?.hovered ?? {},
|
||||||
|
)
|
||||||
|
const unselected_clicked_style = merge(
|
||||||
|
base_properties,
|
||||||
|
{ background: background(layer, "pressed") },
|
||||||
|
unselected?.clicked ?? {},
|
||||||
|
)
|
||||||
|
const selected_default_style = merge(
|
||||||
|
base_properties,
|
||||||
|
{
|
||||||
|
background: background(colorScheme.lowest),
|
||||||
|
text: text(colorScheme.lowest, "sans", { size: "sm" }),
|
||||||
|
},
|
||||||
|
selectedStyle?.default ?? {},
|
||||||
|
)
|
||||||
|
const selected_hovered_style = merge(
|
||||||
|
base_properties,
|
||||||
|
{
|
||||||
|
background: background(colorScheme.lowest, "hovered"),
|
||||||
|
text: text(colorScheme.lowest, "sans", { size: "sm" }),
|
||||||
|
},
|
||||||
|
selectedStyle?.hovered ?? {},
|
||||||
|
)
|
||||||
|
const selected_clicked_style = merge(
|
||||||
|
base_properties,
|
||||||
|
{
|
||||||
|
background: background(colorScheme.lowest, "pressed"),
|
||||||
|
text: text(colorScheme.lowest, "sans", { size: "sm" }),
|
||||||
|
},
|
||||||
|
selectedStyle?.clicked ?? {},
|
||||||
|
)
|
||||||
|
|
||||||
|
return toggleable({
|
||||||
|
state: {
|
||||||
|
inactive: interactive({
|
||||||
|
state: {
|
||||||
|
default: unselected_default_style,
|
||||||
|
hovered: unselected_hovered_style,
|
||||||
|
clicked: unselected_clicked_style,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
active: interactive({
|
||||||
|
state: {
|
||||||
|
default: selected_default_style,
|
||||||
|
hovered: selected_hovered_style,
|
||||||
|
clicked: selected_clicked_style,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultEntry = entry()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
openProjectButton: {
|
openProjectButton: interactive({
|
||||||
background: background(layer),
|
base: {
|
||||||
border: border(layer, "active"),
|
background: background(layer),
|
||||||
cornerRadius: 4,
|
|
||||||
margin: {
|
|
||||||
top: 16,
|
|
||||||
left: 16,
|
|
||||||
right: 16,
|
|
||||||
},
|
|
||||||
padding: {
|
|
||||||
top: 3,
|
|
||||||
bottom: 3,
|
|
||||||
left: 7,
|
|
||||||
right: 7,
|
|
||||||
},
|
|
||||||
...text(layer, "sans", "default", { size: "sm" }),
|
|
||||||
hover: {
|
|
||||||
...text(layer, "sans", "default", { size: "sm" }),
|
|
||||||
background: background(layer, "hovered"),
|
|
||||||
border: border(layer, "active"),
|
border: border(layer, "active"),
|
||||||
|
cornerRadius: 4,
|
||||||
|
margin: {
|
||||||
|
top: 16,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
top: 3,
|
||||||
|
bottom: 3,
|
||||||
|
left: 7,
|
||||||
|
right: 7,
|
||||||
|
},
|
||||||
|
...text(layer, "sans", "default", { size: "sm" }),
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
...text(layer, "sans", "default", { size: "sm" }),
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
border: border(layer, "active"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
...text(layer, "sans", "default", { size: "sm" }),
|
||||||
|
background: background(layer, "pressed"),
|
||||||
|
border: border(layer, "active"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
background: background(layer),
|
background: background(layer),
|
||||||
padding: { left: 6, right: 6, top: 0, bottom: 6 },
|
padding: { left: 6, right: 6, top: 0, bottom: 6 },
|
||||||
indentWidth: 12,
|
indentWidth: 12,
|
||||||
entry,
|
entry: defaultEntry,
|
||||||
draggedEntry: {
|
draggedEntry: {
|
||||||
...baseEntry,
|
...defaultEntry.inactive.default,
|
||||||
text: text(layer, "mono", "on", { size: "sm" }),
|
text: text(layer, "sans", "on", { size: "sm" }),
|
||||||
background: withOpacity(background(layer, "on"), 0.9),
|
background: withOpacity(background(layer, "on"), 0.9),
|
||||||
border: border(layer),
|
border: border(layer),
|
||||||
status,
|
|
||||||
},
|
},
|
||||||
ignoredEntry: {
|
ignoredEntry: entry(
|
||||||
...entry,
|
{
|
||||||
iconColor: foreground(layer, "disabled"),
|
default: {
|
||||||
text: text(layer, "mono", "disabled"),
|
text: text(layer, "sans", "disabled"),
|
||||||
active: {
|
},
|
||||||
...entry.active,
|
|
||||||
iconColor: foreground(layer, "variant"),
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
cutEntry: {
|
default: {
|
||||||
...entry,
|
iconColor: foreground(layer, "variant"),
|
||||||
text: text(layer, "mono", "disabled"),
|
},
|
||||||
active: {
|
}
|
||||||
background: background(layer, "active"),
|
),
|
||||||
text: text(layer, "mono", "disabled", { size: "sm" }),
|
cutEntry: entry(
|
||||||
|
{
|
||||||
|
default: {
|
||||||
|
text: text(layer, "sans", "disabled"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
|
default: {
|
||||||
|
background: background(layer, "active"),
|
||||||
|
text: text(layer, "sans", "disabled", { size: "sm" }),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
filenameEditor: {
|
filenameEditor: {
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
text: text(layer, "mono", "on", { size: "sm" }),
|
text: text(layer, "sans", "on", { size: "sm" }),
|
||||||
selection: colorScheme.players[0],
|
selection: colorScheme.players[0],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { withOpacity } from "../theme/color"
|
import { withOpacity } from "../theme/color"
|
||||||
import { background, border, foreground, text } from "./components"
|
import { background, border, foreground, text } from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
|
||||||
export default function search(colorScheme: ColorScheme) {
|
export default function search(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.highest
|
let layer = colorScheme.highest
|
||||||
|
@ -35,36 +36,50 @@ export default function search(colorScheme: ColorScheme) {
|
||||||
return {
|
return {
|
||||||
// TODO: Add an activeMatchBackground on the rust side to differentiate between active and inactive
|
// TODO: Add an activeMatchBackground on the rust side to differentiate between active and inactive
|
||||||
matchBackground: withOpacity(foreground(layer, "accent"), 0.4),
|
matchBackground: withOpacity(foreground(layer, "accent"), 0.4),
|
||||||
optionButton: {
|
optionButton: toggleable({
|
||||||
...text(layer, "mono", "on"),
|
base: interactive({
|
||||||
background: background(layer, "on"),
|
base: {
|
||||||
cornerRadius: 6,
|
...text(layer, "mono", "on"),
|
||||||
border: border(layer, "on"),
|
background: background(layer, "on"),
|
||||||
margin: {
|
cornerRadius: 6,
|
||||||
right: 4,
|
border: border(layer, "on"),
|
||||||
|
margin: {
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
bottom: 2,
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
top: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
...text(layer, "mono", "on", "hovered"),
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
border: border(layer, "on", "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
...text(layer, "mono", "on", "pressed"),
|
||||||
|
background: background(layer, "on", "pressed"),
|
||||||
|
border: border(layer, "on", "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
...text(layer, "mono", "accent"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
...text(layer, "mono", "accent", "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
...text(layer, "mono", "accent", "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
padding: {
|
}),
|
||||||
bottom: 2,
|
|
||||||
left: 10,
|
|
||||||
right: 10,
|
|
||||||
top: 2,
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
...text(layer, "mono", "on", "inverted"),
|
|
||||||
background: background(layer, "on", "inverted"),
|
|
||||||
border: border(layer, "on", "inverted"),
|
|
||||||
},
|
|
||||||
clicked: {
|
|
||||||
...text(layer, "mono", "on", "pressed"),
|
|
||||||
background: background(layer, "on", "pressed"),
|
|
||||||
border: border(layer, "on", "pressed"),
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
...text(layer, "mono", "on", "hovered"),
|
|
||||||
background: background(layer, "on", "hovered"),
|
|
||||||
border: border(layer, "on", "hovered"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
editor,
|
editor,
|
||||||
invalidEditor: {
|
invalidEditor: {
|
||||||
...editor,
|
...editor,
|
||||||
|
@ -97,17 +112,24 @@ export default function search(colorScheme: ColorScheme) {
|
||||||
...text(layer, "mono", "on"),
|
...text(layer, "mono", "on"),
|
||||||
size: 18,
|
size: 18,
|
||||||
},
|
},
|
||||||
dismissButton: {
|
dismissButton: interactive({
|
||||||
color: foreground(layer, "variant"),
|
base: {
|
||||||
iconWidth: 12,
|
color: foreground(layer, "variant"),
|
||||||
buttonWidth: 14,
|
iconWidth: 12,
|
||||||
padding: {
|
buttonWidth: 14,
|
||||||
left: 10,
|
padding: {
|
||||||
right: 10,
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hover: {
|
state: {
|
||||||
color: foreground(layer, "hovered"),
|
hovered: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
color: foreground(layer, "pressed"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, border, foreground, text } from "./components"
|
import { background, border, foreground, text } from "./components"
|
||||||
|
import { interactive } from "../element"
|
||||||
|
|
||||||
const headerPadding = 8
|
const headerPadding = 8
|
||||||
|
|
||||||
|
@ -12,33 +13,41 @@ export default function simpleMessageNotification(
|
||||||
...text(layer, "sans", { size: "xs" }),
|
...text(layer, "sans", { size: "xs" }),
|
||||||
margin: { left: headerPadding, right: headerPadding },
|
margin: { left: headerPadding, right: headerPadding },
|
||||||
},
|
},
|
||||||
actionMessage: {
|
actionMessage: interactive({
|
||||||
...text(layer, "sans", { size: "xs" }),
|
base: {
|
||||||
border: border(layer, "active"),
|
...text(layer, "sans", { size: "xs" }),
|
||||||
cornerRadius: 4,
|
|
||||||
padding: {
|
|
||||||
top: 3,
|
|
||||||
bottom: 3,
|
|
||||||
left: 7,
|
|
||||||
right: 7,
|
|
||||||
},
|
|
||||||
|
|
||||||
margin: { left: headerPadding, top: 6, bottom: 6 },
|
|
||||||
hover: {
|
|
||||||
...text(layer, "sans", "default", { size: "xs" }),
|
|
||||||
background: background(layer, "hovered"),
|
|
||||||
border: border(layer, "active"),
|
border: border(layer, "active"),
|
||||||
|
cornerRadius: 4,
|
||||||
|
padding: {
|
||||||
|
top: 3,
|
||||||
|
bottom: 3,
|
||||||
|
left: 7,
|
||||||
|
right: 7,
|
||||||
|
},
|
||||||
|
|
||||||
|
margin: { left: headerPadding, top: 6, bottom: 6 },
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
dismissButton: {
|
hovered: {
|
||||||
color: foreground(layer),
|
...text(layer, "sans", "default", { size: "xs" }),
|
||||||
iconWidth: 8,
|
background: background(layer, "hovered"),
|
||||||
iconHeight: 8,
|
border: border(layer, "active"),
|
||||||
buttonWidth: 8,
|
},
|
||||||
buttonHeight: 8,
|
|
||||||
hover: {
|
|
||||||
color: foreground(layer, "hovered"),
|
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
|
dismissButton: interactive({
|
||||||
|
base: {
|
||||||
|
color: foreground(layer),
|
||||||
|
iconWidth: 8,
|
||||||
|
iconHeight: 8,
|
||||||
|
buttonWidth: 8,
|
||||||
|
buttonHeight: 8,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, border, foreground, text } from "./components"
|
import { background, border, foreground, text } from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
export default function statusBar(colorScheme: ColorScheme) {
|
export default function statusBar(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.lowest
|
let layer = colorScheme.lowest
|
||||||
|
|
||||||
|
@ -25,95 +25,123 @@ export default function statusBar(colorScheme: ColorScheme) {
|
||||||
},
|
},
|
||||||
border: border(layer, { top: true, overlay: true }),
|
border: border(layer, { top: true, overlay: true }),
|
||||||
cursorPosition: text(layer, "sans", "variant"),
|
cursorPosition: text(layer, "sans", "variant"),
|
||||||
activeLanguage: {
|
activeLanguage: interactive({
|
||||||
padding: { left: 6, right: 6 },
|
base: {
|
||||||
...text(layer, "sans", "variant"),
|
padding: { left: 6, right: 6 },
|
||||||
hover: {
|
...text(layer, "sans", "variant"),
|
||||||
...text(layer, "sans", "on"),
|
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
...text(layer, "sans", "on"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
autoUpdateProgressMessage: text(layer, "sans", "variant"),
|
autoUpdateProgressMessage: text(layer, "sans", "variant"),
|
||||||
autoUpdateDoneMessage: text(layer, "sans", "variant"),
|
autoUpdateDoneMessage: text(layer, "sans", "variant"),
|
||||||
lspStatus: {
|
lspStatus: interactive({
|
||||||
...diagnosticStatusContainer,
|
base: {
|
||||||
iconSpacing: 4,
|
...diagnosticStatusContainer,
|
||||||
iconWidth: 14,
|
iconSpacing: 4,
|
||||||
height: 18,
|
iconWidth: 14,
|
||||||
message: text(layer, "sans"),
|
height: 18,
|
||||||
iconColor: foreground(layer),
|
|
||||||
hover: {
|
|
||||||
message: text(layer, "sans"),
|
message: text(layer, "sans"),
|
||||||
iconColor: foreground(layer),
|
iconColor: foreground(layer),
|
||||||
background: background(layer, "hovered"),
|
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
diagnosticMessage: {
|
hovered: {
|
||||||
...text(layer, "sans"),
|
message: text(layer, "sans"),
|
||||||
hover: text(layer, "sans", "hovered"),
|
iconColor: foreground(layer),
|
||||||
},
|
background: background(layer, "hovered"),
|
||||||
diagnosticSummary: {
|
},
|
||||||
height: 20,
|
|
||||||
iconWidth: 16,
|
|
||||||
iconSpacing: 2,
|
|
||||||
summarySpacing: 6,
|
|
||||||
text: text(layer, "sans", { size: "sm" }),
|
|
||||||
iconColorOk: foreground(layer, "variant"),
|
|
||||||
iconColorWarning: foreground(layer, "warning"),
|
|
||||||
iconColorError: foreground(layer, "negative"),
|
|
||||||
containerOk: {
|
|
||||||
cornerRadius: 6,
|
|
||||||
padding: { top: 3, bottom: 3, left: 7, right: 7 },
|
|
||||||
},
|
},
|
||||||
containerWarning: {
|
}),
|
||||||
...diagnosticStatusContainer,
|
diagnosticMessage: interactive({
|
||||||
background: background(layer, "warning"),
|
base: {
|
||||||
border: border(layer, "warning"),
|
...text(layer, "sans"),
|
||||||
},
|
},
|
||||||
containerError: {
|
state: { hovered: text(layer, "sans", "hovered") },
|
||||||
...diagnosticStatusContainer,
|
}),
|
||||||
background: background(layer, "negative"),
|
diagnosticSummary: interactive({
|
||||||
border: border(layer, "negative"),
|
base: {
|
||||||
},
|
height: 20,
|
||||||
hover: {
|
iconWidth: 16,
|
||||||
iconColorOk: foreground(layer, "on"),
|
iconSpacing: 2,
|
||||||
|
summarySpacing: 6,
|
||||||
|
text: text(layer, "sans", { size: "sm" }),
|
||||||
|
iconColorOk: foreground(layer, "variant"),
|
||||||
|
iconColorWarning: foreground(layer, "warning"),
|
||||||
|
iconColorError: foreground(layer, "negative"),
|
||||||
containerOk: {
|
containerOk: {
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
padding: { top: 3, bottom: 3, left: 7, right: 7 },
|
padding: { top: 3, bottom: 3, left: 7, right: 7 },
|
||||||
background: background(layer, "on", "hovered"),
|
|
||||||
},
|
},
|
||||||
containerWarning: {
|
containerWarning: {
|
||||||
...diagnosticStatusContainer,
|
...diagnosticStatusContainer,
|
||||||
background: background(layer, "warning", "hovered"),
|
background: background(layer, "warning"),
|
||||||
border: border(layer, "warning", "hovered"),
|
border: border(layer, "warning"),
|
||||||
},
|
},
|
||||||
containerError: {
|
containerError: {
|
||||||
...diagnosticStatusContainer,
|
...diagnosticStatusContainer,
|
||||||
background: background(layer, "negative", "hovered"),
|
background: background(layer, "negative"),
|
||||||
border: border(layer, "negative", "hovered"),
|
border: border(layer, "negative"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
iconColorOk: foreground(layer, "on"),
|
||||||
|
containerOk: {
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
},
|
||||||
|
containerWarning: {
|
||||||
|
background: background(layer, "warning", "hovered"),
|
||||||
|
border: border(layer, "warning", "hovered"),
|
||||||
|
},
|
||||||
|
containerError: {
|
||||||
|
background: background(layer, "negative", "hovered"),
|
||||||
|
border: border(layer, "negative", "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
panelButtons: {
|
panelButtons: {
|
||||||
groupLeft: {},
|
groupLeft: {},
|
||||||
groupBottom: {},
|
groupBottom: {},
|
||||||
groupRight: {},
|
groupRight: {},
|
||||||
button: {
|
button: toggleable({
|
||||||
...statusContainer,
|
base: interactive({
|
||||||
iconSize: 16,
|
base: {
|
||||||
iconColor: foreground(layer, "variant"),
|
...statusContainer,
|
||||||
label: {
|
iconSize: 16,
|
||||||
margin: { left: 6 },
|
iconColor: foreground(layer, "variant"),
|
||||||
...text(layer, "sans", { size: "sm" }),
|
label: {
|
||||||
|
margin: { left: 6 },
|
||||||
|
...text(layer, "sans", { size: "sm" }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
iconColor: foreground(layer, "hovered"),
|
||||||
|
background: background(layer, "variant"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
iconColor: foreground(layer, "active"),
|
||||||
|
background: background(layer, "active"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
iconColor: foreground(layer, "hovered"),
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
iconColor: foreground(layer, "pressed"),
|
||||||
|
background: background(layer, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hover: {
|
}),
|
||||||
iconColor: foreground(layer, "hovered"),
|
|
||||||
background: background(layer, "variant"),
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
iconColor: foreground(layer, "active"),
|
|
||||||
background: background(layer, "active"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
badge: {
|
badge: {
|
||||||
cornerRadius: 3,
|
cornerRadius: 3,
|
||||||
padding: 2,
|
padding: 2,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { withOpacity } from "../theme/color"
|
import { withOpacity } from "../theme/color"
|
||||||
import { text, border, background, foreground } from "./components"
|
import { text, border, background, foreground } from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
|
||||||
export default function tabBar(colorScheme: ColorScheme) {
|
export default function tabBar(colorScheme: ColorScheme) {
|
||||||
const height = 32
|
const height = 32
|
||||||
|
@ -87,17 +88,36 @@ export default function tabBar(colorScheme: ColorScheme) {
|
||||||
inactiveTab: inactivePaneInactiveTab,
|
inactiveTab: inactivePaneInactiveTab,
|
||||||
},
|
},
|
||||||
draggedTab,
|
draggedTab,
|
||||||
paneButton: {
|
paneButton: toggleable({
|
||||||
color: foreground(layer, "variant"),
|
base: interactive({
|
||||||
iconWidth: 12,
|
base: {
|
||||||
buttonWidth: activePaneActiveTab.height,
|
color: foreground(layer, "variant"),
|
||||||
hover: {
|
iconWidth: 12,
|
||||||
color: foreground(layer, "hovered"),
|
buttonWidth: activePaneActiveTab.height,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
color: foreground(layer, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
color: foreground(layer, "accent"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
color: foreground(layer, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
active: {
|
}),
|
||||||
color: foreground(layer, "accent"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
paneButtonContainer: {
|
paneButtonContainer: {
|
||||||
background: tab.background,
|
background: tab.background,
|
||||||
border: {
|
border: {
|
||||||
|
|
266
styles/src/styleTree/titlebar.ts
Normal file
266
styles/src/styleTree/titlebar.ts
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
import { ColorScheme } from "../common"
|
||||||
|
import { icon_button, toggleable_icon_button } from "../component/icon_button"
|
||||||
|
import { toggleable_text_button } from "../component/text_button"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
|
import { withOpacity } from "../theme/color"
|
||||||
|
import { background, border, foreground, text } from "./components"
|
||||||
|
|
||||||
|
const ITEM_SPACING = 8
|
||||||
|
const TITLEBAR_HEIGHT = 32
|
||||||
|
|
||||||
|
function build_spacing(
|
||||||
|
container_height: number,
|
||||||
|
element_height: number,
|
||||||
|
spacing: number
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
group: spacing,
|
||||||
|
item: spacing / 2,
|
||||||
|
half_item: spacing / 4,
|
||||||
|
marginY: (container_height - element_height) / 2,
|
||||||
|
marginX: (container_height - element_height) / 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function call_controls(theme: ColorScheme) {
|
||||||
|
const button_height = 18
|
||||||
|
|
||||||
|
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
|
||||||
|
const marginY = {
|
||||||
|
top: space.marginY,
|
||||||
|
bottom: space.marginY,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
toggle_microphone_button: toggleable_icon_button(theme, {
|
||||||
|
margin: {
|
||||||
|
...marginY,
|
||||||
|
left: space.group,
|
||||||
|
right: space.half_item,
|
||||||
|
},
|
||||||
|
active_color: "negative",
|
||||||
|
}),
|
||||||
|
|
||||||
|
toggle_speakers_button: toggleable_icon_button(theme, {
|
||||||
|
margin: {
|
||||||
|
...marginY,
|
||||||
|
left: space.half_item,
|
||||||
|
right: space.half_item,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
screen_share_button: toggleable_icon_button(theme, {
|
||||||
|
margin: {
|
||||||
|
...marginY,
|
||||||
|
left: space.half_item,
|
||||||
|
right: space.group,
|
||||||
|
},
|
||||||
|
active_color: "accent",
|
||||||
|
}),
|
||||||
|
|
||||||
|
muted: foreground(theme.lowest, "negative"),
|
||||||
|
speaking: foreground(theme.lowest, "accent"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the User Menu when toggled
|
||||||
|
*
|
||||||
|
* When logged in shows the user's avatar and a chevron,
|
||||||
|
* When logged out only shows a chevron.
|
||||||
|
*/
|
||||||
|
function user_menu(theme: ColorScheme) {
|
||||||
|
const button_height = 18
|
||||||
|
|
||||||
|
const space = build_spacing(TITLEBAR_HEIGHT, button_height, ITEM_SPACING)
|
||||||
|
|
||||||
|
const build_button = ({ online }: { online: boolean }) => {
|
||||||
|
const button = toggleable({
|
||||||
|
base: interactive({
|
||||||
|
base: {
|
||||||
|
cornerRadius: 6,
|
||||||
|
height: button_height,
|
||||||
|
width: online ? 37 : 24,
|
||||||
|
padding: {
|
||||||
|
top: 2,
|
||||||
|
bottom: 2,
|
||||||
|
left: 6,
|
||||||
|
right: 6,
|
||||||
|
},
|
||||||
|
margin: {
|
||||||
|
left: space.item,
|
||||||
|
right: space.item,
|
||||||
|
},
|
||||||
|
...text(theme.lowest, "sans", { size: "xs" }),
|
||||||
|
background: background(theme.lowest),
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
...text(theme.lowest, "sans", "hovered", {
|
||||||
|
size: "xs",
|
||||||
|
}),
|
||||||
|
background: background(theme.lowest, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
...text(theme.lowest, "sans", "pressed", {
|
||||||
|
size: "xs",
|
||||||
|
}),
|
||||||
|
background: background(theme.lowest, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
...text(theme.lowest, "sans", "active", { size: "xs" }),
|
||||||
|
background: background(theme.middle),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
...text(theme.lowest, "sans", "active", { size: "xs" }),
|
||||||
|
background: background(theme.middle, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
...text(theme.lowest, "sans", "active", { size: "xs" }),
|
||||||
|
background: background(theme.middle, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
user_menu: button,
|
||||||
|
avatar: {
|
||||||
|
icon_width: 16,
|
||||||
|
icon_height: 16,
|
||||||
|
corner_radius: 4,
|
||||||
|
outer_width: 16,
|
||||||
|
outer_corner_radius: 16,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
margin: {
|
||||||
|
top: 2,
|
||||||
|
left: online ? space.item : 0,
|
||||||
|
right: space.group,
|
||||||
|
bottom: 2,
|
||||||
|
},
|
||||||
|
width: 11,
|
||||||
|
height: 11,
|
||||||
|
color: foreground(theme.lowest),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
userMenuButtonOnline: build_button({ online: true }),
|
||||||
|
userMenuButtonOffline: build_button({ online: false }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function titlebar(theme: ColorScheme) {
|
||||||
|
const avatarWidth = 15
|
||||||
|
const avatarOuterWidth = avatarWidth + 4
|
||||||
|
const followerAvatarWidth = 14
|
||||||
|
const followerAvatarOuterWidth = followerAvatarWidth + 4
|
||||||
|
|
||||||
|
return {
|
||||||
|
item_spacing: ITEM_SPACING,
|
||||||
|
facePileSpacing: 2,
|
||||||
|
height: TITLEBAR_HEIGHT,
|
||||||
|
background: background(theme.lowest),
|
||||||
|
border: border(theme.lowest, { bottom: true }),
|
||||||
|
padding: {
|
||||||
|
left: 80,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Project
|
||||||
|
title: text(theme.lowest, "sans", "variant"),
|
||||||
|
highlight_color: text(theme.lowest, "sans", "active").color,
|
||||||
|
|
||||||
|
// Collaborators
|
||||||
|
leaderAvatar: {
|
||||||
|
width: avatarWidth,
|
||||||
|
outerWidth: avatarOuterWidth,
|
||||||
|
cornerRadius: avatarWidth / 2,
|
||||||
|
outerCornerRadius: avatarOuterWidth / 2,
|
||||||
|
},
|
||||||
|
followerAvatar: {
|
||||||
|
width: followerAvatarWidth,
|
||||||
|
outerWidth: followerAvatarOuterWidth,
|
||||||
|
cornerRadius: followerAvatarWidth / 2,
|
||||||
|
outerCornerRadius: followerAvatarOuterWidth / 2,
|
||||||
|
},
|
||||||
|
inactiveAvatarGrayscale: true,
|
||||||
|
followerAvatarOverlap: 8,
|
||||||
|
leaderSelection: {
|
||||||
|
margin: {
|
||||||
|
top: 4,
|
||||||
|
bottom: 4,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
left: 2,
|
||||||
|
right: 2,
|
||||||
|
top: 2,
|
||||||
|
bottom: 2,
|
||||||
|
},
|
||||||
|
cornerRadius: 6,
|
||||||
|
},
|
||||||
|
avatarRibbon: {
|
||||||
|
height: 3,
|
||||||
|
width: 14,
|
||||||
|
// TODO: Chore: Make avatarRibbon colors driven by the theme rather than being hard coded.
|
||||||
|
},
|
||||||
|
|
||||||
|
sign_in_button: toggleable_text_button(theme, {}),
|
||||||
|
offlineIcon: {
|
||||||
|
color: foreground(theme.lowest, "variant"),
|
||||||
|
width: 16,
|
||||||
|
margin: {
|
||||||
|
left: ITEM_SPACING,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
right: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// When the collaboration server is out of date, show a warning
|
||||||
|
outdatedWarning: {
|
||||||
|
...text(theme.lowest, "sans", "warning", { size: "xs" }),
|
||||||
|
background: withOpacity(background(theme.lowest, "warning"), 0.3),
|
||||||
|
border: border(theme.lowest, "warning"),
|
||||||
|
margin: {
|
||||||
|
left: ITEM_SPACING,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
},
|
||||||
|
cornerRadius: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
leave_call_button: icon_button(theme, {
|
||||||
|
margin: {
|
||||||
|
left: ITEM_SPACING / 2,
|
||||||
|
right: ITEM_SPACING,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
...call_controls(theme),
|
||||||
|
|
||||||
|
toggle_contacts_button: toggleable_icon_button(theme, {
|
||||||
|
margin: {
|
||||||
|
left: ITEM_SPACING,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Jewel that notifies you that there are new contact requests
|
||||||
|
toggleContactsBadge: {
|
||||||
|
cornerRadius: 3,
|
||||||
|
padding: 2,
|
||||||
|
margin: { top: 3, left: 3 },
|
||||||
|
border: border(theme.lowest),
|
||||||
|
background: foreground(theme.lowest, "accent"),
|
||||||
|
},
|
||||||
|
shareButton: toggleable_text_button(theme, {}),
|
||||||
|
user_menu: user_menu(theme),
|
||||||
|
}
|
||||||
|
}
|
47
styles/src/styleTree/toggle.ts
Normal file
47
styles/src/styleTree/toggle.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import merge from "ts-deepmerge"
|
||||||
|
|
||||||
|
type ToggleState = "inactive" | "active"
|
||||||
|
|
||||||
|
type Toggleable<T> = Record<ToggleState, T>
|
||||||
|
|
||||||
|
const NO_INACTIVE_OR_BASE_ERROR =
|
||||||
|
"A toggleable object must have an inactive state, or a base property."
|
||||||
|
const NO_ACTIVE_ERROR = "A toggleable object must have an active state."
|
||||||
|
|
||||||
|
interface ToggleableProps<T> {
|
||||||
|
base?: T
|
||||||
|
state: Partial<Record<ToggleState, T>>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for creating Toggleable objects.
|
||||||
|
* @template T The type of the object being toggled.
|
||||||
|
* @param props Object containing the base (inactive) state and state modifications to create the active state.
|
||||||
|
* @returns A Toggleable object containing both the inactive and active states.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* toggleable({
|
||||||
|
* base: { background: "#000000", text: "#CCCCCC" },
|
||||||
|
* state: { active: { text: "#CCCCCC" } },
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function toggleable<T extends object>(
|
||||||
|
props: ToggleableProps<T>
|
||||||
|
): Toggleable<T> {
|
||||||
|
const { base, state } = props
|
||||||
|
|
||||||
|
if (!base && !state.inactive) throw new Error(NO_INACTIVE_OR_BASE_ERROR)
|
||||||
|
if (!state.active) throw new Error(NO_ACTIVE_ERROR)
|
||||||
|
|
||||||
|
const inactiveState = base
|
||||||
|
? ((state.inactive ? merge(base, state.inactive) : base) as T)
|
||||||
|
: (state.inactive as T)
|
||||||
|
|
||||||
|
const toggleObj: Toggleable<T> = {
|
||||||
|
inactive: inactiveState,
|
||||||
|
active: merge(base ?? {}, state.active) as T,
|
||||||
|
}
|
||||||
|
|
||||||
|
return toggleObj
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { background, border, text } from "./components"
|
import { background, border, text } from "./components"
|
||||||
|
import { interactive, toggleable } from "../element"
|
||||||
export default function dropdownMenu(colorScheme: ColorScheme) {
|
export default function dropdownMenu(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.middle
|
let layer = colorScheme.middle
|
||||||
|
|
||||||
|
@ -9,38 +9,56 @@ export default function dropdownMenu(colorScheme: ColorScheme) {
|
||||||
background: background(layer),
|
background: background(layer),
|
||||||
border: border(layer),
|
border: border(layer),
|
||||||
shadow: colorScheme.popoverShadow,
|
shadow: colorScheme.popoverShadow,
|
||||||
header: {
|
header: interactive({
|
||||||
...text(layer, "sans", { size: "sm" }),
|
base: {
|
||||||
secondaryText: text(layer, "sans", { size: "sm", color: "#aaaaaa" }),
|
...text(layer, "sans", { size: "sm" }),
|
||||||
secondaryTextSpacing: 10,
|
secondaryText: text(layer, "sans", {
|
||||||
padding: { left: 8, right: 8, top: 2, bottom: 2 },
|
size: "sm",
|
||||||
cornerRadius: 6,
|
color: "#aaaaaa",
|
||||||
background: background(layer, "on"),
|
}),
|
||||||
border: border(layer, "on", { overlay: true }),
|
secondaryTextSpacing: 10,
|
||||||
hover: {
|
padding: { left: 8, right: 8, top: 2, bottom: 2 },
|
||||||
background: background(layer, "hovered"),
|
cornerRadius: 6,
|
||||||
...text(layer, "sans", "hovered", { size: "sm" }),
|
background: background(layer, "on"),
|
||||||
}
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer, "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
sectionHeader: {
|
sectionHeader: {
|
||||||
...text(layer, "sans", { size: "sm" }),
|
...text(layer, "sans", { size: "sm" }),
|
||||||
padding: { left: 8, right: 8, top: 8, bottom: 8 },
|
padding: { left: 8, right: 8, top: 8, bottom: 8 },
|
||||||
},
|
},
|
||||||
item: {
|
item: toggleable({
|
||||||
...text(layer, "sans", { size: "sm" }),
|
base: interactive({
|
||||||
secondaryTextSpacing: 10,
|
base: {
|
||||||
secondaryText: text(layer, "sans", { size: "sm" }),
|
...text(layer, "sans", { size: "sm" }),
|
||||||
padding: { left: 18, right: 18, top: 2, bottom: 2 },
|
secondaryTextSpacing: 10,
|
||||||
hover: {
|
secondaryText: text(layer, "sans", { size: "sm" }),
|
||||||
background: background(layer, "hovered"),
|
padding: { left: 18, right: 18, top: 2, bottom: 2 },
|
||||||
...text(layer, "sans", "hovered", { size: "sm" }),
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
...text(layer, "sans", "hovered", { size: "sm" }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
background: background(layer, "active"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
active: {
|
}),
|
||||||
background: background(layer, "active"),
|
|
||||||
},
|
|
||||||
activeHover: {
|
|
||||||
background: background(layer, "active"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { foreground, text } from "./components"
|
import { foreground, text } from "./components"
|
||||||
|
import { interactive } from "../element"
|
||||||
|
|
||||||
const headerPadding = 8
|
const headerPadding = 8
|
||||||
|
|
||||||
|
@ -10,22 +11,30 @@ export default function updateNotification(colorScheme: ColorScheme): Object {
|
||||||
...text(layer, "sans", { size: "xs" }),
|
...text(layer, "sans", { size: "xs" }),
|
||||||
margin: { left: headerPadding, right: headerPadding },
|
margin: { left: headerPadding, right: headerPadding },
|
||||||
},
|
},
|
||||||
actionMessage: {
|
actionMessage: interactive({
|
||||||
...text(layer, "sans", { size: "xs" }),
|
base: {
|
||||||
margin: { left: headerPadding, top: 6, bottom: 6 },
|
...text(layer, "sans", { size: "xs" }),
|
||||||
hover: {
|
margin: { left: headerPadding, top: 6, bottom: 6 },
|
||||||
color: foreground(layer, "hovered"),
|
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
dismissButton: {
|
hovered: {
|
||||||
color: foreground(layer),
|
color: foreground(layer, "hovered"),
|
||||||
iconWidth: 8,
|
},
|
||||||
iconHeight: 8,
|
|
||||||
buttonWidth: 8,
|
|
||||||
buttonHeight: 8,
|
|
||||||
hover: {
|
|
||||||
color: foreground(layer, "hovered"),
|
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
|
dismissButton: interactive({
|
||||||
|
base: {
|
||||||
|
color: foreground(layer),
|
||||||
|
iconWidth: 8,
|
||||||
|
iconHeight: 8,
|
||||||
|
buttonWidth: 8,
|
||||||
|
buttonHeight: 8,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
color: foreground(layer, "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
TextProperties,
|
TextProperties,
|
||||||
svg,
|
svg,
|
||||||
} from "./components"
|
} from "./components"
|
||||||
|
import { interactive } from "../element"
|
||||||
|
|
||||||
export default function welcome(colorScheme: ColorScheme) {
|
export default function welcome(colorScheme: ColorScheme) {
|
||||||
let layer = colorScheme.highest
|
let layer = colorScheme.highest
|
||||||
|
@ -63,27 +64,31 @@ export default function welcome(colorScheme: ColorScheme) {
|
||||||
bottom: 2,
|
bottom: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
button: {
|
button: interactive({
|
||||||
background: background(layer),
|
base: {
|
||||||
border: border(layer, "active"),
|
background: background(layer),
|
||||||
cornerRadius: 4,
|
|
||||||
margin: {
|
|
||||||
top: 4,
|
|
||||||
bottom: 4,
|
|
||||||
},
|
|
||||||
padding: {
|
|
||||||
top: 3,
|
|
||||||
bottom: 3,
|
|
||||||
left: 7,
|
|
||||||
right: 7,
|
|
||||||
},
|
|
||||||
...text(layer, "sans", "default", interactive_text_size),
|
|
||||||
hover: {
|
|
||||||
...text(layer, "sans", "default", interactive_text_size),
|
|
||||||
background: background(layer, "hovered"),
|
|
||||||
border: border(layer, "active"),
|
border: border(layer, "active"),
|
||||||
|
cornerRadius: 4,
|
||||||
|
margin: {
|
||||||
|
top: 4,
|
||||||
|
bottom: 4,
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
top: 3,
|
||||||
|
bottom: 3,
|
||||||
|
left: 7,
|
||||||
|
right: 7,
|
||||||
|
},
|
||||||
|
...text(layer, "sans", "default", interactive_text_size),
|
||||||
},
|
},
|
||||||
},
|
state: {
|
||||||
|
hovered: {
|
||||||
|
...text(layer, "sans", "default", interactive_text_size),
|
||||||
|
background: background(layer, "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
usageNote: {
|
usageNote: {
|
||||||
...text(layer, "sans", "variant", { size: "2xs" }),
|
...text(layer, "sans", "variant", { size: "2xs" }),
|
||||||
padding: {
|
padding: {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ColorScheme } from "../theme/colorScheme"
|
import { ColorScheme } from "../theme/colorScheme"
|
||||||
import { withOpacity } from "../theme/color"
|
import { withOpacity } from "../theme/color"
|
||||||
|
import { toggleable } from "../element"
|
||||||
import {
|
import {
|
||||||
background,
|
background,
|
||||||
border,
|
border,
|
||||||
|
@ -10,38 +11,53 @@ import {
|
||||||
} from "./components"
|
} from "./components"
|
||||||
import statusBar from "./statusBar"
|
import statusBar from "./statusBar"
|
||||||
import tabBar from "./tabBar"
|
import tabBar from "./tabBar"
|
||||||
|
import { interactive } from "../element"
|
||||||
|
import merge from "ts-deepmerge"
|
||||||
export default function workspace(colorScheme: ColorScheme) {
|
export default function workspace(colorScheme: ColorScheme) {
|
||||||
const layer = colorScheme.lowest
|
const layer = colorScheme.lowest
|
||||||
const isLight = colorScheme.isLight
|
const isLight = colorScheme.isLight
|
||||||
const itemSpacing = 8
|
const itemSpacing = 8
|
||||||
const titlebarButton = {
|
const titlebarButton = toggleable({
|
||||||
cornerRadius: 6,
|
base: interactive({
|
||||||
padding: {
|
base: {
|
||||||
top: 1,
|
cornerRadius: 6,
|
||||||
bottom: 1,
|
padding: {
|
||||||
left: 8,
|
top: 1,
|
||||||
right: 8,
|
bottom: 1,
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
},
|
||||||
|
...text(layer, "sans", "variant", { size: "xs" }),
|
||||||
|
background: background(layer, "variant"),
|
||||||
|
border: border(layer),
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
...text(layer, "sans", "variant", "hovered", {
|
||||||
|
size: "xs",
|
||||||
|
}),
|
||||||
|
background: background(layer, "variant", "hovered"),
|
||||||
|
border: border(layer, "variant", "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
...text(layer, "sans", "variant", "pressed", {
|
||||||
|
size: "xs",
|
||||||
|
}),
|
||||||
|
background: background(layer, "variant", "pressed"),
|
||||||
|
border: border(layer, "variant", "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
...text(layer, "sans", "variant", "active", { size: "xs" }),
|
||||||
|
background: background(layer, "variant", "active"),
|
||||||
|
border: border(layer, "variant", "active"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
...text(layer, "sans", "variant", { size: "xs" }),
|
})
|
||||||
background: background(layer, "variant"),
|
|
||||||
border: border(layer),
|
|
||||||
hover: {
|
|
||||||
...text(layer, "sans", "variant", "hovered", { size: "xs" }),
|
|
||||||
background: background(layer, "variant", "hovered"),
|
|
||||||
border: border(layer, "variant", "hovered"),
|
|
||||||
},
|
|
||||||
clicked: {
|
|
||||||
...text(layer, "sans", "variant", "pressed", { size: "xs" }),
|
|
||||||
background: background(layer, "variant", "pressed"),
|
|
||||||
border: border(layer, "variant", "pressed"),
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
...text(layer, "sans", "variant", "active", { size: "xs" }),
|
|
||||||
background: background(layer, "variant", "active"),
|
|
||||||
border: border(layer, "variant", "active"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const avatarWidth = 18
|
const avatarWidth = 18
|
||||||
const avatarOuterWidth = avatarWidth + 4
|
const avatarOuterWidth = avatarWidth + 4
|
||||||
const followerAvatarWidth = 14
|
const followerAvatarWidth = 14
|
||||||
|
@ -78,19 +94,24 @@ export default function workspace(colorScheme: ColorScheme) {
|
||||||
},
|
},
|
||||||
cornerRadius: 4,
|
cornerRadius: 4,
|
||||||
},
|
},
|
||||||
keyboardHint: {
|
keyboardHint: interactive({
|
||||||
...text(layer, "sans", "variant", { size: "sm" }),
|
base: {
|
||||||
padding: {
|
...text(layer, "sans", "variant", { size: "sm" }),
|
||||||
top: 3,
|
padding: {
|
||||||
left: 8,
|
top: 3,
|
||||||
right: 8,
|
left: 8,
|
||||||
bottom: 3,
|
right: 8,
|
||||||
|
bottom: 3,
|
||||||
|
},
|
||||||
|
cornerRadius: 8,
|
||||||
},
|
},
|
||||||
cornerRadius: 8,
|
state: {
|
||||||
hover: {
|
hovered: {
|
||||||
...text(layer, "sans", "active", { size: "sm" }),
|
...text(layer, "sans", "active", { size: "sm" }),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
|
|
||||||
keyboardHintWidth: 320,
|
keyboardHintWidth: 320,
|
||||||
},
|
},
|
||||||
joiningProjectAvatar: {
|
joiningProjectAvatar: {
|
||||||
|
@ -201,12 +222,15 @@ export default function workspace(colorScheme: ColorScheme) {
|
||||||
|
|
||||||
// Sign in buttom
|
// Sign in buttom
|
||||||
// FlatButton, Variant
|
// FlatButton, Variant
|
||||||
signInPrompt: {
|
signInPrompt: merge(titlebarButton, {
|
||||||
margin: {
|
inactive: {
|
||||||
left: itemSpacing,
|
default: {
|
||||||
|
margin: {
|
||||||
|
left: itemSpacing,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
...titlebarButton,
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
// Offline Indicator
|
// Offline Indicator
|
||||||
offlineIcon: {
|
offlineIcon: {
|
||||||
|
@ -234,40 +258,68 @@ export default function workspace(colorScheme: ColorScheme) {
|
||||||
},
|
},
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
},
|
},
|
||||||
callControl: {
|
callControl: interactive({
|
||||||
cornerRadius: 6,
|
base: {
|
||||||
color: foreground(layer, "variant"),
|
cornerRadius: 6,
|
||||||
iconWidth: 12,
|
color: foreground(layer, "variant"),
|
||||||
buttonWidth: 20,
|
iconWidth: 12,
|
||||||
hover: {
|
buttonWidth: 20,
|
||||||
background: background(layer, "variant", "hovered"),
|
},
|
||||||
color: foreground(layer, "variant", "hovered"),
|
state: {
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "variant", "hovered"),
|
||||||
|
color: foreground(layer, "variant", "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
toggleContactsButton: toggleable({
|
||||||
|
base: interactive({
|
||||||
|
base: {
|
||||||
|
margin: { left: itemSpacing },
|
||||||
|
cornerRadius: 6,
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
iconWidth: 14,
|
||||||
|
buttonWidth: 20,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
clicked: {
|
||||||
|
background: background(layer, "variant", "pressed"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "variant", "hovered"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
state: {
|
||||||
|
active: {
|
||||||
|
default: {
|
||||||
|
background: background(layer, "on", "default"),
|
||||||
|
},
|
||||||
|
hovered: {
|
||||||
|
background: background(layer, "on", "hovered"),
|
||||||
|
},
|
||||||
|
clicked: {
|
||||||
|
background: background(layer, "on", "pressed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
userMenuButton: merge(titlebarButton, {
|
||||||
|
inactive: {
|
||||||
|
default: {
|
||||||
|
buttonWidth: 20,
|
||||||
|
iconWidth: 12,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
toggleContactsButton: {
|
|
||||||
margin: { left: itemSpacing },
|
|
||||||
cornerRadius: 6,
|
|
||||||
color: foreground(layer, "variant"),
|
|
||||||
iconWidth: 14,
|
|
||||||
buttonWidth: 20,
|
|
||||||
active: {
|
active: {
|
||||||
background: background(layer, "variant", "active"),
|
// posiewic: these properties are not currently set on main
|
||||||
color: foreground(layer, "variant", "active"),
|
default: {
|
||||||
|
iconWidth: 12,
|
||||||
|
button_width: 20,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
clicked: {
|
}),
|
||||||
background: background(layer, "variant", "pressed"),
|
|
||||||
color: foreground(layer, "variant", "pressed"),
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
background: background(layer, "variant", "hovered"),
|
|
||||||
color: foreground(layer, "variant", "hovered"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
userMenuButton: {
|
|
||||||
buttonWidth: 20,
|
|
||||||
iconWidth: 12,
|
|
||||||
...titlebarButton,
|
|
||||||
},
|
|
||||||
toggleContactsBadge: {
|
toggleContactsBadge: {
|
||||||
cornerRadius: 3,
|
cornerRadius: 3,
|
||||||
padding: 2,
|
padding: 2,
|
||||||
|
@ -285,12 +337,45 @@ export default function workspace(colorScheme: ColorScheme) {
|
||||||
background: background(colorScheme.highest),
|
background: background(colorScheme.highest),
|
||||||
border: border(colorScheme.highest, { bottom: true }),
|
border: border(colorScheme.highest, { bottom: true }),
|
||||||
itemSpacing: 8,
|
itemSpacing: 8,
|
||||||
navButton: {
|
navButton: interactive({
|
||||||
color: foreground(colorScheme.highest, "on"),
|
base: {
|
||||||
iconWidth: 12,
|
color: foreground(colorScheme.highest, "on"),
|
||||||
buttonWidth: 24,
|
iconWidth: 12,
|
||||||
|
buttonWidth: 24,
|
||||||
|
cornerRadius: 6,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
|
color: foreground(colorScheme.highest, "on", "hovered"),
|
||||||
|
background: background(
|
||||||
|
colorScheme.highest,
|
||||||
|
"on",
|
||||||
|
"hovered"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
color: foreground(
|
||||||
|
colorScheme.highest,
|
||||||
|
"on",
|
||||||
|
"disabled"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
padding: { left: 8, right: 8, top: 4, bottom: 4 },
|
||||||
|
},
|
||||||
|
breadcrumbHeight: 24,
|
||||||
|
breadcrumbs: interactive({
|
||||||
|
base: {
|
||||||
|
...text(colorScheme.highest, "sans", "variant"),
|
||||||
cornerRadius: 6,
|
cornerRadius: 6,
|
||||||
hover: {
|
padding: {
|
||||||
|
left: 6,
|
||||||
|
right: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
hovered: {
|
||||||
color: foreground(colorScheme.highest, "on", "hovered"),
|
color: foreground(colorScheme.highest, "on", "hovered"),
|
||||||
background: background(
|
background: background(
|
||||||
colorScheme.highest,
|
colorScheme.highest,
|
||||||
|
@ -298,25 +383,8 @@ export default function workspace(colorScheme: ColorScheme) {
|
||||||
"hovered"
|
"hovered"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
disabled: {
|
|
||||||
color: foreground(colorScheme.highest, "on", "disabled"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
padding: { left: 8, right: 8, top: 4, bottom: 4 },
|
}),
|
||||||
},
|
|
||||||
breadcrumbHeight: 24,
|
|
||||||
breadcrumbs: {
|
|
||||||
...text(colorScheme.highest, "sans", "variant"),
|
|
||||||
cornerRadius: 6,
|
|
||||||
padding: {
|
|
||||||
left: 6,
|
|
||||||
right: 6,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
color: foreground(colorScheme.highest, "on", "hovered"),
|
|
||||||
background: background(colorScheme.highest, "on", "hovered"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
disconnectedOverlay: {
|
disconnectedOverlay: {
|
||||||
...text(layer, "sans"),
|
...text(layer, "sans"),
|
||||||
background: withOpacity(background(layer), 0.8),
|
background: withOpacity(background(layer), 0.8),
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
import { SingleBoxShadowToken, SingleColorToken, SingleOtherToken, TokenTypes } from "@tokens-studio/types"
|
import {
|
||||||
import { ColorScheme, Shadow, SyntaxHighlightStyle, ThemeSyntax } from "../colorScheme"
|
SingleBoxShadowToken,
|
||||||
|
SingleColorToken,
|
||||||
|
SingleOtherToken,
|
||||||
|
TokenTypes,
|
||||||
|
} from "@tokens-studio/types"
|
||||||
|
import {
|
||||||
|
ColorScheme,
|
||||||
|
Shadow,
|
||||||
|
SyntaxHighlightStyle,
|
||||||
|
ThemeSyntax,
|
||||||
|
} from "../colorScheme"
|
||||||
import { LayerToken, layerToken } from "./layer"
|
import { LayerToken, layerToken } from "./layer"
|
||||||
import { PlayersToken, playersToken } from "./players"
|
import { PlayersToken, playersToken } from "./players"
|
||||||
import { colorToken } from "./token"
|
import { colorToken } from "./token"
|
||||||
import { Syntax } from "../syntax";
|
import { Syntax } from "../syntax"
|
||||||
import editor from "../../styleTree/editor"
|
import editor from "../../styleTree/editor"
|
||||||
|
|
||||||
interface ColorSchemeTokens {
|
interface ColorSchemeTokens {
|
||||||
|
@ -18,27 +28,32 @@ interface ColorSchemeTokens {
|
||||||
syntax?: Partial<ThemeSyntaxColorTokens>
|
syntax?: Partial<ThemeSyntaxColorTokens>
|
||||||
}
|
}
|
||||||
|
|
||||||
const createShadowToken = (shadow: Shadow, tokenName: string): SingleBoxShadowToken => {
|
const createShadowToken = (
|
||||||
|
shadow: Shadow,
|
||||||
|
tokenName: string
|
||||||
|
): SingleBoxShadowToken => {
|
||||||
return {
|
return {
|
||||||
name: tokenName,
|
name: tokenName,
|
||||||
type: TokenTypes.BOX_SHADOW,
|
type: TokenTypes.BOX_SHADOW,
|
||||||
value: `${shadow.offset[0]}px ${shadow.offset[1]}px ${shadow.blur}px 0px ${shadow.color}`
|
value: `${shadow.offset[0]}px ${shadow.offset[1]}px ${shadow.blur}px 0px ${shadow.color}`,
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const popoverShadowToken = (colorScheme: ColorScheme): SingleBoxShadowToken => {
|
const popoverShadowToken = (colorScheme: ColorScheme): SingleBoxShadowToken => {
|
||||||
const shadow = colorScheme.popoverShadow;
|
const shadow = colorScheme.popoverShadow
|
||||||
return createShadowToken(shadow, "popoverShadow");
|
return createShadowToken(shadow, "popoverShadow")
|
||||||
};
|
}
|
||||||
|
|
||||||
const modalShadowToken = (colorScheme: ColorScheme): SingleBoxShadowToken => {
|
const modalShadowToken = (colorScheme: ColorScheme): SingleBoxShadowToken => {
|
||||||
const shadow = colorScheme.modalShadow;
|
const shadow = colorScheme.modalShadow
|
||||||
return createShadowToken(shadow, "modalShadow");
|
return createShadowToken(shadow, "modalShadow")
|
||||||
};
|
}
|
||||||
|
|
||||||
type ThemeSyntaxColorTokens = Record<keyof ThemeSyntax, SingleColorToken>
|
type ThemeSyntaxColorTokens = Record<keyof ThemeSyntax, SingleColorToken>
|
||||||
|
|
||||||
function syntaxHighlightStyleColorTokens(syntax: Syntax): ThemeSyntaxColorTokens {
|
function syntaxHighlightStyleColorTokens(
|
||||||
|
syntax: Syntax
|
||||||
|
): ThemeSyntaxColorTokens {
|
||||||
const styleKeys = Object.keys(syntax) as (keyof Syntax)[]
|
const styleKeys = Object.keys(syntax) as (keyof Syntax)[]
|
||||||
|
|
||||||
return styleKeys.reduce((acc, styleKey) => {
|
return styleKeys.reduce((acc, styleKey) => {
|
||||||
|
@ -46,13 +61,16 @@ function syntaxHighlightStyleColorTokens(syntax: Syntax): ThemeSyntaxColorTokens
|
||||||
// This can happen because we have a "constructor" property on the syntax object
|
// This can happen because we have a "constructor" property on the syntax object
|
||||||
// and a "constructor" property on the prototype of the syntax object
|
// and a "constructor" property on the prototype of the syntax object
|
||||||
// To work around this just assert that the type of the style is not a function
|
// To work around this just assert that the type of the style is not a function
|
||||||
if (!syntax[styleKey] || typeof syntax[styleKey] === 'function') return acc;
|
if (!syntax[styleKey] || typeof syntax[styleKey] === "function")
|
||||||
const { color } = syntax[styleKey] as Required<SyntaxHighlightStyle>;
|
return acc
|
||||||
return { ...acc, [styleKey]: colorToken(styleKey, color) };
|
const { color } = syntax[styleKey] as Required<SyntaxHighlightStyle>
|
||||||
}, {} as ThemeSyntaxColorTokens);
|
return { ...acc, [styleKey]: colorToken(styleKey, color) }
|
||||||
|
}, {} as ThemeSyntaxColorTokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
const syntaxTokens = (colorScheme: ColorScheme): ColorSchemeTokens['syntax'] => {
|
const syntaxTokens = (
|
||||||
|
colorScheme: ColorScheme
|
||||||
|
): ColorSchemeTokens["syntax"] => {
|
||||||
const syntax = editor(colorScheme).syntax
|
const syntax = editor(colorScheme).syntax
|
||||||
|
|
||||||
return syntaxHighlightStyleColorTokens(syntax)
|
return syntaxHighlightStyleColorTokens(syntax)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { SingleColorToken } from "@tokens-studio/types";
|
import { SingleColorToken } from "@tokens-studio/types"
|
||||||
import { Layer, Style, StyleSet } from "../colorScheme";
|
import { Layer, Style, StyleSet } from "../colorScheme"
|
||||||
import { colorToken } from "./token";
|
import { colorToken } from "./token"
|
||||||
|
|
||||||
interface StyleToken {
|
interface StyleToken {
|
||||||
background: SingleColorToken,
|
background: SingleColorToken
|
||||||
border: SingleColorToken,
|
border: SingleColorToken
|
||||||
foreground: SingleColorToken,
|
foreground: SingleColorToken
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StyleSetToken {
|
interface StyleSetToken {
|
||||||
|
@ -37,24 +37,27 @@ export const styleToken = (style: Style, name: string): StyleToken => {
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
export const styleSetToken = (styleSet: StyleSet, name: string): StyleSetToken => {
|
export const styleSetToken = (
|
||||||
const token: StyleSetToken = {} as StyleSetToken;
|
styleSet: StyleSet,
|
||||||
|
name: string
|
||||||
|
): StyleSetToken => {
|
||||||
|
const token: StyleSetToken = {} as StyleSetToken
|
||||||
|
|
||||||
for (const style in styleSet) {
|
for (const style in styleSet) {
|
||||||
const s = style as keyof StyleSet;
|
const s = style as keyof StyleSet
|
||||||
token[s] = styleToken(styleSet[s], `${name}${style}`);
|
token[s] = styleToken(styleSet[s], `${name}${style}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
export const layerToken = (layer: Layer, name: string): LayerToken => {
|
export const layerToken = (layer: Layer, name: string): LayerToken => {
|
||||||
const token: LayerToken = {} as LayerToken;
|
const token: LayerToken = {} as LayerToken
|
||||||
|
|
||||||
for (const styleSet in layer) {
|
for (const styleSet in layer) {
|
||||||
const s = styleSet as keyof Layer;
|
const s = styleSet as keyof Layer
|
||||||
token[s] = styleSetToken(layer[s], `${name}${styleSet}`);
|
token[s] = styleSetToken(layer[s], `${name}${styleSet}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
return token
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,21 @@ export type PlayerToken = Record<"selection" | "cursor", SingleColorToken>
|
||||||
|
|
||||||
export type PlayersToken = Record<keyof Players, PlayerToken>
|
export type PlayersToken = Record<keyof Players, PlayerToken>
|
||||||
|
|
||||||
function buildPlayerToken(colorScheme: ColorScheme, index: number): PlayerToken {
|
function buildPlayerToken(
|
||||||
|
colorScheme: ColorScheme,
|
||||||
|
index: number
|
||||||
|
): PlayerToken {
|
||||||
const playerNumber = index.toString() as keyof Players
|
const playerNumber = index.toString() as keyof Players
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selection: colorToken(`player${index}Selection`, colorScheme.players[playerNumber].selection),
|
selection: colorToken(
|
||||||
cursor: colorToken(`player${index}Cursor`, colorScheme.players[playerNumber].cursor),
|
`player${index}Selection`,
|
||||||
|
colorScheme.players[playerNumber].selection
|
||||||
|
),
|
||||||
|
cursor: colorToken(
|
||||||
|
`player${index}Cursor`,
|
||||||
|
colorScheme.players[playerNumber].cursor
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,5 +32,5 @@ export const playersToken = (colorScheme: ColorScheme): PlayersToken => ({
|
||||||
"4": buildPlayerToken(colorScheme, 4),
|
"4": buildPlayerToken(colorScheme, 4),
|
||||||
"5": buildPlayerToken(colorScheme, 5),
|
"5": buildPlayerToken(colorScheme, 5),
|
||||||
"6": buildPlayerToken(colorScheme, 6),
|
"6": buildPlayerToken(colorScheme, 6),
|
||||||
"7": buildPlayerToken(colorScheme, 7)
|
"7": buildPlayerToken(colorScheme, 7),
|
||||||
})
|
})
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue