Pane context menus & focus shenanigans
Co-Authored-By: Conrad Irwin <conrad@zed.dev>
This commit is contained in:
parent
21dfe58ad9
commit
d516ae0d8a
3 changed files with 176 additions and 133 deletions
|
@ -2,14 +2,15 @@ use crate::{
|
||||||
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
|
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
|
||||||
toolbar::Toolbar,
|
toolbar::Toolbar,
|
||||||
workspace_settings::{AutosaveSetting, WorkspaceSettings},
|
workspace_settings::{AutosaveSetting, WorkspaceSettings},
|
||||||
SplitDirection, Workspace,
|
NewCenterTerminal, NewFile, NewSearch, SplitDirection, ToggleZoom, Workspace,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, prelude::*, Action, AnyWeakView, AppContext, AsyncWindowContext, Div, EntityId,
|
actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
|
||||||
EventEmitter, FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render,
|
AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
|
||||||
Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View, ViewContext,
|
||||||
|
VisualContext, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project2::{Project, ProjectEntryId, ProjectPath};
|
use project2::{Project, ProjectEntryId, ProjectPath};
|
||||||
|
@ -25,8 +26,8 @@ use std::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ui::v_stack;
|
use ui::{menu_handle, prelude::*, Color, Icon, IconButton, IconElement, Tooltip};
|
||||||
use ui::{prelude::*, Color, Icon, IconButton, IconElement, Tooltip};
|
use ui::{v_stack, ContextMenu};
|
||||||
use util::truncate_and_remove_front;
|
use util::truncate_and_remove_front;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
||||||
|
@ -50,7 +51,7 @@ pub enum SaveIntent {
|
||||||
|
|
||||||
//todo!("Do we need the default bound on actions? Decide soon")
|
//todo!("Do we need the default bound on actions? Decide soon")
|
||||||
// #[register_action]
|
// #[register_action]
|
||||||
#[derive(Clone, Deserialize, PartialEq, Debug)]
|
#[derive(Action, Clone, Deserialize, PartialEq, Debug)]
|
||||||
pub struct ActivateItem(pub usize);
|
pub struct ActivateItem(pub usize);
|
||||||
|
|
||||||
// #[derive(Clone, PartialEq)]
|
// #[derive(Clone, PartialEq)]
|
||||||
|
@ -158,7 +159,9 @@ pub struct Pane {
|
||||||
autoscroll: bool,
|
autoscroll: bool,
|
||||||
nav_history: NavHistory,
|
nav_history: NavHistory,
|
||||||
toolbar: View<Toolbar>,
|
toolbar: View<Toolbar>,
|
||||||
// tab_bar_context_menu: TabBarContextMenu,
|
tab_bar_focus_handle: FocusHandle,
|
||||||
|
new_item_menu: Option<View<ContextMenu>>,
|
||||||
|
split_item_menu: Option<View<ContextMenu>>,
|
||||||
// tab_context_menu: ViewHandle<ContextMenu>,
|
// tab_context_menu: ViewHandle<ContextMenu>,
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
project: Model<Project>,
|
project: Model<Project>,
|
||||||
|
@ -323,6 +326,9 @@ impl Pane {
|
||||||
next_timestamp,
|
next_timestamp,
|
||||||
}))),
|
}))),
|
||||||
toolbar: cx.build_view(|_| Toolbar::new()),
|
toolbar: cx.build_view(|_| Toolbar::new()),
|
||||||
|
tab_bar_focus_handle: cx.focus_handle(),
|
||||||
|
new_item_menu: None,
|
||||||
|
split_item_menu: None,
|
||||||
// tab_bar_context_menu: TabBarContextMenu {
|
// tab_bar_context_menu: TabBarContextMenu {
|
||||||
// kind: TabBarContextMenuKind::New,
|
// kind: TabBarContextMenuKind::New,
|
||||||
// handle: context_menu,
|
// handle: context_menu,
|
||||||
|
@ -397,6 +403,7 @@ impl Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_focus(&self, cx: &WindowContext) -> bool {
|
pub fn has_focus(&self, cx: &WindowContext) -> bool {
|
||||||
|
// todo!(); // inline this manually
|
||||||
self.focus_handle.contains_focused(cx)
|
self.focus_handle.contains_focused(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,11 +429,11 @@ impl Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
active_item.focus_handle(cx).focus(cx);
|
active_item.focus_handle(cx).focus(cx);
|
||||||
// todo!() Do this once we have tab bar context menu
|
} else if !self.tab_bar_focus_handle.contains_focused(cx) {
|
||||||
// } else if !self.tab_bar_context_menu.handle.is_focused() {
|
if let Some(focused) = cx.focused() {
|
||||||
} else if let Some(focused) = cx.focused() {
|
self.last_focused_view_by_item
|
||||||
self.last_focused_view_by_item
|
.insert(active_item.item_id(), focused);
|
||||||
.insert(active_item.item_id(), focused);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -673,21 +680,16 @@ impl Pane {
|
||||||
.position(|i| i.item_id() == item.item_id())
|
.position(|i| i.item_id() == item.item_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
|
pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
|
||||||
// // Potentially warn the user of the new keybinding
|
if self.zoomed {
|
||||||
// let workspace_handle = self.workspace().clone();
|
cx.emit(Event::ZoomOut);
|
||||||
// cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) })
|
} else if !self.items.is_empty() {
|
||||||
// .detach();
|
if !self.focus_handle.contains_focused(cx) {
|
||||||
|
cx.focus_self();
|
||||||
// if self.zoomed {
|
}
|
||||||
// cx.emit(Event::ZoomOut);
|
cx.emit(Event::ZoomIn);
|
||||||
// } else if !self.items.is_empty() {
|
}
|
||||||
// if !self.has_focus {
|
}
|
||||||
// cx.focus_self();
|
|
||||||
// }
|
|
||||||
// cx.emit(Event::ZoomIn);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn activate_item(
|
pub fn activate_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1424,7 +1426,7 @@ impl Pane {
|
||||||
let close_right = ItemSettings::get_global(cx).close_position.right();
|
let close_right = ItemSettings::get_global(cx).close_position.right();
|
||||||
let is_active = ix == self.active_item_index;
|
let is_active = ix == self.active_item_index;
|
||||||
|
|
||||||
div()
|
let tab = div()
|
||||||
.group("")
|
.group("")
|
||||||
.id(ix)
|
.id(ix)
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
|
@ -1498,13 +1500,41 @@ impl Pane {
|
||||||
.children((!close_right).then(|| close_icon()))
|
.children((!close_right).then(|| close_icon()))
|
||||||
.child(label)
|
.child(label)
|
||||||
.children(close_right.then(|| close_icon())),
|
.children(close_right.then(|| close_icon())),
|
||||||
)
|
);
|
||||||
|
|
||||||
|
menu_handle(ix).child(|_| tab).menu(|cx| {
|
||||||
|
ContextMenu::build(cx, |menu, cx| {
|
||||||
|
menu.action(
|
||||||
|
"Close Active Item",
|
||||||
|
CloseActiveItem { save_intent: None }.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.action("Close Inactive Items", CloseInactiveItems.boxed_clone(), cx)
|
||||||
|
.action("Close Clean Items", CloseCleanItems.boxed_clone(), cx)
|
||||||
|
.action(
|
||||||
|
"Close Items To The Left",
|
||||||
|
CloseItemsToTheLeft.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.action(
|
||||||
|
"Close Items To The Right",
|
||||||
|
CloseItemsToTheRight.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.action(
|
||||||
|
"Close All Items",
|
||||||
|
CloseAllItems { save_intent: None }.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
|
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
|
||||||
div()
|
div()
|
||||||
.group("tab_bar")
|
.group("tab_bar")
|
||||||
.id("tab_bar")
|
.id("tab_bar")
|
||||||
|
.track_focus(&self.tab_bar_focus_handle)
|
||||||
.w_full()
|
.w_full()
|
||||||
.flex()
|
.flex()
|
||||||
.bg(cx.theme().colors().tab_bar_background)
|
.bg(cx.theme().colors().tab_bar_background)
|
||||||
|
@ -1563,20 +1593,87 @@ impl Pane {
|
||||||
.gap_px()
|
.gap_px()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
.bg(gpui::blue())
|
||||||
.border()
|
.border()
|
||||||
.border_color(gpui::red())
|
.border_color(gpui::red())
|
||||||
.child(IconButton::new("plus", Icon::Plus)),
|
.child(IconButton::new("plus", Icon::Plus).on_click(
|
||||||
|
cx.listener(|this, _, cx| {
|
||||||
|
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||||
|
menu.action("New File", NewFile.boxed_clone(), cx)
|
||||||
|
.action(
|
||||||
|
"New Terminal",
|
||||||
|
NewCenterTerminal.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.action(
|
||||||
|
"New Search",
|
||||||
|
NewSearch.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
cx.subscribe(
|
||||||
|
&menu,
|
||||||
|
|this, _, event: &DismissEvent, cx| {
|
||||||
|
this.focus(cx);
|
||||||
|
this.new_item_menu = None;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.detach();
|
||||||
|
this.new_item_menu = Some(menu);
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
.when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
|
||||||
|
el.child(Self::render_menu_overlay(new_item_menu))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.border()
|
.border()
|
||||||
.border_color(gpui::red())
|
.border_color(gpui::red())
|
||||||
.child(IconButton::new("split", Icon::Split)),
|
.child(IconButton::new("split", Icon::Split).on_click(
|
||||||
|
cx.listener(|this, _, cx| {
|
||||||
|
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||||
|
menu.action(
|
||||||
|
"Split Right",
|
||||||
|
SplitRight.boxed_clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.action("Split Left", SplitLeft.boxed_clone(), cx)
|
||||||
|
.action("Split Up", SplitUp.boxed_clone(), cx)
|
||||||
|
.action("Split Down", SplitDown.boxed_clone(), cx)
|
||||||
|
});
|
||||||
|
cx.subscribe(
|
||||||
|
&menu,
|
||||||
|
|this, _, event: &DismissEvent, cx| {
|
||||||
|
this.focus(cx);
|
||||||
|
this.split_item_menu = None;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.detach();
|
||||||
|
this.split_item_menu = Some(menu);
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
.when_some(
|
||||||
|
self.split_item_menu.as_ref(),
|
||||||
|
|el, split_item_menu| {
|
||||||
|
el.child(Self::render_menu_overlay(split_item_menu))
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.z_index(1)
|
||||||
|
.bottom_0()
|
||||||
|
.right_0()
|
||||||
|
.size_0()
|
||||||
|
.child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
// fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
|
// fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
|
||||||
// let theme = theme::current(cx).clone();
|
// let theme = theme::current(cx).clone();
|
||||||
|
|
||||||
|
@ -2004,25 +2101,53 @@ impl Render for Pane {
|
||||||
.on_action(cx.listener(|pane: &mut Pane, _: &SplitDown, cx| {
|
.on_action(cx.listener(|pane: &mut Pane, _: &SplitDown, cx| {
|
||||||
pane.split(SplitDirection::Down, cx)
|
pane.split(SplitDirection::Down, cx)
|
||||||
}))
|
}))
|
||||||
// cx.add_action(Pane::toggle_zoom);
|
.on_action(cx.listener(Pane::toggle_zoom))
|
||||||
// cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
|
.on_action(cx.listener(|pane: &mut Pane, action: &ActivateItem, cx| {
|
||||||
// pane.activate_item(action.0, true, true, cx);
|
pane.activate_item(action.0, true, true, cx);
|
||||||
// });
|
}))
|
||||||
// cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| {
|
.on_action(cx.listener(|pane: &mut Pane, _: &ActivateLastItem, cx| {
|
||||||
// pane.activate_item(pane.items.len() - 1, true, true, cx);
|
pane.activate_item(pane.items.len() - 1, true, true, cx);
|
||||||
// });
|
}))
|
||||||
// cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
|
.on_action(cx.listener(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
|
||||||
// pane.activate_prev_item(true, cx);
|
pane.activate_prev_item(true, cx);
|
||||||
// });
|
}))
|
||||||
// cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
|
.on_action(cx.listener(|pane: &mut Pane, _: &ActivateNextItem, cx| {
|
||||||
// pane.activate_next_item(true, cx);
|
pane.activate_next_item(true, cx);
|
||||||
// });
|
}))
|
||||||
// cx.add_async_action(Pane::close_active_item);
|
.on_action(
|
||||||
// cx.add_async_action(Pane::close_inactive_items);
|
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
|
||||||
// cx.add_async_action(Pane::close_clean_items);
|
pane.close_active_item(action, cx)
|
||||||
// cx.add_async_action(Pane::close_items_to_the_left);
|
.map(|task| task.detach_and_log_err(cx));
|
||||||
// cx.add_async_action(Pane::close_items_to_the_right);
|
}),
|
||||||
// cx.add_async_action(Pane::close_all_items);
|
)
|
||||||
|
.on_action(
|
||||||
|
cx.listener(|pane: &mut Self, action: &CloseInactiveItems, cx| {
|
||||||
|
pane.close_inactive_items(action, cx)
|
||||||
|
.map(|task| task.detach_and_log_err(cx));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_action(
|
||||||
|
cx.listener(|pane: &mut Self, action: &CloseCleanItems, cx| {
|
||||||
|
pane.close_clean_items(action, cx)
|
||||||
|
.map(|task| task.detach_and_log_err(cx));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_action(
|
||||||
|
cx.listener(|pane: &mut Self, action: &CloseItemsToTheLeft, cx| {
|
||||||
|
pane.close_items_to_the_left(action, cx)
|
||||||
|
.map(|task| task.detach_and_log_err(cx));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_action(
|
||||||
|
cx.listener(|pane: &mut Self, action: &CloseItemsToTheRight, cx| {
|
||||||
|
pane.close_items_to_the_right(action, cx)
|
||||||
|
.map(|task| task.detach_and_log_err(cx));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_action(cx.listener(|pane: &mut Self, action: &CloseAllItems, cx| {
|
||||||
|
pane.close_all_items(action, cx)
|
||||||
|
.map(|task| task.detach_and_log_err(cx));
|
||||||
|
}))
|
||||||
.size_full()
|
.size_full()
|
||||||
.on_action(
|
.on_action(
|
||||||
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
|
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
|
||||||
|
|
|
@ -290,7 +290,6 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for View<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
|
fn focus_changed(&mut self, pane_focused: bool, cx: &mut WindowContext) {
|
||||||
println!("focus changed, pane_focused: {pane_focused}");
|
|
||||||
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();
|
||||||
|
|
|
@ -3587,87 +3587,6 @@ fn open_items(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo!()
|
|
||||||
// fn notify_of_new_dock(workspace: &WeakView<Workspace>, cx: &mut AsyncAppContext) {
|
|
||||||
// const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system";
|
|
||||||
// const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key";
|
|
||||||
// const MESSAGE_ID: usize = 2;
|
|
||||||
|
|
||||||
// if workspace
|
|
||||||
// .read_with(cx, |workspace, cx| {
|
|
||||||
// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
|
|
||||||
// })
|
|
||||||
// .unwrap_or(false)
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if db::kvp::KEY_VALUE_STORE
|
|
||||||
// .read_kvp(NEW_DOCK_HINT_KEY)
|
|
||||||
// .ok()
|
|
||||||
// .flatten()
|
|
||||||
// .is_some()
|
|
||||||
// {
|
|
||||||
// if !workspace
|
|
||||||
// .read_with(cx, |workspace, cx| {
|
|
||||||
// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
|
|
||||||
// })
|
|
||||||
// .unwrap_or(false)
|
|
||||||
// {
|
|
||||||
// cx.update(|cx| {
|
|
||||||
// cx.update_global::<NotificationTracker, _, _>(|tracker, _| {
|
|
||||||
// let entry = tracker
|
|
||||||
// .entry(TypeId::of::<MessageNotification>())
|
|
||||||
// .or_default();
|
|
||||||
// if !entry.contains(&MESSAGE_ID) {
|
|
||||||
// entry.push(MESSAGE_ID);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// cx.spawn(|_| async move {
|
|
||||||
// db::kvp::KEY_VALUE_STORE
|
|
||||||
// .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string())
|
|
||||||
// .await
|
|
||||||
// .ok();
|
|
||||||
// })
|
|
||||||
// .detach();
|
|
||||||
|
|
||||||
// workspace
|
|
||||||
// .update(cx, |workspace, cx| {
|
|
||||||
// workspace.show_notification_once(2, cx, |cx| {
|
|
||||||
// cx.build_view(|_| {
|
|
||||||
// MessageNotification::new_element(|text, _| {
|
|
||||||
// Text::new(
|
|
||||||
// "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.",
|
|
||||||
// text,
|
|
||||||
// )
|
|
||||||
// .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| {
|
|
||||||
// let code_span_background_color = settings::get::<ThemeSettings>(cx)
|
|
||||||
// .theme
|
|
||||||
// .editor
|
|
||||||
// .document_highlight_read_background;
|
|
||||||
|
|
||||||
// cx.scene().push_quad(gpui::Quad {
|
|
||||||
// bounds,
|
|
||||||
// background: Some(code_span_background_color),
|
|
||||||
// border: Default::default(),
|
|
||||||
// corner_radii: (2.0).into(),
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
// .into_any()
|
|
||||||
// })
|
|
||||||
// .with_click_message("Read more about the new panel system")
|
|
||||||
// .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST))
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
// .ok();
|
|
||||||
|
|
||||||
fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncAppContext) {
|
fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncAppContext) {
|
||||||
const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";
|
const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue