Merge branch 'dev'
This commit is contained in:
commit
d93d684727
|
|
@ -20,6 +20,6 @@ module.exports = {
|
|||
"react-hooks/exhaustive-deps":
|
||||
process.env.NODE_ENV === "production" ? "off" : "warn",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"react/no-unknown-property": ['error', { ignore: ['tw'] }]
|
||||
"react/no-unknown-property": ["error", { ignore: ["tw"] }],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@ module.exports = {
|
|||
semi: false,
|
||||
trailingComma: "all",
|
||||
endOfLine: "lf",
|
||||
plugins: [
|
||||
require.resolve("prettier-package-json"),
|
||||
require.resolve("@trivago/prettier-plugin-sort-imports"),
|
||||
],
|
||||
plugins: ["prettier-package-json", "@ianvs/prettier-plugin-sort-imports"],
|
||||
|
||||
importOrderParserPlugins: [
|
||||
"classProperties",
|
||||
|
|
@ -16,11 +13,13 @@ module.exports = {
|
|||
],
|
||||
importOrder: [
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"",
|
||||
"^@(.*)/(.*)$",
|
||||
"",
|
||||
"^~/(.*)$",
|
||||
"",
|
||||
"^@/(.*)$",
|
||||
"",
|
||||
"^[./]",
|
||||
],
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
"name": "xLog contributor",
|
||||
"link": "https://github.com/Crossbell-Box/xLog/graphs/contributors",
|
||||
"list": [
|
||||
10, 30, 12, 45, 19, 4583, 4381, 33446, 33492, 52055,
|
||||
153, 50143, 51470, 31132, 45089, 50351, 52343, 5825, 51406, 53864, 43086,
|
||||
47393, 54315, 50131, 54939, 55096, 55081, 45828, 4565, 50088, 55399,
|
||||
52323, 55960, 51512, 56881, 51657, 40943, 54435
|
||||
10, 30, 12, 45, 19, 4583, 4381, 33446, 33492, 52055, 153, 50143, 51470,
|
||||
31132, 45089, 50351, 52343, 5825, 51406, 53864, 43086, 47393, 54315,
|
||||
50131, 54939, 55096, 55081, 45828, 4565, 50088, 55399, 52323, 55960,
|
||||
51512, 56881, 51657, 40943, 54435
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ updates:
|
|||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 0
|
||||
open-pull-requests-limit: 0
|
||||
|
|
|
|||
81
package.json
81
package.json
|
|
@ -31,14 +31,14 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@birdgg/react-github": "0.2.3",
|
||||
"@codemirror/autocomplete": "^6.9.1",
|
||||
"@codemirror/commands": "6.2.5",
|
||||
"@codemirror/lang-markdown": "6.2.1",
|
||||
"@codemirror/autocomplete": "^6.9.2",
|
||||
"@codemirror/commands": "6.3.0",
|
||||
"@codemirror/lang-markdown": "6.2.2",
|
||||
"@codemirror/language": "6.9.1",
|
||||
"@codemirror/language-data": "6.3.1",
|
||||
"@codemirror/state": "6.2.1",
|
||||
"@codemirror/theme-one-dark": "6.1.2",
|
||||
"@codemirror/view": "6.20.2",
|
||||
"@codemirror/view": "6.21.3",
|
||||
"@crossbell/connect-kit": "1.5.14",
|
||||
"@crossbell/contract": "1.5.13",
|
||||
"@crossbell/indexer": "1.5.13",
|
||||
|
|
@ -50,25 +50,26 @@
|
|||
"@dqbd/tiktoken": "1.0.7",
|
||||
"@emoji-mart/data": "1.1.2",
|
||||
"@emoji-mart/react": "1.1.1",
|
||||
"@floating-ui/react": "0.25.4",
|
||||
"@floating-ui/react": "0.26.0",
|
||||
"@headlessui/react": "1.7.17",
|
||||
"@heroicons/react": "2.0.18",
|
||||
"@iconify-json/mingcute": "1.1.10",
|
||||
"@lezer/highlight": "1.1.6",
|
||||
"@mantine/core": "7.0.2",
|
||||
"@mantine/dates": "7.0.2",
|
||||
"@mantine/core": "7.1.2",
|
||||
"@mantine/dates": "7.1.2",
|
||||
"@microflash/remark-callout-directives": "1.6.0",
|
||||
"@monaco-editor/react": "4.5.2",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@prisma/client": "5.3.1",
|
||||
"@prisma/client": "5.4.1",
|
||||
"@supercharge/request-ip": "1.2.0",
|
||||
"@tanstack/react-query": "4.35.3",
|
||||
"@tanstack/react-query-devtools": "4.35.3",
|
||||
"@uiw/codemirror-extensions-events": "4.21.18",
|
||||
"@tanstack/react-query": "4.35.7",
|
||||
"@tanstack/react-query-devtools": "4.35.7",
|
||||
"@uiw/codemirror-extensions-events": "4.21.19",
|
||||
"@urql/core": "4.1.3",
|
||||
"ahooks": "3.7.8",
|
||||
"aplayer": "^1.10.1",
|
||||
"async-lock": "1.4.0",
|
||||
"canvas-confetti": "1.6.0",
|
||||
"canvas-confetti": "1.9.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"chroma-js": "2.4.2",
|
||||
"clsx": "2.0.0",
|
||||
|
|
@ -82,27 +83,27 @@
|
|||
"hast-util-to-html": "9.0.0",
|
||||
"i18next": "23.5.1",
|
||||
"i18next-resources-to-backend": "1.1.4",
|
||||
"immer": "10.0.2",
|
||||
"immer": "10.0.3",
|
||||
"ioredis": "5.3.2",
|
||||
"isomorphic-dompurify": "1.8.0",
|
||||
"isomorphic-dompurify": "1.9.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsdom": "22.1.0",
|
||||
"jsonfeed-to-rss": "3.0.7",
|
||||
"katex": "0.16.8",
|
||||
"langchain": "0.0.154",
|
||||
"katex": "0.16.9",
|
||||
"langchain": "0.0.161",
|
||||
"lottie-react": "2.4.0",
|
||||
"mdast-util-to-hast": "13.0.2",
|
||||
"mdast-util-toc": "7.0.0",
|
||||
"medium-zoom": "1.0.8",
|
||||
"mermaid": "10.4.0",
|
||||
"mermaid": "10.5.0",
|
||||
"nanoid": "5.0.1",
|
||||
"next": "13.5.3",
|
||||
"next": "13.5.4",
|
||||
"next-themes": "0.2.1",
|
||||
"node-id3": "0.2.6",
|
||||
"open-graph-scraper": "^6.2.2",
|
||||
"open-graph-scraper": "^6.3.0",
|
||||
"pangu": "4.0.7",
|
||||
"path-to-regexp": "6.2.1",
|
||||
"pinyin-pro": "3.16.6",
|
||||
"pinyin-pro": "3.16.7",
|
||||
"plaiceholder": "3.0.0",
|
||||
"prismjs": "1.29.0",
|
||||
"qier-progress": "1.0.4",
|
||||
|
|
@ -110,10 +111,10 @@
|
|||
"rc-dplayer": "1.0.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "7.46.2",
|
||||
"react-hook-form": "7.47.0",
|
||||
"react-hot-toast": "2.4.1",
|
||||
"react-i18next": "13.2.2",
|
||||
"react-parallax-tilt": "1.7.168",
|
||||
"react-parallax-tilt": "1.7.169",
|
||||
"react-popper": "2.3.0",
|
||||
"react-sortablejs": "6.1.4",
|
||||
"react-string-replace": "1.1.1",
|
||||
|
|
@ -144,7 +145,7 @@
|
|||
"serialize-javascript": "6.0.1",
|
||||
"sharp": "0.32.6",
|
||||
"sortablejs": "1.15.0",
|
||||
"swiper": "^10.3.0",
|
||||
"swiper": "^10.3.1",
|
||||
"tailwind-merge": "1.14.0",
|
||||
"tailwind-scrollbar-hide": "1.1.7",
|
||||
"tailwindcss-animate": "1.0.7",
|
||||
|
|
@ -152,14 +153,14 @@
|
|||
"unist-builder": "4.0.0",
|
||||
"unist-util-visit": "5.0.0",
|
||||
"uuid": "9.0.1",
|
||||
"viem": "1.12.2",
|
||||
"wagmi": "1.4.2",
|
||||
"zustand": "4.4.1"
|
||||
"viem": "1.15.4",
|
||||
"wagmi": "1.4.3",
|
||||
"zustand": "4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ianvs/prettier-plugin-sort-imports": "4.1.0",
|
||||
"@iconify/tailwind": "0.1.3",
|
||||
"@next/bundle-analyzer": "13.5.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "4.2.0",
|
||||
"@next/bundle-analyzer": "13.5.4",
|
||||
"@types/async-lock": "1.4.0",
|
||||
"@types/canvas-confetti": "1.6.1",
|
||||
"@types/chroma-js": "2.4.1",
|
||||
|
|
@ -167,39 +168,39 @@
|
|||
"@types/hast": "3.0.1",
|
||||
"@types/js-yaml": "4.0.6",
|
||||
"@types/katex": "0.16.3",
|
||||
"@types/mdast": "4.0.0",
|
||||
"@types/mjml": "4.7.1",
|
||||
"@types/node": "20.7.0",
|
||||
"@types/mdast": "4.0.1",
|
||||
"@types/mjml": "4.7.2",
|
||||
"@types/node": "20.8.2",
|
||||
"@types/pangu": "4.0.0",
|
||||
"@types/prismjs": "1.26.1",
|
||||
"@types/react": "18.2.23",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"@types/react": "18.2.25",
|
||||
"@types/react-dom": "18.2.10",
|
||||
"@types/react-infinite-scroller": "1.2.3",
|
||||
"@types/remove-markdown": "0.3.1",
|
||||
"@types/remove-markdown": "0.3.2",
|
||||
"@types/serialize-javascript": "5.0.2",
|
||||
"@types/sortablejs": "1.15.3",
|
||||
"@types/ua-parser-js": "0.7.37",
|
||||
"@types/uuid": "9.0.4",
|
||||
"autoprefixer": "10.4.16",
|
||||
"babel-loader": "9.1.3",
|
||||
"cspell": "7.3.6",
|
||||
"esbuild": "0.19.3",
|
||||
"cspell": "7.3.7",
|
||||
"esbuild": "0.19.4",
|
||||
"esbuild-register": "3.5.0",
|
||||
"eslint": "8.50.0",
|
||||
"eslint-config-next": "13.5.3",
|
||||
"eslint-config-next": "13.5.4",
|
||||
"eslint-plugin-import": "2.28.1",
|
||||
"eslint-plugin-react": "7.33.2",
|
||||
"eslint-plugin-unused-imports": "3.0.0",
|
||||
"husky": "8.0.3",
|
||||
"lint-staged": "14.0.1",
|
||||
"postcss": "^8.4.30",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-import": "15.1.0",
|
||||
"postcss-preset-mantine": "^1.7.0",
|
||||
"postcss-preset-mantine": "^1.8.0",
|
||||
"postcss-prune-var": "1.1.1",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "3.0.3",
|
||||
"prettier-package-json": "2.8.0",
|
||||
"prisma": "5.3.1",
|
||||
"prisma": "5.4.1",
|
||||
"tailwindcss": "3.3.3",
|
||||
"tailwindcss-variable-colors": "0.0.1",
|
||||
"typescript": "5.2.2"
|
||||
|
|
|
|||
1651
pnpm-lock.yaml
1651
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import ShortPreviewList from "~/components/site/ShortPreviewList"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { SearchInput } from "~/components/common/SearchInput"
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import ParticipateButton from "~/components/home/ParticipateButton"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import ParticipateButton from "~/components/home/ParticipateButton"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { Button } from "~/components/ui/Button"
|
|||
import { Image } from "~/components/ui/Image"
|
||||
import { CSB_SCAN, GITHUB_LINK } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { Trans, getTranslation } from "~/lib/i18n"
|
||||
import { getTranslation, Trans } from "~/lib/i18n"
|
||||
|
||||
async function Home() {
|
||||
const { t, i18n } = await getTranslation("index")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { notFound } from "next/navigation"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import PageContent from "~/components/common/PageContent"
|
||||
import PostCover from "~/components/home/PostCover"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// This API is used to bypass the restriction that the Cloudflare Image Resizing service's onerror=redirect can only be used in current domain
|
||||
import { redirect } from "next/navigation"
|
||||
import { type NextRequest, NextResponse } from "next/server"
|
||||
import { NextResponse, type NextRequest } from "next/server"
|
||||
|
||||
import { getQuery } from "~/lib/server-helper"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { NextResponse } from "next/server"
|
||||
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { checkDomainServer } from "~/models/site.model"
|
||||
|
||||
// /api/check-domain?handle=innei-4525&domain=blog.innei.ren
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { getFeed } from "~/models/home.model"
|
||||
|
||||
export async function GET(req: Request) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
import { cacheGet } from "~/lib/redis.server"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { checkDomainServer, fetchTenant } from "~/models/site.model"
|
||||
|
||||
// /api/host2handle?host=blog.innei.ren
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { getPlaiceholder } from "plaiceholder"
|
||||
|
||||
import { cacheGet } from "~/lib/redis.server"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
|
||||
const getImage = async (src: string) => {
|
||||
return cacheGet({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getDefaultSlug } from "~/lib/default-slug"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const { address } = getQuery(req)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import ogs from "open-graph-scraper"
|
||||
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const query = getQuery(req)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { PageVisibilityEnum, PagesSortTypes } from "~/lib/types"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { PagesSortTypes, PageVisibilityEnum } from "~/lib/types"
|
||||
import { getPagesBySite } from "~/models/page.model"
|
||||
|
||||
export async function GET(req: Request) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import * as cheerio from "cheerio"
|
|||
|
||||
import { PORTFOLIO_GITHUB_TOKEN } from "~/lib/env.server"
|
||||
import { cacheGet } from "~/lib/redis.server"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { PortfolioStats } from "~/lib/types"
|
||||
|
||||
export async function GET(req: Request) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { gql } from "@urql/core"
|
|||
|
||||
import { IS_DEV, IS_VERCEL_PREVIEW } from "~/lib/constants"
|
||||
import { getNoteSlug, getSiteLink } from "~/lib/helpers"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { checkDomainServer } from "~/models/site.model"
|
||||
import { client } from "~/queries/graphql"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import countCharacters from "~/lib/character-count"
|
|||
import { toGateway } from "~/lib/ipfs-parser"
|
||||
import prisma from "~/lib/prisma.server"
|
||||
import { cacheGet } from "~/lib/redis.server"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
|
||||
const lock = new AsyncLock()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
import { getQuery, NextServerResponse } from "~/lib/server-helper"
|
||||
import { getSummary } from "~/queries/page.server"
|
||||
|
||||
export async function GET(req: Request): Promise<Response> {
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { Tweet, getTweet } from "react-tweet/api"
|
||||
|
||||
import { cacheGet } from "~/lib/redis.server"
|
||||
import { NextServerResponse, getQuery } from "~/lib/server-helper"
|
||||
|
||||
export async function GET(req: Request): Promise<Response> {
|
||||
let { id: tweetId } = getQuery(req)
|
||||
const res = new NextServerResponse()
|
||||
|
||||
if (!tweetId) {
|
||||
return res.status(400).json({ error: "Bad request." })
|
||||
}
|
||||
|
||||
try {
|
||||
const tweet = (await cacheGet({
|
||||
key: `tweet/${tweetId}`,
|
||||
noUpdate: true,
|
||||
expireTime: 60 * 60 * 24 * 3,
|
||||
getValueFun: async () => await getTweet(tweetId),
|
||||
})) as Tweet | undefined
|
||||
return res.status(tweet ? 200 : 404).json({ data: tweet ?? null })
|
||||
} catch (error: any) {
|
||||
console.error(error)
|
||||
return res.status(400).json({ error: error?.message ?? "Bad request." })
|
||||
}
|
||||
}
|
||||
|
|
@ -8,14 +8,14 @@ import toast from "react-hot-toast"
|
|||
import { useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import PublishedModal from "~/components/dashboard/PublishedModal"
|
||||
import EditorAutofill from "~/components/dashboard/editor-properties/EditorAutofill"
|
||||
import EditorCover from "~/components/dashboard/editor-properties/EditorCover"
|
||||
import EditorExcerpt from "~/components/dashboard/editor-properties/EditorExcerpt"
|
||||
import EditorExternalUrl from "~/components/dashboard/editor-properties/EditorExternalUrl"
|
||||
import EditorPublishAt from "~/components/dashboard/editor-properties/EditorPublishAt"
|
||||
import EditorTitle from "~/components/dashboard/editor-properties/EditorTitle"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import PublishedModal from "~/components/dashboard/PublishedModal"
|
||||
import { useModalStack } from "~/components/ui/ModalStack"
|
||||
import { initialEditorState, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useGetState } from "~/hooks/useGetState"
|
||||
|
|
|
|||
|
|
@ -10,20 +10,21 @@ import { useQueryClient } from "@tanstack/react-query"
|
|||
|
||||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import DualColumnEditor from "~/components/dashboard/DualColumnEditor"
|
||||
import { EditorToolbar } from "~/components/dashboard/EditorToolbar"
|
||||
import { OptionsButton } from "~/components/dashboard/OptionsButton"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import PublishedModal from "~/components/dashboard/PublishedModal"
|
||||
import EditorCover from "~/components/dashboard/editor-properties/EditorCover"
|
||||
import EditorDisableAISummary from "~/components/dashboard/editor-properties/EditorDisableAISummary"
|
||||
import EditorExcerpt from "~/components/dashboard/editor-properties/EditorExcerpt"
|
||||
import EditorPublishAt from "~/components/dashboard/editor-properties/EditorPublishAt"
|
||||
import EditorSlug from "~/components/dashboard/editor-properties/EditorSlug"
|
||||
import EditorTags from "~/components/dashboard/editor-properties/EditorTags"
|
||||
import { EditorToolbar } from "~/components/dashboard/EditorToolbar"
|
||||
import { OptionsButton } from "~/components/dashboard/OptionsButton"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import PublishedModal from "~/components/dashboard/PublishedModal"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { useModalStack } from "~/components/ui/ModalStack"
|
||||
import { initialEditorState, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useGetState } from "~/hooks/useGetState"
|
||||
import { useIsFullscreen } from "~/hooks/useIsFullscreen"
|
||||
import { useIsMobileLayout } from "~/hooks/useMobileLayout"
|
||||
import { useBeforeMounted } from "~/hooks/useSyncOnce"
|
||||
import { showConfetti } from "~/lib/confetti"
|
||||
|
|
@ -64,6 +65,8 @@ export default function PostEditor() {
|
|||
|
||||
const site = useGetSite(subdomain)
|
||||
|
||||
const isFullscreen = useIsFullscreen()
|
||||
|
||||
const [draftKey, setDraftKey] = useState<string>("")
|
||||
useEffect(() => {
|
||||
if (subdomain) {
|
||||
|
|
@ -401,67 +404,68 @@ export default function PostEditor() {
|
|||
>
|
||||
<EditorToolbar view={view}></EditorToolbar>
|
||||
</div>
|
||||
{isMobileLayout ? (
|
||||
<div className="flex items-center space-x-3 w-auto pl-5">
|
||||
<OptionsButton
|
||||
visibility={visibility}
|
||||
savePage={savePage}
|
||||
deletePage={deletePage}
|
||||
published={visibility !== PageVisibilityEnum.Draft}
|
||||
isRendering={isRendering}
|
||||
renderPage={setIsRendering}
|
||||
propertiesWidget={extraProperties}
|
||||
previewPage={onPreviewButtonClick}
|
||||
type={type}
|
||||
isModified={visibility === PageVisibilityEnum.Modified}
|
||||
discardChanges={discardChanges}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center space-x-3 flex-shrink-0">
|
||||
<span
|
||||
className={cn(
|
||||
`text-sm capitalize`,
|
||||
visibility === PageVisibilityEnum.Draft
|
||||
? `text-zinc-300`
|
||||
: visibility === PageVisibilityEnum.Modified
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
)}
|
||||
>
|
||||
{t(visibility as string)}
|
||||
</span>
|
||||
<Button isAutoWidth onClick={onPreviewButtonClick}>
|
||||
{t("Preview")}
|
||||
</Button>
|
||||
<PublishButton
|
||||
savePage={savePage}
|
||||
deletePage={deletePage}
|
||||
twitterShareUrl={
|
||||
page.data && site.data
|
||||
? getTwitterShareUrl({
|
||||
page: page.data,
|
||||
site: site.data,
|
||||
t,
|
||||
})
|
||||
: ""
|
||||
}
|
||||
published={visibility !== PageVisibilityEnum.Draft}
|
||||
isSaving={
|
||||
createPage.isLoading ||
|
||||
updatePage.isLoading ||
|
||||
deleteP.isLoading
|
||||
}
|
||||
isDisabled={
|
||||
visibility !== PageVisibilityEnum.Modified &&
|
||||
visibility !== PageVisibilityEnum.Draft
|
||||
}
|
||||
type={type}
|
||||
isModified={visibility === PageVisibilityEnum.Modified}
|
||||
discardChanges={discardChanges}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!isFullscreen &&
|
||||
(isMobileLayout ? (
|
||||
<div className="flex items-center space-x-3 w-auto pl-5">
|
||||
<OptionsButton
|
||||
visibility={visibility}
|
||||
savePage={savePage}
|
||||
deletePage={deletePage}
|
||||
published={visibility !== PageVisibilityEnum.Draft}
|
||||
isRendering={isRendering}
|
||||
renderPage={setIsRendering}
|
||||
propertiesWidget={extraProperties}
|
||||
previewPage={onPreviewButtonClick}
|
||||
type={type}
|
||||
isModified={visibility === PageVisibilityEnum.Modified}
|
||||
discardChanges={discardChanges}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center space-x-3 flex-shrink-0">
|
||||
<span
|
||||
className={cn(
|
||||
`text-sm capitalize`,
|
||||
visibility === PageVisibilityEnum.Draft
|
||||
? `text-zinc-300`
|
||||
: visibility === PageVisibilityEnum.Modified
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
)}
|
||||
>
|
||||
{t(visibility as string)}
|
||||
</span>
|
||||
<Button isAutoWidth onClick={onPreviewButtonClick}>
|
||||
{t("Preview")}
|
||||
</Button>
|
||||
<PublishButton
|
||||
savePage={savePage}
|
||||
deletePage={deletePage}
|
||||
twitterShareUrl={
|
||||
page.data && site.data
|
||||
? getTwitterShareUrl({
|
||||
page: page.data,
|
||||
site: site.data,
|
||||
t,
|
||||
})
|
||||
: ""
|
||||
}
|
||||
published={visibility !== PageVisibilityEnum.Draft}
|
||||
isSaving={
|
||||
createPage.isLoading ||
|
||||
updatePage.isLoading ||
|
||||
deleteP.isLoading
|
||||
}
|
||||
isDisabled={
|
||||
visibility !== PageVisibilityEnum.Modified &&
|
||||
visibility !== PageVisibilityEnum.Draft
|
||||
}
|
||||
type={type}
|
||||
isModified={visibility === PageVisibilityEnum.Modified}
|
||||
discardChanges={discardChanges}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</header>
|
||||
<div
|
||||
className={`pt-14 flex w-full ${
|
||||
|
|
@ -500,7 +504,7 @@ export default function PostEditor() {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!isMobileLayout && extraProperties}
|
||||
{!isMobileLayout && !isFullscreen && extraProperties}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ import toast from "react-hot-toast"
|
|||
import { useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import PublishedModal from "~/components/dashboard/PublishedModal"
|
||||
import EditorContent from "~/components/dashboard/editor-properties/EditorContent"
|
||||
import EditorImages from "~/components/dashboard/editor-properties/EditorImages"
|
||||
import EditorPublishAt from "~/components/dashboard/editor-properties/EditorPublishAt"
|
||||
import EditorTitle from "~/components/dashboard/editor-properties/EditorTitle"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import PublishedModal from "~/components/dashboard/PublishedModal"
|
||||
import { useModalStack } from "~/components/ui/ModalStack"
|
||||
import { initialEditorState, useEditorState } from "~/hooks/useEditorState"
|
||||
import { useGetState } from "~/hooks/useGetState"
|
||||
|
|
@ -131,6 +131,10 @@ export default function ShortEditor() {
|
|||
const updatePage = useUpdatePage()
|
||||
|
||||
const savePage = async () => {
|
||||
if (!values.images?.length) {
|
||||
toast.error(t("Please upload at least one image."))
|
||||
return
|
||||
}
|
||||
const baseValues = {
|
||||
...values,
|
||||
characterId: site.data?.characterId,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"use client"
|
||||
|
||||
import {
|
||||
type ReadonlyURLSearchParams,
|
||||
useParams,
|
||||
usePathname,
|
||||
useSearchParams,
|
||||
type ReadonlyURLSearchParams,
|
||||
} from "next/navigation"
|
||||
import React, { useEffect } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
|
|
|||
|
|
@ -4,12 +4,9 @@ import { headers } from "next/headers"
|
|||
import Script from "next/script"
|
||||
import { Toaster } from "react-hot-toast"
|
||||
|
||||
import "@crossbell/connect-kit/colors.css"
|
||||
import { updateIndexerFetchOptions } from "@crossbell/indexer"
|
||||
import { ColorSchemeScript, MantineProvider } from "@mantine/core"
|
||||
import "@mantine/core/styles.css"
|
||||
|
||||
import "~/css/main.css"
|
||||
import { getAcceptLang } from "~/lib/accept-lang"
|
||||
import {
|
||||
APP_DESCRIPTION,
|
||||
|
|
@ -24,6 +21,10 @@ import { getColorScheme } from "~/lib/get-color-scheme"
|
|||
import { ColorSchemeInjector } from "./ColorSchemeInjector"
|
||||
import Providers, { mantineDefaultColorScheme, mantineTheme } from "./providers"
|
||||
|
||||
import "@crossbell/connect-kit/colors.css"
|
||||
import "@mantine/core/styles.css"
|
||||
import "~/css/main.css"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: `${APP_NAME} - ${APP_SLOGAN}`,
|
||||
description: APP_DESCRIPTION,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Metadata } from "next"
|
|||
import { notFound } from "next/navigation"
|
||||
import serialize from "serialize-javascript"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import PageContent from "~/components/common/PageContent"
|
||||
import PostCover from "~/components/home/PostCover"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { SiteArchives } from "~/components/site/SiteArchives"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Metadata } from "next"
|
|||
import { headers } from "next/headers"
|
||||
import { notFound, redirect } from "next/navigation"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { BlockchainInfo } from "~/components/common/BlockchainInfo"
|
||||
import { SitePlayerContainer } from "~/components/common/SitePlayer"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import ShortPreviewList from "~/components/site/ShortPreviewList"
|
||||
import SiteHome from "~/components/site/SiteHome"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import SiteHome from "~/components/site/SiteHome"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import SiteHome from "~/components/site/SiteHome"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { SiteArchives } from "~/components/site/SiteArchives"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { type MutableRefObject, memo } from "react"
|
||||
import { memo, type MutableRefObject } from "react"
|
||||
|
||||
import PostActions from "~/components/site/PostActions"
|
||||
import PostToc from "~/components/site/PostToc"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { PropsWithChildren, createContext, useContext, useMemo } from "react"
|
||||
import { createContext, PropsWithChildren, useContext, useMemo } from "react"
|
||||
import { createPortal } from "react-dom"
|
||||
|
||||
import { useIsClient } from "~/hooks/useClient"
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ export const DashboardMain = ({
|
|||
: "min-w-[270px] relative px-5 py-5 md:px-10 md:py-8",
|
||||
className,
|
||||
isMobileLayout ? "max-w-[100vw]" : "",
|
||||
"min-h-full flex flex-col",
|
||||
"min-h-full flex flex-col bg-white",
|
||||
)}
|
||||
id="dashboard-main"
|
||||
>
|
||||
{title && <p className="text-2xl font-bold mb-8">{t(title)}</p>}
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { Tooltip } from "~/components/ui/Tooltip"
|
|||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { isMacOS } from "~/lib/utils"
|
||||
|
||||
import { type ICommand, toolbars } from "./toolbars"
|
||||
import { toolbars, type ICommand } from "./toolbars"
|
||||
|
||||
type IToolbarItemProps = ICommand<any> & { view?: EditorView }
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import type { InfiniteData } from "@tanstack/react-query"
|
|||
import { useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import { DeleteConfirmationModal } from "~/components/common/DeleteConfirmationModal"
|
||||
import { type TabItem, Tabs } from "~/components/ui/Tabs"
|
||||
import { Tabs, type TabItem } from "~/components/ui/Tabs"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { delStorage, getStorage, setStorage } from "~/lib/storage"
|
||||
import { ExpandedNote, NoteType } from "~/lib/types"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useXSettingsModal } from "@crossbell/connect-kit"
|
|||
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
|
||||
import { type TabItem, Tabs } from "../ui/Tabs"
|
||||
import { Tabs, type TabItem } from "../ui/Tabs"
|
||||
import { DashboardMain } from "./DashboardMain"
|
||||
|
||||
export const SettingsLayout = ({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { DateInput } from "@mantine/dates"
|
||||
|
||||
import "@mantine/dates/styles.css"
|
||||
|
||||
import { useEditorState } from "~/hooks/useEditorState"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type EditorView } from "@codemirror/view"
|
||||
|
||||
import { type ICommand, wrapExecute } from "."
|
||||
import { wrapExecute, type ICommand } from "."
|
||||
|
||||
const action = (view: EditorView) => {
|
||||
wrapExecute({ view, prepend: "**", append: "**" })
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
import { ICommand } from "."
|
||||
|
||||
export const Focus: ICommand = {
|
||||
name: "focus",
|
||||
label: "Toggle Focus Mode",
|
||||
icon: "icon-[mingcute--fullscreen-line]",
|
||||
execute: () => {
|
||||
let elem = document.getElementById("dashboard-main")
|
||||
if (!elem) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!document.fullscreenElement) {
|
||||
elem.requestFullscreen().catch((err) => {
|
||||
alert(
|
||||
`Error attempting to enable fullscreen mode: ${err.message} (${err.name})`,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { type EditorView } from "@codemirror/view"
|
||||
|
||||
import { type ICommand, wrapExecute } from "."
|
||||
import { wrapExecute, type ICommand } from "."
|
||||
|
||||
const action = (view: EditorView) => {
|
||||
wrapExecute({ view, prepend: "_", append: "_" })
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type EditorView } from "@codemirror/view"
|
||||
|
||||
import { type ICommand, wrapExecute } from "."
|
||||
import { wrapExecute, type ICommand } from "."
|
||||
|
||||
const action = (view: EditorView) => {
|
||||
wrapExecute({ view, prepend: "[", append: "]()" })
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type EditorView } from "@codemirror/view"
|
||||
|
||||
import { type ICommand, wrapExecute } from "."
|
||||
import { wrapExecute, type ICommand } from "."
|
||||
|
||||
const action = (view: EditorView) => {
|
||||
wrapExecute({ view, prepend: "<u>", append: "</u>" })
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { Cloud } from "./Cloud"
|
|||
import { Code } from "./Code"
|
||||
import { CodeBlock } from "./CodeBlock"
|
||||
import { Emoji } from "./Emoji"
|
||||
import { Focus } from "./Focus"
|
||||
import { Formula } from "./Formula"
|
||||
import { Heading } from "./Heading"
|
||||
import { Help } from "./Help"
|
||||
|
|
@ -105,6 +106,7 @@ export const toolbars: ICommand[] = [
|
|||
Formula,
|
||||
Emoji,
|
||||
Cloud,
|
||||
Focus,
|
||||
Help,
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type CharacterEntity } from "crossbell"
|
||||
|
||||
import { type Completion, autocompletion } from "@codemirror/autocomplete"
|
||||
import { autocompletion, type Completion } from "@codemirror/autocomplete"
|
||||
import { indexer } from "@crossbell/indexer"
|
||||
import { getDefaultAvatarIpfsUrl } from "@crossbell/ui"
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
|
||||
import { SearchInput } from "~/components/common/SearchInput"
|
||||
import { Avatar } from "~/components/ui/Avatar"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { Trans, getTranslation } from "~/lib/i18n"
|
||||
import { getTranslation, Trans } from "~/lib/i18n"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { getShowcase } from "~/queries/home.server"
|
||||
import { getBlockNumber } from "~/queries/site.server"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import "swiper/css"
|
||||
import "swiper/css/navigation"
|
||||
import "swiper/css/scrollbar"
|
||||
|
||||
import { Navigation, Scrollbar } from "swiper/modules"
|
||||
import { Swiper, SwiperSlide } from "swiper/react"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Script from "next/script"
|
|||
|
||||
import { Logo } from "~/components/common/Logo"
|
||||
import { SITE_URL } from "~/lib/env"
|
||||
import { Trans, getTranslation } from "~/lib/i18n"
|
||||
import { getTranslation, Trans } from "~/lib/i18n"
|
||||
import { ExpandedCharacter } from "~/lib/types"
|
||||
|
||||
import { DarkModeSwitch } from "../common/DarkModeSwitch"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import NextImage from "next/image"
|
|||
import { FollowingButton } from "~/components/common/FollowingButton"
|
||||
import { FollowingCount } from "~/components/common/FollowingCount"
|
||||
import { PatronButton } from "~/components/common/PatronButton"
|
||||
import { type TabItem, Tabs } from "~/components/ui/Tabs"
|
||||
import { Tabs, type TabItem } from "~/components/ui/Tabs"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
import { useState } from "react"
|
||||
|
||||
import PostList from "~/components/site/PostList"
|
||||
import { type TabItem, Tabs } from "~/components/ui/Tabs"
|
||||
import { NoteType, PageVisibilityEnum, PagesSortTypes } from "~/lib/types"
|
||||
import { Tabs, type TabItem } from "~/components/ui/Tabs"
|
||||
import { NoteType, PagesSortTypes, PageVisibilityEnum } from "~/lib/types"
|
||||
import { useGetPagesBySiteLite, usePinnedPage } from "~/queries/page"
|
||||
import { useGetSite } from "~/queries/site"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
CSSProperties,
|
||||
createElement,
|
||||
CSSProperties,
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
|
|
@ -27,14 +27,14 @@ import {
|
|||
import { languages } from "@codemirror/language-data"
|
||||
import { Annotation, EditorState } from "@codemirror/state"
|
||||
import {
|
||||
EditorView,
|
||||
KeyBinding,
|
||||
ViewUpdate,
|
||||
crosshairCursor,
|
||||
drawSelection,
|
||||
dropCursor,
|
||||
EditorView,
|
||||
KeyBinding,
|
||||
keymap,
|
||||
placeholder,
|
||||
ViewUpdate,
|
||||
} from "@codemirror/view"
|
||||
import { tags } from "@lezer/highlight"
|
||||
import { scroll } from "@uiw/codemirror-extensions-events"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Fragment, forwardRef, useCallback, useMemo, useState } from "react"
|
||||
import { forwardRef, Fragment, useCallback, useMemo, useState } from "react"
|
||||
import type { ReactElement } from "react"
|
||||
|
||||
import { Combobox, Transition } from "@headlessui/react"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Link from "next/link"
|
||||
import React, { Fragment } from "react"
|
||||
|
||||
import { Placement, autoUpdate, shift, useFloating } from "@floating-ui/react"
|
||||
import { autoUpdate, Placement, shift, useFloating } from "@floating-ui/react"
|
||||
import { Menu as HeadlessUiMenu } from "@headlessui/react"
|
||||
|
||||
import { cn } from "~/lib/utils"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import React, { Fragment, forwardRef } from "react"
|
||||
import React, { forwardRef, Fragment } from "react"
|
||||
|
||||
import { Dialog, Transition } from "@headlessui/react"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import type { FC, PropsWithChildren, ReactNode, SetStateAction } from "react"
|
||||
import {
|
||||
Dispatch,
|
||||
createContext,
|
||||
createElement,
|
||||
Dispatch,
|
||||
memo,
|
||||
useCallback,
|
||||
useContext,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
import { useState } from "react"
|
||||
|
||||
import {
|
||||
Placement,
|
||||
autoUpdate,
|
||||
flip,
|
||||
offset,
|
||||
Placement,
|
||||
shift,
|
||||
useDismiss,
|
||||
useFloating,
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
import dynamic from "next/dynamic"
|
||||
import Image from "next/image"
|
||||
import type { TwitterComponents } from "react-tweet"
|
||||
|
||||
import { SITE_URL } from "~/lib/env"
|
||||
|
||||
const ReactTweet = dynamic(async () => (await import("react-tweet")).Tweet)
|
||||
|
||||
const components: TwitterComponents = {
|
||||
AvatarImg: (props) => <Image {...props} alt="avatar" />,
|
||||
MediaImg: (props) => <Image {...props} fill unoptimized alt="tweet-media" />,
|
||||
}
|
||||
|
||||
export default function Tweet({ id }: { id: string }) {
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<ReactTweet
|
||||
id={id}
|
||||
components={components}
|
||||
apiUrl={`${SITE_URL}/api/tweet?id=${id}`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -136,20 +136,94 @@
|
|||
}
|
||||
|
||||
.prose .callout {
|
||||
@apply border border-zinc-100 bg-zinc-50 text-accent rounded-xl p-5 md:p-8 font-normal;
|
||||
@apply -mx-5 md:-mx-8 my-10;
|
||||
--callout-color: hsl(207, 14%, 62%);
|
||||
--callout-background: ;
|
||||
--callout-border: hsl(205, 15%, 33%);
|
||||
--callout-radius: 8px;
|
||||
--callout-padding: 2ch;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--callout-border);
|
||||
border-radius: var(--callout-radius);
|
||||
background: var(--callout-background);
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
.prose .callout code,
|
||||
.prose .callout pre {
|
||||
background-color: hsl(0, 0%, 100%, 0.1);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.prose .callout-content {
|
||||
padding-left: var(--callout-padding);
|
||||
padding-right: var(--callout-padding);
|
||||
}
|
||||
|
||||
.prose .callout-content:first-child,
|
||||
.prose .callout-content:only-child {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
|
||||
.prose .callout-content:last-child,
|
||||
.prose .callout-content:only-child {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
.prose .callout-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--callout-padding) var(--callout-padding) 0;
|
||||
}
|
||||
|
||||
.prose .callout-hint {
|
||||
margin-inline-end: calc(var(--callout-padding) / 4);
|
||||
color: var(--callout-color);
|
||||
}
|
||||
|
||||
.prose .callout-title {
|
||||
margin-inline-start: calc(var(--callout-padding) / 4);
|
||||
color: var(--callout-color);
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.prose .callout-note {
|
||||
--callout-background: hsla(0, 0%, 0%, 0.05);
|
||||
--callout-color: hsl(0, 0%, 43%);
|
||||
--callout-border: hsl(0, 0%, 0%, 0.25);
|
||||
}
|
||||
|
||||
:root.dark .prose .callout-note {
|
||||
--callout-background: hsl(0, 0%, 100%, 0.05);
|
||||
--callout-color: hsl(0, 0%, 43%);
|
||||
--callout-border: hsl(0, 0%, 100%, 0.25);
|
||||
}
|
||||
|
||||
.prose .callout-commend {
|
||||
--callout-background: hsl(153, 47%, 49%, 0.05);
|
||||
--callout-color: hsl(153, 62%, 54%);
|
||||
--callout-border: hsl(153, 47%, 49%, 0.25);
|
||||
}
|
||||
|
||||
.prose .callout-warn {
|
||||
@apply border-orange-200 bg-orange-100 text-orange-700;
|
||||
--callout-background: hsl(45, 100%, 55%, 0.05);
|
||||
--callout-color: hsl(45, 97%, 66%);
|
||||
--callout-border: hsl(45, 100%, 55%, 0.25);
|
||||
}
|
||||
|
||||
.prose .callout-success {
|
||||
@apply border-green-200 bg-green-100 text-green-700;
|
||||
.prose .callout-deter {
|
||||
--callout-background: hsl(353, 83%, 58%, 0.05);
|
||||
--callout-color: hsl(341, 89%, 63%);
|
||||
--callout-border: hsl(353, 83%, 58%, 0.25);
|
||||
}
|
||||
|
||||
.prose .callout-danger {
|
||||
@apply border-red-200 bg-red-100 text-red-700;
|
||||
.prose .callout-assert {
|
||||
--callout-background: rgba(47, 131, 157, 0.05);
|
||||
--callout-color: hsl(201, 89%, 63%);
|
||||
--callout-border: hsl(195, 61%, 56%, 0.25);
|
||||
}
|
||||
|
||||
.prose .code-wrapper {
|
||||
|
|
|
|||
|
|
@ -439,4 +439,4 @@ textarea.input {
|
|||
|
||||
.swiper-wrapper {
|
||||
@apply flex items-center;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import dayjs from "dayjs"
|
||||
|
||||
import "dayjs/locale/en"
|
||||
import "dayjs/locale/ja"
|
||||
import "dayjs/locale/zh"
|
||||
import "dayjs/locale/zh-tw"
|
||||
|
||||
import duration from "dayjs/plugin/duration"
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat"
|
||||
import relativeTime from "dayjs/plugin/relativeTime"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import { useCallback, useEffect, useState } from "react"
|
||||
|
||||
export function useIsFullscreen() {
|
||||
const [isFullscreen, setIsFullscreen] = useState(false)
|
||||
const onFullscreenChange = useCallback(() => {
|
||||
setIsFullscreen(document.fullscreenElement !== null)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
setIsFullscreen(document.fullscreenElement !== null)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
document.addEventListener("fullscreenchange", onFullscreenChange)
|
||||
return () => {
|
||||
document.removeEventListener("fullscreenchange", onFullscreenChange)
|
||||
}
|
||||
})
|
||||
return isFullscreen
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { createInstance } from "i18next"
|
||||
import resourcesToBackend from "i18next-resources-to-backend"
|
||||
import { Trans as TransW } from "react-i18next/TransWithoutContext"
|
||||
import { initReactI18next } from "react-i18next/initReactI18next"
|
||||
import { Trans as TransW } from "react-i18next/TransWithoutContext"
|
||||
|
||||
import { getAcceptLang } from "~/lib/accept-lang"
|
||||
|
||||
|
|
|
|||
|
|
@ -94,4 +94,4 @@
|
|||
"Share": "分享",
|
||||
"Share Message": "嘿,我發現了一片寶藏文章「{{title}}」哩,快來看看吧!",
|
||||
"Pinned": "釘選"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,4 +69,4 @@
|
|||
"Experience a world of imagination through these fiction stories.": "年輕人的第一個虛擬世界。",
|
||||
"Coding": "Coding",
|
||||
"Participate in Topic": "加入話題"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,5 +232,6 @@
|
|||
"Content": "内容",
|
||||
"Home": "主页",
|
||||
"Archives": "归档",
|
||||
"About": "关于"
|
||||
"About": "关于",
|
||||
"Please upload at least one image." : "请至少上传一张图片。"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { GetServerSidePropsContext } from "next"
|
||||
import url from "url"
|
||||
import type { GetServerSidePropsContext } from "next"
|
||||
|
||||
export const languageDetector = (ctx: GetServerSidePropsContext) => {
|
||||
const queryLang = url.parse(ctx.req.url!, true).query.lang as string
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@ const getBilibiliIFrameSrc = (url: URL) => {
|
|||
if (!match || match.length < 1) return
|
||||
const bvid = match[1]
|
||||
|
||||
const page = url.searchParams.get('p')
|
||||
const pageParam = (page) ? `&page=${page}` : ''
|
||||
const page = url.searchParams.get("p")
|
||||
const pageParam = page ? `&page=${page}` : ""
|
||||
|
||||
const embedUrl = new URL(
|
||||
`https://player.bilibili.com/player.html?bvid=${bvid}&autoplay=false` + pageParam,
|
||||
`https://player.bilibili.com/player.html?bvid=${bvid}&autoplay=false` +
|
||||
pageParam,
|
||||
)
|
||||
|
||||
return embedUrl.toString()
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
import { match } from "path-to-regexp"
|
||||
|
||||
import type { Transformer } from "../rehype-embed"
|
||||
import { isHostIncludes } from "./utils"
|
||||
|
||||
export const TweetTransformer: Transformer = {
|
||||
name: "Tweet",
|
||||
shouldTransform(url) {
|
||||
const { host, pathname } = url
|
||||
|
||||
return isHostIncludes("twitter.com", host) && pathname.includes("/status/")
|
||||
},
|
||||
getHTML(url) {
|
||||
const { pathname } = url
|
||||
const matched = match<{ id: string }>("/:user/status/:id")(pathname)
|
||||
if (!matched) return
|
||||
return `<tweet id="${matched.params.id}" />`
|
||||
},
|
||||
}
|
||||
|
|
@ -4,12 +4,10 @@ import { CodeSandboxTransformer } from "./CodeSandBox"
|
|||
import { GitHubRepoTransformer } from "./GitHub"
|
||||
import { NetEaseMusicTransformer } from "./NetEaseMusic"
|
||||
import { SpotifyTransformer } from "./Spotify"
|
||||
import { TweetTransformer } from "./Tweet"
|
||||
import { YouTubeTransformer } from "./YouTube"
|
||||
|
||||
export const transformers: Transformer[] = [
|
||||
CodeSandboxTransformer,
|
||||
TweetTransformer,
|
||||
GitHubRepoTransformer,
|
||||
YouTubeTransformer,
|
||||
NetEaseMusicTransformer,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import jsYaml from "js-yaml"
|
||||
import type { Root } from "mdast"
|
||||
import { Result as TocResult, toc } from "mdast-util-toc"
|
||||
import { toc, Result as TocResult } from "mdast-util-toc"
|
||||
import dynamic from "next/dynamic"
|
||||
import { ReactElement, createElement } from "react"
|
||||
import { createElement, ReactElement } from "react"
|
||||
import { toast } from "react-hot-toast"
|
||||
import { refractor } from "refractor"
|
||||
import jsx from "refractor/lang/jsx"
|
||||
|
|
@ -29,6 +29,9 @@ import remarkParse from "remark-parse"
|
|||
import remarkRehype from "remark-rehype"
|
||||
import { unified } from "unified"
|
||||
|
||||
// @ts-expect-error
|
||||
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
|
||||
|
||||
import AdvancedImage from "~/components/ui/AdvancedImage"
|
||||
|
||||
import { transformers } from "./embed-transformers"
|
||||
|
|
@ -44,7 +47,6 @@ import { rehypeTable } from "./rehype-table"
|
|||
import { rehypeVideo } from "./rehype-video"
|
||||
import { rehypeWrapCode } from "./rehype-wrap-code"
|
||||
import { rehypeExternalLink } from "./rehyper-external-link"
|
||||
import { remarkCallout } from "./remark-callout"
|
||||
import { remarkMermaid } from "./remark-mermaid"
|
||||
import { remarkPangu } from "./remark-pangu"
|
||||
import { remarkYoutube } from "./remark-youtube"
|
||||
|
|
@ -53,7 +55,6 @@ import sanitizeScheme from "./sanitize-schema"
|
|||
const Style = dynamic(() => import("~/components/common/Style"))
|
||||
const Mention = dynamic(() => import("~/components/ui/Mention"))
|
||||
const Mermaid = dynamic(() => import("~/components/ui/Mermaid"))
|
||||
const Tweet = dynamic(() => import("~/components/ui/Tweet"))
|
||||
const GithubRepo = dynamic(() => import("~/components/ui/GithubRepo"))
|
||||
const APlayer = dynamic(() => import("~/components/ui/APlayer"))
|
||||
|
||||
|
|
@ -121,9 +122,9 @@ export const renderPageContent = (
|
|||
.use(remarkGfm, {
|
||||
singleTilde: false,
|
||||
})
|
||||
.use(remarkCallout)
|
||||
.use(remarkDirective)
|
||||
.use(remarkDirectiveRehype)
|
||||
.use(remarkCalloutDirectives)
|
||||
.use(remarkYoutube)
|
||||
.use(remarkMermaid)
|
||||
.use(remarkMath, {
|
||||
|
|
@ -228,7 +229,6 @@ export const renderPageContent = (
|
|||
mermaid: Mermaid,
|
||||
audio: APlayer,
|
||||
video: DPlayer,
|
||||
tweet: Tweet,
|
||||
"github-repo": GithubRepo,
|
||||
style: Style,
|
||||
} as any,
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
import type { Root } from "mdast"
|
||||
import type { Plugin } from "unified"
|
||||
import { visit } from "unist-util-visit"
|
||||
|
||||
const CALLOUT_RE = /^(TIP|WARN|SUCCESS|DANGER):(\n|$)/
|
||||
|
||||
export const remarkCallout: Plugin<[], Root> = () => (tree: Root) => {
|
||||
visit(tree, { type: "blockquote" }, (node, index, parent) => {
|
||||
if (node.type !== "blockquote") return
|
||||
|
||||
const firstChild = node.children[0]
|
||||
if (firstChild?.type === "paragraph") {
|
||||
const paragraphFirstChild = firstChild.children[0]
|
||||
if (
|
||||
paragraphFirstChild.type === "text" &&
|
||||
CALLOUT_RE.test(paragraphFirstChild.value)
|
||||
) {
|
||||
const type = paragraphFirstChild.value.match(CALLOUT_RE)![1]
|
||||
const newValue = paragraphFirstChild.value.replace(CALLOUT_RE, "")
|
||||
// In the following case `newValue` will be empty empty
|
||||
// We should remove the paragraph
|
||||
// > TIP:
|
||||
// >
|
||||
// > some tip
|
||||
if (!newValue) {
|
||||
node.children.splice(0, 1)
|
||||
}
|
||||
paragraphFirstChild.value = newValue
|
||||
const className = `callout callout-${type.toLowerCase()}`
|
||||
node.data = {
|
||||
...node.data,
|
||||
hProperties: {
|
||||
class: className,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const allowedBlockquoteAttrs: Array<[string, ...string[]]> = [
|
||||
[
|
||||
"className",
|
||||
"callout",
|
||||
"callout-tip",
|
||||
"callout-warn",
|
||||
"callout-success",
|
||||
"callout-danger",
|
||||
],
|
||||
]
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { defaultSchema } from "rehype-sanitize"
|
||||
|
||||
import { allowedBlockquoteAttrs } from "./remark-callout"
|
||||
|
||||
const scheme = {
|
||||
...defaultSchema,
|
||||
tagNames: [
|
||||
|
|
@ -16,11 +14,12 @@ const scheme = {
|
|||
"audio",
|
||||
"source",
|
||||
"mermaid",
|
||||
"aside",
|
||||
...["svg", "path", "circle"],
|
||||
],
|
||||
attributes: {
|
||||
...defaultSchema.attributes,
|
||||
"*": [...(defaultSchema.attributes?.["*"] || []), "className", "style"],
|
||||
blockquote: allowedBlockquoteAttrs,
|
||||
video: ["src", "controls", "loop", "muted", "autoPlay", "playsInline"],
|
||||
audio: [
|
||||
"src",
|
||||
|
|
@ -35,6 +34,19 @@ const scheme = {
|
|||
],
|
||||
source: ["src", "type"],
|
||||
iframe: ["src", "allowFullScreen", "frameborder", "allow"],
|
||||
svg: [
|
||||
"xmlns",
|
||||
"width",
|
||||
"height",
|
||||
"viewBox",
|
||||
"fill",
|
||||
"stroke",
|
||||
"strokeLineCap",
|
||||
"strokeLineJoin",
|
||||
"strokeWidth",
|
||||
],
|
||||
path: ["d", "fill"],
|
||||
circle: ["cx", "cy", "r", "fill"],
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import { getKeys, getStorage } from "~/lib/storage"
|
|||
import {
|
||||
ExpandedNote,
|
||||
NoteType,
|
||||
PageVisibilityEnum,
|
||||
PagesSortTypes,
|
||||
PageVisibilityEnum,
|
||||
} from "~/lib/types"
|
||||
import { client } from "~/queries/graphql"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import i18next from "i18next"
|
||||
import { ReactNode, createContext } from "react"
|
||||
import { createContext, ReactNode } from "react"
|
||||
|
||||
import { languages } from "~/lib/i18n/settings"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue