Common Google Chrome Keyboard Shortcuts

Window and Tab Shortcuts

Ctrl+N Open a new window

Ctrl+T Open a new tab

Ctrl+W or Ctrl+F4 Close the current tab

Alt+F4 Close the current window

Hold Ctrl and click a link (or middle-click with mouse) Open link in a new tab

Ctrl+K or Ctrl+E Focus the address bar to enter search text

Ctrl+D Bookmark the current tab

Ctrl+Shift+D Bookmark all open tabs in the current window to a new folder

F11 Toggle full screen

F12 or Ctrl+Shift+I Open Developer Tools

Shift+Esc Open Task Manager to view CPU usage, memory, etc. per tab

Alt+F or Alt+E Open the “Tools” menu for customizing and controlling Chrome settings

Ctrl+Shift+B Toggle the bookmarks bar

Ctrl+H Open the “History” page

Ctrl+Shift+Delete Open the “Clear browsing data” dialog

Ctrl+J Open the “Downloads” page

Webpage Shortcuts

Ctrl+P Print the current page

Ctrl+S Save the current page

F5 or Ctrl+R Reload the current page (refresh)

Ctrl+F5 or Shift+F5 Reload the current page, ignoring cache (hard refresh)

Esc Stop loading the current page

Ctrl+F Open the find bar

Ctrl+U View the page source code

Hold Ctrl and scroll mouse wheel up/down Zoom in/out on all page content

Ctrl+0 Reset page zoom to normal size

Home Scroll to the top of the page

End Scroll to the bottom of the page

Hold Shift and scroll mouse wheel Scroll horizontally on the page

Address Bar Shortcuts

Ctrl+Enter (When cursor is in address bar) Add “www.” and “.com” around input and open the URL

Long-press (hold left mouse button) on “Back” or “Forward” button Show a dropdown list of recent page URLs; alternatively, right-click for the same menu

谷歌浏览器常用快捷键

窗口和标签页快捷键
Ctrl+N 打开一个新窗口
Ctrl+T 打开新标签
Ctrl+W或Ctrl+F4 关闭当前标签页
Alt+F4 关闭当前窗口

按住Ctrl键点击链接(或使用鼠标中键点击) 在新标签页打开链接
Ctrl+K或Ctrl+E 地址栏输入要搜索的文本
Ctrl+D 将当前标签页保存为书签
Ctrl+Shift+D 将当前窗口打开的全部标签页以书签方式保存在一个新建文件夹里
F11 全屏

F12或Ctrl+Shift+I 打开开发者工具
Shift+Esc 打开任务管理器,可以查看每个标签页CPU使用率,内存占用等
Alt+F或Alt+E 打开“工具”菜单,用该菜单可自定义和控制Chrome浏览器中的设置
Ctrl+Shift+B 打开和关闭书签栏
Ctrl+H 打开“历史记录”页面
Ctrl+Shift+Delete 打开“清除浏览数据”对话框
Ctrl+J 打开“下载内容”页面

网页快捷键
Ctrl+P 打印当前网页
Ctrl+S 保存当前网页
F5或Ctrl+R 重新载入当前网页,即刷新当前页面
Ctrl+F5或Shift+F5 重新加载当前页面,忽略缓存的内容,即强制刷新当前页面
Esc 停止载入当前网页
Ctrl+F 打开查找栏
Ctrl+U 打开当前网页的源代码
按住Ctrl键并向上滚动鼠标滚轮放大或向下滚动鼠标滚轮缩小网页上的所有内容
Ctrl+0 将网页上的所有内容都恢复到正常大小
Home 转至网页顶部
End 转至网页底部
按住Shift键的同时滚动鼠标滚轮 在网页上横向滚动

地址栏快捷键
Ctrl+Enter (当光标位于地址栏中时)在地址栏的输入内容前后分别加上www.和.com,并打开网址
长按(不要放开鼠标左键)“后退”或“前进”按钮,即可显示刚才浏览过的页面地址的列表。你也可以右击“后退”或“前进”按钮来弹出该下拉菜单

安装VirtualBox7警告missing dependencies python core_win32api的解决方法

解决这个警告的步骤是:

1 先退出VirtualBox安装程序

2 下载安装最新版本的Python,并把Python可执行程序所在目录加入环境变量PATH

3 打开终端窗口,运行以下命令安装Python的pywin32包:

py -m pip install pywin32

4 重新运行VirtualBox安装程序,应该就没有这个警告了

参考

https://stackoverflow.com/questions/78414273/virtual-box-python-core-win-32-api-issue
https://www.sysnettechsolutions.com/en/fix-python-win32api-virtualbox/

如何居中一个div元素

本文翻译自《How To Center a Div》。

长期以来,将元素置于其父元素内是一件非常棘手的事情。随着CSS的发展,我们获得了越来越多的工具来解决这个问题。如今,我们有很多选择!

我决定创建本教程来帮助你了解不同方法之间的权衡,并为你提供一系列可用于处理各种场景的居中策略。

老实说,这比我最初想象的要有趣得多。即使你已经使用CSS一段时间了,我敢打赌你至少会学到1种新策略!

使用自动边距(auto margin)来居中

我们将要介绍的第一个策略是最古老的策略之一。如果我们想将元素水平居中,我们可以使用设置为特殊值auto的边距(margin)来实现:

.element {
  max-width: fit-content;
  margin-left: auto;
  margin-right: auto;
}

(译者注:案例请看原文)

首先,我们需要限制元素的宽度;默认情况下,流式布局(Flow layout)中的元素将水平扩展其宽度以填充可用空间,我们无法真正将全宽的东西居中。

我可以用一个固定值(例如200px)来限制宽度,但在上述场景下,我真正想要的是让元素收缩到其内容的周围。 fit-content是一个神奇的值,它就是用来做这个的。本质上,它使“宽度”表现得像“高度”,这样元素的大小就由其内容决定了。

为什么我要使用max-width而不是width因为我的目标是阻止元素水平扩展。我想限制其最大尺寸。如果我改用width,它会将其宽度锁定为其内容的宽度,当容器非常窄时,这个元素就会溢出。使用max-width,如果将改变“容器宽度”的滑块一直拖到最左侧,你会看到该元素的宽度随其容器一起缩小。

现在我们的元素受到限制,我们可以用自动边距将其居中。 我喜欢将自动边距想象成“饥饿的河马”。每个自动边距都会尝试吞噬尽可能多的空间。例如,看看如果我们只设置margin-left: auto会发生什么:

.element {
  max-width: fit-content;
  margin-left: auto;
}

译者注:案例请看原文)

margin-left是唯一具有自动边距的一侧时,所有额外空间都会作为边距应用于该侧。当我们同时设置margin-left: automargin-right: auto时,两只河马会各自占用相同大小的空间。这就会将元素强制置于中间。

另外:我一直使用margin-leftmargin-right,因为我对它们很熟悉,但有一种更好、更现代的方法可以做到这一点:

.element {
  max-width: fit-content;
  margin-inline: auto;
}

(译者注:案例请看原文)

margin-inline将把margin-leftmargin-right设置为相同的值(例如上例的auto)。它具有非常好的浏览器支持,几年前就已登陆所有主流浏览器。

尽管这种居中方法已经存在很久了,但我仍然发现自己经常使用它!当我们想将单个子元素居中而不影响其任何兄弟元素时,这种方法特别有用(例如,博客文章中段落之间的图像)。

逻辑属性(logical properties)

margin-inline不仅仅是margin-left + margin-right的便捷简写。它是逻辑属性集合的一部分,旨在使网络国际化变得更加容易。

在英语中,字符从左到右水平书写。这些字符组成单词和句子,并组装成“块”(段落、标题、列表等)。块从上到下垂直堆叠。我们可以将此视为英语网站的内容方向。

但这并不是通用的!有些语言,如阿拉伯语和希伯来语,是从右到左书写的。其他语言,如中文,历史上是垂直书写的,字符从上到下,块从一边到另一边。

逻辑属性的主要目标是创建一个超越这些差异的抽象。我们可以使用margin-inline-start,而不是为从左到右的语言设置margin-left并将其翻转为margin-right以适应从右到左的语言。margin-inline-start会根据页面的语言,边距将自动应用于正确的一侧。

使用弹性盒(Flexbox)布局来居中

Flexbox旨在让我们在沿主轴分布一组元素时拥有强大的控制力。它提供了一些非常强大的居中工具!

使用SSH客户端导出同一个id_ecdsa_256私钥,每次内容都不一样的原因

在 Xshell 或其他 SSH 客户端中,每次导出相同的 ECDSA 私钥(id_ecdsa_256),但其文件内容不同,通常是因为 PEM 格式的加密方式和随机化 影响了最终的存储结果。具体而言,有以下几个关键原因。

1. PEM 格式中的加密随机性

如果你的私钥是加密存储的(即在导出时选择了密码保护),那么PEM 格式(通常是 OpenSSL 兼容的)会使用 PBKDF2 或 bcrypt 进行密钥派生,并为每次导出生成 随机盐值。由于加密盐值不同,即使私钥的核心内容相同,最终的加密数据会有所不同。

可以检查你的私钥文件头部,例如:

-----BEGIN ENCRYPTED PRIVATE KEY-----

如果看到 ENCRYPTED,则意味着该私钥是加密存储的,每次导出的加密数据可能不同。

2. ASN.1 序列化中的随机性

即使你的私钥未加密,它仍然可能以 ASN.1(Abstract Syntax Notation One)编码的 DER(Distinguished Encoding Rules)格式存储:

  • ASN.1 的 DER 编码可能会有不同的填充方式,导致即使内容相同,字节级表示不同。
  • 在某些情况下,ECDSA 私钥的元数据(如生成时间)可能会导致数据的微小变化。

3. 不同的软件导出方式

不同的 SSH 客户端或 OpenSSL 版本可能会使用不同的方式序列化私钥:

  • Xshell 可能在导出时重新编码了私钥,使其格式不同,但实际上仍然是同一个密钥。
  • 使用 OpenSSH 或 OpenSSL 重新导出同一私钥,也可能会生成不同的文件内容,但仍然对应相同的公钥。

4. Base64 编码的多样性

如果你的私钥是以 PEM 格式(Base64 编码)存储,则:

  • 行长(Line Wrapping) 可能不同,即 Base64 编码的换行方式可以有所变化。
  • PEM 头部或尾部的额外信息 可能发生改变,但不影响核心私钥内容。

5. 验证是否是相同私钥

你可以使用以下方法验证你的私钥是否实际上是相同的:

5.1 查看公钥是否一致

ssh-keygen -y -f id_ecdsa_256

如果你每次导出的私钥 生成的公钥相同,则可以确认它们是相同的私钥,只是存储格式不同。

5.2 查看私钥指纹

ssh-keygen -lf id_ecdsa_256

这个命令会计算私钥的指纹,若指纹相同,则说明私钥内容本质上是一样的。

5.3 比较 DER 格式

你可以将 PEM 格式转换为 DER 格式并比较:

openssl ec -in id_ecdsa_256 -outform DER -out id_ecdsa_256.der

md5sum id_ecdsa_256.der

如果 MD5 哈希值相同,说明私钥实际上是一样的。

Rust库程序编译为.dll或.so文件的方法

Rust 语言的库代码可以编译为 Windows 系统的 .dll 文件或 Linux 系统的 .so 文件。Rust 提供了对动态链接库(shared library,共享库)的良好支持,可以生成这些共享库以供 C、C++ 或其他语言使用。

