跳到主要内容

· 阅读需 2 分钟

https://github.com/gopasspw/gopass https://www.youtube.com/watch?v=rImp8rCiPEY&t

为啥要用?

我这次研究这个的目的是我的 OPENAI_API_KEY 需要在 neovim 的 gp.nvim 的配置中使用

但是我的配置是公开的

之前是直接在 zshrc 里面 source 一个本地文件把环境变量写进去,这样 gp.nvim 就可以直接读取这个环境变量了

但是现在我用了 neovide 之后,这个环境变量变得不可靠(不可用),所以我需要别的方法去做这个事情

而且之前真实的项目上也用到了这个 gopass 工具作为 cicd 的密码管理工具,并且可以很好的在团队成员中间同步项目密码

所以这次我自己来研究下怎么使用这个东西

基本用法

安装和配置我就跳过了,大家自己研究,有问题留言交流

列出所有 entry | ls

ls

新增一个 entry | insert & generate

insert

查看 entry | cat & show

cat&show

· 阅读需 1 分钟
vim.keymap.set("n", "<leader>fl", "<cmd>Neotree reveal reveal_force_cwd<cr>", { desc = "ExplorerFindFileLocation" })
  • Neotree reveal 命令会定位显示buffer
  • reveal_force_cwd 这个选项会自动切换目录(否则会有弹窗让你确认),即使当前目录不属于现在的项目目录下

show

· 阅读需 1 分钟

https://www.reddit.com/r/neovim/comments/113z6bb/switching_neovim_configs_is_easier_than_ever_now/

大家应该都知道,我们的配置文件的路径通常是 ~/.config/nvim

但是,通过 NVIM_APPNAME 这个环境变量,可以使 nvim 使用 ~/.config/ 目录下的其他子目录作为配置文件存放的路径

比如

NVIM_APPNAME="tinyvim" nvim

这是我 ~/.config/ 目录

~/.config 
❯ tree -L 2
.
├── nvim -> ../dotfiles/nvim/.config/nvim 我 nvim 配置的默认路径
└── tinyvim # NVIM_APPNAME=tinyvim 时候的配置
   └── init.lua

使用场景

那你就可以使用 alias 同时存在多个配置的 nvim

  • 一个插件配满的版本,一个(很少/没有)插件的版本
  • debug: cp 配置目录,然后更改配置查看结果 ...

show

· 阅读需 1 分钟

There's multiple ways to config a plugin in lazy.nvim

Mainly we use opts or config to specify the config of a plugin in lazy.Nvim

You can ref to https://github.com/folke/lazy.nvim#-plugin-spec if you want to read by your self

Here I created a very simple plugin that provide with a api that you can print out the in-use configuration of this plugin

Thus to play with multiple config methods and see the final result.

You can find the plugin at https://github.com/LintaoAmons/print-config.nvim

Here I list some of the ways to config the plugin

Opts

./lua/plugin/print-config.lua
return {
"LintaoAmons/print-config.nvim",
opts = {
...
}
}

Opts with function

./lua/plugin/print-config.lua
return {
"LintaoAmons/print-config.nvim",
opts = function (_, opts)
...
end
}

You can do something inside the config function to modify the opts, or return a table

config

./lua/plugin/print-config.lua
return {
"LintaoAmons/print-config.nvim",
config = function ()
...
end
}

Check active config

After you finish the configuration, try :lua require("print-config").check_config() to see if it's behaving as expected.

· 阅读需 5 分钟

REF:

Every object in JavaScript has a built-in property, which is called its prototype. The prototype is itself an object, so the prototype will have its own prototype, making what's called a prototype chain. The chain ends when we reach a prototype that has null for its own prototype.

┌────────┐ ┌──────────────┐ │myObject│--->│ prototype │ ┌───────────────┐ └────────┘ │Also an Object│---> ... --->│prototype(null)│ └──────────────┘ └───────────────┘

其实任何一个函数都可以是构造函数是吧?主要是里面有没有this?

🤖: 是的,你是对的。在JavaScript中,任何函数都可以被当作构造函数。但是,如果你想要你的函数作为构造函数使用,那么你通常会在函数中使用this关键字来引用并设置新创建的对象的属性。

例如,以下函数可以作为构造函数:

function MyConstructor() {
this.property = "value";
}

你可以使用new关键字创建一个新的MyConstructor对象:

let myObject = new MyConstructor();
console.log(myObject.property); // 输出 "value"

然而,如果你不使用this关键字,那么你的“构造函数”就不会设置任何对象的属性,这可能并不是你想要的:

function NotReallyAConstructor() {
let property = "value";
}

在这个例子中,property变量是NotReallyAConstructor函数的一个局部变量,而不是新创建的对象的一个属性。因此,这个函数虽然可以被当作构造函数使用,但实际上它并没有做任何构造函数应该做的事情。

所以,虽然技术上来说任何函数都可以被当作构造函数,但实际上,一个有效的构造函数通常会使用this关键字来设置新对象的属性。

所以关键在于 new 干了什么事?

REF: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new

🤖: 在JavaScript中,new关键字是用来创建和初始化新对象的。当你使用new关键字调用一个函数(我们通常称这种函数为构造函数)时,JavaScript会执行以下步骤:

  1. 创建一个新对象:JavaScript首先会创建一个新的空对象。

  2. 设置原型链:新创建的对象的__proto__属性(也就是它的原型)会被设置为构造函数的prototype对象。这意味着新创建的对象可以访问在构造函数的prototype对象中定义的所有属性和方法。

  3. 调用构造函数:接着,JavaScript会使用新创建的对象作为上下文(也就是this的值)来调用构造函数。这意味着在构造函数内部,this关键字会引用新创建的对象。构造函数通常会使用this关键字来设置新对象的属性。

  4. 返回新对象:如果构造函数没有显式地返回一个对象,那么new表达式会自动返回新创建的对象。如果构造函数返回了一个对象,那么new表达式会返回这个对象。如果构造函数返回的是一个非对象类型的值,那么这个返回值会被忽略,new表达式还是会返回新创建的对象。

这就是new关键字在JavaScript中的作用。使用new关键字可以确保构造函数总是返回一个对象,而且这个对象可以访问在构造函数的prototype对象中定义的所有属性和方法。同时,new关键字还确保了在构造函数内部,this关键字总是引用新创建的对象。

标签:

· 阅读需 16 分钟

You can find the lib.es2015.promise.d.ts source code at the end of this blog

.d.ts 文件

.d.ts 文件在 TypeScript 中被称为声明文件。这些文件的主要目的是为了提供 TypeScript 编译器关于 JavaScript 库的类型信息。JavaScript 是一种动态类型语言,因此它的库和框架通常不包含类型信息。为了在 TypeScript 中使用这些库和框架,开发者需要提供类型信息,这就是 .d.ts 文件的作用。

.d.ts 文件中,你可以声明变量、函数、类等的类型,以便 TypeScript 编译器能理解这些 JavaScript 代码的类型信息。

举个例子,比如你在项目中使用了 jQuery 这个 JavaScript 库,但是 TypeScript 编译器并不知道 $jQuery 这些对象的类型。在这种情况下,你需要一个 jquery.d.ts 文件,其中包含了所有 jQuery 对象和函数的类型声明,这样 TypeScript 编译器就能理解这些对象和函数了。

在大多数情况下,你不需要自己写 .d.ts 文件。许多流行的 JavaScript 库和框架的类型声明文件都可以在 DefinitelyTyped 项目中找到。你可以通过 npm 安装这些类型声明文件到你的项目中。

三斜线指令(Triple-Slash Directives)

/// 是 TypeScript 中的一个特殊注释语法,称为三斜线指令(Triple-Slash Directives)。这种指令为编译器提供了某些指示或信息。

Triple-Slash Directives 是包含单个 XML 标签的单行注释。注释的内容会影响编译器的行为。比如:

  • /// <reference path="..." /> 用来告诉编译器在编译当前文件时,还需要包含指定路径下的另一个文件。
  • /// <reference types="..." /> 用来声明当前文件需要依赖的类型定义模块。
  • /// <reference lib="..." /> 用来引入额外的内置库文件(比如 "dom", "es2015" 等)。
  • /// <reference no-default-lib="true"/> 用来告诉编译器不要包含默认的库文件。

这些指令提供了一种在源文件中包含附加的编译信息的方式。

在这里的作用

/// <reference no-default-lib="true"/>

interface PromiseConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: Promise<any>;
...
}

🤖: 这段代码看起来像是定义了一个 Promise 的 TypeScript 类型声明 (d.ts) 文件。在这种情况下, /// <reference no-default-lib="true"/> 可能被使用的原因如下:

通常,TypeScript 在编译时会自动引入一些默认的库文件,包括对一些基本的 JavaScript 特性(如 Promise、Array、Object 等)的声明。这个行为通常是有用的,因为它让 TypeScript 能够理解并检查这些基本特性的使用。

然而,在这个特定的文件中,作者似乎想要自定义 Promise 的类型声明,可能是因为他们需要一些非标准的行为,或者他们正在创建一个特定的环境,这个环境需要特殊的 Promise 实现。

在这个情况下, /// <reference no-default-lib="true"/> 指令被添加到文件的开头,用来告诉 TypeScript 编译器在处理这个文件时,不要自动引入默认的库文件。这样,作者就可以自由地自定义 Promise 的类型声明,而不用担心和默认的声明冲突。

declare

这是 TypeScript 代码,declare var Promise: PromiseConstructor; 这一句的意思是声明一个全局的变量 Promise,它的类型是 PromiseConstructor

在 TypeScript 中,declare var 用于声明全局变量。Promise 是 JavaScript 的一个内置对象,用于处理异步操作。在这里,这句代码是在告诉 TypeScript,全局有一个名为 Promise 的变量,并且它的类型是 PromiseConstructor

PromiseConstructor 是一个接口,定义了 Promise 对象的构造函数和一些静态方法,例如 all, race, rejectresolve

当你在代码中使用 Promise 对象时,TypeScript 就会根据这个声明来检查你的代码是否正确使用了 Promise 和它的方法。

==到这里就可以理解到: 在ts中使用 Promise 类型时,他类型信息就在这个文件声明定义了==

prototype TODO

interface PromiseConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: Promise<any>; 这个咋理解
...
}

all 方法

/**
* 创建一个Promise,当提供的所有Promise都解析时,该Promise会用结果数组进行解析,如果有任何Promise被拒绝,那么它会被拒绝。
* @param values 一个Promise数组。
* @returns 一个新的Promise。
*/
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;

🤖: 这是 TypeScript 中的一个泛型方法声明,我们逐部分来看:

  1. all<T extends readonly unknown[] | []>: 这是声明一个泛型方法 allT 是泛型参数。T extends readonly unknown[] | [] 指定了 T 可以是任何只读数组,或者空数组。unknown 是 TypeScript 的一个类型,表示未知类型。这里的 unknown[] 表示元素类型未知的数组。

  2. (values: T): 这是方法的参数列表。方法接收一个名为 values 的参数,其类型为 T。因为 T 已经被定义为只读数组或空数组,所以 values 必须是这些类型的实例。

  3. Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>: 这是方法的返回类型。这个方法返回一个 Promise 对象,这个 Promise 对象解析后的结果是一个对象。这个对象的类型通过映射类型 { -readonly [P in keyof T]: Awaited<T[P]> } 定义。

    • { -readonly [P in keyof T]: Awaited<T[P]> } 是一个映射类型,它会遍历 T(即 values 参数的类型)的所有键(即数组的索引),并将每个键 P 映射为一个新的类型 Awaited<T[P]>

    • Awaited<T[P]> 是一个条件类型,它等待 T[P] 的 Promise 解析。如果 T[P] 是一个 Promise,Awaited<T[P]> 就是这个 Promise 解析后的类型;如果 T[P] 不是一个 Promise,Awaited<T[P]> 就是 T[P]

    • -readonly 用于移除映射类型的 readonly 修饰符,使得结果类型的属性不是只读的。

总的来说,这个方法接收一个只读数组(或空数组),数组中的元素可以是 Promise 或者非 Promise。这个方法返回一个 Promise,这个 Promise 解析后的结果是一个对象,这个对象的每个属性都对应输入数组的一个元素,属性的类型是相应元素的 Promise 解析后的类型(如果元素是 Promise 的话)。

why there's a - before readonly

🤖: In TypeScript, the - prefix before a modifier like readonly in a mapped type is used to remove that modifier.

In the context of this specific type { -readonly [P in keyof T]: Awaited<T[P]> }, the -readonly is used to ensure that the resulting mapped type's properties are not readonly, even if they were readonly in the original type T.

This syntax is useful when you want to create a new type based on an existing type, but you want to change some modifiers of the original type. For instance, if you want to create a new type that is similar to an existing type but allows mutation of the properties, you can use -readonly to remove the readonly modifier from the properties.

Here's a simple example:

type ReadonlyType = {
readonly a: number;
readonly b: number;
};

type MutableType = {
-readonly [P in keyof ReadonlyType]: ReadonlyType[P];
};

// Now `MutableType` is equivalent to:
// type MutableType = {
// a: number;
// b: number;
// };

In MutableType, the -readonly removes the readonly modifier from the properties of ReadonlyType, creating a new type where a and b can be mutated.

Example

🤖: 这个描述是JavaScript中 Promise.all 方法的用法,下面是一个例子:

