WIP UI Tweaks (#2911)

- Tighten up toolbar
- Reduce intensity of active tools
- Remove divider between project + branch
- Add a styletree for toolbar + move breadcrumb into it
- Some ts theme tidying

[[PR Description]]

Release Notes:

- Improved density and contrast of a number of UI elements.
This commit is contained in:
Nate Butler 2023-08-30 11:19:05 -04:00 committed by GitHub
commit e808386765
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 493 additions and 372 deletions

View File

@ -1,4 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 8L6.5 9L9 5.5" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> <path d="M5 8L6.5 9L9 5.5" stroke="#11181C" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="7" cy="7" r="4.875" stroke="black" stroke-width="1.25"/> <circle cx="7" cy="7" r="4.875" stroke="#11181C" stroke-width="1.25"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 287 B

View File

@ -1,4 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.86396 2C8.99657 2 9.12375 2.05268 9.21751 2.14645L11.8536 4.78249C11.9473 4.87625 12 5.00343 12 5.13604L12 8.86396C12 8.99657 11.9473 9.12375 11.8536 9.21751L9.21751 11.8536C9.12375 11.9473 8.99657 12 8.86396 12L5.13604 12C5.00343 12 4.87625 11.9473 4.78249 11.8536L2.14645 9.21751C2.05268 9.12375 2 8.99657 2 8.86396L2 5.13604C2 5.00343 2.05268 4.87625 2.14645 4.78249L4.78249 2.14645C4.87625 2.05268 5.00343 2 5.13604 2L8.86396 2Z" stroke="black" stroke-width="1.25" stroke-linejoin="round"/> <path d="M8.86396 2C8.99657 2 9.12375 2.05268 9.21751 2.14645L11.8536 4.78249C11.9473 4.87625 12 5.00343 12 5.13604L12 8.86396C12 8.99657 11.9473 9.12375 11.8536 9.21751L9.21751 11.8536C9.12375 11.9473 8.99657 12 8.86396 12L5.13604 12C5.00343 12 4.87625 11.9473 4.78249 11.8536L2.14645 9.21751C2.05268 9.12375 2 8.99657 2 8.86396L2 5.13604C2 5.00343 2.05268 4.87625 2.14645 4.78249L4.78249 2.14645C4.87625 2.05268 5.00343 2 5.13604 2L8.86396 2Z" fill="#001A33" fill-opacity="0.157" stroke="#11181C" stroke-width="1.25" stroke-linejoin="round"/>
<path d="M8.89063 5.10938L5.10937 8.89063M8.89063 8.89063L5.10937 5.10938" stroke="black" stroke-width="1.25" stroke-linecap="round"/> <path d="M8.89063 5.10938L5.10937 8.89063M8.89063 8.89063L5.10937 5.10938" stroke="#11181C" stroke-width="1.25" stroke-linecap="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 745 B

After

Width:  |  Height:  |  Size: 785 B

View File

@ -1,5 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.5 6.5L11.994 11.625C12.1556 11.9571 11.9137 12.3438 11.5444 12.3438H2.45563C2.08628 12.3438 1.84442 11.9571 2.00603 11.625L4.5 6.5" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> <path d="M2.45563 12.3438H11.5444C11.9137 12.3438 12.1556 11.9571 11.994 11.625L10.2346 8.00952C9.77174 7.05841 8.89104 6.37821 7.85383 6.17077C7.29019 6.05804 6.70981 6.05804 6.14617 6.17077C5.10896 6.37821 4.22826 7.05841 3.76542 8.00952L2.00603 11.625C1.84442 11.9571 2.08628 12.3438 2.45563 12.3438Z" fill="#001A33" fill-opacity="0.157"/>
<path d="M7 7L7 2" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> <path d="M9.5 6.5L11.994 11.625C12.1556 11.9571 11.9137 12.3438 11.5444 12.3438H2.45563C2.08628 12.3438 1.84442 11.9571 2.00603 11.625L4.5 6.5" stroke="#11181C" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="7" cy="9.24219" r="0.75" fill="black"/> <path d="M7 7L7 2" stroke="#11181C" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="7" cy="9.24219" r="0.75" fill="#11181C"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 835 B

View File

@ -50,7 +50,7 @@ impl View for Breadcrumbs {
let not_editor = active_item.downcast::<editor::Editor>().is_none(); let not_editor = active_item.downcast::<editor::Editor>().is_none();
let theme = theme::current(cx).clone(); let theme = theme::current(cx).clone();
let style = &theme.workspace.breadcrumbs; let style = &theme.workspace.toolbar.breadcrumbs;
let breadcrumbs = match active_item.breadcrumbs(&theme, cx) { let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
Some(breadcrumbs) => breadcrumbs, Some(breadcrumbs) => breadcrumbs,
@ -60,7 +60,7 @@ impl View for Breadcrumbs {
.map(|breadcrumb| { .map(|breadcrumb| {
Text::new( Text::new(
breadcrumb.text, breadcrumb.text,
theme.workspace.breadcrumbs.default.text.clone(), theme.workspace.toolbar.breadcrumbs.default.text.clone(),
) )
.with_highlights(breadcrumb.highlights.unwrap_or_default()) .with_highlights(breadcrumb.highlights.unwrap_or_default())
.into_any() .into_any()
@ -68,10 +68,10 @@ impl View for Breadcrumbs {
let crumbs = Flex::row() let crumbs = Flex::row()
.with_children(Itertools::intersperse_with(breadcrumbs, || { .with_children(Itertools::intersperse_with(breadcrumbs, || {
Label::new(" ", style.default.text.clone()).into_any() Label::new(" ", style.default.text.clone()).into_any()
})) }))
.constrained() .constrained()
.with_height(theme.workspace.breadcrumb_height) .with_height(theme.workspace.toolbar.breadcrumb_height)
.contained(); .contained();
if not_editor || !self.pane_focused { if not_editor || !self.pane_focused {

View File

@ -213,7 +213,6 @@ impl CollabTitlebarItem {
.map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH)); .map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH));
let project_style = theme.titlebar.project_menu_button.clone(); let project_style = theme.titlebar.project_menu_button.clone();
let git_style = theme.titlebar.git_menu_button.clone(); let git_style = theme.titlebar.git_menu_button.clone();
let divider_style = theme.titlebar.project_name_divider.clone();
let item_spacing = theme.titlebar.item_spacing; let item_spacing = theme.titlebar.item_spacing;
let mut ret = Flex::row().with_child( let mut ret = Flex::row().with_child(
@ -248,49 +247,37 @@ impl CollabTitlebarItem {
); );
if let Some(git_branch) = branch_prepended { if let Some(git_branch) = branch_prepended {
ret = ret.with_child( ret = ret.with_child(
Flex::row() Flex::row().with_child(
.with_child( Stack::new()
Label::new("/", divider_style.text) .with_child(
.contained() MouseEventHandler::new::<ToggleVcsMenu, _>(0, cx, |mouse_state, cx| {
.with_style(divider_style.container) enum BranchPopoverTooltip {}
.aligned() let style = git_style
.left(), .in_state(self.branch_popover.is_some())
) .style_for(mouse_state);
.with_child( Label::new(git_branch, style.text.clone())
Stack::new() .contained()
.with_child( .with_style(style.container.clone())
MouseEventHandler::new::<ToggleVcsMenu, _>( .with_margin_right(item_spacing)
0, .aligned()
cx, .left()
|mouse_state, cx| { .with_tooltip::<BranchPopoverTooltip>(
enum BranchPopoverTooltip {} 0,
let style = git_style "Recent branches",
.in_state(self.branch_popover.is_some()) Some(Box::new(ToggleVcsMenu)),
.style_for(mouse_state); theme.tooltip.clone(),
Label::new(git_branch, style.text.clone()) cx,
.contained() )
.with_style(style.container.clone()) .into_any_named("title-project-branch")
.with_margin_right(item_spacing) })
.aligned() .with_cursor_style(CursorStyle::PointingHand)
.left() .on_down(MouseButton::Left, move |_, this, cx| {
.with_tooltip::<BranchPopoverTooltip>( this.toggle_vcs_menu(&Default::default(), cx)
0, })
"Recent branches", .on_click(MouseButton::Left, move |_, _, _| {}),
Some(Box::new(ToggleVcsMenu)), )
theme.tooltip.clone(), .with_children(self.render_branches_popover_host(&theme.titlebar, cx)),
cx, ),
)
.into_any_named("title-project-branch")
},
)
.with_cursor_style(CursorStyle::PointingHand)
.on_down(MouseButton::Left, move |_, this, cx| {
this.toggle_vcs_menu(&Default::default(), cx)
})
.on_click(MouseButton::Left, move |_, _, _| {}),
)
.with_children(self.render_branches_popover_host(&theme.titlebar, cx)),
),
) )
} }
ret.into_any() ret.into_any()

View File

@ -88,8 +88,6 @@ pub struct Workspace {
pub dock: Dock, pub dock: Dock,
pub status_bar: StatusBar, pub status_bar: StatusBar,
pub toolbar: Toolbar, pub toolbar: Toolbar,
pub breadcrumb_height: f32,
pub breadcrumbs: Interactive<ContainedText>,
pub disconnected_overlay: ContainedText, pub disconnected_overlay: ContainedText,
pub modal: ContainerStyle, pub modal: ContainerStyle,
pub zoomed_panel_foreground: ContainerStyle, pub zoomed_panel_foreground: ContainerStyle,
@ -120,7 +118,6 @@ pub struct Titlebar {
pub height: f32, pub height: f32,
pub menu: TitlebarMenu, pub menu: TitlebarMenu,
pub project_menu_button: Toggleable<Interactive<ContainedText>>, pub project_menu_button: Toggleable<Interactive<ContainedText>>,
pub project_name_divider: ContainedText,
pub git_menu_button: Toggleable<Interactive<ContainedText>>, pub git_menu_button: Toggleable<Interactive<ContainedText>>,
pub item_spacing: f32, pub item_spacing: f32,
pub face_pile_spacing: f32, pub face_pile_spacing: f32,
@ -411,6 +408,8 @@ pub struct Toolbar {
pub height: f32, pub height: f32,
pub item_spacing: f32, pub item_spacing: f32,
pub toggleable_tool: Toggleable<Interactive<IconButton>>, pub toggleable_tool: Toggleable<Interactive<IconButton>>,
pub breadcrumb_height: f32,
pub breadcrumbs: Interactive<ContainedText>,
} }
#[derive(Clone, Deserialize, Default, JsonSchema)] #[derive(Clone, Deserialize, Default, JsonSchema)]

View File

@ -21,9 +21,7 @@ function clear_themes(theme_directory: string) {
} }
} }
const all_themes: Theme[] = themes.map((theme) => const all_themes: Theme[] = themes.map((theme) => create_theme(theme))
create_theme(theme)
)
function write_themes(themes: Theme[], output_directory: string) { function write_themes(themes: Theme[], output_directory: string) {
clear_themes(output_directory) clear_themes(output_directory)
@ -34,10 +32,7 @@ function write_themes(themes: Theme[], output_directory: string) {
const style_tree = app() const style_tree = app()
const style_tree_json = JSON.stringify(style_tree, null, 2) const style_tree_json = JSON.stringify(style_tree, null, 2)
const temp_path = path.join(temp_directory, `${theme.name}.json`) const temp_path = path.join(temp_directory, `${theme.name}.json`)
const out_path = path.join( const out_path = path.join(output_directory, `${theme.name}.json`)
output_directory,
`${theme.name}.json`
)
fs.writeFileSync(temp_path, style_tree_json) fs.writeFileSync(temp_path, style_tree_json)
fs.renameSync(temp_path, out_path) fs.renameSync(temp_path, out_path)
console.log(`- ${out_path} created`) console.log(`- ${out_path} created`)

View File

@ -83,8 +83,6 @@ function write_tokens(themes: Theme[], tokens_directory: string) {
console.log(`- ${METADATA_FILE} created`) console.log(`- ${METADATA_FILE} created`)
} }
const all_themes: Theme[] = themes.map((theme) => const all_themes: Theme[] = themes.map((theme) => create_theme(theme))
create_theme(theme)
)
write_tokens(all_themes, TOKENS_DIRECTORY) write_tokens(all_themes, TOKENS_DIRECTORY)

View File

@ -5,7 +5,7 @@ import { TextStyle, background } from "../style_tree/components"
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Button { export namespace Button {
export type Options = { export type Options = {
layer: Layer, layer: Layer
background: keyof Theme["lowest"] background: keyof Theme["lowest"]
color: keyof Theme["lowest"] color: keyof Theme["lowest"]
variant: Button.Variant variant: Button.Variant
@ -16,13 +16,13 @@ export namespace Button {
bottom?: number bottom?: number
left?: number left?: number
right?: number right?: number
}, }
states: { states: {
enabled?: boolean, enabled?: boolean
hovered?: boolean, hovered?: boolean
pressed?: boolean, pressed?: boolean
focused?: boolean, focused?: boolean
disabled?: boolean, disabled?: boolean
} }
} }
@ -38,26 +38,26 @@ export namespace Button {
export const CORNER_RADIUS = 6 export const CORNER_RADIUS = 6
export const variant = { export const variant = {
Default: 'filled', Default: "filled",
Outline: 'outline', Outline: "outline",
Ghost: 'ghost' Ghost: "ghost",
} as const } as const
export type Variant = typeof variant[keyof typeof variant] export type Variant = (typeof variant)[keyof typeof variant]
export const shape = { export const shape = {
Rectangle: 'rectangle', Rectangle: "rectangle",
Square: 'square' Square: "square",
} as const } as const
export type Shape = typeof shape[keyof typeof shape] export type Shape = (typeof shape)[keyof typeof shape]
export const size = { export const size = {
Small: "sm", Small: "sm",
Medium: "md" Medium: "md",
} as const } as const
export type Size = typeof size[keyof typeof size] export type Size = (typeof size)[keyof typeof size]
export type BaseStyle = { export type BaseStyle = {
corder_radius: number corder_radius: number
@ -67,8 +67,8 @@ export namespace Button {
bottom: number bottom: number
left: number left: number
right: number right: number
}, }
margin: Button.Options['margin'] margin: Button.Options["margin"]
button_height: number button_height: number
} }
@ -81,15 +81,18 @@ export namespace Button {
shape: Button.shape.Rectangle, shape: Button.shape.Rectangle,
states: { states: {
hovered: true, hovered: true,
pressed: true pressed: true,
} },
} }
): BaseStyle => { ): BaseStyle => {
const theme = useTheme() const theme = useTheme()
const layer = options.layer ?? theme.middle const layer = options.layer ?? theme.middle
const color = options.color ?? "base" const color = options.color ?? "base"
const background_color = options.variant === Button.variant.Ghost ? null : background(layer, options.background ?? color) const background_color =
options.variant === Button.variant.Ghost
? null
: background(layer, options.background ?? color)
const m = { const m = {
top: options.margin?.top ?? 0, top: options.margin?.top ?? 0,
@ -106,8 +109,14 @@ export namespace Button {
padding: { padding: {
top: padding, top: padding,
bottom: padding, bottom: padding,
left: options.shape === Button.shape.Rectangle ? padding + Button.RECTANGLE_PADDING : padding, left:
right: options.shape === Button.shape.Rectangle ? padding + Button.RECTANGLE_PADDING : padding options.shape === Button.shape.Rectangle
? padding + Button.RECTANGLE_PADDING
: padding,
right:
options.shape === Button.shape.Rectangle
? padding + Button.RECTANGLE_PADDING
: padding,
}, },
margin: m, margin: m,
button_height: 16, button_height: 16,

View File

@ -11,11 +11,9 @@ export type Margin = {
} }
interface IconButtonOptions { interface IconButtonOptions {
layer?: layer?: Theme["lowest"] | Theme["middle"] | Theme["highest"]
| Theme["lowest"]
| Theme["middle"]
| Theme["highest"]
color?: keyof Theme["lowest"] color?: keyof Theme["lowest"]
background_color?: keyof Theme["lowest"]
margin?: Partial<Margin> margin?: Partial<Margin>
variant?: Button.Variant variant?: Button.Variant
size?: Button.Size size?: Button.Size
@ -23,18 +21,25 @@ interface IconButtonOptions {
type ToggleableIconButtonOptions = IconButtonOptions & { type ToggleableIconButtonOptions = IconButtonOptions & {
active_color?: keyof Theme["lowest"] active_color?: keyof Theme["lowest"]
active_background_color?: keyof Theme["lowest"]
active_layer?: Layer active_layer?: Layer
active_variant?: Button.Variant
} }
export function icon_button({ color, margin, layer, variant, size }: IconButtonOptions = { export function icon_button(
variant: Button.variant.Default, { color, background_color, margin, layer, variant, size }: IconButtonOptions = {
size: Button.size.Medium, variant: Button.variant.Default,
}) { size: Button.size.Medium,
}
) {
const theme = useTheme() const theme = useTheme()
if (!color) color = "base" if (!color) color = "base"
const background_color = variant === Button.variant.Ghost ? null : background(layer ?? theme.lowest, color) const default_background =
variant === Button.variant.Ghost
? null
: background(layer ?? theme.lowest, background_color ?? color)
const m = { const m = {
top: margin?.top ?? 0, top: margin?.top ?? 0,
@ -55,42 +60,51 @@ export function icon_button({ color, margin, layer, variant, size }: IconButtonO
corner_radius: 6, corner_radius: 6,
padding: padding, padding: padding,
margin: m, margin: m,
icon_width: 12, icon_width: 14,
icon_height: 14, icon_height: 14,
button_width: size === Button.size.Small ? 16 : 20, button_width: size === Button.size.Small ? 16 : 20,
button_height: 14, button_height: 14,
}, },
state: { state: {
default: { default: {
background: background_color, background: default_background,
color: foreground(layer ?? theme.lowest, color), color: foreground(layer ?? theme.lowest, color),
}, },
hovered: { hovered: {
background: background(layer ?? theme.lowest, color, "hovered"), background: background(layer ?? theme.lowest, background_color ?? color, "hovered"),
color: foreground(layer ?? theme.lowest, color, "hovered"), color: foreground(layer ?? theme.lowest, color, "hovered"),
}, },
clicked: { clicked: {
background: background(layer ?? theme.lowest, color, "pressed"), background: background(layer ?? theme.lowest, background_color ?? color, "pressed"),
color: foreground(layer ?? theme.lowest, color, "pressed"), color: foreground(layer ?? theme.lowest, color, "pressed"),
}, },
}, },
}) })
} }
export function toggleable_icon_button( export function toggleable_icon_button({
theme: Theme, color,
{ color, active_color, margin, variant, size, active_layer }: ToggleableIconButtonOptions background_color,
) { active_color,
active_background_color,
active_variant,
margin,
variant,
size,
active_layer,
}: ToggleableIconButtonOptions) {
if (!color) color = "base" if (!color) color = "base"
return toggleable({ return toggleable({
state: { state: {
inactive: icon_button({ color, margin, variant, size }), inactive: icon_button({ color, background_color, margin, variant, size }),
active: icon_button({ active: icon_button({
color: active_color ? active_color : color, color: active_color ? active_color : color,
background_color: active_background_color ? active_background_color : background_color,
margin, margin,
layer: active_layer, layer: active_layer,
size variant: active_variant || variant,
size,
}), }),
}, },
}) })

View File

@ -0,0 +1,6 @@
export * from "./icon_button"
export * from "./indicator"
export * from "./input"
export * from "./tab"
export * from "./tab_bar_button"
export * from "./text_button"

View File

@ -1,7 +1,13 @@
import { foreground } from "../style_tree/components" import { foreground } from "../style_tree/components"
import { Layer, StyleSets } from "../theme" import { Layer, StyleSets } from "../theme"
export const indicator = ({ layer, color }: { layer: Layer, color: StyleSets }) => ({ export const indicator = ({
layer,
color,
}: {
layer: Layer
color: StyleSets
}) => ({
corner_radius: 4, corner_radius: 4,
padding: 4, padding: 4,
margin: { top: 12, left: 12 }, margin: { top: 12, left: 12 },

View File

@ -18,6 +18,6 @@ export const input = () => {
bottom: 3, bottom: 3,
left: 12, left: 12,
right: 8, right: 8,
} },
} }
} }

View File

@ -9,7 +9,7 @@ type TabProps = {
export const tab = ({ layer }: TabProps) => { export const tab = ({ layer }: TabProps) => {
const active_color = text(layer, "sans", "base").color const active_color = text(layer, "sans", "base").color
const inactive_border: Border = { const inactive_border: Border = {
color: '#FFFFFF00', color: "#FFFFFF00",
width: 1, width: 1,
bottom: true, bottom: true,
left: false, left: false,
@ -27,7 +27,7 @@ export const tab = ({ layer }: TabProps) => {
top: 8, top: 8,
left: 8, left: 8,
right: 8, right: 8,
bottom: 6 bottom: 6,
}, },
border: inactive_border, border: inactive_border,
} }
@ -35,17 +35,17 @@ export const tab = ({ layer }: TabProps) => {
const i = interactive({ const i = interactive({
state: { state: {
default: { default: {
...base ...base,
}, },
hovered: { hovered: {
...base, ...base,
...text(layer, "sans", "base", "hovered") ...text(layer, "sans", "base", "hovered"),
}, },
clicked: { clicked: {
...base, ...base,
...text(layer, "sans", "base", "pressed") ...text(layer, "sans", "base", "pressed"),
}, },
} },
}) })
return toggleable({ return toggleable({
@ -60,14 +60,14 @@ export const tab = ({ layer }: TabProps) => {
hovered: { hovered: {
...i, ...i,
...text(layer, "sans", "base", "hovered"), ...text(layer, "sans", "base", "hovered"),
border: active_border border: active_border,
}, },
clicked: { clicked: {
...i, ...i,
...text(layer, "sans", "base", "pressed"), ...text(layer, "sans", "base", "pressed"),
border: active_border border: active_border,
}, },
} },
} },
}) })
} }

View File

@ -12,44 +12,47 @@ type TabBarButtonProps = TabBarButtonOptions & {
state?: Partial<Record<InteractiveState, Partial<TabBarButtonOptions>>> state?: Partial<Record<InteractiveState, Partial<TabBarButtonOptions>>>
} }
export function tab_bar_button(theme: Theme, { icon, color = "base" }: TabBarButtonProps) { export function tab_bar_button(
theme: Theme,
{ icon, color = "base" }: TabBarButtonProps
) {
const button_spacing = 8 const button_spacing = 8
return ( return interactive({
interactive({ base: {
base: { icon: {
icon: { color: foreground(theme.middle, color),
color: foreground(theme.middle, color), asset: icon,
asset: icon, dimensions: {
dimensions: { width: 15,
width: 15, height: 15,
height: 15,
},
}, },
},
container: {
corner_radius: 4,
padding: {
top: 4,
bottom: 4,
left: 4,
right: 4,
},
margin: {
left: button_spacing / 2,
right: button_spacing / 2,
},
},
},
state: {
hovered: {
container: { container: {
corner_radius: 4, background: background(theme.middle, color, "hovered"),
padding: {
top: 4, bottom: 4, left: 4, right: 4
},
margin: {
left: button_spacing / 2,
right: button_spacing / 2,
},
}, },
}, },
state: { clicked: {
hovered: { container: {
container: { background: background(theme.middle, color, "pressed"),
background: background(theme.middle, color, "hovered"),
}
},
clicked: {
container: {
background: background(theme.middle, color, "pressed"),
}
}, },
}, },
}) },
) })
} }

View File

@ -10,10 +10,7 @@ import { Button } from "./button"
import { Margin } from "./icon_button" import { Margin } from "./icon_button"
interface TextButtonOptions { interface TextButtonOptions {
layer?: layer?: Theme["lowest"] | Theme["middle"] | Theme["highest"]
| Theme["lowest"]
| Theme["middle"]
| Theme["highest"]
variant?: Button.Variant variant?: Button.Variant
color?: keyof Theme["lowest"] color?: keyof Theme["lowest"]
margin?: Partial<Margin> margin?: Partial<Margin>
@ -36,7 +33,10 @@ export function text_button({
const theme = useTheme() const theme = useTheme()
if (!color) color = "base" if (!color) color = "base"
const background_color = variant === Button.variant.Ghost ? null : background(layer ?? theme.lowest, color) const background_color =
variant === Button.variant.Ghost
? null
: background(layer ?? theme.lowest, color)
const text_options: TextProperties = { const text_options: TextProperties = {
size: "xs", size: "xs",
@ -67,20 +67,38 @@ export function text_button({
state: { state: {
default: { default: {
background: background_color, background: background_color,
color: color: disabled
disabled ? foreground(layer ?? theme.lowest, "disabled")
? foreground(layer ?? theme.lowest, "disabled") : foreground(layer ?? theme.lowest, color),
: foreground(layer ?? theme.lowest, color),
}, },
hovered: hovered: disabled
disabled ? {} : { ? {}
background: background(layer ?? theme.lowest, color, "hovered"), : {
color: foreground(layer ?? theme.lowest, color, "hovered"), background: background(
layer ?? theme.lowest,
color,
"hovered"
),
color: foreground(
layer ?? theme.lowest,
color,
"hovered"
),
},
clicked: disabled
? {}
: {
background: background(
layer ?? theme.lowest,
color,
"pressed"
),
color: foreground(
layer ?? theme.lowest,
color,
"pressed"
),
}, },
clicked: disabled ? {} : {
background: background(layer ?? theme.lowest, color, "pressed"),
color: foreground(layer ?? theme.lowest, color, "pressed"),
},
}, },
}) })
} }

View File

@ -1,4 +1,6 @@
import { interactive, Interactive } from "./interactive" import { interactive, Interactive } from "./interactive"
import { toggleable, Toggleable } from "./toggle" import { toggleable, Toggleable } from "./toggle"
export * from "./padding"
export * from "./margin"
export { interactive, Interactive, toggleable, Toggleable } export { interactive, Interactive, toggleable, Toggleable }

View File

@ -16,19 +16,26 @@ export type MarginStyle = {
export const margin_style = (options: MarginOptions): MarginStyle => { export const margin_style = (options: MarginOptions): MarginStyle => {
const { all, top, bottom, left, right } = options const { all, top, bottom, left, right } = options
if (all !== undefined) return { if (all !== undefined)
top: all, return {
bottom: all, top: all,
left: all, bottom: all,
right: all left: all,
} right: all,
}
if (top === undefined && bottom === undefined && left === undefined && right === undefined) throw new Error("Margin must have at least one value") if (
top === undefined &&
bottom === undefined &&
left === undefined &&
right === undefined
)
throw new Error("Margin must have at least one value")
return { return {
top: top || 0, top: top || 0,
bottom: bottom || 0, bottom: bottom || 0,
left: left || 0, left: left || 0,
right: right || 0 right: right || 0,
} }
} }

View File

@ -16,19 +16,26 @@ export type PaddingStyle = {
export const padding_style = (options: PaddingOptions): PaddingStyle => { export const padding_style = (options: PaddingOptions): PaddingStyle => {
const { all, top, bottom, left, right } = options const { all, top, bottom, left, right } = options
if (all !== undefined) return { if (all !== undefined)
top: all, return {
bottom: all, top: all,
left: all, bottom: all,
right: all left: all,
} right: all,
}
if (top === undefined && bottom === undefined && left === undefined && right === undefined) throw new Error("Padding must have at least one value") if (
top === undefined &&
bottom === undefined &&
left === undefined &&
right === undefined
)
throw new Error("Padding must have at least one value")
return { return {
top: top || 0, top: top || 0,
bottom: bottom || 0, bottom: bottom || 0,
left: left || 0, left: left || 0,
right: right || 0 right: right || 0,
} }
} }

View File

@ -8,50 +8,48 @@ type RoleCycleButton = TextStyle & {
} }
// TODO: Replace these with zed types // TODO: Replace these with zed types
type RemainingTokens = TextStyle & { type RemainingTokens = TextStyle & {
background: string, background: string
margin: { top: number, right: number }, margin: { top: number; right: number }
padding: { padding: {
right: number, right: number
left: number, left: number
top: number, top: number
bottom: number, bottom: number
}, }
corner_radius: number, corner_radius: number
} }
export default function assistant(): any { export default function assistant(): any {
const theme = useTheme() const theme = useTheme()
const interactive_role = (color: StyleSets): Interactive<RoleCycleButton> => { const interactive_role = (
return ( color: StyleSets
interactive({ ): Interactive<RoleCycleButton> => {
base: { return interactive({
base: {
...text(theme.highest, "sans", color, { size: "sm" }),
},
state: {
hovered: {
...text(theme.highest, "sans", color, { size: "sm" }), ...text(theme.highest, "sans", color, { size: "sm" }),
background: background(theme.highest, color, "hovered"),
}, },
state: { clicked: {
hovered: { ...text(theme.highest, "sans", color, { size: "sm" }),
...text(theme.highest, "sans", color, { size: "sm" }), background: background(theme.highest, color, "pressed"),
background: background(theme.highest, color, "hovered"),
},
clicked: {
...text(theme.highest, "sans", color, { size: "sm" }),
background: background(theme.highest, color, "pressed"),
}
}, },
}) },
) })
} }
const tokens_remaining = (color: StyleSets): RemainingTokens => { const tokens_remaining = (color: StyleSets): RemainingTokens => {
return ( return {
{ ...text(theme.highest, "mono", color, { size: "xs" }),
...text(theme.highest, "mono", color, { size: "xs" }), background: background(theme.highest, "on", "default"),
background: background(theme.highest, "on", "default"), margin: { top: 12, right: 20 },
margin: { top: 12, right: 20 }, padding: { right: 4, left: 4, top: 1, bottom: 1 },
padding: { right: 4, left: 4, top: 1, bottom: 1 }, corner_radius: 6,
corner_radius: 6, }
}
)
} }
return { return {
@ -172,7 +170,10 @@ export default function assistant(): any {
base: { base: {
background: background(theme.middle), background: background(theme.middle),
padding: { top: 4, bottom: 4 }, padding: { top: 4, bottom: 4 },
border: border(theme.middle, "default", { top: true, overlay: true }), border: border(theme.middle, "default", {
top: true,
overlay: true,
}),
}, },
state: { state: {
hovered: { hovered: {
@ -180,7 +181,7 @@ export default function assistant(): any {
}, },
clicked: { clicked: {
background: background(theme.middle, "pressed"), background: background(theme.middle, "pressed"),
} },
}, },
}), }),
saved_at: { saved_at: {

View File

@ -39,7 +39,12 @@ export default function channel_modal(): any {
row_height: ITEM_HEIGHT, row_height: ITEM_HEIGHT,
header: { header: {
background: background(theme.lowest), background: background(theme.lowest),
border: border(theme.middle, { "bottom": true, "top": false, left: false, right: false }), border: border(theme.middle, {
bottom: true,
top: false,
left: false,
right: false,
}),
padding: { padding: {
top: SPACING, top: SPACING,
left: SPACING - BUTTON_OFFSET, left: SPACING - BUTTON_OFFSET,
@ -48,7 +53,7 @@ export default function channel_modal(): any {
corner_radii: { corner_radii: {
top_right: 12, top_right: 12,
top_left: 12, top_left: 12,
} },
}, },
body: { body: {
background: background(theme.middle), background: background(theme.middle),
@ -57,12 +62,11 @@ export default function channel_modal(): any {
left: SPACING, left: SPACING,
right: SPACING, right: SPACING,
bottom: SPACING, bottom: SPACING,
}, },
corner_radii: { corner_radii: {
bottom_right: 12, bottom_right: 12,
bottom_left: 12, bottom_left: 12,
} },
}, },
modal: { modal: {
background: background(theme.middle), background: background(theme.middle),
@ -74,7 +78,6 @@ export default function channel_modal(): any {
right: 0, right: 0,
top: 0, top: 0,
}, },
}, },
// FIXME: due to a bug in the picker's size calculation, this must be 600 // FIXME: due to a bug in the picker's size calculation, this must be 600
max_height: 600, max_height: 600,
@ -83,7 +86,7 @@ export default function channel_modal(): any {
...text(theme.middle, "sans", "on", { size: "lg" }), ...text(theme.middle, "sans", "on", { size: "lg" }),
padding: { padding: {
left: BUTTON_OFFSET, left: BUTTON_OFFSET,
} },
}, },
picker: { picker: {
empty_container: {}, empty_container: {},
@ -108,8 +111,8 @@ export default function channel_modal(): any {
background: background(theme.middle), background: background(theme.middle),
padding: { padding: {
left: 7, left: 7,
right: 7 right: 7,
} },
}, },
cancel_invite_button: { cancel_invite_button: {
...text(theme.middle, "sans", { size: "xs" }), ...text(theme.middle, "sans", { size: "xs" }),
@ -125,7 +128,7 @@ export default function channel_modal(): any {
padding: { padding: {
left: 4, left: 4,
right: 4, right: 4,
} },
}, },
contact_avatar: { contact_avatar: {
corner_radius: 10, corner_radius: 10,
@ -147,6 +150,6 @@ export default function channel_modal(): any {
background: background(theme.middle, "disabled"), background: background(theme.middle, "disabled"),
color: foreground(theme.middle, "disabled"), color: foreground(theme.middle, "disabled"),
}, },
} },
} }
} }

View File

@ -27,7 +27,7 @@ export default function contacts_panel(): any {
color: foreground(layer, "on"), color: foreground(layer, "on"),
icon_width: 14, icon_width: 14,
button_width: 16, button_width: 16,
corner_radius: 8 corner_radius: 8,
} }
const project_row = { const project_row = {
@ -61,7 +61,7 @@ export default function contacts_panel(): any {
width: 14, width: 14,
} }
const header_icon_button = toggleable_icon_button(theme, { const header_icon_button = toggleable_icon_button({
variant: "ghost", variant: "ghost",
size: "sm", size: "sm",
active_layer: theme.lowest, active_layer: theme.lowest,
@ -275,7 +275,7 @@ export default function contacts_panel(): any {
list_empty_label_container: { list_empty_label_container: {
margin: { margin: {
left: NAME_MARGIN, left: NAME_MARGIN,
} },
}, },
list_empty_icon: { list_empty_icon: {
color: foreground(layer, "variant"), color: foreground(layer, "variant"),
@ -289,7 +289,7 @@ export default function contacts_panel(): any {
top: SPACING / 2, top: SPACING / 2,
bottom: SPACING / 2, bottom: SPACING / 2,
left: SPACING, left: SPACING,
right: SPACING right: SPACING,
}, },
}, },
state: { state: {
@ -330,7 +330,7 @@ export default function contacts_panel(): any {
right: 4, right: 4,
}, },
background: background(layer, "hovered"), background: background(layer, "hovered"),
...text(layer, "sans", "hovered", { size: "xs" }) ...text(layer, "sans", "hovered", { size: "xs" }),
}, },
contact_status_free: indicator({ layer, color: "positive" }), contact_status_free: indicator({ layer, color: "positive" }),
contact_status_busy: indicator({ layer, color: "negative" }), contact_status_busy: indicator({ layer, color: "negative" }),
@ -404,7 +404,7 @@ export default function contacts_panel(): any {
channel_editor: { channel_editor: {
padding: { padding: {
left: NAME_MARGIN, left: NAME_MARGIN,
} },
} },
} }
} }

View File

@ -1,4 +1,3 @@
import { useTheme } from "../common" import { useTheme } from "../common"
import { text_button } from "../component/text_button" import { text_button } from "../component/text_button"
import { icon_button } from "../component/icon_button" import { icon_button } from "../component/icon_button"
@ -14,14 +13,14 @@ export default function contacts_panel(): any {
base: text_button({}), base: text_button({}),
state: { state: {
active: { active: {
...text_button({ color: "accent" }) ...text_button({ color: "accent" }),
} },
} },
}), }),
disclosure: { disclosure: {
...text(theme.lowest, "sans", "base"), ...text(theme.lowest, "sans", "base"),
button: icon_button({ variant: "ghost" }), button: icon_button({ variant: "ghost" }),
spacing: 4, spacing: 4,
} },
} }
} }

View File

@ -3,5 +3,4 @@ import { background, border } from "./components"
export default function contacts_popover(): any { export default function contacts_popover(): any {
const theme = useTheme() const theme = useTheme()
} }

View File

@ -307,7 +307,7 @@ export default function editor(): any {
? with_opacity(theme.ramps.green(0.5).hex(), 0.8) ? with_opacity(theme.ramps.green(0.5).hex(), 0.8)
: with_opacity(theme.ramps.green(0.4).hex(), 0.8), : with_opacity(theme.ramps.green(0.4).hex(), 0.8),
}, },
selections: foreground(layer, "accent") selections: foreground(layer, "accent"),
}, },
composition_mark: { composition_mark: {
underline: { underline: {

View File

@ -37,7 +37,7 @@ export default function feedback(): any {
...text(theme.highest, "mono", "on", "disabled"), ...text(theme.highest, "mono", "on", "disabled"),
background: background(theme.highest, "on", "disabled"), background: background(theme.highest, "on", "disabled"),
border: border(theme.highest, "on", "disabled"), border: border(theme.highest, "on", "disabled"),
} },
}, },
}), }),
button_margin: 8, button_margin: 8,

View File

@ -152,7 +152,7 @@ export default function picker(): any {
0.5 0.5
), ),
}, },
} },
}), }),
} }
} }