1 编译为 Windows .dll 文件 在 Cargo.toml 中,需要指定 crate-type 为 cdylib 或 dylib:

[lib]
name = "mylib"
crate-type = ["cdylib"]

然后编写 Rust 代码(src/lib.rs):

#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

使用 cargo build –release 进行编译后,在 target/release/ 目录下会生成 mylib.dll 文件。

2 编译 Linux .so 文件

在 Linux 上,同样的 Cargo.toml 适用于 .so 文件,Rust 会自动根据目标平台生成 .so:

[lib]
name = "mylib"
crate-type = ["cdylib"]

Rust 代码保持不变,使用 cargo build –release,Rust 会在 target/release/ 目录下生成 libmylib.so。

3 crate-type的值

  • cdylib:生成 C 兼容的动态库,适用于其他语言(如 Python、C++)调用。
  • dylib:Rust 专用动态库,带有 Rust 运行时,通常用于 Rust 内部。

如果是给其他语言(如 C 或 Python)使用,推荐 cdylib,因为它不会带 Rust 运行时。

4 跨平台编译(交叉编译)

Rust 允许通过 –target 选项进行交叉编译。 Windows 交叉编译 Linux .so:

cargo build --release --target x86_64-unknown-linux-gnu

Linux 交叉编译 Windows .dll:

cargo build --release --target x86_64-pc-windows-gnu

5 示例:C 语言调用 Rust 共享库

在 C 代码中调用 Rust 编写的 add 函数:

#include <stdio.h>

extern int add(int a, int b);

int main() {
    printf("Result: %d\n", add(2, 3));
    return 0;
}

然后

  • Windows 需链接 mylib.dll
  • Linux 需链接 libmylib.so

结论

Rust 可以生成 .dll 和 .so 文件,并且可以通过 #[no_mangle] 和 extern “C” 让 Rust 代码能被 C 语言或其他语言调用。使用 cargo build –release 可编译出目标共享库,并可结合 –target 进行跨平台编译。

使用Solana主网账户和程序(合约)

通常,本地测试依赖于默认情况下本地验证器上不可用的程序(合约)和帐户。Solana CLI允许:

  • 下载程序(合约)和帐户
  • 将程序(合约)和帐户加载到本地验证器

如何从主网下载帐户

可以将JUP代币铸币帐户下载到本地文件中:

# solana account -u <source cluster> --output <output format> --output-file <destination file name/path> <address of account to fetch>
solana account -u m --output json-compact --output-file jup.json JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN

然后通过在启动验证器时(在本地集群上)传递帐户的文件和目标地址将其加载到你的本地网络:

# solana-test-validator --account <address to load the account to> <path to account file> --reset
solana-test-validator --account JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN jup.json --reset

同样,也可以下载 Openbook 程序:

# solana program dump -u <source cluster> <address of account to fetch> <destination file name/path>
solana program dump -u m srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX openbook.so

然后通过在启动验证器时(在本地集群上)传递程序的文件和目标地址将其加载到你的本地网络:

# solana-test-validator --bpf-program <address to load the program to> <path to program file> --reset
solana-test-validator --bpf-program srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX openbook.so --reset

参考

Using Mainnet Accounts and Programs

订阅Solana事件

Solana提供了一个基于Websocket的发布/订阅RPC接口,你可以在其中监听某些事件。你无需每隔一段时间ping一个HTTP端点来获取频繁的更新,而是可以只在更新事件发生时接收这些更新。

当你创建新的连接(Connection)实例时,Solana的web3 Connection 会生成一个websocket端点并注册一个websocket客户端(请参阅此处的源代码)。

Connection类公开了发布pub/订阅sub方法 – 它们都以on开头,就像事件发射器一样。当你调用这些监听器方法时,它会向该Connection实例的websocket客户端注册一个新的订阅。下面我们使用的示例发布/订阅方法是onAccountChange。回调将通过参数提供更新的状态数据(请参阅AccountChangeCallback作为示例)。

