1
0

ref: change printWidth to 100 & re-format

This commit is contained in:
alikia2x (寒寒) 2025-10-06 01:37:57 +08:00
parent e3c416583a
commit 1d0a165723
52 changed files with 2465 additions and 2337 deletions

View File

@ -2,4 +2,4 @@ export const SECOND = 1000;
export const MINUTE = 60 * SECOND;
export const HOUR = 60 * MINUTE;
export const DAY = 24 * HOUR;
export const WEEK = 7 * DAY;
export const WEEK = 7 * DAY;

View File

@ -5,4 +5,4 @@ export const sql = postgres(postgresConfig);
export const sqlCred = postgres(postgresConfigCred);
export const sqlTest = postgres(postgresConfig);
export const sqlTest = postgres(postgresConfig);

View File

@ -4,7 +4,7 @@ const host = process.env.REDIS_HOST || "localhost";
const port = parseInt(process.env.REDIS_PORT) || 6379;
export const redis = new Redis({
port: port,
host: host,
maxRetriesPerRequest: null,
port: port,
host: host,
maxRetriesPerRequest: null
});

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1758438506680,
"tag": "0000_aspiring_mikhail_rasputin",
"breakpoints": true
}
]
}
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1758438506680,
"tag": "0000_aspiring_mikhail_rasputin",
"breakpoints": true
}
]
}

View File

@ -1,3 +1,2 @@
import { relations } from "drizzle-orm/relations";
import { } from "./schema";
import {} from "./schema";

View File

@ -1,183 +1,329 @@
import { pgTable, uniqueIndex, index, integer, bigint, varchar, text, timestamp, smallint, boolean, unique, serial, bigserial, uuid, pgSequence } from "drizzle-orm/pg-core"
import { sql } from "drizzle-orm"
import {
pgTable,
uniqueIndex,
index,
integer,
bigint,
varchar,
text,
timestamp,
smallint,
boolean,
unique,
serial,
bigserial,
uuid,
pgSequence
} from "drizzle-orm/pg-core";
import { sql } from "drizzle-orm";
export const allDataIdSeq = pgSequence("all_data_id_seq", {
startWith: "1",
increment: "1",
minValue: "1",
maxValue: "2147483647",
cache: "1",
cycle: false
});
export const labelingResultIdSeq = pgSequence("labeling_result_id_seq", {
startWith: "1",
increment: "1",
minValue: "1",
maxValue: "2147483647",
cache: "1",
cycle: false
});
export const songsIdSeq = pgSequence("songs_id_seq", {
startWith: "1",
increment: "1",
minValue: "1",
maxValue: "2147483647",
cache: "1",
cycle: false
});
export const videoSnapshotIdSeq = pgSequence("video_snapshot_id_seq", {
startWith: "1",
increment: "1",
minValue: "1",
maxValue: "2147483647",
cache: "1",
cycle: false
});
export const viewsIncrementRateIdSeq = pgSequence("views_increment_rate_id_seq", {
startWith: "1",
increment: "1",
minValue: "1",
maxValue: "9223372036854775807",
cache: "1",
cycle: false
});
export const allDataIdSeq = pgSequence("all_data_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
export const labelingResultIdSeq = pgSequence("labeling_result_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
export const songsIdSeq = pgSequence("songs_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
export const videoSnapshotIdSeq = pgSequence("video_snapshot_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
export const viewsIncrementRateIdSeq = pgSequence("views_increment_rate_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
export const bilibiliMetadata = pgTable(
"bilibili_metadata",
{
id: integer()
.default(sql`nextval('all_data_id_seq'::regclass)`)
.notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
bvid: varchar({ length: 12 }),
description: text(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
uid: bigint({ mode: "number" }),
tags: text(),
title: text(),
publishedAt: timestamp("published_at", { withTimezone: true, mode: "string" }),
duration: integer(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" }).default(sql`CURRENT_TIMESTAMP`),
status: integer().default(0).notNull(),
coverUrl: text("cover_url")
},
(table) => [
uniqueIndex("all_data_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
index("idx_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_all-data_bvid").using("btree", table.bvid.asc().nullsLast().op("text_ops")),
index("idx_all-data_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
index("idx_bili-meta_status").using("btree", table.status.asc().nullsLast().op("int4_ops")),
uniqueIndex("unq_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops"))
]
);
export const bilibiliMetadata = pgTable("bilibili_metadata", {
id: integer().default(sql`nextval('all_data_id_seq'::regclass)`).notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
bvid: varchar({ length: 12 }),
description: text(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
uid: bigint({ mode: "number" }),
tags: text(),
title: text(),
publishedAt: timestamp("published_at", { withTimezone: true, mode: 'string' }),
duration: integer(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`),
status: integer().default(0).notNull(),
coverUrl: text("cover_url"),
}, (table) => [
uniqueIndex("all_data_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
index("idx_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_all-data_bvid").using("btree", table.bvid.asc().nullsLast().op("text_ops")),
index("idx_all-data_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
index("idx_bili-meta_status").using("btree", table.status.asc().nullsLast().op("int4_ops")),
uniqueIndex("unq_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
]);
export const labellingResult = pgTable(
"labelling_result",
{
id: integer()
.default(sql`nextval('labeling_result_id_seq'::regclass)`)
.notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
label: smallint().notNull(),
modelVersion: text("model_version").notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
logits: smallint().array()
},
(table) => [
index("idx_labeling_label_model-version").using(
"btree",
table.label.asc().nullsLast().op("int2_ops"),
table.modelVersion.asc().nullsLast().op("int2_ops")
),
index("idx_labeling_model-version").using("btree", table.modelVersion.asc().nullsLast().op("text_ops")),
index("idx_labelling_aid-label").using(
"btree",
table.aid.asc().nullsLast().op("int2_ops"),
table.label.asc().nullsLast().op("int2_ops")
),
uniqueIndex("labeling_result_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
uniqueIndex("unq_labelling-result_aid_model-version").using(
"btree",
table.aid.asc().nullsLast().op("int8_ops"),
table.modelVersion.asc().nullsLast().op("int8_ops")
)
]
);
export const labellingResult = pgTable("labelling_result", {
id: integer().default(sql`nextval('labeling_result_id_seq'::regclass)`).notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
label: smallint().notNull(),
modelVersion: text("model_version").notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
logits: smallint().array(),
}, (table) => [
index("idx_labeling_label_model-version").using("btree", table.label.asc().nullsLast().op("int2_ops"), table.modelVersion.asc().nullsLast().op("int2_ops")),
index("idx_labeling_model-version").using("btree", table.modelVersion.asc().nullsLast().op("text_ops")),
index("idx_labelling_aid-label").using("btree", table.aid.asc().nullsLast().op("int2_ops"), table.label.asc().nullsLast().op("int2_ops")),
uniqueIndex("labeling_result_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
uniqueIndex("unq_labelling-result_aid_model-version").using("btree", table.aid.asc().nullsLast().op("int8_ops"), table.modelVersion.asc().nullsLast().op("int8_ops")),
]);
export const latestVideoSnapshot = pgTable(
"latest_video_snapshot",
{
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).primaryKey().notNull(),
time: timestamp({ withTimezone: true, mode: "string" }).notNull(),
views: integer().notNull(),
coins: integer().notNull(),
likes: integer().notNull(),
favorites: integer().notNull(),
replies: integer().notNull(),
danmakus: integer().notNull(),
shares: integer().notNull()
},
(table) => [
index("idx_latest-video-snapshot_time").using("btree", table.time.asc().nullsLast().op("timestamptz_ops")),
index("idx_latest-video-snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops"))
]
);
export const latestVideoSnapshot = pgTable("latest_video_snapshot", {
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).primaryKey().notNull(),
time: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
views: integer().notNull(),
coins: integer().notNull(),
likes: integer().notNull(),
favorites: integer().notNull(),
replies: integer().notNull(),
danmakus: integer().notNull(),
shares: integer().notNull(),
}, (table) => [
index("idx_latest-video-snapshot_time").using("btree", table.time.asc().nullsLast().op("timestamptz_ops")),
index("idx_latest-video-snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops")),
]);
export const videoSnapshot = pgTable(
"video_snapshot",
{
id: integer()
.default(sql`nextval('video_snapshot_id_seq'::regclass)`)
.notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
views: integer().notNull(),
coins: integer().notNull(),
likes: integer().notNull(),
favorites: integer().notNull(),
shares: integer().notNull(),
danmakus: integer().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
replies: integer().notNull()
},
(table) => [
index("idx_vid_snapshot_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_vid_snapshot_aid_created_at").using(
"btree",
table.aid.asc().nullsLast().op("timestamptz_ops"),
table.createdAt.asc().nullsLast().op("timestamptz_ops")
),
index("idx_vid_snapshot_time").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")),
index("idx_vid_snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops")),
uniqueIndex("video_snapshot_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops"))
]
);
export const videoSnapshot = pgTable("video_snapshot", {
id: integer().default(sql`nextval('video_snapshot_id_seq'::regclass)`).notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
views: integer().notNull(),
coins: integer().notNull(),
likes: integer().notNull(),
favorites: integer().notNull(),
shares: integer().notNull(),
danmakus: integer().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
replies: integer().notNull(),
}, (table) => [
index("idx_vid_snapshot_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_vid_snapshot_aid_created_at").using("btree", table.aid.asc().nullsLast().op("timestamptz_ops"), table.createdAt.asc().nullsLast().op("timestamptz_ops")),
index("idx_vid_snapshot_time").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")),
index("idx_vid_snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops")),
uniqueIndex("video_snapshot_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
]);
export const songs = pgTable(
"songs",
{
id: integer()
.default(sql`nextval('songs_id_seq'::regclass)`)
.notNull(),
name: text(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }),
publishedAt: timestamp("published_at", { withTimezone: true, mode: "string" }),
duration: integer(),
type: smallint(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
neteaseId: bigint("netease_id", { mode: "number" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
deleted: boolean().default(false).notNull(),
image: text(),
producer: text()
},
(table) => [
index("idx_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_hash_songs_aid").using("hash", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops")),
index("idx_published_at").using("btree", table.publishedAt.asc().nullsLast().op("timestamptz_ops")),
index("idx_type").using("btree", table.type.asc().nullsLast().op("int2_ops")),
uniqueIndex("songs_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
uniqueIndex("unq_songs_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
uniqueIndex("unq_songs_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops"))
]
);
export const songs = pgTable("songs", {
id: integer().default(sql`nextval('songs_id_seq'::regclass)`).notNull(),
name: text(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }),
publishedAt: timestamp("published_at", { withTimezone: true, mode: 'string' }),
duration: integer(),
type: smallint(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
neteaseId: bigint("netease_id", { mode: "number" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
deleted: boolean().default(false).notNull(),
image: text(),
producer: text(),
}, (table) => [
index("idx_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_hash_songs_aid").using("hash", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops")),
index("idx_published_at").using("btree", table.publishedAt.asc().nullsLast().op("timestamptz_ops")),
index("idx_type").using("btree", table.type.asc().nullsLast().op("int2_ops")),
uniqueIndex("songs_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
uniqueIndex("unq_songs_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
uniqueIndex("unq_songs_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops")),
]);
export const bilibiliUser = pgTable("bilibili_user", {
id: serial().primaryKey().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
uid: bigint({ mode: "number" }).notNull(),
username: text().notNull(),
desc: text().notNull(),
fans: integer().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
}, (table) => [
index("idx_bili-user_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
unique("unq_bili-user_uid").on(table.uid),
]);
export const bilibiliUser = pgTable(
"bilibili_user",
{
id: serial().primaryKey().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
uid: bigint({ mode: "number" }).notNull(),
username: text().notNull(),
desc: text().notNull(),
fans: integer().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull()
},
(table) => [
index("idx_bili-user_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
unique("unq_bili-user_uid").on(table.uid)
]
);
export const singer = pgTable("singer", {
id: serial().primaryKey().notNull(),
name: text().notNull(),
name: text().notNull()
});
export const relations = pgTable("relations", {
id: serial().primaryKey().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
sourceId: bigint("source_id", { mode: "number" }).notNull(),
sourceType: text("source_type").notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
targetId: bigint("target_id", { mode: "number" }).notNull(),
targetType: text("target_type").notNull(),
relation: text().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
}, (table) => [
index("idx_relations_source_id_source_type_relation").using("btree", table.sourceId.asc().nullsLast().op("int8_ops"), table.sourceType.asc().nullsLast().op("int8_ops"), table.relation.asc().nullsLast().op("text_ops")),
index("idx_relations_target_id_target_type_relation").using("btree", table.targetId.asc().nullsLast().op("text_ops"), table.targetType.asc().nullsLast().op("text_ops"), table.relation.asc().nullsLast().op("text_ops")),
unique("unq_relations").on(table.sourceId, table.sourceType, table.targetId, table.targetType, table.relation),
]);
export const relations = pgTable(
"relations",
{
id: serial().primaryKey().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
sourceId: bigint("source_id", { mode: "number" }).notNull(),
sourceType: text("source_type").notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
targetId: bigint("target_id", { mode: "number" }).notNull(),
targetType: text("target_type").notNull(),
relation: text().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull()
},
(table) => [
index("idx_relations_source_id_source_type_relation").using(
"btree",
table.sourceId.asc().nullsLast().op("int8_ops"),
table.sourceType.asc().nullsLast().op("int8_ops"),
table.relation.asc().nullsLast().op("text_ops")
),
index("idx_relations_target_id_target_type_relation").using(
"btree",
table.targetId.asc().nullsLast().op("text_ops"),
table.targetType.asc().nullsLast().op("text_ops"),
table.relation.asc().nullsLast().op("text_ops")
),
unique("unq_relations").on(table.sourceId, table.sourceType, table.targetId, table.targetType, table.relation)
]
);
export const globalKv = pgTable("global_kv", {
key: text().primaryKey().notNull(),
value: text().notNull(),
value: text().notNull()
});
export const snapshotSchedule = pgTable("snapshot_schedule", {
id: bigserial({ mode: "bigint" }).notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
type: text(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
startedAt: timestamp("started_at", { withTimezone: true, mode: 'string' }),
finishedAt: timestamp("finished_at", { withTimezone: true, mode: 'string' }),
status: text().default('pending').notNull(),
}, (table) => [
index("idx_snapshot_schedule_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_snapshot_schedule_started_at").using("btree", table.startedAt.asc().nullsLast().op("timestamptz_ops")),
index("idx_snapshot_schedule_status").using("btree", table.status.asc().nullsLast().op("text_ops")),
index("idx_snapshot_schedule_type").using("btree", table.type.asc().nullsLast().op("text_ops")),
uniqueIndex("snapshot_schedule_pkey").using("btree", table.id.asc().nullsLast().op("int8_ops")),
]);
export const snapshotSchedule = pgTable(
"snapshot_schedule",
{
id: bigserial({ mode: "bigint" }).notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
type: text(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
startedAt: timestamp("started_at", { withTimezone: true, mode: "string" }),
finishedAt: timestamp("finished_at", { withTimezone: true, mode: "string" }),
status: text().default("pending").notNull()
},
(table) => [
index("idx_snapshot_schedule_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_snapshot_schedule_started_at").using(
"btree",
table.startedAt.asc().nullsLast().op("timestamptz_ops")
),
index("idx_snapshot_schedule_status").using("btree", table.status.asc().nullsLast().op("text_ops")),
index("idx_snapshot_schedule_type").using("btree", table.type.asc().nullsLast().op("text_ops")),
uniqueIndex("snapshot_schedule_pkey").using("btree", table.id.asc().nullsLast().op("int8_ops"))
]
);
export const classifiedLabelsHuman = pgTable("classified_labels_human", {
id: serial().primaryKey().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
author: uuid().notNull(),
label: smallint().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
}, (table) => [
index("idx_classified-labels-human_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_classified-labels-human_author").using("btree", table.author.asc().nullsLast().op("uuid_ops")),
index("idx_classified-labels-human_created-at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")),
index("idx_classified-labels-human_label").using("btree", table.label.asc().nullsLast().op("int2_ops")),
]);
export const classifiedLabelsHuman = pgTable(
"classified_labels_human",
{
id: serial().primaryKey().notNull(),
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
aid: bigint({ mode: "number" }).notNull(),
author: uuid().notNull(),
label: smallint().notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
.default(sql`CURRENT_TIMESTAMP`)
.notNull()
},
(table) => [
index("idx_classified-labels-human_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
index("idx_classified-labels-human_author").using("btree", table.author.asc().nullsLast().op("uuid_ops")),
index("idx_classified-labels-human_created-at").using(
"btree",
table.createdAt.asc().nullsLast().op("timestamptz_ops")
),
index("idx_classified-labels-human_label").using("btree", table.label.asc().nullsLast().op("int2_ops"))
]
);

View File

@ -7,4 +7,4 @@ export type SensitiveUserFields = "password" | "unqId";
export type BilibiliMetadataType = InferSelectModel<typeof bilibiliMetadata>;
export type VideoSnapshotType = InferSelectModel<typeof videoSnapshot>;
export type LatestVideoSnapshotType = InferSelectModel<typeof latestVideoSnapshot>;
export type SongType = InferSelectModel<typeof songs>;
export type SongType = InferSelectModel<typeof songs>;

View File

@ -1,15 +1,15 @@
export function generateRandomId(length: number): string {
const characters = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
const characters = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
const charactersLength = characters.length;
const randomBytes = new Uint8Array(length);
crypto.getRandomValues(randomBytes);
let result = '';
let result = "";
for (let i = 0; i < length; i++) {
const randomIndex = randomBytes[i] % charactersLength;
result += characters.charAt(randomIndex);
}
return result;
}
}

View File

@ -48,7 +48,7 @@ const createTransport = (level: string, filename: string) => {
maxsize,
tailable,
maxFiles,
format: format.combine(timestampFormat, format.json({ replacer })),
format: format.combine(timestampFormat, format.json({ replacer }))
});
};
@ -65,13 +65,13 @@ const winstonLogger = winston.createLogger({
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss.SSS" }),
format.colorize(),
format.errors({ stack: true }),
customFormat,
),
customFormat
)
}),
createTransport("silly", sillyLogPath),
createTransport("warn", warnLogPath),
createTransport("error", errorLogPath),
],
createTransport("error", errorLogPath)
]
});
const logger = {
@ -96,7 +96,7 @@ const logger = {
} else {
winstonLogger.error(error, { service, codePath });
}
},
}
};
export default logger;

View File

@ -2,54 +2,51 @@ import { RateLimiter as Limiter } from "@koshnic/ratelimit";
import { redis } from "@core/db/redis.ts";
export interface RateLimiterConfig {
duration: number;
max: number;
duration: number;
max: number;
}
export class RateLimiterError extends Error {
public code: string;
constructor(message: string) {
super(message);
this.name = "RateLimiterError";
this.code = "RATE_LIMIT_EXCEEDED";
}
public code: string;
constructor(message: string) {
super(message);
this.name = "RateLimiterError";
this.code = "RATE_LIMIT_EXCEEDED";
}
}
export class MultipleRateLimiter {
private readonly name: string;
private readonly configs: RateLimiterConfig[] = [];
private readonly limiter: Limiter;
private readonly name: string;
private readonly configs: RateLimiterConfig[] = [];
private readonly limiter: Limiter;
/*
* @param name The name of the rate limiter
* @param configs The configuration of the rate limiter, containing:
* - duration: The duration of window in seconds
* - max: The maximum number of tokens allowed in the window
*/
constructor(
name: string,
configs: RateLimiterConfig[]
) {
this.configs = configs;
this.limiter = new Limiter(redis);
this.name = name;
}
/*
* @param name The name of the rate limiter
* @param configs The configuration of the rate limiter, containing:
* - duration: The duration of window in seconds
* - max: The maximum number of tokens allowed in the window
*/
constructor(name: string, configs: RateLimiterConfig[]) {
this.configs = configs;
this.limiter = new Limiter(redis);
this.name = name;
}
/*
* Trigger an event in the rate limiter
*/
async trigger(shouldThrow = true): Promise<void> {
for (let i = 0; i < this.configs.length; i++) {
const { duration, max } = this.configs[i];
const { allowed } = await this.limiter.allow(`cvsa:${this.name}_${i}`, {
burst: max,
ratePerPeriod: max,
period: duration,
cost: 1
});
if (!allowed && shouldThrow) {
throw new RateLimiterError("Rate limit exceeded")
}
}
}
}
/*
* Trigger an event in the rate limiter
*/
async trigger(shouldThrow = true): Promise<void> {
for (let i = 0; i < this.configs.length; i++) {
const { duration, max } = this.configs[i];
const { allowed } = await this.limiter.allow(`cvsa:${this.name}_${i}`, {
burst: max,
ratePerPeriod: max,
period: duration,
cost: 1
});
if (!allowed && shouldThrow) {
throw new RateLimiterError("Rate limit exceeded");
}
}
}
}

View File

@ -54,4 +54,4 @@ export class SlidingWindow {
const key = `cvsa:sliding_window:${eventName}`;
return this.redis.del(key);
}
}
}

View File

@ -7,7 +7,7 @@ import { spawn, SpawnOptions } from "child_process";
export function spawnPromise(
command: string,
args: string[] = [],
options?: SpawnOptions,
options?: SpawnOptions
): Promise<{ stdout: string; stderr: string }> {
return new Promise((resolve, reject) => {
const child = spawn(command, args, options);
@ -171,8 +171,7 @@ class NetworkDelegate {
for (const proxyName of shuffleArray(proxiesNames)) {
try {
return await this.proxyRequest<R>(url, proxyName, task, method);
}
catch (e) {
} catch (e) {
if (e instanceof RateLimiterError) {
continue;
}
@ -202,7 +201,7 @@ class NetworkDelegate {
proxyName: string,
task: string,
method: string = "GET",
force: boolean = false,
force: boolean = false
): Promise<R> {
const proxy = this.proxies[proxyName];
if (!proxy) {
@ -232,7 +231,7 @@ class NetworkDelegate {
const response = await fetch(url, {
method,
signal: controller.signal,
signal: controller.signal
});
clearTimeout(timeout);
@ -262,7 +261,7 @@ class NetworkDelegate {
"--connect-timeout",
"10",
"--profile",
`CVSA-${region}`,
`CVSA-${region}`
]);
const out = output.stdout;
const rawData = JSON.parse(out);
@ -270,7 +269,7 @@ class NetworkDelegate {
// noinspection ExceptionCaughtLocallyJS
throw new NetSchedulerError(
`Error proxying ${url} to ali-fc region ${region}, code: ${rawData.statusCode}.`,
"ALICLOUD_PROXY_ERR",
"ALICLOUD_PROXY_ERR"
);
} else {
return JSON.parse(rawData.body) as R;
@ -280,7 +279,7 @@ class NetworkDelegate {
throw new NetSchedulerError(
`Unhandled error: Cannot proxy ${url} to ali-fc-${region}.`,
"ALICLOUD_PROXY_ERR",
e,
e
);
}
}
@ -290,38 +289,38 @@ const networkDelegate = new NetworkDelegate();
const videoInfoRateLimiterConfig: RateLimiterConfig[] = [
{
duration: 0.3,
max: 1,
max: 1
},
{
duration: 3,
max: 5,
max: 5
},
{
duration: 30,
max: 30,
max: 30
},
{
duration: 2 * 60,
max: 50,
},
max: 50
}
];
const biliLimiterConfig: RateLimiterConfig[] = [
{
duration: 1,
max: 6,
max: 6
},
{
duration: 5,
max: 20,
max: 20
},
{
duration: 30,
max: 100,
max: 100
},
{
duration: 5 * 60,
max: 200,
},
max: 200
}
];
const bili_test = [...biliLimiterConfig];
@ -369,7 +368,7 @@ networkDelegate.addTask("getLatestVideos", "bilibili", "all");
networkDelegate.addTask(
"snapshotMilestoneVideo",
"bilibili",
regions.map((region) => `alicloud-${region}`),
regions.map((region) => `alicloud-${region}`)
);
networkDelegate.addTask("snapshotVideo", "bili_test", [
"alicloud-qingdao",
@ -377,7 +376,7 @@ networkDelegate.addTask("snapshotVideo", "bili_test", [
"alicloud-zhangjiakou",
"alicloud-chengdu",
"alicloud-shenzhen",
"alicloud-hohhot",
"alicloud-hohhot"
]);
networkDelegate.addTask("bulkSnapshot", "bili_strict", [
"alicloud-qingdao",
@ -385,7 +384,7 @@ networkDelegate.addTask("bulkSnapshot", "bili_strict", [
"alicloud-zhangjiakou",
"alicloud-chengdu",
"alicloud-shenzhen",
"alicloud-hohhot",
"alicloud-hohhot"
]);
networkDelegate.setTaskLimiter("getVideoInfo", videoInfoRateLimiterConfig);
networkDelegate.setTaskLimiter("getLatestVideos", null);

View File

@ -53,7 +53,8 @@ export const rootHandler = new Elysia().get(
},
detail: {
summary: "Root route",
description: "The root path. It returns a JSON object containing a random virtual singer, \
description:
"The root path. It returns a JSON object containing a random virtual singer, \
backend version, current server time and other miscellaneous information."
}
}

View File

@ -88,7 +88,8 @@ export const getSongInfoHandler = new Elysia({ prefix: "/song" }).get(
},
detail: {
summary: "Get information of a song",
description: "This endpoint retrieves detailed information about a song using its unique ID, \
description:
"This endpoint retrieves detailed information about a song using its unique ID, \
which can be provided in several formats. \
The endpoint accepts a song ID in either a numerical format as the internal ID in our database\
or as a bilibili video ID (either av or BV format). \

View File

@ -9,20 +9,20 @@ import svelte from "@astrojs/svelte";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
output: "server",
adapter: node({
mode: "standalone",
}),
integrations: [svelte()],
vite: {
server: {
fs: {
allow: [".", "../../"],
},
},
plugins: [tsconfigPaths(), tailwindcss()],
},
markdown: {
remarkRehype: { footnoteLabel: "脚注", footnoteBackLabel: "回到引用 1" },
}
});
output: "server",
adapter: node({
mode: "standalone"
}),
integrations: [svelte()],
vite: {
server: {
fs: {
allow: [".", "../../"]
}
},
plugins: [tsconfigPaths(), tailwindcss()]
},
markdown: {
remarkRehype: { footnoteLabel: "脚注", footnoteBackLabel: "回到引用 1" }
}
});

View File

@ -1,25 +1,25 @@
const N_1024 = BigInt(
"129023318876534346704360951712586568674758913224876821534686030409476129469193481910786173836188085930974906857867802234113909470848523288588793477904039083513378341278558405407018889387577114155572311708428733260891448259786041525189132461448841652472631435226032063278124857443496954605482776113964107326943",
"129023318876534346704360951712586568674758913224876821534686030409476129469193481910786173836188085930974906857867802234113909470848523288588793477904039083513378341278558405407018889387577114155572311708428733260891448259786041525189132461448841652472631435226032063278124857443496954605482776113964107326943"
);
const N_2048 = BigInt(
"23987552118069940970878653610463005981599204778388399885550631951871084945075866571231062435627294546200946516668493107358732376187241747090707087544153108117326163500579370560400058549184722138636116585329496684877258304519458316233517215780035360354808658620079068489084797380781488445517430961701007542207001544091884001098497324624368085682074645221148086075871342544591022944384890014176612259729018968864426602901247715051556212559854689574013699665035317257438297910516976812428036717668766321871780963854649899276251822244719887233041422346429752896925499321431273560130952088238625622570366815755926694833109",
"23987552118069940970878653610463005981599204778388399885550631951871084945075866571231062435627294546200946516668493107358732376187241747090707087544153108117326163500579370560400058549184722138636116585329496684877258304519458316233517215780035360354808658620079068489084797380781488445517430961701007542207001544091884001098497324624368085682074645221148086075871342544591022944384890014176612259729018968864426602901247715051556212559854689574013699665035317257438297910516976812428036717668766321871780963854649899276251822244719887233041422346429752896925499321431273560130952088238625622570366815755926694833109"
);
const N_1792 = BigInt(
"23987552118069940970878653610463005981599204778388399885550631951871084945075866571231062435627294546200946516668493107358732376187241747090707087544153108117326163500579370560400058549184722138636116585329496684877258304519458316233517215780035360354808658620079068489084797380781488445517430961701007542207001544091884001098497324624368085682074645221148086075871342544591022944384890014176612259729018968864426602901247715051556212559854689574013699665035317257438297910516976812428036717668766321871780963854649899276251822244719887233041422346429752896925499321431273560130952088238625622570366815755926694833109",
"23987552118069940970878653610463005981599204778388399885550631951871084945075866571231062435627294546200946516668493107358732376187241747090707087544153108117326163500579370560400058549184722138636116585329496684877258304519458316233517215780035360354808658620079068489084797380781488445517430961701007542207001544091884001098497324624368085682074645221148086075871342544591022944384890014176612259729018968864426602901247715051556212559854689574013699665035317257438297910516976812428036717668766321871780963854649899276251822244719887233041422346429752896925499321431273560130952088238625622570366815755926694833109"
);
const N_1536 = BigInt(
"1694330250214463438908848400950857073137355630337290254958754184668036770489801447652464038218330711288158361242955860326168191830448553710492926795708495297280933502917598985378231124113971732841791156356676046934277122699383776036675381503510992810963611269045078440132744168908318454891211962146563551929591147663448816841024591820348784855441153716551049843185172472891407933214238000452095646085222944171689449292644270516031799660928056315886939284985905227",
"1694330250214463438908848400950857073137355630337290254958754184668036770489801447652464038218330711288158361242955860326168191830448553710492926795708495297280933502917598985378231124113971732841791156356676046934277122699383776036675381503510992810963611269045078440132744168908318454891211962146563551929591147663448816841024591820348784855441153716551049843185172472891407933214238000452095646085222944171689449292644270516031799660928056315886939284985905227"
);
const N_3072 = BigInt(
"4432919939296042464443862503456460073874727648022810391370558006281079088795179408238989283371442564716849343712703672836423961818025813387453469700639513190304802553045342607888612037304066433501317127429264242784608682213025490491212489901736408833027611579294436675682774458141490718959615677971745638214649336218217578937534746160749039668886450447773018369168258067682196337978245372237157696236362344796867228581553446331915147012787367438751646936429739232247148712001806846526947508445039707404287951727838234648917450736371192435665040644040487427986702098273581288935278964444790007953559851323281510927332862225214878776790605026472021669614552481167977412450477230442015077669503312683966631454347169703030544483487968842349634064181183599641180349414682042575010303056241481622837185325228233789954078775053744988023738762706404546546146837242590884760044438874357295029411988267287001033032827035809135092270843",
"4432919939296042464443862503456460073874727648022810391370558006281079088795179408238989283371442564716849343712703672836423961818025813387453469700639513190304802553045342607888612037304066433501317127429264242784608682213025490491212489901736408833027611579294436675682774458141490718959615677971745638214649336218217578937534746160749039668886450447773018369168258067682196337978245372237157696236362344796867228581553446331915147012787367438751646936429739232247148712001806846526947508445039707404287951727838234648917450736371192435665040644040487427986702098273581288935278964444790007953559851323281510927332862225214878776790605026472021669614552481167977412450477230442015077669503312683966631454347169703030544483487968842349634064181183599641180349414682042575010303056241481622837185325228233789954078775053744988023738762706404546546146837242590884760044438874357295029411988267287001033032827035809135092270843"
);
const N_4096 = BigInt(
"703671044356805218391078271512201582198770553281951369783674142891088501340774249238173262580562112786670043634665390581120113644316651934154746357220932310140476300088580654571796404198410555061275065442553506658401183560336140989074165998202690496991174269748740565700402715364422506782445179963440819952745241176450402011121226863984008975377353558155910994380700267903933205531681076494639818328879475919332604951949178075254600102192323286738973253864238076198710173840170988339024438220034106150475640983877458155141500313471699516670799821379238743709125064098477109094533426340852518505385314780319279862586851512004686798362431227795743253799490998475141728082088984359237540124375439664236138519644100625154580910233437864328111620708697941949936338367445851449766581651338876219676721272448769082914348242483068204896479076062102236087066428603930888978596966798402915747531679758905013008059396214343112694563043918465373870648649652122703709658068801764236979191262744515840224548957285182453209028157886219424802426566456408109642062498413592155064289314088837031184200671561102160059065729282902863248815224399131391716503171191977463328439766546574118092303414702384104112719959325482439604572518549918705623086363111",
"703671044356805218391078271512201582198770553281951369783674142891088501340774249238173262580562112786670043634665390581120113644316651934154746357220932310140476300088580654571796404198410555061275065442553506658401183560336140989074165998202690496991174269748740565700402715364422506782445179963440819952745241176450402011121226863984008975377353558155910994380700267903933205531681076494639818328879475919332604951949178075254600102192323286738973253864238076198710173840170988339024438220034106150475640983877458155141500313471699516670799821379238743709125064098477109094533426340852518505385314780319279862586851512004686798362431227795743253799490998475141728082088984359237540124375439664236138519644100625154580910233437864328111620708697941949936338367445851449766581651338876219676721272448769082914348242483068204896479076062102236087066428603930888978596966798402915747531679758905013008059396214343112694563043918465373870648649652122703709658068801764236979191262744515840224548957285182453209028157886219424802426566456408109642062498413592155064289314088837031184200671561102160059065729282902863248815224399131391716503171191977463328439766546574118092303414702384104112719959325482439604572518549918705623086363111"
);
export const N_ARRAY = [N_1024, N_1536, N_1792, N_2048, N_3072, N_4096];

View File

@ -46,7 +46,7 @@
- 对于数据库中有原创性的内容(如贡献者编辑的描述性内容),如无例外,以 [CC BY 4.0 协议](https://creativecommons.org/licenses/by/4.0/) 提供。
- 对于引用、摘编或改编自萌娘百科、VCPedia 的内容,以与原始协议(CC BY-NC-SA 3.0
CN)兼容的协议 [CC BY-NC-SA 4.0 协议](https://creativecommons.org/licenses/by-nc-sa/4.0/) 提供,并注明原始协议 。
> 根据原始协议第四条第 2 项内容CC BY-NC-SA 4.0 协议为与原始协议具有相同授权要素的后续版本(“可适用的协议”)。
> 根据原始协议第四条第 2 项内容CC BY-NC-SA 4.0 协议为与原始协议具有相同授权要素的后续版本(“可适用的协议”)。
- 中 V 档案馆文档使用 [CC BY 4.0 协议](https://creativecommons.org/licenses/by/4.0/)。
### 软件代码

View File

@ -1,8 +1,8 @@
import { sql } from "@core/db/dbNew";
export async function aidExists(aid: number) {
const res = await sql`
const res = await sql`
SELECT 1 FROM bilibili_metadata WHERE aid = ${aid}
`;
return res.length > 0;
}
return res.length > 0;
}

View File

@ -1,15 +1,15 @@
import { sql } from "@core/db/dbNew";
export async function getAidFromBV(bv: string) {
const res = await sql`
const res = await sql`
SELECT aid FROM bilibili_metadata WHERE bvid = ${bv}
`
if (res.length <= 0) {
return null;
}
const row = res[0];
if (row && row.aid) {
return Number(row.aid);
}
return null;
`;
if (res.length <= 0) {
return null;
}
const row = res[0];
if (row && row.aid) {
return Number(row.aid);
}
return null;
}

View File

@ -1,15 +1,15 @@
import { sql } from "@core/db/dbNew";
export async function getVideoMetadata(aid: number) {
const res = await sql`
const res = await sql`
SELECT * FROM bilibili_metadata WHERE aid = ${aid}
`;
if (res.length <= 0) {
return null;
}
const row = res[0];
if (row) {
return row;
}
return {};
}
if (res.length <= 0) {
return null;
}
const row = res[0];
if (row) {
return row;
}
return {};
}

View File

@ -1,11 +1,11 @@
import { sql } from "@core/db/dbNew";
export async function getAllSnapshots(aid: number) {
const res = await sql`
const res = await sql`
SELECT * FROM video_snapshot WHERE aid = ${aid} ORDER BY created_at DESC
`;
if (res.length <= 0) {
return null;
}
return res;
if (res.length <= 0) {
return null;
}
return res;
}

View File

@ -1,4 +1,3 @@
// Define interfaces for input and output
interface VdfProgressCallback {
(progress: number): void;
@ -73,7 +72,12 @@ const workerContent = `addEventListener("message", async (event) => {
* @param onProgress - Optional callback function to receive progress updates (0-100).
* @returns A Promise that resolves with the VDF result and time taken.
*/
export function computeVdfInWorker(g: bigint, N: bigint, difficulty: bigint, onProgress?: VdfProgressCallback): Promise<VdfResult> {
export function computeVdfInWorker(
g: bigint,
N: bigint,
difficulty: bigint,
onProgress?: VdfProgressCallback
): Promise<VdfResult> {
return new Promise((resolve, reject) => {
// Create a Blob containing the worker script
const blob = new Blob([workerContent], { type: "text/javascript" });

View File

@ -1,450 +1,449 @@
@font-face {
font-family: InterVariable;
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable.woff2") format("woff2");
font-family: InterVariable;
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable.woff2") format("woff2");
}
@font-face {
font-family: InterVariable;
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable-Italic.woff2") format("woff2");
font-family: InterVariable;
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable-Italic.woff2") format("woff2");
}
/* static fonts */
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("Inter-Thin.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("Inter-Thin.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("Inter-ThinItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("Inter-ThinItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLight.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLight.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLightItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLightItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("Inter-Light.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("Inter-Light.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("Inter-LightItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("Inter-LightItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("Inter-Regular.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("Inter-Regular.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("Inter-Italic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("Inter-Italic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("Inter-Medium.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("Inter-Medium.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("Inter-MediumItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("Inter-MediumItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBold.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBoldItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("Inter-Bold.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("Inter-Bold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("Inter-BoldItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("Inter-BoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBold.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("Inter-Black.woff2") format("woff2");
font-family: "Inter";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("Inter-Black.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("Inter-BlackItalic.woff2") format("woff2");
font-family: "Inter";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("Inter-BlackItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("InterDisplay-Thin.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("InterDisplay-Thin.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("InterDisplay-ThinItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("InterDisplay-ThinItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("InterDisplay-ExtraLight.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("InterDisplay-ExtraLight.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("InterDisplay-ExtraLightItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("InterDisplay-ExtraLightItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("InterDisplay-Light.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("InterDisplay-Light.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("InterDisplay-LightItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("InterDisplay-LightItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("InterDisplay-Regular.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("InterDisplay-Regular.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("InterDisplay-Italic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("InterDisplay-Italic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("InterDisplay-Medium.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("InterDisplay-Medium.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("InterDisplay-MediumItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("InterDisplay-MediumItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("InterDisplay-SemiBold.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("InterDisplay-SemiBold.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("InterDisplay-SemiBoldItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("InterDisplay-SemiBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("InterDisplay-Bold.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("InterDisplay-Bold.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("InterDisplay-BoldItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("InterDisplay-BoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("InterDisplay-ExtraBold.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("InterDisplay-ExtraBold.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("InterDisplay-ExtraBoldItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("InterDisplay-ExtraBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("InterDisplay-Black.woff2") format("woff2");
font-family: "InterDisplay";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("InterDisplay-Black.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("InterDisplay-BlackItalic.woff2") format("woff2");
font-family: "InterDisplay";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("InterDisplay-BlackItalic.woff2") format("woff2");
}
@font-feature-values InterVariable {
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes &amp; commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes &amp; commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
}
@font-feature-values Inter {
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes &amp; commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes &amp; commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
}
@font-feature-values InterDisplay {
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes &amp; commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes &amp; commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
}

View File

@ -1,88 +1,87 @@
@font-face {
font-family: "MiSans VF";
font-style: normal;
font-weight: 150 700;
font-display: swap;
src: url("MiSans VF.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("MiSans-Thin.woff2") format("woff2");
font-family: "MiSans VF";
font-style: normal;
font-weight: 150 700;
font-display: swap;
src: url("MiSans VF.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("MiSans-ExtraLight.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("MiSans-Thin.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("MiSans-Light.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("MiSans-ExtraLight.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 360;
font-display: swap;
src: url("MiSans-Normal.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("MiSans-Light.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("MiSans-Regular.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 360;
font-display: swap;
src: url("MiSans-Normal.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("MiSans-Medium.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("MiSans-Regular.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("MiSans-Demibold.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("MiSans-Medium.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("MiSans-Semibold.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("MiSans-Demibold.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("MiSans-Bold.woff2") format("woff2");
font-family: "MiSans";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("MiSans-Semibold.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("MiSans-Heavy.woff2") format("woff2");
}
font-family: "MiSans";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("MiSans-Bold.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("MiSans-Heavy.woff2") format("woff2");
}

View File

@ -1,7 +1,6 @@
@import "tailwindcss";
.content {
h1 {
@apply text-3xl;
}

View File

@ -1,126 +1,125 @@
@import url('InterFont/Inter.css');
@import url('MiSans/MiSans.css');
@import url("InterFont/Inter.css");
@import url("MiSans/MiSans.css");
@import "tailwindcss";
@theme {
--color-surface-container-high: #f7e4e1;
--color-on-surface-variant: #534341;
--color-dark-on-surface-variant: #d8c2be;
--color-dark-surface-container-high: #322826;
--color-dark-surface-container: #271d1c;
--color-surface-container: #fceae7;
--color-on-surface: #231918;
--color-dark-on-surface: #f1dfdc;
--color-surface: #fff8f6;
--color-dark-surface: #1a1110;
--color-primary: #904b40;
--color-dark-primary: #ffb2b7;
--color-primary-container: #ffdad4;
--color-dark-primary-container: #73342a;
--color-on-primary: #ffffff;
--color-dark-on-primary: #561e16;
--color-dark-primary-fixed-dim: #ffb4a8;
--color-secondary-container: #ffdad4;
--color-dark-secondary-container: #5d3f3b;
--color-surface-container-high: #f7e4e1;
--color-on-surface-variant: #534341;
--color-dark-on-surface-variant: #d8c2be;
--color-dark-surface-container-high: #322826;
--color-dark-surface-container: #271d1c;
--color-surface-container: #fceae7;
--color-on-surface: #231918;
--color-dark-on-surface: #f1dfdc;
--color-surface: #fff8f6;
--color-dark-surface: #1a1110;
--color-primary: #904b40;
--color-dark-primary: #ffb2b7;
--color-primary-container: #ffdad4;
--color-dark-primary-container: #73342a;
--color-on-primary: #ffffff;
--color-dark-on-primary: #561e16;
--color-dark-primary-fixed-dim: #ffb4a8;
--color-secondary-container: #ffdad4;
--color-dark-secondary-container: #5d3f3b;
--color-surface-tint: rgb(144 75 64);
--color-dark-surface-tint: rgb(255 180 168);
--color-on-primary-container: rgb(115 52 42);
--color-dark-on-primary-container: rgb(255 218 212);
--color-secondary: rgb(119 86 81);
--color-dark-secondary: rgb(231 189 182);
--color-on-secondary: rgb(255 255 255);
--color-dark-on-secondary: rgb(68 41 37);
--color-on-secondary-container: rgb(93 63 59);
--color-dark-on-secondary-container: rgb(255 218 212);
--color-tertiary: rgb(112 92 46);
--color-dark-tertiary: rgb(222 196 140);
--color-on-tertiary: rgb(255 255 255);
--color-dark-on-tertiary: rgb(62 46 4);
--color-tertiary-container: rgb(251 223 166);
--color-dark-tertiary-container: rgb(86 68 25);
--color-on-tertiary-container: rgb(86 68 25);
--color-dark-on-tertiary-container: rgb(251 223 166);
--color-error: rgb(186 26 26);
--color-dark-error: rgb(255 180 171);
--color-on-error: rgb(255 255 255);
--color-dark-on-error: rgb(105 0 5);
--color-error-container: rgb(255 218 214);
--color-dark-error-container: rgb(147 0 10);
--color-on-error-container: rgb(147 0 10);
--color-dark-on-error-container: rgb(255 218 214);
--color-background: rgb(255 248 246);
--color-dark-background: rgb(26 17 16);
--color-on-background: rgb(35 25 24);
--color-dark-on-background: rgb(241 223 220);
--color-surface-variant: rgb(245 221 218);
--color-dark-surface-variant: rgb(83 67 65);
--color-outline: rgb(133 115 112);
--color-dark-outline: rgb(160 140 137);
--color-outline-variant: rgb(216 194 190);
--color-dark-outline-variant: rgb(83 67 65);
--color-shadow: rgb(0 0 0);
--color-dark-shadow: rgb(0 0 0);
--color-scrim: rgb(0 0 0);
--color-dark-scrim: rgb(0 0 0);
--color-inverse-surface: rgb(57 46 44);
--color-dark-inverse-surface: rgb(241 223 220);
--color-inverse-on-surface: rgb(255 237 234);
--color-dark-inverse-on-surface: rgb(57 46 44);
--color-inverse-primary: rgb(255 180 168);
--color-dark-inverse-primary: rgb(144 75 64);
--color-primary-fixed: rgb(255 218 212);
--color-dark-primary-fixed: rgb(255 218 212);
--color-on-primary-fixed: rgb(58 9 5);
--color-dark-on-primary-fixed: rgb(58 9 5);
--color-primary-fixed-dim: rgb(255 180 168);
--color-on-primary-fixed-variant: rgb(115 52 42);
--color-dark-on-primary-fixed-variant: rgb(115 52 42);
--color-secondary-fixed: rgb(255 218 212);
--color-dark-secondary-fixed: rgb(255 218 212);
--color-on-secondary-fixed: rgb(44 21 18);
--color-dark-on-secondary-fixed: rgb(44 21 18);
--color-secondary-fixed-dim: rgb(231 189 182);
--color-dark-secondary-fixed-dim: rgb(231 189 182);
--color-on-secondary-fixed-variant: rgb(93 63 59);
--color-dark-on-secondary-fixed-variant: rgb(93 63 59);
--color-tertiary-fixed: rgb(251 223 166);
--color-dark-tertiary-fixed: rgb(251 223 166);
--color-on-tertiary-fixed: rgb(37 26 0);
--color-dark-on-tertiary-fixed: rgb(37 26 0);
--color-tertiary-fixed-dim: rgb(222 196 140);
--color-dark-tertiary-fixed-dim: rgb(222 196 140);
--color-on-tertiary-fixed-variant: rgb(86 68 25);
--color-dark-on-tertiary-fixed-variant: rgb(86 68 25);
--color-surface-dim: rgb(232 214 211);
--color-dark-surface-dim: rgb(26 17 16);
--color-surface-bright: rgb(255 248 246);
--color-dark-surface-bright: rgb(66 55 53);
--color-surface-container-lowest: rgb(255 255 255);
--color-dark-surface-container-lowest: rgb(20 12 11);
--color-surface-container-low: rgb(255 240 238);
--color-dark-surface-container-low: rgb(35 25 24);
--color-surface-container-highest: rgb(241 223 220);
--color-dark-surface-container-highest: rgb(61 50 48);
--color-surface-tint: rgb(144 75 64);
--color-dark-surface-tint: rgb(255 180 168);
--color-on-primary-container: rgb(115 52 42);
--color-dark-on-primary-container: rgb(255 218 212);
--color-secondary: rgb(119 86 81);
--color-dark-secondary: rgb(231 189 182);
--color-on-secondary: rgb(255 255 255);
--color-dark-on-secondary: rgb(68 41 37);
--color-on-secondary-container: rgb(93 63 59);
--color-dark-on-secondary-container: rgb(255 218 212);
--color-tertiary: rgb(112 92 46);
--color-dark-tertiary: rgb(222 196 140);
--color-on-tertiary: rgb(255 255 255);
--color-dark-on-tertiary: rgb(62 46 4);
--color-tertiary-container: rgb(251 223 166);
--color-dark-tertiary-container: rgb(86 68 25);
--color-on-tertiary-container: rgb(86 68 25);
--color-dark-on-tertiary-container: rgb(251 223 166);
--color-error: rgb(186 26 26);
--color-dark-error: rgb(255 180 171);
--color-on-error: rgb(255 255 255);
--color-dark-on-error: rgb(105 0 5);
--color-error-container: rgb(255 218 214);
--color-dark-error-container: rgb(147 0 10);
--color-on-error-container: rgb(147 0 10);
--color-dark-on-error-container: rgb(255 218 214);
--color-background: rgb(255 248 246);
--color-dark-background: rgb(26 17 16);
--color-on-background: rgb(35 25 24);
--color-dark-on-background: rgb(241 223 220);
--color-surface-variant: rgb(245 221 218);
--color-dark-surface-variant: rgb(83 67 65);
--color-outline: rgb(133 115 112);
--color-dark-outline: rgb(160 140 137);
--color-outline-variant: rgb(216 194 190);
--color-dark-outline-variant: rgb(83 67 65);
--color-shadow: rgb(0 0 0);
--color-dark-shadow: rgb(0 0 0);
--color-scrim: rgb(0 0 0);
--color-dark-scrim: rgb(0 0 0);
--color-inverse-surface: rgb(57 46 44);
--color-dark-inverse-surface: rgb(241 223 220);
--color-inverse-on-surface: rgb(255 237 234);
--color-dark-inverse-on-surface: rgb(57 46 44);
--color-inverse-primary: rgb(255 180 168);
--color-dark-inverse-primary: rgb(144 75 64);
--color-primary-fixed: rgb(255 218 212);
--color-dark-primary-fixed: rgb(255 218 212);
--color-on-primary-fixed: rgb(58 9 5);
--color-dark-on-primary-fixed: rgb(58 9 5);
--color-primary-fixed-dim: rgb(255 180 168);
--color-on-primary-fixed-variant: rgb(115 52 42);
--color-dark-on-primary-fixed-variant: rgb(115 52 42);
--color-secondary-fixed: rgb(255 218 212);
--color-dark-secondary-fixed: rgb(255 218 212);
--color-on-secondary-fixed: rgb(44 21 18);
--color-dark-on-secondary-fixed: rgb(44 21 18);
--color-secondary-fixed-dim: rgb(231 189 182);
--color-dark-secondary-fixed-dim: rgb(231 189 182);
--color-on-secondary-fixed-variant: rgb(93 63 59);
--color-dark-on-secondary-fixed-variant: rgb(93 63 59);
--color-tertiary-fixed: rgb(251 223 166);
--color-dark-tertiary-fixed: rgb(251 223 166);
--color-on-tertiary-fixed: rgb(37 26 0);
--color-dark-on-tertiary-fixed: rgb(37 26 0);
--color-tertiary-fixed-dim: rgb(222 196 140);
--color-dark-tertiary-fixed-dim: rgb(222 196 140);
--color-on-tertiary-fixed-variant: rgb(86 68 25);
--color-dark-on-tertiary-fixed-variant: rgb(86 68 25);
--color-surface-dim: rgb(232 214 211);
--color-dark-surface-dim: rgb(26 17 16);
--color-surface-bright: rgb(255 248 246);
--color-dark-surface-bright: rgb(66 55 53);
--color-surface-container-lowest: rgb(255 255 255);
--color-dark-surface-container-lowest: rgb(20 12 11);
--color-surface-container-low: rgb(255 240 238);
--color-dark-surface-container-low: rgb(35 25 24);
--color-surface-container-highest: rgb(241 223 220);
--color-dark-surface-container-highest: rgb(61 50 48);
--font-zh: "InterVariable", "MiSans VF", sans-serif;
--font-zh: "InterVariable", "MiSans VF", sans-serif;
}
a {
@apply text-primary dark:text-dark-primary;
@apply text-primary dark:text-dark-primary;
}
:root {
font-family: "MiSans", "Inter", sans-serif;
font-weight: 400;
@apply bg-surface dark:bg-dark-surface text-on-surface dark:text-dark-on-surface
font-family: "MiSans", "Inter", sans-serif;
font-weight: 400;
@apply bg-surface dark:bg-dark-surface text-on-surface dark:text-dark-on-surface;
}
@supports (font-variation-settings: normal) {
:root {
font-family: "InterVariable", "MiSans VF", sans-serif;
font-optical-sizing: auto;
font-weight: 330;
}
:root {
font-family: "InterVariable", "MiSans VF", sans-serif;
font-optical-sizing: auto;
font-weight: 330;
}
}

View File

@ -2,7 +2,7 @@
module.exports = {
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
theme: {
extend: {},
extend: {}
},
plugins: [],
plugins: []
};

View File

@ -10,7 +10,7 @@
"@lib/*": ["src/lib/*"],
"@assets/*": ["src/assets/*"],
"@styles": ["src/styles/*"],
"@core/*": ["../core/*"],
"@core/*": ["../core/*"]
},
"verbatimModuleSyntax": true
}

View File

@ -65,9 +65,7 @@ const OutlineTextField: React.FC<InputProps> = ({
<div
className={`flex-grow rounded-r-sm border-outline dark:border-dark-outline
${focus ?
"border-primary dark:border-dark-primary border-r-2 border-y-2" :
"border-r-[1px] border-y-[1px] "}
${focus ? "border-primary dark:border-dark-primary border-r-2 border-y-2" : "border-r-[1px] border-y-[1px] "}
`}
></div>
</div>

View File

@ -46,7 +46,7 @@
- 对于数据库中有原创性的内容(如贡献者编辑的描述性内容),如无例外,以 [CC BY 4.0 协议](https://creativecommons.org/licenses/by/4.0/) 提供。
- 对于引用、摘编或改编自萌娘百科、VCPedia 的内容,以与原始协议(CC BY-NC-SA 3.0
CN)兼容的协议 [CC BY-NC-SA 4.0 协议](https://creativecommons.org/licenses/by-nc-sa/4.0/) 提供,并注明原始协议 。
> 根据原始协议第四条第 2 项内容CC BY-NC-SA 4.0 协议为与原始协议具有相同授权要素的后续版本(“可适用的协议”)。
> 根据原始协议第四条第 2 项内容CC BY-NC-SA 4.0 协议为与原始协议具有相同授权要素的后续版本(“可适用的协议”)。
- 中 V 档案馆文档使用 [CC BY 4.0 协议](https://creativecommons.org/licenses/by/4.0/)。
### 软件代码

View File

@ -8,7 +8,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap" rel="stylesheet" />
</head>
<body class="bg-gray-50 dark:bg-black dark:text-gray-50 ">
<body class="bg-gray-50 dark:bg-black dark:text-gray-50">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

View File

@ -22,8 +22,8 @@ export const Switch: React.FC<SwitchProps> = ({ checked, onChange, disabled = fa
disabled={disabled}
className={`relative flex items-center justify-center w-12 h-6 rounded-full transition-all duration-300
focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 ${
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer hover:scale-105"
} ${checked ? "bg-green-500" : "bg-zinc-300 dark:bg-zinc-600"}`}
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer hover:scale-105"
} ${checked ? "bg-green-500" : "bg-zinc-300 dark:bg-zinc-600"}`}
aria-checked={checked}
aria-disabled={disabled}
>
@ -49,4 +49,4 @@ export const Switch: React.FC<SwitchProps> = ({ checked, onChange, disabled = fa
)}
</div>
);
};
};

View File

@ -2,7 +2,7 @@ import { SVGProps } from "react";
export const Checkmark: React.FC<SVGProps<SVGSVGElement>> = (props) => {
return (
<svg viewBox="10 5 90 85" xmlns="http://www.w3.org/2000/svg" {...props}>
<svg viewBox="10 5 90 85" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
fill="none"
stroke="currentColor"
@ -27,4 +27,4 @@ export const Checkmark: React.FC<SVGProps<SVGSVGElement>> = (props) => {
</style>
</svg>
);
};
};

View File

@ -6,80 +6,82 @@ import { Checkmark } from "./Check";
import { getAdjustedColor } from "../utils";
interface ColorBlockProps {
baseColor: Oklch;
text: string;
l?: number;
c?: number;
h?: number;
baseColor: Oklch;
text: string;
l?: number;
c?: number;
h?: number;
}
const copy = async (text: string) => {
await navigator.clipboard.writeText(text);
};
export const ColorBlock = ({ baseColor, text, l, c, h }: ColorBlockProps) => {
const [hover, setHover] = useState(false);
const [check, setCheck] = useState(false);
const color = getAdjustedColor(baseColor, l, c, h);
const [hover, setHover] = useState(false);
const [check, setCheck] = useState(false);
const color = getAdjustedColor(baseColor, l, c, h);
const Icon = () => {
if (!check) {
return (
<AnimatePresence>
<motion.div exit={{ opacity: 0 }} initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
<Copy size={14} strokeWidth={2.5} />
</motion.div>
</AnimatePresence>
);
} else {
return (
<AnimatePresence>
<motion.div exit={{ opacity: 0 }} initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
<Checkmark width={16} height={16} strokeWidth={14} />
</motion.div>
</AnimatePresence>
);
}
};
const Icon = () => {
if (!check) {
return (
<AnimatePresence>
<motion.div exit={{ opacity: 0 }} initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
<Copy size={14} strokeWidth={2.5} />
</motion.div>
</AnimatePresence>
);
} else {
return (
<AnimatePresence>
<motion.div exit={{ opacity: 0 }} initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
<Checkmark width={16} height={16} strokeWidth={14} />
</motion.div>
</AnimatePresence>
);
}
};
return (
<div className="w-26 md:w-30 h-36 flex flex-col items-center">
<div className="w-full h-20 relative rounded-lg duration-50" style={{ backgroundColor: formatHex(color) }} />
<span className="mt-2 text-sm">{text}</span>
<div
className="flex items-center justify-center text-sm font-medium px-2 py-0.5 cursor-pointer
return (
<div className="w-26 md:w-30 h-36 flex flex-col items-center">
<div
className="w-full h-20 relative rounded-lg duration-50"
style={{ backgroundColor: formatHex(color) }}
/>
<span className="mt-2 text-sm">{text}</span>
<div
className="flex items-center justify-center text-sm font-medium px-2 py-0.5 cursor-pointer
mt-1 hover:bg-gray-200 dark:hover:bg-zinc-700 rounded-md"
onClick={() => {
copy(formatHex(color));
setCheck(true);
setTimeout(() => {
setCheck(false);
}, 2500);
}}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => {
setHover(false);
setCheck(false);
}}
>
<AnimatePresence>
{hover && (
<motion.div
exit={{ opacity: 0, width: 0 }}
initial={{ opacity: 0, width: 0 }}
animate={{ opacity: 1, width: 22 }}
transition={{
opacity: { duration: 0.2, ease: "backOut" },
width: { type: "spring", bounce: 0.2, duration: 0.5 }
}}
>
<Icon />
</motion.div>
)}
</AnimatePresence>
{formatHex(color)}
</div>
</div>
);
};
onClick={() => {
copy(formatHex(color));
setCheck(true);
setTimeout(() => {
setCheck(false);
}, 2500);
}}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => {
setHover(false);
setCheck(false);
}}
>
<AnimatePresence>
{hover && (
<motion.div
exit={{ opacity: 0, width: 0 }}
initial={{ opacity: 0, width: 0 }}
animate={{ opacity: 1, width: 22 }}
transition={{
opacity: { duration: 0.2, ease: "backOut" },
width: { type: "spring", bounce: 0.2, duration: 0.5 }
}}
>
<Icon />
</motion.div>
)}
</AnimatePresence>
{formatHex(color)}
</div>
</div>
);
};

View File

@ -8,8 +8,10 @@ export function ColorPalette({ baseColor }: { baseColor: Oklch }) {
const tokens = buildColorTokens(baseColor)[theme];
return (
<div className="mx-6 grid gap-1 md:gap-4 [grid-template-columns:repeat(auto-fill,104px)]
md:[grid-template-columns:repeat(auto-fill,120px)] justify-between">
<div
className="mx-6 grid gap-1 md:gap-4 [grid-template-columns:repeat(auto-fill,104px)]
md:[grid-template-columns:repeat(auto-fill,120px)] justify-between"
>
{Object.entries(tokens).map(([name, color]) => (
<ColorBlock key={name} baseColor={color} text={name} />
))}

View File

@ -26,7 +26,7 @@ export const Handle = ({
const x = clientX - sliderRect.left;
const percentage = Math.max(0, Math.min(1, x / sliderRect.width));
return (percentage * maxValue);
return percentage * maxValue;
};
const handleMouseDown = () => {
@ -50,13 +50,13 @@ export const Handle = ({
const handleTouchStart = (e: React.TouchEvent) => {
isTouching.current = true;
const touch = e.touches[0];
if (touch) {
const value = getValueFromPosition(touch.clientX);
onChange(value);
}
onTouchStart?.(e);
};
@ -94,4 +94,4 @@ export const Handle = ({
onTouchCancel={handleTouchEnd}
/>
);
};
};

View File

@ -23,14 +23,14 @@ export const Slider = ({ useP3, channel, color, onChange, i18nProvider }: Slider
const canvasRef = useRef<null | HTMLCanvasElement>(null);
useOklchCanvas({ channel: channel, max: maxValue[channel], canvasRef: canvasRef, color, useP3 });
const getSliderPosition = (value: number, max: number) => {
return (value / max) * 100;
};
const getValueFromPosition = (clientX: number) => {
if (!containerRef.current) return 0;
const rect = containerRef.current.getBoundingClientRect();
const x = clientX - rect.left;
const percentage = Math.max(0, Math.min(1, x / rect.width));
@ -122,7 +122,7 @@ export const Slider = ({ useP3, channel, color, onChange, i18nProvider }: Slider
</div>
</div>
<div
<div
ref={containerRef}
className="relative h-10"
onTouchMove={handleTouchMove}
@ -152,4 +152,4 @@ export const Slider = ({ useP3, channel, color, onChange, i18nProvider }: Slider
</div>
</div>
);
};
};

View File

@ -2,10 +2,10 @@ import { useEffect, type RefObject } from "react";
import { oklch, formatHex, inGamut, type Oklch } from "culori";
interface UseOklchCanvasOptions {
useP3: boolean
useP3: boolean;
channel: "l" | "c" | "h";
max: number;
canvasRef: RefObject<HTMLCanvasElement | null>
canvasRef: RefObject<HTMLCanvasElement | null>;
color: Oklch;
}

View File

@ -57,11 +57,10 @@
}
}
@font-face {
font-family: "Martian Mono";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/MartianMono.woff2") format("woff2");
}
}

View File

@ -3,14 +3,13 @@ import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import { ThemeProvider } from "./ThemeContext.tsx";
import { SpeedInsights } from "@vercel/speed-insights/react"
import { SpeedInsights } from "@vercel/speed-insights/react";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ThemeProvider>
<App />
<SpeedInsights/>
<SpeedInsights />
</ThemeProvider>
</StrictMode>
);

View File

@ -16,11 +16,10 @@ export const i18nProvider = (key: i18nKeys) => {
}
};
export const getAdjustedColor = (color: Oklch, l?: number, c?: number, h?: number) => {
const newColor = { ...color };
if (l) newColor.l = l;
if (c) newColor.c = c;
if (h) newColor.h = h;
return newColor;
};
const newColor = { ...color };
if (l) newColor.l = l;
if (c) newColor.c = c;
if (h) newColor.h = h;
return newColor;
};

View File

@ -2,8 +2,6 @@ import presetWind4 from "@unocss/preset-wind4";
import { defineConfig } from "unocss";
export default defineConfig({
presets: [presetWind4({ dark: "media"})],
rules: [
['font-mono', { 'font-family': '"Martian Mono", monospace' }],
]
presets: [presetWind4({ dark: "media" })],
rules: [["font-mono", { "font-family": '"Martian Mono", monospace' }]]
});

View File

@ -7,5 +7,5 @@ export default defineConfig({
dbCredentials: {
url: process.env.DATABASE_URL_MAIN!
},
tablesFilter: ["*"],
tablesFilter: ["*"]
});

View File

@ -1,5 +1,5 @@
@import url("https://assets.projectcvsa.com/hm-sans/index.css");
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
@tailwind base;
@tailwind components;

View File

@ -4,7 +4,7 @@ import { TabSwitcher } from "~/components/song/TabSwitcher";
import { Staff } from "~/components/song/Staff";
import { SongType } from "~db/outerSchema";
export const Content: Component<{data: SongType | null}> = (props) => {
export const Content: Component<{ data: SongType | null }> = (props) => {
return (
<>
<Card variant="outlined" class="w-full max-lg:rounded-none max-lg:border-none">
@ -145,4 +145,4 @@ export const Content: Component<{data: SongType | null}> = (props) => {
</article>
</>
);
};
};

View File

@ -48,4 +48,4 @@ export const LeftSideBar: Component = () => {
</div>
</>
);
};
};

View File

@ -14,4 +14,4 @@ export const RightSideBar: Component = () => {
<TabSwitcher />
</>
);
};
};

View File

@ -34,4 +34,4 @@ export const Staff: Component<{ name: string; role: string; num: number }> = (pr
</IconButton>
</A>
);
};
};