View File

@ -64,17 +64,17 @@ export default function project_panel(): any {
const unselected_default_style = merge( const unselected_default_style = merge(
base_properties, base_properties,
unselected?.default ?? {}, unselected?.default ?? {},
{}, {}
) )
const unselected_hovered_style = merge( const unselected_hovered_style = merge(
base_properties, base_properties,
{ background: background(theme.middle, "hovered") }, { background: background(theme.middle, "hovered") },
unselected?.hovered ?? {}, unselected?.hovered ?? {}
) )
const unselected_clicked_style = merge( const unselected_clicked_style = merge(
base_properties, base_properties,
{ background: background(theme.middle, "pressed") }, { background: background(theme.middle, "pressed") },
unselected?.clicked ?? {}, unselected?.clicked ?? {}
) )
const selected_default_style = merge( const selected_default_style = merge(
base_properties, base_properties,
@ -82,7 +82,7 @@ export default function project_panel(): any {
background: background(theme.lowest), background: background(theme.lowest),
text: text(theme.lowest, "sans", { size: "sm" }), text: text(theme.lowest, "sans", { size: "sm" }),
}, },
selected_style?.default ?? {}, selected_style?.default ?? {}
) )
const selected_hovered_style = merge( const selected_hovered_style = merge(
base_properties, base_properties,
@ -90,7 +90,7 @@ export default function project_panel(): any {
background: background(theme.lowest, "hovered"), background: background(theme.lowest, "hovered"),
text: text(theme.lowest, "sans", { size: "sm" }), text: text(theme.lowest, "sans", { size: "sm" }),
}, },
selected_style?.hovered ?? {}, selected_style?.hovered ?? {}
) )
const selected_clicked_style = merge( const selected_clicked_style = merge(
base_properties, base_properties,
@ -98,7 +98,7 @@ export default function project_panel(): any {
background: background(theme.lowest, "pressed"), background: background(theme.lowest, "pressed"),
text: text(theme.lowest, "sans", { size: "sm" }), text: text(theme.lowest, "sans", { size: "sm" }),
}, },
selected_style?.clicked ?? {}, selected_style?.clicked ?? {}
) )
return toggleable({ return toggleable({
@ -175,7 +175,7 @@ export default function project_panel(): any {
default: { default: {
icon_color: foreground(theme.middle, "variant"), icon_color: foreground(theme.middle, "variant"),
}, },
}, }
), ),
cut_entry: entry( cut_entry: entry(
{ {
@ -190,7 +190,7 @@ export default function project_panel(): any {
size: "sm", size: "sm",
}), }),
}, },
}, }
), ),
filename_editor: { filename_editor: {
background: background(theme.middle, "on"), background: background(theme.middle, "on"),

View File

@ -31,7 +31,7 @@ export default function search(): any {
text: text(theme.highest, "mono", "default"), text: text(theme.highest, "mono", "default"),
border: border(theme.highest), border: border(theme.highest),
margin: { margin: {
right: 9, right: SEARCH_ROW_SPACING,
}, },
padding: { padding: {
top: 4, top: 4,
@ -48,7 +48,7 @@ export default function search(): any {
} }
return { return {
padding: { top: 4, bottom: 4 }, padding: { top: 0, bottom: 0 },
option_button: toggleable({ option_button: toggleable({
base: interactive({ base: interactive({
@ -60,7 +60,8 @@ export default function search(): any {
corner_radius: 2, corner_radius: 2,
margin: { right: 2 }, margin: { right: 2 },
border: { border: {
width: 1., color: background(theme.highest, "on") width: 1,
color: background(theme.highest, "on"),
}, },
padding: { padding: {
left: 4, left: 4,
@ -74,14 +75,16 @@ export default function search(): any {
...text(theme.highest, "mono", "variant", "hovered"), ...text(theme.highest, "mono", "variant", "hovered"),
background: background(theme.highest, "on", "hovered"), background: background(theme.highest, "on", "hovered"),
border: { border: {
width: 1., color: background(theme.highest, "on", "hovered") width: 1,
color: background(theme.highest, "on", "hovered"),
}, },
}, },
clicked: { clicked: {
...text(theme.highest, "mono", "variant", "pressed"), ...text(theme.highest, "mono", "variant", "pressed"),
background: background(theme.highest, "on", "pressed"), background: background(theme.highest, "on", "pressed"),
border: { border: {
width: 1., color: background(theme.highest, "on", "pressed") width: 1,
color: background(theme.highest, "on", "pressed"),
}, },
}, },
}, },
@ -96,11 +99,19 @@ export default function search(): any {
border: border(theme.highest, "accent"), border: border(theme.highest, "accent"),
}, },
hovered: { hovered: {
background: background(theme.highest, "accent", "hovered"), background: background(
theme.highest,
"accent",
"hovered"
),
border: border(theme.highest, "accent", "hovered"), border: border(theme.highest, "accent", "hovered"),
}, },
clicked: { clicked: {
background: background(theme.highest, "accent", "pressed"), background: background(
theme.highest,
"accent",
"pressed"
),
border: border(theme.highest, "accent", "pressed"), border: border(theme.highest, "accent", "pressed"),
}, },
}, },
@ -117,7 +128,8 @@ export default function search(): any {
corner_radius: 2, corner_radius: 2,
margin: { right: 2 }, margin: { right: 2 },
border: { border: {
width: 1., color: background(theme.highest, "on") width: 1,
color: background(theme.highest, "on"),
}, },
padding: { padding: {
left: 4, left: 4,
@ -131,14 +143,16 @@ export default function search(): any {
...text(theme.highest, "mono", "variant", "hovered"), ...text(theme.highest, "mono", "variant", "hovered"),
background: background(theme.highest, "on", "hovered"), background: background(theme.highest, "on", "hovered"),
border: { border: {
width: 1., color: background(theme.highest, "on", "hovered") width: 1,
color: background(theme.highest, "on", "hovered"),
}, },
}, },
clicked: { clicked: {
...text(theme.highest, "mono", "variant", "pressed"), ...text(theme.highest, "mono", "variant", "pressed"),
background: background(theme.highest, "on", "pressed"), background: background(theme.highest, "on", "pressed"),
border: { border: {
width: 1., color: background(theme.highest, "on", "pressed") width: 1,
color: background(theme.highest, "on", "pressed"),
}, },
}, },
}, },
@ -153,11 +167,19 @@ export default function search(): any {
border: border(theme.highest, "accent"), border: border(theme.highest, "accent"),
}, },
hovered: { hovered: {
background: background(theme.highest, "accent", "hovered"), background: background(
theme.highest,
"accent",
"hovered"
),
border: border(theme.highest, "accent", "hovered"), border: border(theme.highest, "accent", "hovered"),
}, },
clicked: { clicked: {
background: background(theme.highest, "accent", "pressed"), background: background(
theme.highest,
"accent",
"pressed"
),
border: border(theme.highest, "accent", "pressed"), border: border(theme.highest, "accent", "pressed"),
}, },
}, },
@ -168,9 +190,20 @@ export default function search(): any {
// Disabled elements should use a disabled state of an interactive element, not a toggleable element with the inactive state being disabled // Disabled elements should use a disabled state of an interactive element, not a toggleable element with the inactive state being disabled
action_button: toggleable({ action_button: toggleable({
state: { state: {
inactive: text_button({ variant: "ghost", layer: theme.highest, disabled: true, margin: { right: SEARCH_ROW_SPACING }, text_properties: { size: "sm" } }), inactive: text_button({
active: text_button({ variant: "ghost", layer: theme.highest, margin: { right: SEARCH_ROW_SPACING }, text_properties: { size: "sm" } }) variant: "ghost",
} layer: theme.highest,
disabled: true,
margin: { right: SEARCH_ROW_SPACING },
text_properties: { size: "sm" },
}),
active: text_button({
variant: "ghost",
layer: theme.highest,
margin: { right: SEARCH_ROW_SPACING },
text_properties: { size: "sm" },
}),
},
}), }),
editor, editor,
invalid_editor: { invalid_editor: {
@ -216,12 +249,12 @@ export default function search(): any {
dimensions: { dimensions: {
width: 14, width: 14,
height: 14, height: 14,
} },
}, },
container: { container: {
margin: { right: 4 }, margin: { right: 4 },
padding: { left: 1, right: 1 }, padding: { left: 1, right: 1 },
} },
}, },
// Toggle group buttons - Text | Regex | Semantic // Toggle group buttons - Text | Regex | Semantic
mode_button: toggleable({ mode_button: toggleable({
@ -233,27 +266,39 @@ export default function search(): any {
border: { border: {
...border(theme.highest, "on"), ...border(theme.highest, "on"),
left: false, left: false,
right: false right: false,
}, },
margin: { margin: {
top: 1, top: 1,
bottom: 1, bottom: 1,
}, },
padding: { padding: {
left: 12, left: 10,
right: 12, right: 10,
}, },
corner_radius: 6, corner_radius: 6,
}, },
state: { state: {
hovered: { hovered: {
...text(theme.highest, "mono", "variant", "hovered", { size: "sm" }), ...text(theme.highest, "mono", "variant", "hovered", {
background: background(theme.highest, "variant", "hovered"), size: "sm",
}),
background: background(
theme.highest,
"variant",
"hovered"
),
border: border(theme.highest, "on", "hovered"), border: border(theme.highest, "on", "hovered"),
}, },
clicked: { clicked: {
...text(theme.highest, "mono", "variant", "pressed", { size: "sm" }), ...text(theme.highest, "mono", "variant", "pressed", {
background: background(theme.highest, "variant", "pressed"), size: "sm",
}),
background: background(
theme.highest,
"variant",
"pressed"
),
border: border(theme.highest, "on", "pressed"), border: border(theme.highest, "on", "pressed"),
}, },
}, },
@ -262,15 +307,19 @@ export default function search(): any {
active: { active: {
default: { default: {
...text(theme.highest, "mono", "on", { size: "sm" }), ...text(theme.highest, "mono", "on", { size: "sm" }),
background: background(theme.highest, "on") background: background(theme.highest, "on"),
}, },
hovered: { hovered: {
...text(theme.highest, "mono", "on", "hovered", { size: "sm" }), ...text(theme.highest, "mono", "on", "hovered", {
background: background(theme.highest, "on", "hovered") size: "sm",
}),
background: background(theme.highest, "on", "hovered"),
}, },
clicked: { clicked: {
...text(theme.highest, "mono", "on", "pressed", { size: "sm" }), ...text(theme.highest, "mono", "on", "pressed", {
background: background(theme.highest, "on", "pressed") size: "sm",
}),
background: background(theme.highest, "on", "pressed"),
}, },
}, },
}, },
@ -300,8 +349,8 @@ export default function search(): any {
}, },
}, },
state: { state: {
hovered: {} hovered: {},
} },
}), }),
active: interactive({ active: interactive({
base: { base: {
@ -325,22 +374,30 @@ export default function search(): any {
state: { state: {
hovered: { hovered: {
...text(theme.highest, "mono", "on", "hovered"), ...text(theme.highest, "mono", "on", "hovered"),
background: background(theme.highest, "on", "hovered"), background: background(
theme.highest,
"on",
"hovered"
),
border: border(theme.highest, "on", "hovered"), border: border(theme.highest, "on", "hovered"),
}, },
clicked: { clicked: {
...text(theme.highest, "mono", "on", "pressed"), ...text(theme.highest, "mono", "on", "pressed"),
background: background(theme.highest, "on", "pressed"), background: background(
theme.highest,
"on",
"pressed"
),
border: border(theme.highest, "on", "pressed"), border: border(theme.highest, "on", "pressed"),
}, },
}, },
}) }),
} },
}), }),
search_bar_row_height: 34, search_bar_row_height: 32,
search_row_spacing: 8, search_row_spacing: 8,
option_button_height: 22, option_button_height: 22,
modes_container: {}, modes_container: {},
...search_results() ...search_results(),
} }
} }