// 创建一个模拟异步操作的函数
function asyncOperation(value, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (delay > 2000) {
reject('Delay is too long');
} else {
resolve(value);
}
}, delay);
});
}

// 创建一个Promise数组
let promises = [asyncOperation('first', 1000), asyncOperation('second', 500), asyncOperation('third', 1500)];

// 使用Promise.all处理这个Promise数组
Promise.all(promises)
.then(results => {
// 当所有Promise都解析时,这个函数会被调用,并且results会是一个包含所有解析值的数组
console.log(results); // 输出: ['first', 'second', 'third']
})
.catch(error => {
// 如果有任何一个Promise被拒绝,这个函数会被调用
console.log('Error: ' + error); // 输出: 'Error: Delay is too long'
});

在这个例子中,Promise.all 接收一个Promise数组作为参数,返回一个新的Promise。当所有Promise都解决时,返回的Promise会解决,并且解决值是一个包含所有解决值的数组。如果有任何一个Promise被拒绝,返回的Promise就会被拒绝,拒绝理由是第一个被拒绝的Promise的拒绝理由。

Await type

/**
* Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`.
*/
type Awaited<T> =
T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
Awaited<V> : // recursively unwrap the value
never : // the argument to `then` was not callable
T; // non-object or non-thenable

怎么读: 三元表达式,找问号和冒号

这是一个 TypeScript 中的高级类型(Advanced Type)定义,其名为 Awaited。这个类型是用来递归地解开一个类型的 "awaited type"。换句话说,它是用来获取到一个 Promise 所包裹的类型。

让我们一步步地理解这个定义:

  1. T extends null | undefined ? T ::如果 T 是 null 或 undefined,直接返回 T。这是针对 TypeScript 非严格空值检查模式下的特殊情况。

  2. T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? :这个判断是查看 T 是否是一个包含 then 方法的对象。如果 T 是一个对象,并且这个对象有一个 then 方法,那么就进入下一步的判断。如果不是,就直接返回 T。

  3. F extends ((value: infer V, ...args: infer _) => any) ?:这个判断是查看 then 方法的第一个参数是否是一个函数。如果是,那么就提取出这个函数的第一个参数的类型(V),并进入下一步的判断。

  4. Awaited<V> ::如果上一步的判断成立,那么就递归地对 V 使用 Awaited 类型,也就是尝试继续解开 V 的 "awaited type"。如果 V 不是一个 Promise,那么这个递归就会在第二步的 T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? 那里结束,并返回 V。

  5. never ::如果 then 方法的第一个参数不是一个函数,那么就返回 never。

综上,这个 Awaited 类型实际上是用来解开一个 Promise 类型,获取到 Promise 所包裹的类型。例如,如果有 type X = Promise<string>,那么 Awaited<X> 就会是 string。如果 X 是一个 Promise<Promise<string>>,那么 Awaited<X> 也会是 string,因为 Awaited 会递归地解开所有层级的 Promise。

Source Code

lib.es2015.promise.d.ts source code
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0

THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.

See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */


/// <reference no-default-lib="true"/>

interface PromiseConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: Promise<any>;

/**
* Creates a new Promise.
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
* a resolve callback used to resolve the promise with a value or the result of another promise,
* and a reject callback used to reject the promise with a provided reason or error.
*/
new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;

/**
* Creates a Promise that is resolved with an array of results when all of the provided Promises
* resolve, or rejected when any Promise is rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;

// see: lib.es2015.iterable.d.ts
// all<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>[]>;

/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T extends readonly unknown[] | []>(values: T): Promise<Awaited<T[number]>>;

// see: lib.es2015.iterable.d.ts
// race<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>>;

/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject<T = never>(reason?: any): Promise<T>;

/**
* Creates a new resolved promise.
* @returns A resolved promise.
*/
resolve(): Promise<void>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T): Promise<Awaited<T>>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T | PromiseLike<T>): Promise<Awaited<T>>;
}

declare var Promise: PromiseConstructor;
标签:

· 阅读需 10 分钟

new Promise 的时候, Promise 就已经开始跑了吗?

🤖: 是的,当你创建一个新的Promise时,它会立即开始执行。Promise构造函数接受一个executor函数作为参数,这个函数会立即执行。这个executor函数接受两个参数:resolve和reject,它们被用来改变Promise的状态。

这是一个简单的示例,演示了Promise是如何立即执行的:

let p = new Promise((resolve, reject) => {
console.log('Promise is running');
resolve();
});

console.log('After creating the Promise');

运行这段代码,你将会看到"Promise is running"先于"After creating the Promise"打印出来,这证明了Promise在创建时就开始运行了。

resolve 后面的代码会继续跑吗?

let p = new Promise((resolve, reject) => {
console.log('Promise is running');
resolve();
console.log('Promise is running2');
});

console.log('After creating the Promise');

Promise is running2 会跑吗?

🤖: 是的,"Promise is running2" 这段代码会执行。当你在Promise的executor函数中调用resolve()或reject()方法后,其他代码还会继续执行,直到executor函数结束。但是,一旦Promise的状态被resolve()或reject()改变,它就不能再改变了。

所以在你的示例中,console.log('Promise is running2'); 这段代码会在 resolve(); 后执行,然后打印 "Promise is running2"。

但是,需要注意的是,这并不会改变Promise的状态,resolve();已经将Promise的状态设置为fulfilled,以后的任何resolve()或reject()调用都不会改变Promise的状态。

以下是你的代码的运行结果:

Promise is running
Promise is running2
After creating the Promise

这确认了 "Promise is running2" 这段代码确实被执行了。

即使再调用reject 都不会再改变了吗?

🤖: 是的,一旦Promise的状态被改变(无论是通过resolve()设为fulfilled还是通过reject()设为rejected),它就会永久保持这个状态,不能再被改变。这就是所谓的Promise的“已定型”(settled)特性。

这意味着,如果你在一个Promise的executor函数中首先调用了resolve(),然后又调用了reject(),那么reject()将不会有任何效果。Promise的状态已经在resolve()被调用时设为了fulfilled,后续的reject()调用不能再改变这个状态。

同样的,如果你首先调用了reject(),然后又调用了resolve(),那么resolve()也不会有任何效果。

这是一个示例:

let p = new Promise((resolve, reject) => {
resolve('first resolve');
reject('then reject'); // This will have no effect
});

p.then(value => console.log('Resolved with ' + value))
.catch(reason => console.log('Rejected with ' + reason));

// Outputs: "Resolved with first resolve"

在这个示例中,尽管reject()被调用了,但Promise仍然以resolve()调用中的值fulfilled,因为这是首个改变Promise状态的调用。catch()回调函数并未被执行,因为Promise并未被reject。