/* subscribe-to-events.ts */
import { clusterApiUrl, Connection, Keypair } from "@solana/web3.js";

(async () => {
  // 建立到开发网devnet的新连接 - 连接到开发网devnet的websocket客户端也在此处注册
  const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

  // 创建一个测试钱包来监听事件
  const wallet = Keypair.generate();

  // 注册一个回调函数来来监听这个钱包的账户变化信息(即websocket订阅)
  connection.onAccountChange(
    wallet.publicKey,
    (updatedAccountInfo, context) =>
      console.log("Updated account info: ", updatedAccountInfo),
    "confirmed",
  );
})();

参考

Subscribing to Events

获取测试用SOL币

当你在本地工作时,你需要一些SOL才能进行交易。在非主网环境中,你可以通过将SOL空投到你的地址来接收SOL:

/* TS使用grill库 */
import {
  address,
  lamports,
  airdropFactory,
  createSolanaClient,
  LAMPORTS_PER_SOL,
} from "gill";

const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient(
  {
    urlOrMoniker: "devnet", // or `localnet`, etc
  },
);

const wallet = address("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5");

const { value: initialBalance } = await rpc.getBalance(wallet).send();
console.log("Initial balance:", initialBalance);

/**
 * 注意:开发网devnet和测试网testnet有空投速率限制。
 * 强烈建议使用本地测试网localnet和本地测试验证器
 */
await airdropFactory({ rpc, rpcSubscriptions })({
  commitment: "confirmed",
  lamports: lamports(LAMPORTS_PER_SOL), // 申请1 SOL空投
  recipientAddress: wallet,
});

const { value: newBalance } = await rpc.getBalance(wallet).send();
console.log("New balance:", newBalance);

/* TS使用web3.js库 */
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";

const connection = new Connection("http://127.0.0.1:8899", "confirmed");

const wallet = new PublicKey("nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5");

/**
 * 注意:开发网devnet和测试网testnet有空投速率限制。
 * 强烈建议使用本地测试网localnet和本地测试验证器
 */
const signature = await connection.requestAirdrop(wallet, LAMPORTS_PER_SOL); // 申请1 SOL空投

const { blockhash, lastValidBlockHeight } =
  await connection.getLatestBlockhash();

// 注意:确认空投交易是非常重要的,用来确保你的钱包拿到空投
await connection.confirmTransaction({
  blockhash,
  lastValidBlockHeight,
  signature,
});

参考

Getting Test SOL

连接到Solana环境

当你进行Solana开发时,你需要连接到特定的RPC API端点。Solana有3个公共开发环境:

  • 主网beta(mainnet-beta):https://api.mainnet-beta.solana.com
  • 开发网(devnet):https://api.devnet.solana.com
  • 测试网(testnet):https://api.testnet.solana.com

使用网络昵称进行连接

使用昵称或名称连接到Solana公共RPC端点:

/* TS使用grill库 */
import { createSolanaClient } from "gill";

const { rpc, rpcSubscriptions } = createSolanaClient({
  urlOrMoniker: "devnet", // or `mainnet`, `localnet`, etc
});

/* TS使用web3.js库 */
import { clusterApiUrl, Connection } from "@solana/web3.js";

const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");

使用特定RPC URL进行连接

要连接到特定RPC API端点:本地测试网验证器或远程RPC提供程序,请使用以下命令:

/* TS使用grill库 */
import { createSolanaRpc, createSolanaRpcSubscriptions, devnet } from "gill";

const rpc = createSolanaRpc(devnet("https://api.devnet.solana.com"));

const rpcSubscriptions = createSolanaRpcSubscriptions(
  devnet("wss://api.devnet.solana.com"),
);

/* TS使用web3.js库 */
import { Connection } from "@solana/web3.js";

// 以下一行代码将连接到本地测试网验证器
const connection = new Connection("http://127.0.0.1:8899", "confirmed");

参考

Connecting to a Solana Environment