Merge branch 'dev'

This commit is contained in:
DIYgod 2023-10-07 18:00:08 +08:00
commit d93d684727
No known key found for this signature in database
83 changed files with 1188 additions and 1145 deletions

View File

@ -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"] }],
},
}

View File

@ -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,
}

View File

@ -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
]
},
{

View File

@ -10,4 +10,4 @@ updates:
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 0
open-pull-requests-limit: 0

View File

@ -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"

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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")

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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({

View File

@ -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)

View File

@ -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)

View File

@ -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) {

View File

@ -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) {

View File

@ -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"

View File

@ -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()

View File

@ -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> {

View File

@ -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." })
}
}

View File

@ -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"

View File

@ -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>
</>
)}

View File

@ -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,

View File

@ -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"

View File

@ -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,

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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}

View File

@ -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 }

View File

@ -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"

View File

@ -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 = ({

View File

@ -1,4 +1,5 @@
import { DateInput } from "@mantine/dates"
import "@mantine/dates/styles.css"
import { useEditorState } from "~/hooks/useEditorState"

View File

@ -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: "**" })

View File

@ -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()
}
},
}

View File

@ -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: "_" })

View File

@ -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: "]()" })

View File

@ -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>" })

View File

@ -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,
]

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -1,6 +1,6 @@
"use client"
import React, { Fragment, forwardRef } from "react"
import React, { forwardRef, Fragment } from "react"
import { Dialog, Transition } from "@headlessui/react"

View File

@ -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,

View File

@ -3,10 +3,10 @@
import { useState } from "react"
import {
Placement,
autoUpdate,
flip,
offset,
Placement,
shift,
useDismiss,
useFloating,

View File

@ -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>
)
}

View File

@ -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 {

View File

@ -439,4 +439,4 @@ textarea.input {
.swiper-wrapper {
@apply flex items-center;
}
}

View File

@ -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"

View File

@ -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
}

View File

@ -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"

View File

@ -94,4 +94,4 @@
"Share": "分享",
"Share Message": "嘿,我發現了一片寶藏文章「{{title}}」哩,快來看看吧!",
"Pinned": "釘選"
}
}

View File

@ -69,4 +69,4 @@
"Experience a world of imagination through these fiction stories.": "年輕人的第一個虛擬世界。",
"Coding": "Coding",
"Participate in Topic": "加入話題"
}
}

View File

@ -232,5 +232,6 @@
"Content": "内容",
"Home": "主页",
"Archives": "归档",
"About": "关于"
"About": "关于",
"Please upload at least one image." : "请至少上传一张图片。"
}

View File

@ -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

View File

@ -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()

View File

@ -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}" />`
},
}

View File

@ -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,

View File

@ -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,

View File

@ -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",
],
]

View File

@ -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"],
},
}

View File

@ -23,8 +23,8 @@ import { getKeys, getStorage } from "~/lib/storage"
import {
ExpandedNote,
NoteType,
PageVisibilityEnum,
PagesSortTypes,
PageVisibilityEnum,
} from "~/lib/types"
import { client } from "~/queries/graphql"

View File

@ -1,5 +1,5 @@
import i18next from "i18next"
import { ReactNode, createContext } from "react"
import { createContext, ReactNode } from "react"
import { languages } from "~/lib/i18n/settings"