resolve 函数和 reject 函数接受的参数值会作为then函数或者catch函数的入参?

🤖: 是的,你完全正确。当你调用Promise的resolve函数或者reject函数时,你传递给这两个函数的参数将会被用作.then()或者.catch()回调函数的参数。

对于resolve()函数来说,你传给它的参数将作为.then()回调函数的参数。例如:

let p = new Promise((resolve, reject) => {
resolve('Hello, Promise!');
});

p.then(value => console.log(value)); // Outputs: "Hello, Promise!"

在这个例子中,字符串'Hello, Promise!'被传递给了resolve()函数,然后它被用作了.then()回调函数的参数。

对于reject()函数来说,你传给它的参数将作为.catch()回调函数的参数。例如:

let p = new Promise((resolve, reject) => {
reject('Oops, something went wrong');
});

p.catch(reason => console.log(reason)); // Outputs: "Oops, something went wrong"

在这个例子中,字符串'Oops, something went wrong'被传递给了reject()函数,然后它被用作了.catch()回调函数的参数。

这就是Promise的基本工作原理:resolve()函数和reject()函数控制Promise的状态,并将值或原因传递给后续的.then()或.catch()回调函数。

Promise typescript 方法签名

then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): Promise<TResult1 | TResult2>;

这是对Promise的.then()方法在TypeScript中的类型定义。我将一步步解析:

  • then<TResult1 = T, TResult2 = never>: then 是一个方法,接受两个类型参数 TResult1TResult2。如果未提供,TResult1 默认为 Promise 解析值的同一类型(T),TResult2 默认为 never,这是一个代表永不出现的值的类型。

  • (onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null): then 的第一个参数是一个可选的 onfulfilled 函数。当 Promise 解决时,将调用此函数,并接收已解决的值(value: T)。此函数可以返回类型 TResult1 的值或解析为 TResult1 的 PromiseLike 对象。如果未提供此函数,或者值为undefinednull,TypeScript 仍然允许调用 then 方法。

  • (onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): then 的第二个参数是一个可选的 onrejected 函数。当 Promise 被拒绝时,将调用此函数,并接收拒绝原因(reason: any)。此函数可以返回类型 TResult2 的值或解析为 TResult2 的 PromiseLike 对象。如果未提供此函数,或者值为 undefinednull,TypeScript 仍然允许调用 then 方法。

  • : Promise<TResult1 | TResult2>: then 返回一个新的 Promise。新 Promise 的解析值的类型可以是 TResult1TResult2,取决于原始 Promise 是解决还是拒绝,以及 onfulfilledonrejected 函数返回什么。

简单来说,.then() 允许你指定当一个 Promise 解析或拒绝时发生什么,并返回一个新的 Promise,使你能够将异步操作链接在一起。

以下是一个例子,展示了在 .then() 方法中如何使用 TResult1TResult2

let promise: Promise<number> = new Promise((resolve, reject) => {
let success = false; // 改为 true 来解析 Promise
if (success) {
resolve(123);
} else {
reject('出错了');
}
});

promise.then<number, string>(
(value: number) => { console.log(value); return value; }, // 如果 Promise 解析,将会调用此函数
(error: any) => { console.log(error); return error; } // 如果 Promise 被拒绝,将会调用此函数
);

在这个例子中,TResult1number 类型,对应解析值,TResult2string 类型,对应拒绝的原因。 然而,一般来说,我们只在.then()方法中处理resolved状态,而在.catch()方法中处理rejected状态,这样代码更易读,更易于理解。

为了展示 reject 的 reason 可以是 any 类型,而并非 TResult2 类型,优化你刚才的例子

🤖: 当然,这是一个优化后的例子,展示了拒绝的原因可以是any类型:

let promise: Promise<number> = new Promise((resolve, reject) => {
let success = false; // 改为 true 来解析 Promise
if (success) {
resolve(123);
} else {
reject({message: '出错了', code: 500}); // 拒绝的原因现在是一个对象
}
});

promise.then<number, string>(
(value: number) => { console.log(value); return value.toString(); }, // 如果 Promise 解析,将会调用此函数
(error: any) => { console.log(error); return JSON.stringify(error); } // 如果 Promise 被拒绝,将会调用此函数
);

在这个例子中,我将拒绝的原因改为了一个对象 {message: '出错了', code: 500}。这个对象能够被成功地传递给onrejected函数,尽管TResult2在此处被定义为string类型,但是这并不会影响我们拒绝Promise时传递任何类型的原因。因此,onrejected函数接收到的errorany类型。

然后,我们将这个对象转换为字符串返回,因此,返回的新Promise将解析为一个字符串,这与TResult2的类型相匹配。

· 阅读需 1 分钟

REF: https://www.youtube.com/watch?v=zoZiQJ38bXk

Normal Promise

./hello.js
function sendRequest() {
return new Promise(function(resolve, reject){
setTimeout(function(){
resolve("Lintao")
}, 2000)
});
}

let promise = sendRequest();
promise.then(function(username) {
console.log(username)
})

Change to Await

./hello-await.js
function sendRequest() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("Lintao");
}, 2000);
});
}


// must put await inside async function
async function getUsername() {
// put await in front of a function which returns a Promise object
console.log(await sendRequest());
}

getUsername()

Error handling

./hello-await-error-handling.js
function sendRequest() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject("Lintao");
}, 2000);
});
}

async function getUsername() {
// put inside try..catch block
try {
console.log(await sendRequest());
} catch (error) {
console.log(`error: ${error}`);
}
}

getUsername();

With Fetch

./hello-await-fetch.js
function sendRequest() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject("Lintao");
}, 2000);
});
}

async function getUsername() {
// put inside try..catch block
try {
let username = await fetch('https://jsonplaceholder.typicode.com/users')
username = await username.json()
console.log(username);
} catch (error) {
console.log(`error: ${error}`);
}
}

getUsername();

Promise.all and typescript

REF: https://blog.logrocket.com/async-await-in-typescript/

· 阅读需 5 分钟

🗨:

func WithValue(parent Context, key, val any) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}

为什么要用这种 delegate 的结构,而不是直接一个 map 储存多个 键值对

🤖: 这是一个很好的问题。这段代码来自Go的context包,它实现了一个可以传递键值对的context对象。context对象通常在请求作用域内用于传递数据和取消信号。

你提到的"delegate"结构,也就是这里的嵌套context,是Go语言context包的一种设计策略。在这个设计中,每个WithValue调用都会返回一个新的context,这个新的context包含了父context及新的键值对。这是一种链式的存储方式,而不是用一个单独的map去存储所有键值对。

选择这种设计的原因主要有两个:

  1. 并发安全: Go语言是并发友好的语言,这种设计保证了在并发情况下context的安全使用。如果使用单个map存储所有键值对,那么在并发情况下,需要额外的同步机制来保证map的安全访问。

  2. 不可变性: 这种设计使得context具有了不可变性。每次添加键值对都会返回新的context,而不是修改原来的context。这样可以避免在函数间传递context时,由于误操作修改了context的内容,导致数据的不一致。

你可能会问,这种设计不会导致效率降低吗?实际上,由于context的使用场景通常是在请求作用域内,键值对的数量相对较少,因此这种设计的效率影响并不大。同时,它带来的并发安全和不可变性的好处,更符合context的使用需求。

🗨: 对于不可变性,新产生的 valueCtx 中的 parent 是一个全新的ctx 实例吗,还是只是指向同一个 父 ctx instance?

🤖: 新产生的valueCtx中的parent不是一个全新的context实例,而是对父context的引用。这意味着它们都指向同一个父context实例。

这是Go语言context包实现不可变性的方式:每次WithValue被调用,都会创建一个新的valueCtx,这个新的valueCtx包含了对父context的引用和新的键值对。这样,新的valueCtx和父context共享了同一份数据,但是任何对新的valueCtx的修改都不会影响到父context,因为这些修改都是在新的valueCtx上进行的。

这种设计使得context具有了不可变性:你可以安全地在函数间传递context,而不用担心一个函数会不小心修改了另一个函数正在使用的context的数据。

要注意的是,虽然context的不可变性对于并发编程非常有用,但这并不意味着你可以安全地在多个goroutine中共享一个context实例。如果你的程序中存在这种情况,你仍然需要使用适当的同步机制来保证并发安全。

· 阅读需 1 分钟

今天看到这两个东西,想到我的全栈(全干)梦想,感觉前端的东西还是需要了解一点,就索性做做看。

然后 PWA 就看了下 youtube, 了解了大概怎么玩,就没有深入了. PWA 举例的话,比如说 Google Chat

接下来就实践一下 Tailwind elements 这个组件库,学点复制粘贴的活.

Tailwind Elements

REF: https://tailwind-elements.com/learn/te-foundations/basics/introduction/

./te-vite-demo/index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="stylesheet" href="style.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<!-- Start your project here-->

<!-- Main navigation container -->
<nav
class="flex-no-wrap relative flex w-full items-center justify-between bg-white py-2 shadow-md shadow-black/5 dark:bg-neutral-600 dark:shadow-black/10 lg:flex-wrap lg:justify-start lg:py-4"
data-te-navbar-ref
>
<div class="flex w-full flex-wrap items-center justify-between px-3">
<!-- Hamburger button for mobile view -->
<button
class="block border-0 bg-transparent px-2 text-neutral-500 hover:no-underline hover:shadow-none focus:no-underline focus:shadow-none focus:outline-none focus:ring-0 dark:text-neutral-200 lg:hidden"
type="button"
data-te-collapse-init
data-te-target="#navbarSupportedContent1"
aria-controls="navbarSupportedContent1"
aria-expanded="false"
aria-label="Toggle navigation"
>
<!-- Hamburger icon -->
<span class="[&>svg]:w-7">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="h-7 w-7"
>
<path
fill-rule="evenodd"
d="M3 6.75A.75.75 0 013.75 6h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 6.75zM3 12a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 12zm0 5.25a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75z"
clip-rule="evenodd"
/>
</svg>
</span>
</button>

<!-- Collapsible navigation container -->
<div
class="!visible hidden flex-grow basis-[100%] items-center lg:!flex lg:basis-auto"
id="navbarSupportedContent1"
data-te-collapse-item
>
<!-- Logo -->
<a
class="mb-4 mr-2 mt-3 flex items-center text-neutral-900 hover:text-neutral-900 focus:text-neutral-900 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400 lg:mb-0 lg:mt-0"
href="#"
>
<img
src="https://tecdn.b-cdn.net/img/logo/te-transparent-noshadows.webp"
style="height: 15px"
alt=""
loading="lazy"
/>
</a>
<!-- Left navigation links -->
<ul
class="list-style-none mr-auto flex flex-col pl-0 lg:flex-row"
data-te-navbar-nav-ref
>
<li class="mb-4 lg:mb-0 lg:pr-2" data-te-nav-item-ref>
<!-- Dashboard link -->
<a
class="text-neutral-500 hover:text-neutral-700 focus:text-neutral-700 disabled:text-black/30 dark:text-neutral-200 dark:hover:text-neutral-300 dark:focus:text-neutral-300 lg:px-2 [&.active]:text-black/90 dark:[&.active]:text-zinc-400"
href="#"
data-te-nav-link-ref
>Dashboard</a
>
</li>
<!-- Team link -->
<li class="mb-4 lg:mb-0 lg:pr-2" data-te-nav-item-ref>
<a
class="text-neutral-500 hover:text-neutral-700 focus:text-neutral-700 disabled:text-black/30 dark:text-neutral-200 dark:hover:text-neutral-300 dark:focus:text-neutral-300 lg:px-2 [&.active]:text-black/90 dark:[&.active]:text-neutral-400"
href="#"
data-te-nav-link-ref
>Team</a
>
</li>
<!-- Projects link -->
<li class="mb-4 lg:mb-0 lg:pr-2" data-te-nav-item-ref>
<a
class="text-neutral-500 hover:text-neutral-700 focus:text-neutral-700 disabled:text-black/30 dark:text-neutral-200 dark:hover:text-neutral-300 dark:focus:text-neutral-300 lg:px-2 [&.active]:text-black/90 dark:[&.active]:text-neutral-400"
href="#"
data-te-nav-link-ref
>Projects</a
>
</li>
</ul>
</div>

<!-- Right elements -->
<div class="relative flex items-center">
<!-- Cart Icon -->
<a
class="mr-4 text-neutral-500 hover:text-neutral-700 focus:text-neutral-700 disabled:text-black/30 dark:text-neutral-200 dark:hover:text-neutral-300 dark:focus:text-neutral-300 [&.active]:text-black/90 dark:[&.active]:text-neutral-400"
href="#"
>
<span class="[&>svg]:w-5">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="h-5 w-5"
>
<path
d="M2.25 2.25a.75.75 0 000 1.5h1.386c.17 0 .318.114.362.278l2.558 9.592a3.752 3.752 0 00-2.806 3.63c0 .414.336.75.75.75h15.75a.75.75 0 000-1.5H5.378A2.25 2.25 0 017.5 15h11.218a.75.75 0 00.674-.421 60.358 60.358 0 002.96-7.228.75.75 0 00-.525-.965A60.864 60.864 0 005.68 4.509l-.232-.867A1.875 1.875 0 003.636 2.25H2.25zM3.75 20.25a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0zM16.5 20.25a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0z"
/>
</svg>
</span>
</a>

