Table of Contents
KDE 上 Wayland 后天天给我 crash, 就直接换 Hyprland 了
最终效果
特殊工作区
1. 安装
- 在 Arch KDE(或现有桌面) 下
- pacman hyprwayland-scanner 或 yay hyprwayland-scanner-git
- pacman hyprland 或 yay hyprland-git
- [Optional] 可以在 console 用
hyprland
进桌面, 但是基本什么都干不了, tty 下的hyprland
不会接收到我的 Super 按键事件
2. 配置
- 先别急着用 sddm 进 hyprland, 在 Arch KDE(或现有桌面)下
- 创建或更改
~/.config/hypr/hyprland.conf
, 例子 hyprwm/Hyprland, 可以直接复制覆盖 - 确认好基本的设置比如
Super
key 是什么,terminal
有没有安装(默认是kitty
) 可能没有安装然后会导致开不了控制台基本就很难受(一般直接更改到你目前用的比如 konsole) - 然后就可以进去边用边设
下面我会贴上我目前的配置, 应用我设置前建议自己审查一遍, 去掉不需要的那些()
2.1 MONITORS
可以给显示器设置摆放位置和刷新率, 比如我放一个 AOC 在左边, 笔记本屏幕在右边
################### MONITORS ###################
# See https://wiki.hyprland.org/Configuring/Monitors/monitor=,preferred,auto,1# auto put AOC in leftmonitor=desc:Chimei Innolux Corporation 0x15E8,1920x1080@60,1920x0,1monitor=desc:AOC 27B2 0x0000CD6A,1920x1080@75,0x0,1
其中 monitor
的 description 可以在 hyprctl monitors
拿到. 尽量别设负数 position (虽然也能用), 会导致一些程序运行错误, 比如 hyprshot
. 然后也可以用新的 monitor v2
格式
monitorv2 { output = desc:LG Electronics LG ULTRAGEAR+ 202NTEPBR420 mode = 3840x2160@144 position = 0x0 scale = 1.25 bitdepth = 10 cm = hdredid sdrbrightness = 1.2 sdrsaturation = 0.98}
2.2 MY PROGRAMS
可以设 terminal
到你常用的, 比如
# Set programs that you use$terminal = konsole$fileManager = dolphin$menu = rofi -show drun
2.3 Autostart
#################### AUTOSTART ####################
# Autostart necessary processes (like notifications daemons, status bars, etc.)# Or execute your favorite apps at launch like this:
# disable kded6 auto start https://github.com/Alexays/Waybar/issues/3468 bc waybar issueexec-once = printf "[D-BUS Service]\nName=org.kde.kded6\nExec=/bin/false" > $HOME/.local/share/dbus-1/services/org.kde.kded6.serviceexec-shutdown = rm $HOME/.local/share/dbus-1/services/org.kde.kded6.service
exec-once=dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # for screen sharing# exec-once = ~/xdg-deksop-portal-hyprland-startup.sh# notificationexec-once = dunst# copy-paste managerexec-once = wl-paste --type text --watch cliphist store #Stores only text dataexec-once = wl-paste --type image --watch cliphist store #Stores only image data# save paste content after application closedexec-once = wl-clip-persist --clipboard regular# use wal -i wallpaper.jpg to generate scheme# reload last-time color, pywal16 https://github.com/eylles/pywal16exec-once = /home/eritquearcus/.local/share/virtualenvs/github-*/bin/wal -R
# KDE auth kit - auth popup# exec-once=/usr/lib/polkit-kde-authentication-agent-1# Or if you use Hyprland w/o uwsm# check https://wiki.hyprland.org/Hypr-Ecosystem/hyprpolkitagent/# exec-once = systemctl --user start hyprpolkitagent# fcitx5 configexec-once=fcitx5-remote -rexec-once=fcitx5 -d --replaceexec-once=fcitx5-remote -r# icon indicator on waybarexec-once=nm-applet --indicatorexec-once=blueman-applet# payback line inexec-once=pactl load-module module-loopback source=alsa_input.usb-Generic_USB_Audio-00.HiFi__Line1__source
# status barexec-once = waybar -c ~/.config/waybar/config.jsonc# wallpaperexec-once = hyprpaper --config ~/.config/hypr/hyprpaper.confexec-once = $terminal
wl-clipboard
wl-clip-ersist
waybar
hyprpolkitagent
等需要额外安装, 用pacman
或 yaywl-paste
可以用来管理复制的内容, 然后用快捷键比如Super + V
看历史fcitx
解决方法参考 hyprwm/Hyprland/discussions/421
2.4 INPUT
kb_options
可以用来设 compose key 比如kb_options = compose:ralt
follow_mouse
用来选鼠标交互方式, 比如鼠标1=hover
(放上去就选中窗口) 或者2=click
(点击选中)scroll_factor
设置滚动速度
2.5 ENV
################################ ENVIRONMENT VARIABLES ################################
# See https://wiki.hyprland.org/Configuring/Environment-variables/# env = AQ_NO_MODIFIERS,1
# fcitx# env = GTK_IM_MODULE,fcitxenv = QT_IM_MODULE,fcitxenv = QT_IM_MODULES,wayland;fcitx;ibusenv = XMODIFIERS,@im=fcitxenv = SDL_IM_MODULE,fcitxenv = GLFW_IM_MODULE,ibus
env = GDK_BACKEND,wayland,x11,*env = ELECTRON_OZONE_PLATFORM_HINT,waylandenv = QT_QPA_PLATFORM,wayland;xcbenv = SDL_VIDEODRIVER,waylandenv = CLUTTER_BACKEND,wayland
env = XCURSOR_SIZE,24env = HYPRCURSOR_SIZE,24env = XDG_MENU_PREFIX,arch-env = XDG_CURRENT_DESKTOP,Hyprlandenv = XDG_SESSION_TYPE,waylandenv = XDG_SESSION_DESKTOP,Hyprland
# Intel GPU# env = VDPAU_DRIVER,va_gl
# NVenv = LIBVA_DRIVER_NAME,nvidiaenv = __GLX_VENDOR_LIBRARY_NAME,nvidia
env = NVD_BACKEND,directs
2.6 KEYBINDINGSS
大部分都是默认
####################### KEYBINDINGSS #######################
# See https://wiki.hyprland.org/Configuring/Keywords/$mainMod = SUPER # Sets "Windows" key as main modifier
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for morebind = $mainMod, Q, exec, $terminalbind = $mainMod, C, killactive,bind = $mainMod, M, exit,bind = $mainMod, E, exec, $fileManagerbind = $mainMod, F, togglefloating,bind = $mainMod, R, exec, $menu# bind = $mainMod, P, pseudo, # dwindlebind = $mainMod, P, exec, $terminal -e " python"bind = $mainMod, J, togglesplit, # dwindle
# Move focus with mainMod + arrow keysbind = $mainMod, left, movefocus, lbind = $mainMod, right, movefocus, rbind = $mainMod, up, movefocus, ubind = $mainMod, down, movefocus, d# bind = $mainMod, down, layoutmsg, cyclenext
# Switch workspaces with mainMod + [0-9]bind = $mainMod, 1, workspace, 1bind = $mainMod, 2, workspace, 2bind = $mainMod, 3, workspace, 3bind = $mainMod, 4, workspace, 4bind = $mainMod, 5, workspace, 5bind = $mainMod, 6, workspace, 6bind = $mainMod, 7, workspace, 7bind = $mainMod, 8, workspace, 8bind = $mainMod, 9, workspace, 9bind = $mainMod, 0, workspace, 10
# Move active window to a workspace with mainMod + SHIFT + [0-9]bind = $mainMod SHIFT, 1, movetoworkspace, 1bind = $mainMod SHIFT, 2, movetoworkspace, 2bind = $mainMod SHIFT, 3, movetoworkspace, 3bind = $mainMod SHIFT, 4, movetoworkspace, 4bind = $mainMod SHIFT, 5, movetoworkspace, 5bind = $mainMod SHIFT, 6, movetoworkspace, 6bind = $mainMod SHIFT, 7, movetoworkspace, 7bind = $mainMod SHIFT, 8, movetoworkspace, 8bind = $mainMod SHIFT, 9, movetoworkspace, 9bind = $mainMod SHIFT, 0, movetoworkspace, 10
# Example special workspace (scratchpad)bind = $mainMod, S, togglespecialworkspace, magicbind = $mainMod SHIFT, S, movetoworkspace, special:magic
bind = $mainMod, V, exec, cliphist list | wofi --dmenu | cliphist decode | wl-copy# if nvidia# bind = $mainMod, G, exec, google-chrome-stable --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime --use-angle=vulkan --use-cmd-decoder=passthroughbind = $mainMod, G, exec, google-chrome-stable --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-imebind = $mainMode, K, exec, kate# bind = $mainMode, Y, exec, spotify --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime
# Scroll through existing workspaces with mainMod + scrollbind = $mainMod, mouse_down, workspace, e+1bind = $mainMod, mouse_up, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and draggingbindm = $mainMod, mouse:272, movewindowbindm = $mainMod, mouse:273, resizewindow
# bind=,Print,exec,hyprshot -m region --raw | swappy -f -# bind=,Print, exec, grimblast save area /tmp/snap.png && swappy -f /tmp/snap.pngbind=,Print, exec,grim -g "$(slurp)" - | swappy -f -# bindel=, XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+# bindel=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-bind=, XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ togglebind =, XF86AudioPlay, exec, playerctl play-pausebind =, XF86AudioPause, exec, playerctl play-pausebind =, XF86AudioNext, exec, playerctl nextbind =, XF86AudioPrev, exec, playerctl previousbind = , XF86AudioRaiseVolume, exec, volumectl upbind = , XF86AudioLowerVolume, exec, volumectl down# use acpi in /etc/acpi/handlers and /etc/acpi/events# bind = , XF86MonBrightnessUp, exec, lightctl up# bind = , XF86MonBrightnessDown, exec, lightctl downbind = $mainMod, L, exec, hyprlock
bind
bindl
bindm
之类的大体上没有显著差别hyprshot
grimblast
playerctl
hyprlock
,swappy
等要额外安装- Arch 没有
lightctl
可以用acpi
代替, 参考 ejmastnak.com/tutorials/arch/backlight
2.7 Waybar - Status Bar
- 我是直接改的的
SwaykH
的 https://github.com/SwayKh/dotfiles/tree/main/waybar
config
// vim:ft=jsonc// from https://github.com/SwayKh/dotfiles/blob/main/waybar/config.jsonc{ "layer": "top", "position": "top", "margin-left": 4, "margin-right": 4, "margin-top": 4, "margin-bottom": 0, "spacing": 1, "reload_style_on_change": true,
// Choose the order of the modules
"modules-left": ["hyprland/workspaces", "group/custom-group", "hyprland/window", "cava"],
"modules-center": ["clock"],
"modules-right": [ "custom/notification", "network", "cpu", "custom/gpu-usage", "memory", "disk", "temperature", "wireplumber", ],
"group/custom-group": { "orientation": "horizontal", "modules": ["tray"], },
// Modules configuration
"hyprland/workspace":{ },
"hyprland/window":{ "show-special": true },
"tray": { "icon-size": 16, "spacing": 10, "show-passive-items": true, "reverse-direction": true, },
"clock": { "interval": 60, "format": " {:%a %B %e %Y, %H:%M}", "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>", "format-alt": " {:%a %b %d}", },
"temperature": { "hwmon-path": "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon1/temp1_input", "hwmon-path-abs": "/sys/devices/pci0000:00/0000:00:18.3/hwmon", "critical-threshold": 80, "interval": 2, "format": " {temperatureC:>2}°C", "format-icons": ["", "", ""], },
"cpu": { "interval": 2, "format": " {usage:>2}%", },
"memory": { "interval": 2, "format": " {used:0.1f}G/{total:0.1f}G", },
"disk": { "interval": 15, "format": " {percentage_used:>2}%", },
"network": { // "interface": "wlp2*", // (Optional) To force the use of this interface "interval": 1, "format-wifi": " {bandwidthTotalBytes:>2}", //({essid} {signalStrength}%) "format-ethernet": " {bandwidthTotalBytes:>2}", "tooltip-format-ethernet": " {ipaddr}", "tooltip-format-wifi": " {essid} ({signalStrength}%)", "tooltip-format": " {ifname} via {gwaddr}", "format-linked": " {ifname} (No IP)", "format-disconnected": " Disconnected", //Disconnected ⚠ "format-alt": "{ifname}: {gwaddr}/{cidr}", },
"wireplumber": { "scroll-step": 5, "format": "{icon} {volume:>3}%", "format-muted": " {volume:>3}%", "format-icons": ["", "", ""], "on-click": "pamixer -t", "on-click-right": "pavucontrol" },
// This waybar build doesn't have support for cava. // Need to edit the PKGBUILD and rebuild with -Dcava=enabled flag "cava": { "cava_config": "$HOME/.config/cava/config", "autosens": 1, "sensitivity": 1, "bars": 14, "bar_delimiter": 0, "monstercat": false, "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], "actions": { "on-click-right": "mode", }, },
"custom/bluetooth": { "format": "{icon}", "format-icons": "", "exec-on-event": "true", "on-click": "$HOME/scripts/rofi-bluetooth", "tooltip-format": "Bluetooth Menu", },
"custom/wifi": { "format": "{icon}", "format-icons": "", "exec-on-event": "true", "on-click": "$HOME/scripts/rofi-wifi", "tooltip-format": "Wifi Menu", },
"custom/separator": { "format": "{icon}", "format-icons": "|", "tooltip": false, },
"custom/gpu-usage": { "exec": "nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits", "format": "GPU: {}%", "return-type": "", "interval": 5 },
"custom/notification": { "tooltip": true, "format": " {}", "return-type": "json", "exec-if": "which swaync-client", "exec": "swaync-client -swb", "on-click": "swaync-client -t -sw", "on-click-right": "swaync-client -d -sw", "escape": true, },}
其中 pavucontrol
pamixer
得另外装
然后如果要用 cava
就得安装 aur
上的 waybar-cava
style
/* fragile file path *//* need to generate by wal -i */@import url("../../.cache/wal/colors-waybar.css");
/* Use Pywal16 colors */@define-color fg alpha(@background, 1.0);@define-color bg alpha(@foreground, 1.0);@define-color bordercolor alpha(@background, 1.0);@define-color alert #F14241;@define-color disabled #A5A5A5;@define-color highlight #D49621;@define-color activegreen #26A65B;
* { min-height: 0; font-family: "JetBrainsMono Nerd Font", Roboto, Helvetica, Arial, sans-serif; font-size: 1rem; /* Adjust font using gtk-font size, like nwg-look or lxappearance */}
window#waybar { color: @fg; background-color: @bg; background: none;}#window,#workspaces { color: @bordercolor; background-color: @bg; border: 0.1rem solid @bordercolor; border-radius: 0.5rem; padding: 0.1rem 0.5rem; margin: 0.1rem;}#workspaces button { color: @bordercolor; background-color: @bg; border: 0rem solid @bordercolor; padding: 0.1rem; margin: 0.1rem;}
#clock,#cava,#battery,#bluetooth,#cpu,#custom-gpu-usage,#disk,#memory,#disk,#temperature,#language,#backlight,#backlight-slider,#network,#pulseaudio,#wireplumber,#custom-media,#taskbar,#tray,#tray menu,#tray > .needs-attention,#tray > .passive,#tray > .active,#mode,#scratchpad,#custom-notification,#custom-temp,#custom-wifi,#custom-kdeconnect,#custom-bluetooth,#custom-power,#custom-separator,#custom-group,#idle_inhibitor,#window,#mpd { color: @bordercolor; background-color: @bg; border: 0.1rem solid @bordercolor; border-radius: 0.5rem; padding: 0.1rem 0.5rem; margin: 0.1rem;}
#bluetooth { color: @bordercolor; background-color: @color1;}#battery,#network { color: @bordercolor; background-color: @color2;}#disk,#custom-gpu-usage,#cpu { color: @bordercolor; background-color: @color3;}#memory { color: @bordercolor; background-color: @color4;}#temperature { color: @bordercolor; background-color: @color5;}#custom-notification,#backlight { color: @bordercolor; background-color: @color6;}#pulseaudio,#wireplumber { color: @bordercolor; background-color: @color3;}#clock { color: @bordercolor; background-color: @color9;}
#workspaces,#workspaces button { color: @bordercolor; background-color: @color4;}#tray,#tray menu,#tray > .needs-attention,#tray > .passive,#tray > .active,#custom-wifi,#custom-kdeconnect,#custom-bluetooth,#custom-power,#custom-group,#idle_inhibitor { color: @bordercolor; background-color: @color5; border: 0rem solid @bordercolor;}#window { color: @bordercolor; background-color: @color6;}
#custom-group { border: 0.1rem solid @bordercolor;}
#custom-separator { color: @disabled;}
#network.disconnected,#pulseaudio.muted,#wireplumber.muted { background-color: @alert;}
#battery.charging,#battery.plugged { background-color: @activegreen;}
label:focus { background-color: @bg;}
放在 ~/.config/waybar
下然后 exec-once
里启动
2.7.1 Cava 配置
cava
[general]
mode = normalframerate = 30autosens = 1sensitivity = 10lower_cutoff_freq = 50higher_cutoff_freq = 10000
[input]method = pipewiresource = auto
[output]channels = stereoreverse = 0
2.8 Hyprlock - screen Locker
- 创建
~/.config/hypr/hyprlock.conf
- 复制 wiki.hyprland.org/Hypr-Ecosystem/hyprlock 上 Widgets 下来, 比如
background { monitor = # path = /home/me/someImage.png # only png supported for now color = rgba(25, 20, 20, 1.0)
# all these options are taken from hyprland, see https://wiki.hyprland.org/Configuring/Variables/#blur for explanations blur_passes = 0 # 0 disables blurring blur_size = 7 noise = 0.0117 contrast = 0.8916 brightness = 0.8172 vibrancy = 0.1696 vibrancy_darkness = 0.0}input-field { monitor = size = 200, 50 outline_thickness = 3 dots_size = 0.33 # Scale of input-field height, 0.2 - 0.8 dots_spacing = 0.15 # Scale of dots' absolute size, 0.0 - 1.0 dots_center = false dots_rounding = -1 # -1 default circle, -2 follow input-field rounding outer_color = rgb(151515) inner_color = rgb(200, 200, 200) font_color = rgb(10, 10, 10) fade_on_empty = true fade_timeout = 1000 # Milliseconds before fade_on_empty is triggered. placeholder_text = <i>Input Password...</i> # Text rendered in the input box when it's empty. hide_input = false rounding = -1 # -1 means complete rounding (circle/oval) check_color = rgb(204, 136, 34) fail_color = rgb(204, 34, 34) # if authentication failed, changes outer_color and fail message color fail_text = <i>$FAIL <b>($ATTEMPTS)</b></i> # can be set to empty fail_transition = 300 # transition time in ms between normal outer_color and fail_color capslock_color = -1 numlock_color = -1 bothlock_color = -1 # when both locks are active. -1 means don't change outer color (same for above) invert_numlock = false # change color if numlock is off swap_font_color = false # see below
position = 0, -20 halign = center valign = center}label { monitor = text = cmd[update:1000] echo "<span foreground='blue'>$(date)</span> " text_align = center # center/right or any value for default left. multi-line text alignment inside label container color = rgba(200, 200, 200, 1.0) font_size = 25 font_family = Noto Sans rotate = 0 # degrees, counter-clockwise
position = 0, 80 halign = center valign = center}
2.9 Screen sharing
本节为了解决不能分享屏幕或者录屏之类的问题
参考:
- 文档1: https://wiki.hyprland.org/Useful-Utilities/Screen-Sharing/
- 文档2: https://wiki.hyprland.org/Useful-Utilities/Screen-Sharing/ 和 https://gist.github.com/brunoanc/2dea6ddf6974ba4e5d26c3139ffb7580
- https://github.com/hyprwm/xdg-desktop-portal-hyprland/issues/104
- 先按照文档1的安装
pipewire
和wireplumber
, 然后按照文档2的安装xdg-desktop-portal-hyprland
(可以卸载xdg-desktop-portal-wlr
如果有). - 根据文档2把
exec-once=dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
加进hyprland.conf
里
2.10 Jetbrain IDEs in wayland
在
wayland
上跑 Jetbrain IDEs
参考:
- https://youtrack.jetbrains.com/issue/JBR-3206/Native-Wayland-support#focus=Comments-27-9266459.0-0
- https://mvysny.github.io/idea-wayland/
- 把
-Dawt.toolkit.name=WLToolkit
加到help->custom VM options
里
2.11 Line In
如果想监听/playback主板的 LineIn 口(也就是 windows 上的 listern 功能) 可以用 pactl load-module module-loopback source=alsa_input.usb-Generic_USB_Audio-00.pro-input-1
.
其中 source 需要 pactl list short sources
然后找到 LineIn 的 sink, 不行的话可以每个都试一下.
取消监听是 pactl unload-module module-loopback
2.12 Welcome banner
#!/bin/bashRED='\033[0;31m'CYAN='\033[0;36m'YELLOW='\033[1;33m'PURPLE='\033[0;35m'GRAY='\033[0;37m'NC='\033[0m'
# TERM_WIDTH=$(tput cols | tr -d '\n\r ')TERM_WIDTH=$COLUMNScenter_text() { local text="$1" local width=${#text} local padding=$(( (TERM_WIDTH - width) / 2 )) printf "%*s%s\n" $padding "" "$text"}
echo -e "${PURPLE}"center_text "╔════════════════════════════════════════════════════════════════════════════╗"echo -e "${NC}"
echo -e "${CYAN}"figlet -t -w ${TERM_WIDTH} -c -f "$HOME/Downloads/Delta Corps Priest 1" "Eritque Arcus" 2>/dev/null || figlet -c "Eritque Arcus"echo -e "${NC}"
echo -e "${PURPLE}"center_text "╚════════════════════════════════════════════════════════════════════════════╝"echo -e "${NC}"
echo -e "${GRAY}"center_text "Welcome back, $(whoami) | $(date '+%m/%d %H:%M')"echo -e "${NC}"echo ""
用到 figlet
和 https://github.com/xero/figlet-fonts/ 里面的字体
3. Troubleshooting
3.1 Waybar 图标栏(applet)图标消失
参考 https://github.com/Alexays/Waybar/issues/3468 是 kded6 拿了 org.kde.StatusNotifierWatcher
(通过 busctl --user list|grep -i status
验证), 然后可以直接用 ps aux
拿到 kded6 的 PID 然后 kill 掉
3.2 KDE open with 弹窗空白
KDE 提示的弹窗是空白或者他不能根据文件后缀打开
参考: https://www.lorenzobettini.it/2024/05/fixing-the-empty-open-with-in-dolphin-in-hyprland/
- 安装
sudo pacman -S archlinux-xdg-menu
- 把
env = XDG_MENU_PREFIX,arch-
加到hyprland.conf
- 运行
kbuildsycoca6
3.3 Electron on wayland
本节为了解决一些 electron 应用不能复制之类的问题
参考:
- https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland#Chromium_.2F_Electron
- https://github.com/fcitx/fcitx5/issues/996
- 如果遇到 chromium / electron 无法复制 -> 可以开
xeyes
看看是不是跑在 X11 下(用xeyes
), 要跑在 wayland 上就没问题. - 加
export ELECTRON_OZONE_PLATFORM_HINT=wayland
和export QT_QPA_PLATFORM=wayland
让 electron 和 qt 程序跑在 wayland 上 - 如果遇到 fcitx5 无法在 chromium / elctron 在 wayland 上输入, 需要加参数
--enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime
在启动的时候, 比如 chrome 和 vscode