View File

@ -15,9 +15,7 @@ export default createHandler(() => (
{assets}
</head>
<body>
<div id="app">
{children}
</div>
<div id="app">{children}</div>
<div id="modal"></div>
{scripts}
</body>

View File

@ -12,24 +12,24 @@ const getVideoAID = async (id: string) => {
if (id.startsWith("av")) {
return parseInt(id.slice(2));
} else if (id.startsWith("BV")) {
const data = await dbMain
.select()
.from(bilibiliMetadata)
.where(eq(bilibiliMetadata.bvid, id));
const data = await dbMain.select().from(bilibiliMetadata).where(eq(bilibiliMetadata.bvid, id));
return data[0].aid;
}
else {
} else {
return null;
}
};
const findSongIDFromAID = async (aid: number) => {
"use server";
const data = await dbMain.select({
id: songs.id,
}).from(songs).where(eq(songs.aid, aid)).limit(1);
const data = await dbMain
.select({
id: songs.id
})
.from(songs)
.where(eq(songs.aid, aid))
.limit(1);
return data[0].id;
}
};
const getSongInfo = query(async (songID: number) => {
"use server";
@ -42,19 +42,17 @@ const getSongInfoFromID = query(async (id: string) => {
const aid = await getVideoAID(id);
if (!aid && parseInt(id)) {
return getSongInfo(parseInt(id));
}
else if (!aid) {
} else if (!aid) {
return null;
}
const songID = await findSongIDFromAID(aid);
return getSongInfo(songID);
}, "songsRaw")
}, "songsRaw");
export const route = {
preload: ({ params }) => getSongInfoFromID(params.id)
} satisfies RouteDefinition;
export default function Info() {
const params = useParams();
const info = createAsync(() => getSongInfoFromID(params.id));
@ -69,7 +67,7 @@ export default function Info() {
<LeftSideBar />
</nav>
<main class="mb-24">
<Content data={info() || null}/>
<Content data={info() || null} />
</main>
<div class="top-32 hidden lg:flex self-start sticky flex-col pb-12 px-6">
<RightSideBar />

View File

@ -14,7 +14,7 @@
"isolatedModules": true,
"paths": {
"~/*": ["./src/*"],
"~db/*": ["../core/drizzle/*"],
"~db/*": ["../core/drizzle/*"]
}
}
}