<!-- Container with two dropdown menus -->
<div class="relative" data-te-dropdown-ref>
<!-- First dropdown trigger -->
<a
class="hidden-arrow mr-4 flex items-center text-neutral-500 hover:text-neutral-700 focus:text-neutral-700 disabled:text-black/30 dark:text-neutral-200 dark:hover:text-neutral-300 dark:focus:text-neutral-300 [&.active]:text-black/90 dark:[&.active]:text-neutral-400"
href="#"
id="dropdownMenuButton1"
role="button"
data-te-dropdown-toggle-ref
aria-expanded="false"
>
<!-- Dropdown trigger icon -->
<span class="[&>svg]:w-5">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="h-5 w-5"
>
<path
fill-rule="evenodd"
d="M5.25 9a6.75 6.75 0 0113.5 0v.75c0 2.123.8 4.057 2.118 5.52a.75.75 0 01-.297 1.206c-1.544.57-3.16.99-4.831 1.243a3.75 3.75 0 11-7.48 0 24.585 24.585 0 01-4.831-1.244.75.75 0 01-.298-1.205A8.217 8.217 0 005.25 9.75V9zm4.502 8.9a2.25 2.25 0 104.496 0 25.057 25.057 0 01-4.496 0z"
clip-rule="evenodd"
/>
</svg>
</span>
<!-- Notification counter -->
<span
class="absolute -mt-2.5 ml-2 rounded-[0.37rem] bg-danger px-[0.45em] py-[0.2em] text-[0.6rem] leading-none text-white"
>1</span
>
</a>
<!-- First dropdown menu -->
<ul
class="absolute left-auto right-0 z-[1000] float-left m-0 mt-1 hidden min-w-max list-none overflow-hidden rounded-lg border-none bg-white bg-clip-padding text-left text-base shadow-lg dark:bg-neutral-700 [&[data-te-dropdown-show]]:block"
aria-labelledby="dropdownMenuButton1"
data-te-dropdown-menu-ref
>
<!-- First dropdown menu items -->
<li>
<a
class="block w-full whitespace-nowrap bg-transparent px-4 py-2 text-sm font-normal text-neutral-700 hover:bg-neutral-100 active:text-neutral-800 active:no-underline disabled:pointer-events-none disabled:bg-transparent disabled:text-neutral-400 dark:text-neutral-200 dark:hover:bg-white/30"
href="#"
data-te-dropdown-item-ref
>Action</a
>
</li>
<li>
<a
class="block w-full whitespace-nowrap bg-transparent px-4 py-2 text-sm font-normal text-neutral-700 hover:bg-neutral-100 active:text-neutral-800 active:no-underline disabled:pointer-events-none disabled:bg-transparent disabled:text-neutral-400 dark:text-neutral-200 dark:hover:bg-white/30"
href="#"
data-te-dropdown-item-ref
>Another action</a
>
</li>
<li>
<a
class="block w-full whitespace-nowrap bg-transparent px-4 py-2 text-sm font-normal text-neutral-700 hover:bg-neutral-100 active:text-neutral-800 active:no-underline disabled:pointer-events-none disabled:bg-transparent disabled:text-neutral-400 dark:text-neutral-200 dark:hover:bg-white/30"
href="#"
data-te-dropdown-item-ref
>Something else here</a
>
</li>
</ul>
</div>

<!-- Second dropdown container -->
<div class="relative" data-te-dropdown-ref>
<!-- Second dropdown trigger -->
<a
class="hidden-arrow flex items-center whitespace-nowrap transition duration-150 ease-in-out motion-reduce:transition-none"
href="#"
id="dropdownMenuButton2"
role="button"
data-te-dropdown-toggle-ref
aria-expanded="false"
>
<!-- User avatar -->
<img
src="https://tecdn.b-cdn.net/img/new/avatars/2.jpg"
class="rounded-full"
style="height: 25px; width: 25px"
alt=""
loading="lazy"
/>
</a>
<!-- Second dropdown menu -->
<ul
class="absolute left-auto right-0 z-[1000] float-left m-0 mt-1 hidden min-w-max list-none overflow-hidden rounded-lg border-none bg-white bg-clip-padding text-left text-base shadow-lg dark:bg-neutral-700 [&[data-te-dropdown-show]]:block"
aria-labelledby="dropdownMenuButton2"
data-te-dropdown-menu-ref
>
<!-- Second dropdown menu items -->
<li>
<a
class="block w-full whitespace-nowrap bg-transparent px-4 py-2 text-sm font-normal text-neutral-700 hover:bg-neutral-100 active:text-neutral-800 active:no-underline disabled:pointer-events-none disabled:bg-transparent disabled:text-neutral-400 dark:text-neutral-200 dark:hover:bg-white/30"
href="#"
data-te-dropdown-item-ref
>Action</a
>
</li>
<li>
<a
class="block w-full whitespace-nowrap bg-transparent px-4 py-2 text-sm font-normal text-neutral-700 hover:bg-neutral-100 active:text-neutral-800 active:no-underline disabled:pointer-events-none disabled:bg-transparent disabled:text-neutral-400 dark:text-neutral-200 dark:hover:bg-white/30"
href="#"
data-te-dropdown-item-ref
>Another action</a
>
</li>
<li>
<a
class="block w-full whitespace-nowrap bg-transparent px-4 py-2 text-sm font-normal text-neutral-700 hover:bg-neutral-100 active:text-neutral-800 active:no-underline disabled:pointer-events-none disabled:bg-transparent disabled:text-neutral-400 dark:text-neutral-200 dark:hover:bg-white/30"
href="#"
data-te-dropdown-item-ref
>Something else here</a
>
</li>
</ul>
</div>
</div>
</div>
</nav>

<!-- Jumbotron -->
<div
class="relative flex items-center justify-center py-16 bg-gray-50 sm:py-24 lg:py-32 mb-16"
>
<div
class="absolute inset-0 bg-gradient-to-r from-teal-500 to-cyan-600 dark:from-neutral-700 dark:to-neutral-800"
aria-hidden="true"
></div>
<div class="relative px-4 sm:px-6 lg:px-8">
<div class="mx-auto text-lg text-center max-w-4xl">
<h1
class="text-4xl font-extrabold tracking-tight text-white sm:text-4xl lg:text-5xl"
>
<span class="block mb-6">I am learning Tailwind Elements</span>
<span class="block text-3xl">and it's awesome</span>
</h1>
<p class="mt-6 text-xl text-teal-50 max-w-3xl mb-6">
Tailwind Elements is a fantastic library, offering reusable UI
components that save development time, ensure design consistency,
and boost productivity. Its seamless integration with Tailwind CSS
enables effortless customization.
</p>

<a
role="button"
href="https://tailwind-elements.com/learn/te-foundations/basics/introduction/"
target="_blank"
class="inline-block rounded bg-neutral-50 px-7 pb-2.5 pt-3 text-sm font-medium uppercase leading-normal text-neutral-800 shadow-[0_4px_9px_-4px_#cbcbcb] transition duration-150 ease-in-out hover:bg-neutral-100 hover:shadow-[0_8px_9px_-4px_rgba(203,203,203,0.3),0_4px_18px_0_rgba(203,203,203,0.2)] focus:bg-neutral-100 focus:shadow-[0_8px_9px_-4px_rgba(203,203,203,0.3),0_4px_18px_0_rgba(203,203,203,0.2)] focus:outline-none focus:ring-0 active:bg-neutral-200 active:shadow-[0_8px_9px_-4px_rgba(203,203,203,0.3),0_4px_18px_0_rgba(203,203,203,0.2)] dark:shadow-[0_4px_9px_-4px_rgba(251,251,251,0.3)] dark:hover:shadow-[0_8px_9px_-4px_rgba(251,251,251,0.1),0_4px_18px_0_rgba(251,251,251,0.05)] dark:focus:shadow-[0_8px_9px_-4px_rgba(251,251,251,0.1),0_4px_18px_0_rgba(251,251,251,0.05)] dark:active:shadow-[0_8px_9px_-4px_rgba(251,251,251,0.1),0_4px_18px_0_rgba(251,251,251,0.05)]"
>
Learn with me
</a>
</div>
</div>
</div>

<!-- Grid -->
<div class="container mx-auto mb-16">
<div class="grid grid-cols-1 md:grid-cols-3 gap-12">
<!-- Column 1 -->
<div
class="block rounded-lg bg-white shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)] dark:bg-neutral-700"
>
<div
class="relative overflow-hidden bg-cover bg-no-repeat"
data-te-ripple-init
data-te-ripple-color="light"
>
<img
class="rounded-t-lg"
src="https://tecdn.b-cdn.net/img/new/textures/small/1.jpg"
alt=""
/>
<a href="#!">
<div
class="absolute bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-[hsla(0,0%,98%,0.15)] bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-100"
></div>
</a>
</div>
<div class="p-6">
<h5
class="mb-2 text-xl font-medium leading-tight text-neutral-800 dark:text-neutral-50"
>
Are you ready?
</h5>
<p class="mb-4 text-base text-neutral-600 dark:text-neutral-200">
A great adventure is ahead of us! Get ready for a fascinating
journey into the world of web design 🚀
</p>
<button
type="button"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200"
>
Count me in!
</button>
</div>
</div>
<!-- Column 1 -->

<!-- Column 2 -->
<div
class="block rounded-lg bg-white shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)] dark:bg-neutral-700"
>
<div
class="relative overflow-hidden bg-cover bg-no-repeat"
data-te-ripple-init
data-te-ripple-color="light"
>
<img
class="rounded-t-lg"
src="https://tecdn.b-cdn.net/img/new/textures/small/103.jpg"
alt=""
/>
<a href="#!">
<div
class="absolute bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-[hsla(0,0%,98%,0.15)] bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-100"
></div>
</a>
</div>
<div class="p-6">
<h5
class="mb-2 text-xl font-medium leading-tight text-neutral-800 dark:text-neutral-50"
>
UI and UX
</h5>
<p class="mb-4 text-base text-neutral-600 dark:text-neutral-200">
We will explore the unknown lands of creating great interfaces and
designing the perfect user experience
</p>
<button
type="button"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200"
>
Oh, I want it too
</button>
</div>
</div>
<!-- Column 2 -->

<!-- Column 3 -->
<div
class="block rounded-lg bg-white shadow-[0_2px_15px_-3px_rgba(0,0,0,0.07),0_10px_20px_-2px_rgba(0,0,0,0.04)] dark:bg-neutral-700"
>
<div
class="relative overflow-hidden bg-cover bg-no-repeat"
data-te-ripple-init
data-te-ripple-color="light"
>
<img
class="rounded-t-lg"
src="https://tecdn.b-cdn.net/img/new/textures/small/56.jpg"
alt=""
/>
<a href="#!">
<div
class="absolute bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-[hsla(0,0%,98%,0.15)] bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-100"
></div>
</a>
</div>
<div class="p-6">
<h5
class="mb-2 text-xl font-medium leading-tight text-neutral-800 dark:text-neutral-50"
>
Future of web dev
</h5>
<p class="mb-4 text-base text-neutral-600 dark:text-neutral-200">
You know the saying that the future is today? There is no need to
delay, let's start our journey
</p>

<button
type="button"
class="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200"
>
Let's start!
</button>
</div>
</div>
<!-- Column 3 -->
</div>
</div>

<!-- Footer container -->
<footer
class="bg-neutral-100 text-center text-neutral-600 dark:bg-neutral-600 dark:text-neutral-200 lg:text-left"
>
<div
class="flex items-center justify-center border-b-2 border-neutral-200 p-6 dark:border-neutral-500 lg:justify-between"
>
<div class="mr-12 hidden lg:block">
<span>Get connected with us on social networks:</span>
</div>
<!-- Social network icons container -->
<div class="flex justify-center">
<a href="#!" class="mr-6 text-neutral-600 dark:text-neutral-200">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"
/>
</svg>
</a>
<a href="#!" class="mr-6 text-neutral-600 dark:text-neutral-200">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"
/>
</svg>
</a>
<a href="#!" class="mr-6 text-neutral-600 dark:text-neutral-200">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M7 11v2.4h3.97c-.16 1.029-1.2 3.02-3.97 3.02-2.39 0-4.34-1.979-4.34-4.42 0-2.44 1.95-4.42 4.34-4.42 1.36 0 2.27.58 2.79 1.08l1.9-1.83c-1.22-1.14-2.8-1.83-4.69-1.83-3.87 0-7 3.13-7 7s3.13 7 7 7c4.04 0 6.721-2.84 6.721-6.84 0-.46-.051-.81-.111-1.16h-6.61zm0 0 17 2h-3v3h-2v-3h-3v-2h3v-3h2v3h3v2z"
fill-rule="evenodd"
clip-rule="evenodd"
/>
</svg>
</a>
<a href="#!" class="mr-6 text-neutral-600 dark:text-neutral-200">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"
/>
</svg>
</a>
<a href="#!" class="mr-6 text-neutral-600 dark:text-neutral-200">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M4.98 3.5c0 1.381-1.11 2.5-2.48 2.5s-2.48-1.119-2.48-2.5c0-1.38 1.11-2.5 2.48-2.5s2.48 1.12 2.48 2.5zm.02 4.5h-5v16h5v-16zm7.982 0h-4.968v16h4.969v-8.399c0-4.67 6.029-5.052 6.029 0v8.399h4.988v-10.131c0-7.88-8.922-7.593-11.018-3.714v-2.155z"
/>
</svg>
</a>
<a href="#!" class="text-neutral-600 dark:text-neutral-200">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
/>
</svg>
</a>
</div>
</div>