View File

@ -34,9 +34,11 @@ export default function status_bar(): any {
...text(layer, "mono", "base", { size: "xs" }), ...text(layer, "mono", "base", { size: "xs" }),
}, },
active_language: text_button({ active_language: text_button({
color: "base" color: "base",
}),
auto_update_progress_message: text(layer, "sans", "base", {
size: "xs",
}), }),
auto_update_progress_message: text(layer, "sans", "base", { size: "xs" }),
auto_update_done_message: text(layer, "sans", "base", { size: "xs" }), auto_update_done_message: text(layer, "sans", "base", { size: "xs" }),
lsp_status: interactive({ lsp_status: interactive({
base: { base: {
@ -73,34 +75,36 @@ export default function status_bar(): any {
icon_color_error: foreground(layer, "negative"), icon_color_error: foreground(layer, "negative"),
container_ok: { container_ok: {
corner_radius: 6, corner_radius: 6,
padding: { top: 3, bottom: 3, left: 7, right: 7 }, padding: { top: 2, bottom: 2, left: 6, right: 6 },
},
container_warning: {
...diagnostic_status_container,
background: background(layer, "warning"),
border: border(layer, "warning"),
},
container_error: {
...diagnostic_status_container,
background: background(layer, "negative"),
border: border(layer, "negative"),
}, },
container_warning: diagnostic_status_container,
container_error: diagnostic_status_container
}, },
state: { state: {
hovered: { hovered: {
icon_color_ok: foreground(layer, "on"), icon_color_ok: foreground(layer, "on"),
container_ok: { container_ok: {
background: background(layer, "on", "hovered"), background: background(layer, "hovered")
}, },
container_warning: { container_warning: {
background: background(layer, "warning", "hovered"), background: background(layer, "hovered")
border: border(layer, "warning", "hovered"),
}, },
container_error: { container_error: {
background: background(layer, "negative", "hovered"), background: background(layer, "hovered")
border: border(layer, "negative", "hovered"),
}, },
}, },
clicked: {
icon_color_ok: foreground(layer, "on"),
container_ok: {
background: background(layer, "pressed")
},
container_warning: {
background: background(layer, "pressed")
},
container_error: {
background: background(layer, "pressed")
}
}
}, },
}), }),
panel_buttons: { panel_buttons: {
@ -125,7 +129,7 @@ export default function status_bar(): any {
}, },
clicked: { clicked: {
background: background(layer, "pressed"), background: background(layer, "pressed"),
} },
}, },
}), }),
state: { state: {

View File

@ -93,7 +93,7 @@ export default function tab_bar(): any {
border: border(theme.lowest, "on", { border: border(theme.lowest, "on", {
bottom: true, bottom: true,
overlay: true, overlay: true,
}) }),
}, },
state: { state: {
hovered: { hovered: {
@ -101,7 +101,7 @@ export default function tab_bar(): any {
background: background(theme.highest, "on", "hovered"), background: background(theme.highest, "on", "hovered"),
}, },
disabled: { disabled: {
color: foreground(theme.highest, "on", "disabled") color: foreground(theme.highest, "on", "disabled"),
}, },
}, },
}) })
@ -162,6 +162,6 @@ export default function tab_bar(): any {
right: false, right: false,
}, },
}, },
nav_button: nav_button nav_button: nav_button,
} }
} }