<!-- Main container div: holds the entire content of the footer, including four sections (Tailwind Elements, Products, Useful links, and Contact), with responsive styling and appropriate padding/margins. -->
<div class="mx-6 py-10 text-center md:text-left">
<div class="grid-1 grid gap-8 md:grid-cols-2 lg:grid-cols-4">
<!-- Tailwind Elements section -->
<div class="">
<h6
class="mb-4 flex items-center justify-center font-semibold uppercase md:justify-start"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="mr-3 h-4 w-4"
>
<path
d="M12.378 1.602a.75.75 0 00-.756 0L3 6.632l9 5.25 9-5.25-8.622-5.03zM21.75 7.93l-9 5.25v9l8.628-5.032a.75.75 0 00.372-.648V7.93zM11.25 22.18v-9l-9-5.25v8.57a.75.75 0 00.372.648l8.628 5.033z"
/>
</svg>
Tailwind ELEMENTS
</h6>
<p>
Here you can use rows and columns to organize your footer content.
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</p>
</div>
<!-- Products section -->
<div class="">
<h6
class="mb-4 flex justify-center font-semibold uppercase md:justify-start"
>
Products
</h6>
<p class="mb-4">
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>Angular</a
>
</p>
<p class="mb-4">
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>React</a
>
</p>
<p class="mb-4">
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>Vue</a
>
</p>
<p>
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>Laravel</a
>
</p>
</div>
<!-- Useful links section -->
<div class="">
<h6
class="mb-4 flex justify-center font-semibold uppercase md:justify-start"
>
Useful links
</h6>
<p class="mb-4">
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>Pricing</a
>
</p>
<p class="mb-4">
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>Settings</a
>
</p>
<p class="mb-4">
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>Orders</a
>
</p>
<p>
<a href="#!" class="text-neutral-600 dark:text-neutral-200"
>Help</a
>
</p>
</div>
<!-- Contact section -->
<div>
<h6
class="mb-4 flex justify-center font-semibold uppercase md:justify-start"
>
Contact
</h6>
<p class="mb-4 flex items-center justify-center md:justify-start">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="mr-3 h-5 w-5"
>
<path
d="M11.47 3.84a.75.75 0 011.06 0l8.69 8.69a.75.75 0 101.06-1.06l-8.689-8.69a2.25 2.25 0 00-3.182 0l-8.69 8.69a.75.75 0 001.061 1.06l8.69-8.69z"
/>
<path
d="M12 5.432l8.159 8.159c.03.03.06.058.091.086v6.198c0 1.035-.84 1.875-1.875 1.875H15a.75.75 0 01-.75-.75v-4.5a.75.75 0 00-.75-.75h-3a.75.75 0 00-.75.75V21a.75.75 0 01-.75.75H5.625a1.875 1.875 0 01-1.875-1.875v-6.198a2.29 2.29 0 00.091-.086L12 5.43z"
/>
</svg>
New York, NY 10012, US
</p>
<p class="mb-4 flex items-center justify-center md:justify-start">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="mr-3 h-5 w-5"
>
<path
d="M1.5 8.67v8.58a3 3 0 003 3h15a3 3 0 003-3V8.67l-8.928 5.493a3 3 0 01-3.144 0L1.5 8.67z"
/>
<path
d="M22.5 6.908V6.75a3 3 0 00-3-3h-15a3 3 0 00-3 3v.158l9.714 5.978a1.5 1.5 0 001.572 0L22.5 6.908z"
/>
</svg>
info@example.com
</p>
<p class="mb-4 flex items-center justify-center md:justify-start">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="mr-3 h-5 w-5"
>
<path
fill-rule="evenodd"
d="M1.5 4.5a3 3 0 013-3h1.372c.86 0 1.61.586 1.819 1.42l1.105 4.423a1.875 1.875 0 01-.694 1.955l-1.293.97c-.135.101-.164.249-.126.352a11.285 11.285 0 006.697 6.697c.103.038.25.009.352-.126l.97-1.293a1.875 1.875 0 011.955-.694l4.423 1.105c.834.209 1.42.959 1.42 1.82V19.5a3 3 0 01-3 3h-2.25C8.552 22.5 1.5 15.448 1.5 6.75V4.5z"
clip-rule="evenodd"
/>
</svg>
+ 01 234 567 88
</p>
<p class="flex items-center justify-center md:justify-start">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="mr-3 h-5 w-5"
>
<path
fill-rule="evenodd"
d="M7.875 1.5C6.839 1.5 6 2.34 6 3.375v2.99c-.426.053-.851.11-1.274.174-1.454.218-2.476 1.483-2.476 2.917v6.294a3 3 0 003 3h.27l-.155 1.705A1.875 1.875 0 007.232 22.5h9.536a1.875 1.875 0 001.867-2.045l-.155-1.705h.27a3 3 0 003-3V9.456c0-1.434-1.022-2.7-2.476-2.917A48.716 48.716 0 0018 6.366V3.375c0-1.036-.84-1.875-1.875-1.875h-8.25zM16.5 6.205v-2.83A.375.375 0 0016.125 3h-8.25a.375.375 0 00-.375.375v2.83a49.353 49.353 0 019 0zm-.217 8.265c.178.018.317.16.333.337l.526 5.784a.375.375 0 01-.374.409H7.232a.375.375 0 01-.374-.409l.526-5.784a.373.373 0 01.333-.337 41.741 41.741 0 018.566 0zm.967-3.97a.75.75 0 01.75-.75h.008a.75.75 0 01.75.75v.008a.75.75 0 01-.75.75H18a.75.75 0 01-.75-.75V10.5zM15 9.75a.75.75 0 00-.75.75v.008c0 .414.336.75.75.75h.008a.75.75 0 00.75-.75V10.5a.75.75 0 00-.75-.75H15z"
clip-rule="evenodd"
/>
</svg>
+ 01 234 567 89
</p>
</div>
</div>
</div>

<!--Copyright section-->
<div class="bg-neutral-200 p-6 text-center dark:bg-neutral-700">
<span>© 2023 Copyright:</span>
<a
class="font-semibold text-neutral-600 dark:text-neutral-400"
href="https://tailwind-elements.com/"
>Tailwind Elements</a
>
</div>
</footer>

<!-- End your project here-->
<script type="module" src="/main.js"></script>
</body>
</html>
./te-vite-demo/main.js
// Initialization for ES Users
import { Collapse, Dropdown, Ripple, initTE } from "tw-elements";

initTE({ Collapse, Dropdown, Ripple });