View File

@ -1,8 +1,6 @@
import { icon_button, toggleable_icon_button } from "../component/icon_button" import { icon_button, toggleable_icon_button, toggleable_text_button } from "../component"
import { toggleable_text_button } from "../component/text_button"
import { interactive, toggleable } from "../element" import { interactive, toggleable } from "../element"
import { useTheme } from "../theme" import { useTheme, with_opacity } from "../theme"
import { with_opacity } from "../theme/color"
import { background, border, foreground, text } from "./components" import { background, border, foreground, text } from "./components"
const ITEM_SPACING = 8 const ITEM_SPACING = 8
@ -34,16 +32,17 @@ function call_controls() {
} }
return { return {
toggle_microphone_button: toggleable_icon_button(theme, { toggle_microphone_button: toggleable_icon_button({
margin: { margin: {
...margin_y, ...margin_y,
left: space.group, left: space.group,
right: space.half_item, right: space.half_item,
}, },
active_color: "negative", active_color: "negative",
active_background_color: "negative",
}), }),
toggle_speakers_button: toggleable_icon_button(theme, { toggle_speakers_button: toggleable_icon_button({
margin: { margin: {
...margin_y, ...margin_y,
left: space.half_item, left: space.half_item,
@ -51,13 +50,14 @@ function call_controls() {
}, },
}), }),
screen_share_button: toggleable_icon_button(theme, { screen_share_button: toggleable_icon_button({
margin: { margin: {
...margin_y, ...margin_y,
left: space.half_item, left: space.half_item,
right: space.group, right: space.group,
}, },
active_color: "accent", active_color: "accent",
active_background_color: "accent",
}), }),
muted: foreground(theme.lowest, "negative"), muted: foreground(theme.lowest, "negative"),
@ -183,14 +183,12 @@ export function titlebar(): any {
height: 400, height: 400,
}, },
// Project
project_name_divider: text(theme.lowest, "sans", "variant"),
project_menu_button: toggleable_text_button(theme, { project_menu_button: toggleable_text_button(theme, {
color: 'base', color: "base"
}), }),
git_menu_button: toggleable_text_button(theme, { git_menu_button: toggleable_text_button(theme, {
color: 'variant', color: "variant",
}), }),
// Collaborators // Collaborators
@ -263,7 +261,7 @@ export function titlebar(): any {
...call_controls(), ...call_controls(),
toggle_contacts_button: toggleable_icon_button(theme, { toggle_contacts_button: toggleable_icon_button({
margin: { margin: {
left: ITEM_SPACING, left: ITEM_SPACING,
}, },

View File

@ -0,0 +1,38 @@
import { useTheme } from "../common"
import { toggleable_icon_button } from "../component/icon_button"
import { interactive } from "../element"
import { background, border, foreground, text } from "./components"
export const toolbar = () => {
const theme = useTheme()
return {
height: 32,
padding: { left: 4, right: 4, top: 4, bottom: 4 },
background: background(theme.highest),
border: border(theme.highest, { bottom: true }),
item_spacing: 4,
toggleable_tool: toggleable_icon_button({
margin: { left: 4 },
variant: "ghost",
active_color: "accent",
}),
breadcrumb_height: 24,
breadcrumbs: interactive({
base: {
...text(theme.highest, "sans", "variant"),
corner_radius: 6,
padding: {
left: 6,
right: 6,
},
},
state: {
hovered: {
color: foreground(theme.highest, "on", "hovered"),
background: background(theme.highest, "on", "hovered"),
},
},
}),
}
}

View File

@ -12,7 +12,7 @@ import tabBar from "./tab_bar"
import { interactive } from "../element" import { interactive } from "../element"
import { titlebar } from "./titlebar" import { titlebar } from "./titlebar"
import { useTheme } from "../theme" import { useTheme } from "../theme"
import { toggleable_icon_button } from "../component/icon_button" import { toolbar } from "./toolbar"
export default function workspace(): any { export default function workspace(): any {
const theme = useTheme() const theme = useTheme()
@ -128,35 +128,7 @@ export default function workspace(): any {
}, },
status_bar: statusBar(), status_bar: statusBar(),
titlebar: titlebar(), titlebar: titlebar(),
toolbar: { toolbar: toolbar(),
height: 42,
background: background(theme.highest),
border: border(theme.highest, { bottom: true }),
item_spacing: 8,
toggleable_tool: toggleable_icon_button(theme, {
margin: { left: 8 },
variant: "ghost",
active_color: "accent",
}),
padding: { left: 8, right: 8 },
},
breadcrumb_height: 24,
breadcrumbs: interactive({
base: {
...text(theme.highest, "sans", "variant"),
corner_radius: 6,
padding: {
left: 6,
right: 6,
},
},
state: {
hovered: {
color: foreground(theme.highest, "on", "hovered"),
background: background(theme.highest, "on", "hovered"),
},
},
}),
disconnected_overlay: { disconnected_overlay: {
...text(theme.lowest, "sans"), ...text(theme.lowest, "sans"),
background: with_opacity(background(theme.lowest), 0.8), background: with_opacity(background(theme.lowest), 0.8),

View File

@ -13,16 +13,16 @@ export interface Theme {
is_light: boolean is_light: boolean
/** /**
* App background, other elements that should sit directly on top of the background. * App background, other elements that should sit directly on top of the background.
*/ */
lowest: Layer lowest: Layer
/** /**
* Panels, tabs, other UI surfaces that sit on top of the background. * Panels, tabs, other UI surfaces that sit on top of the background.
*/ */
middle: Layer middle: Layer
/** /**
* Editors like code buffers, conversation editors, etc. * Editors like code buffers, conversation editors, etc.
*/ */
highest: Layer highest: Layer
ramps: RampSet ramps: RampSet
@ -206,7 +206,10 @@ function build_color_family(ramps: RampSet): ColorFamily {
for (const ramp in ramps) { for (const ramp in ramps) {
const ramp_value = ramps[ramp as keyof RampSet] const ramp_value = ramps[ramp as keyof RampSet]
const lightnessValues = [ramp_value(0).get('hsl.l') * 100, ramp_value(1).get('hsl.l') * 100] const lightnessValues = [
ramp_value(0).get("hsl.l") * 100,
ramp_value(1).get("hsl.l") * 100,
]
const low = Math.min(...lightnessValues) const low = Math.min(...lightnessValues)
const high = Math.max(...lightnessValues) const high = Math.max(...lightnessValues)
const range = high - low const range = high - low

View File

@ -23,3 +23,4 @@ export * from "./create_theme"
export * from "./ramps" export * from "./ramps"
export * from "./syntax" export * from "./syntax"
export * from "./theme_config" export * from "./theme_config"
export * from "./color"

View File

@ -4,11 +4,7 @@ import {
SingleOtherToken, SingleOtherToken,
TokenTypes, TokenTypes,
} from "@tokens-studio/types" } from "@tokens-studio/types"
import { import { Shadow, SyntaxHighlightStyle, ThemeSyntax } from "../create_theme"
Shadow,
SyntaxHighlightStyle,
ThemeSyntax,
} from "../create_theme"
import { LayerToken, layer_token } from "./layer" import { LayerToken, layer_token } from "./layer"
import { PlayersToken, players_token } from "./players" import { PlayersToken, players_token } from "./players"
import { color_token } from "./token" import { color_token } from "./token"

View File

@ -23,7 +23,5 @@
"skipLibCheck": true, "skipLibCheck": true,
"useUnknownInCatchVariables": false "useUnknownInCatchVariables": false
}, },
"exclude": [ "exclude": ["node_modules"]
"node_modules"
]
} }