adding md viewer
This commit is contained in:
parent
7167ec998f
commit
c877382b2a
@ -15,8 +15,8 @@ export default function AppLayout({ children }: { children: React.ReactNode }) {
|
||||
|
||||
{/* Sidebar */}
|
||||
<Sidebar />
|
||||
<div className="relative z-0 flex shrink-0 grow justify-center overflow-x-hidden">
|
||||
<div className="flex-1 overflow-x-hidden sm:px-5">{children}</div>
|
||||
<div className="relative flex shrink-0 grow justify-center overflow-x-hidden">
|
||||
<div className="flex-1 overflow-x-hidden px-5">{children}</div>
|
||||
</div>
|
||||
{/* Mobile Banner */}
|
||||
<MobileBanner />
|
||||
|
@ -1,16 +1,34 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import HorizontalCarousel from "./_sections/HorizontalCarousel";
|
||||
|
||||
import { RiArrowRightLine } from "react-icons/ri";
|
||||
import LongFormContentCard from "@/components/LongFormContentCard";
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="relative pt-10">
|
||||
<div className="relative -mx-5 space-y-6 overflow-x-hidden">
|
||||
<div className="flex items-center justify-between px-5">
|
||||
<h2 className="font-condensed text-3xl font-bold">
|
||||
<div className="relative space-y-6 pt-5 sm:pt-7">
|
||||
<section className="relative -mx-5 space-y-4 overflow-x-hidden sm:space-y-6">
|
||||
<div className="flex items-center justify-between px-5 max-sm:pr-3">
|
||||
<h2 className="font-condensed text-2xl font-bold sm:text-3xl">
|
||||
Explore Creators
|
||||
</h2>
|
||||
<Button variant={"ghost"}>
|
||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<HorizontalCarousel />
|
||||
</div>
|
||||
</section>
|
||||
<section className="relative space-y-3 overflow-x-hidden sm:space-y-3">
|
||||
<div className="flex items-center justify-between max-sm:pr-3">
|
||||
<h2 className="font-condensed text-xl font-semibold sm:text-xl">
|
||||
Long form content
|
||||
</h2>
|
||||
<Button variant={"ghost"}>
|
||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<LongFormContentCard />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
9
app/(app)/article/[eventId]/layout.tsx
Normal file
9
app/(app)/article/[eventId]/layout.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import { ReactElement } from "react";
|
||||
|
||||
export default function ModalLayout({ children }: { children: ReactElement }) {
|
||||
return (
|
||||
<div className="z-overlay fixed inset-y-[10px] left-[10px] right-[10px] rounded-lg border bg-background px-4 sm:left-[calc(10px_+_var(--sidebar-closed-width))] xl:left-[calc(10px_+_var(--sidebar-open-width))]">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
63
app/(app)/article/[eventId]/page.tsx
Normal file
63
app/(app)/article/[eventId]/page.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
import { useMemo } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RiCloseFill } from "react-icons/ri";
|
||||
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
||||
import { useRouter } from "next/navigation";
|
||||
const Viewer = dynamic(() => import("@/components/LongForm/Viewer"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function ArticlePage({
|
||||
params: { eventId },
|
||||
}: {
|
||||
params: {
|
||||
eventId: string;
|
||||
};
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const markdown = `This is a test
|
||||
### test text
|
||||
|
||||
- First
|
||||
- Second
|
||||
1 nest`;
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="absolute inset-x-0 top-0 flex items-center justify-between border-b pb-4 pt-4">
|
||||
<div className="center gap-x-3">
|
||||
<Avatar className="center h-8 w-8 overflow-hidden rounded-sm bg-muted">
|
||||
<AvatarImage
|
||||
src={
|
||||
"https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
|
||||
}
|
||||
alt="user"
|
||||
/>
|
||||
<AvatarFallback className="text-xs">SC</AvatarFallback>
|
||||
</Avatar>
|
||||
<span className="text-xs uppercase text-muted-foreground">
|
||||
Derek Seivers
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (sessionStorage.getItem("RichHistory")) {
|
||||
void router.back();
|
||||
} else {
|
||||
void router.push("/");
|
||||
}
|
||||
}}
|
||||
size="icon"
|
||||
variant={"outline"}
|
||||
className=""
|
||||
>
|
||||
<RiCloseFill className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="h-[70px] w-full"></div>
|
||||
<Viewer initialMarkdown={markdown} />
|
||||
</div>
|
||||
);
|
||||
}
|
65
components/LongForm/Editor.tsx
Normal file
65
components/LongForm/Editor.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { BlockNoteEditor, PartialBlock, Block } from "@blocknote/core";
|
||||
import { BlockNoteView, useBlockNote } from "@blocknote/react";
|
||||
import Spinner from "../spinner";
|
||||
import "@blocknote/core/style.css";
|
||||
|
||||
interface EditorProps {
|
||||
onChange: (value: string) => void;
|
||||
initialMarkdown?: string;
|
||||
editable?: boolean;
|
||||
}
|
||||
|
||||
const Editor = ({ onChange, initialMarkdown, editable }: EditorProps) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [initialContent, setInitialContent] = useState<Block[]>();
|
||||
|
||||
const editor: BlockNoteEditor = useBlockNote({
|
||||
editable,
|
||||
initialContent: initialContent,
|
||||
onEditorContentChange: (editor) => {
|
||||
onChange(JSON.stringify(editor.topLevelBlocks, null, 2));
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
if (!initialContent && initialMarkdown) {
|
||||
// Whenever the current Markdown content changes, converts it to an array
|
||||
// of Block objects and replaces the editor's content with them.
|
||||
const getBlocks = async () => {
|
||||
const blocks: Block[] =
|
||||
await editor.markdownToBlocks(initialMarkdown);
|
||||
setInitialContent(blocks);
|
||||
editor.replaceBlocks(editor.topLevelBlocks, blocks);
|
||||
setLoading(false);
|
||||
};
|
||||
void getBlocks();
|
||||
} else if (loading) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [editor]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="">
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BlockNoteView
|
||||
editor={editor}
|
||||
theme={resolvedTheme === "dark" ? "dark" : "light"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Editor;
|
63
components/LongForm/Viewer.tsx
Normal file
63
components/LongForm/Viewer.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { BlockNoteEditor, PartialBlock, Block } from "@blocknote/core";
|
||||
import { BlockNoteView, useBlockNote } from "@blocknote/react";
|
||||
import Spinner from "../spinner";
|
||||
import "@blocknote/core/style.css";
|
||||
|
||||
interface EditorProps {
|
||||
initialMarkdown?: string;
|
||||
}
|
||||
|
||||
const Viewer = ({ initialMarkdown }: EditorProps) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [initialContent, setInitialContent] = useState<Block[]>();
|
||||
|
||||
const editor: BlockNoteEditor = useBlockNote({
|
||||
editable: false,
|
||||
initialContent: initialContent,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log("ERFE", editor);
|
||||
if (editor) {
|
||||
if (!initialContent && initialMarkdown) {
|
||||
console.log("initial md", initialMarkdown);
|
||||
// Whenever the current Markdown content changes, converts it to an array
|
||||
// of Block objects and replaces the editor's content with them.
|
||||
const getBlocks = async () => {
|
||||
const blocks: Block[] =
|
||||
await editor.markdownToBlocks(initialMarkdown);
|
||||
setInitialContent(blocks);
|
||||
console.log("Blocks", blocks);
|
||||
// editor.replaceBlocks(editor.topLevelBlocks, blocks);
|
||||
setLoading(false);
|
||||
};
|
||||
void getBlocks();
|
||||
} else if (loading) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [editor]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="">
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BlockNoteView
|
||||
editor={editor}
|
||||
theme={resolvedTheme === "dark" ? "dark" : "light"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Viewer;
|
83
components/LongFormContentCard/index.tsx
Normal file
83
components/LongFormContentCard/index.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { RiMoreFill } from "react-icons/ri";
|
||||
import { HiOutlineLightningBolt } from "react-icons/hi";
|
||||
import {
|
||||
HiOutlineHandThumbUp,
|
||||
HiOutlineChatBubbleLeftEllipsis,
|
||||
HiOutlineEllipsisHorizontal,
|
||||
} from "react-icons/hi2";
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
||||
import { formatDate } from "@/lib/utils/dates";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
type CreatorCardProps = {
|
||||
displayName: string;
|
||||
about: string;
|
||||
picture: string;
|
||||
banner: string;
|
||||
};
|
||||
|
||||
export default function LongFormContentCard() {
|
||||
return (
|
||||
<Card className="relative max-w-[400px] overflow-hidden">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4 pb-4">
|
||||
<div className="center gap-x-3">
|
||||
<Avatar className="center h-8 w-8 overflow-hidden rounded-sm bg-muted">
|
||||
<AvatarImage
|
||||
src={
|
||||
"https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
|
||||
}
|
||||
alt="user"
|
||||
/>
|
||||
<AvatarFallback className="text-xs">SC</AvatarFallback>
|
||||
</Avatar>
|
||||
<span className="text-xs uppercase text-muted-foreground">
|
||||
Derek Seivers
|
||||
</span>
|
||||
</div>
|
||||
<div className="-mr-1 flex items-center gap-x-1.5 text-xs text-muted-foreground">
|
||||
{formatDate(new Date("10-5-23"), "MMM Do")}
|
||||
<Button size={"sm"} variant={"ghost"} className="center h-6 w-6 p-0">
|
||||
<RiMoreFill className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="px-4 pb-3">
|
||||
<CardTitle className="mb-1.5 line-clamp-2 text-lg font-semibold">
|
||||
The start of the Nostr revolution
|
||||
</CardTitle>
|
||||
<CardDescription className="line-clamp-4 text-sm">
|
||||
This is the summary of this artilce. Let's hope that it is a good
|
||||
article and that it will end up being worth reading. I don't want to
|
||||
waste my time on some random other stuff.
|
||||
</CardDescription>
|
||||
<div className="mt-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-1">
|
||||
<Button size={"sm"} variant={"ghost"} className="gap-x-1.5 px-2">
|
||||
<HiOutlineHandThumbUp className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size={"sm"} variant={"ghost"} className="gap-x-1.5 px-2">
|
||||
<HiOutlineChatBubbleLeftEllipsis className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<Button size={"sm"} className="gap-x-1.5">
|
||||
<HiOutlineLightningBolt className="h-4 w-4" />
|
||||
<span>zap</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
26
components/spinner.tsx
Normal file
26
components/spinner.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
const Spinner = () => {
|
||||
return (
|
||||
<svg
|
||||
className="h-5 w-5 animate-spin"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Spinner;
|
@ -1,73 +1,81 @@
|
||||
import dayjs from "dayjs";
|
||||
import relative from "dayjs/plugin/relativeTime";
|
||||
import updateLocale from "dayjs/plugin/updateLocale";
|
||||
import advancedFormat from "dayjs/plugin/advancedFormat";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
|
||||
export function relativeTimeUnix(timestamp: number) {
|
||||
const config = {
|
||||
thresholds: [
|
||||
{ l: "s", r: 1 },
|
||||
{ l: "m", r: 1 },
|
||||
{ l: "mm", r: 59, d: "minute" },
|
||||
{ l: "h", r: 1 },
|
||||
{ l: "hh", r: 23, d: "hour" },
|
||||
{ l: "d", r: 1 },
|
||||
{ l: "dd", r: 364, d: "day" },
|
||||
{ l: "y", r: 1 },
|
||||
{ l: "yy", d: "year" },
|
||||
],
|
||||
rounding: Math.floor,
|
||||
};
|
||||
dayjs.extend(updateLocale);
|
||||
|
||||
dayjs.updateLocale("en", {
|
||||
relativeTime: {
|
||||
future: "in %s",
|
||||
past: "%s ago",
|
||||
s: "%s seconds",
|
||||
m: "1 min",
|
||||
mm: "%d mins",
|
||||
h: "1 hour",
|
||||
hh: "%d hours",
|
||||
d: "1 day",
|
||||
dd: "%d days",
|
||||
y: "1 year",
|
||||
yy: "%d years",
|
||||
},
|
||||
});
|
||||
dayjs.extend(relative, config);
|
||||
return dayjs(timestamp * 1000).fromNow();
|
||||
}
|
||||
export function relativeTime(timestamp: Date) {
|
||||
const config = {
|
||||
thresholds: [
|
||||
{ l: "s", r: 1 },
|
||||
{ l: "m", r: 1 },
|
||||
{ l: "mm", r: 59, d: "minute" },
|
||||
{ l: "h", r: 1 },
|
||||
{ l: "hh", r: 23, d: "hour" },
|
||||
{ l: "d", r: 1 },
|
||||
{ l: "dd", r: 364, d: "day" },
|
||||
{ l: "y", r: 1 },
|
||||
{ l: "yy", d: "year" },
|
||||
],
|
||||
rounding: Math.floor,
|
||||
};
|
||||
dayjs.extend(updateLocale);
|
||||
|
||||
dayjs.updateLocale("en", {
|
||||
relativeTime: {
|
||||
future: "in %s",
|
||||
past: "%s ago",
|
||||
s: "%s seconds",
|
||||
m: "1 min",
|
||||
mm: "%d mins",
|
||||
h: "1 hour",
|
||||
hh: "%d hours",
|
||||
d: "1 day",
|
||||
dd: "%d days",
|
||||
y: "1 year",
|
||||
yy: "%d years",
|
||||
},
|
||||
});
|
||||
dayjs.extend(relative, config);
|
||||
return dayjs(timestamp).fromNow();
|
||||
}
|
||||
const config = {
|
||||
thresholds: [
|
||||
{ l: "s", r: 1 },
|
||||
{ l: "m", r: 1 },
|
||||
{ l: "mm", r: 59, d: "minute" },
|
||||
{ l: "h", r: 1 },
|
||||
{ l: "hh", r: 23, d: "hour" },
|
||||
{ l: "d", r: 1 },
|
||||
{ l: "dd", r: 364, d: "day" },
|
||||
{ l: "y", r: 1 },
|
||||
{ l: "yy", d: "year" },
|
||||
],
|
||||
rounding: Math.floor,
|
||||
};
|
||||
dayjs.extend(updateLocale);
|
||||
|
||||
dayjs.updateLocale("en", {
|
||||
relativeTime: {
|
||||
future: "in %s",
|
||||
past: "%s ago",
|
||||
s: "%s seconds",
|
||||
m: "1 min",
|
||||
mm: "%d mins",
|
||||
h: "1 hour",
|
||||
hh: "%d hours",
|
||||
d: "1 day",
|
||||
dd: "%d days",
|
||||
y: "1 year",
|
||||
yy: "%d years",
|
||||
},
|
||||
});
|
||||
dayjs.extend(relative, config);
|
||||
return dayjs(timestamp * 1000).fromNow();
|
||||
}
|
||||
export function relativeTime(timestamp: Date) {
|
||||
const config = {
|
||||
thresholds: [
|
||||
{ l: "s", r: 1 },
|
||||
{ l: "m", r: 1 },
|
||||
{ l: "mm", r: 59, d: "minute" },
|
||||
{ l: "h", r: 1 },
|
||||
{ l: "hh", r: 23, d: "hour" },
|
||||
{ l: "d", r: 1 },
|
||||
{ l: "dd", r: 364, d: "day" },
|
||||
{ l: "y", r: 1 },
|
||||
{ l: "yy", d: "year" },
|
||||
],
|
||||
rounding: Math.floor,
|
||||
};
|
||||
dayjs.extend(updateLocale);
|
||||
|
||||
dayjs.updateLocale("en", {
|
||||
relativeTime: {
|
||||
future: "in %s",
|
||||
past: "%s ago",
|
||||
s: "%s seconds",
|
||||
m: "1 min",
|
||||
mm: "%d mins",
|
||||
h: "1 hour",
|
||||
hh: "%d hours",
|
||||
d: "1 day",
|
||||
dd: "%d days",
|
||||
y: "1 year",
|
||||
yy: "%d years",
|
||||
},
|
||||
});
|
||||
dayjs.extend(relative, config);
|
||||
return dayjs(timestamp).fromNow();
|
||||
}
|
||||
export function formatDate(timestamp: Date, format?: string) {
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.extend(timezone);
|
||||
return dayjs(timestamp).format(format ?? "MMMM Do, YYYY");
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blocknote/core": "^0.9.5",
|
||||
"@blocknote/react": "^0.9.5",
|
||||
"@hookform/resolvers": "^3.3.2",
|
||||
"@noble/hashes": "^1.3.2",
|
||||
"@nostr-dev-kit/ndk": "^2.0.0",
|
||||
@ -29,6 +31,7 @@
|
||||
"focus-trap-react": "^10.2.3",
|
||||
"framer-motion": "^10.16.4",
|
||||
"next": "13.5.4",
|
||||
"next-themes": "^0.2.1",
|
||||
"node-html-parser": "^6.1.10",
|
||||
"nostr-tools": "^1.16.0",
|
||||
"ramda": "^0.29.1",
|
||||
@ -45,6 +48,7 @@
|
||||
"zustand": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/crypto-js": "^4.1.2",
|
||||
"@types/node": "^20",
|
||||
"@types/ramda": "^0.29.6",
|
||||
|
@ -84,16 +84,20 @@ module.exports = {
|
||||
80: 80,
|
||||
90: 90,
|
||||
99: 99,
|
||||
mobileTabs: 980,
|
||||
"header-": 989,
|
||||
header: 990,
|
||||
"header+": 991,
|
||||
headerDialog: 991,
|
||||
"modal-": 995,
|
||||
modal: 996,
|
||||
"modal+": 997,
|
||||
"top-": 998,
|
||||
top: 999,
|
||||
mobileTabs: 900,
|
||||
"header-": 919,
|
||||
header: 920,
|
||||
"header+": 921,
|
||||
headerDialog: 922,
|
||||
"overlay-": 929,
|
||||
overlay: 930,
|
||||
"overlay+": 931,
|
||||
"modal-": 939,
|
||||
modal: 940,
|
||||
"modal+": 941,
|
||||
"top-": 949,
|
||||
top: 950,
|
||||
"top+": 951,
|
||||
},
|
||||
flex: {
|
||||
2: 2,
|
||||
@ -106,5 +110,6 @@ module.exports = {
|
||||
require("tailwindcss-animate"),
|
||||
require("@tailwindcss/container-queries"),
|
||||
require("tailwind-scrollbar"),
|
||||
require("@tailwindcss/typography"),
|
||||
],
|
||||
};
|
||||
|
221
test.md
Normal file
221
test.md
Normal file
@ -0,0 +1,221 @@
|
||||
# h1 Heading 8-)
|
||||
|
||||
## h2 Heading
|
||||
|
||||
### h3 Heading
|
||||
|
||||
#### h4 Heading
|
||||
|
||||
##### h5 Heading
|
||||
|
||||
###### h6 Heading
|
||||
|
||||
## Horizontal Rules
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Typographic replacements
|
||||
|
||||
Enable typographer option to see result.
|
||||
|
||||
(c) (C) (r) (R) (tm) (TM) (p) (P) +-
|
||||
|
||||
test.. test... test..... test?..... test!....
|
||||
|
||||
!!!!!! ???? ,, -- ---
|
||||
|
||||
"Smartypants, double quotes" and 'single quotes'
|
||||
|
||||
## Emphasis
|
||||
|
||||
**This is bold text**
|
||||
|
||||
**This is bold text**
|
||||
|
||||
_This is italic text_
|
||||
|
||||
_This is italic text_
|
||||
|
||||
~~Strikethrough~~
|
||||
|
||||
## Blockquotes
|
||||
|
||||
> Blockquotes can also be nested...
|
||||
>
|
||||
> > ...by using additional greater-than signs right next to each other...
|
||||
> >
|
||||
> > > ...or with spaces between arrows.
|
||||
|
||||
## Lists
|
||||
|
||||
Unordered
|
||||
|
||||
- Create a list by starting a line with `+`, `-`, or `*`
|
||||
- Sub-lists are made by indenting 2 spaces:
|
||||
- Marker character change forces new list start:
|
||||
- Ac tristique libero volutpat at
|
||||
* Facilisis in pretium nisl aliquet
|
||||
- Nulla volutpat aliquam velit
|
||||
- Very easy!
|
||||
|
||||
Ordered
|
||||
|
||||
1. Lorem ipsum dolor sit amet
|
||||
2. Consectetur adipiscing elit
|
||||
3. Integer molestie lorem at massa
|
||||
|
||||
4. You can use sequential numbers...
|
||||
5. ...or keep all the numbers as `1.`
|
||||
|
||||
Start numbering with offset:
|
||||
|
||||
57. foo
|
||||
1. bar
|
||||
|
||||
## Code
|
||||
|
||||
Inline `code`
|
||||
|
||||
Indented code
|
||||
|
||||
// Some comments
|
||||
line 1 of code
|
||||
line 2 of code
|
||||
line 3 of code
|
||||
|
||||
Block code "fences"
|
||||
|
||||
```
|
||||
Sample text here...
|
||||
```
|
||||
|
||||
Syntax highlighting
|
||||
|
||||
```js
|
||||
var foo = function (bar) {
|
||||
return bar++;
|
||||
};
|
||||
|
||||
console.log(foo(5));
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ------------------------------------------------------------------------- |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
Right aligned columns
|
||||
|
||||
| Option | Description |
|
||||
| -----: | ------------------------------------------------------------------------: |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
## Links
|
||||
|
||||
[link text](http://dev.nodeca.com)
|
||||
|
||||
[link with title](http://nodeca.github.io/pica/demo/ "title text!")
|
||||
|
||||
Autoconverted link https://github.com/nodeca/pica (enable linkify to see)
|
||||
|
||||
## Images
|
||||
|
||||

|
||||

|
||||
|
||||
Like links, Images also have a footnote style syntax
|
||||
|
||||
![Alt text][id]
|
||||
|
||||
With a reference later in the document defining the URL location:
|
||||
|
||||
[id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"
|
||||
|
||||
## Plugins
|
||||
|
||||
The killer feature of `markdown-it` is very effective support of
|
||||
[syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin).
|
||||
|
||||
### [Emojies](https://github.com/markdown-it/markdown-it-emoji)
|
||||
|
||||
> Classic markup: :wink: :crush: :cry: :tear: :laughing: :yum:
|
||||
>
|
||||
> Shortcuts (emoticons): :-) :-( 8-) ;)
|
||||
|
||||
see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji.
|
||||
|
||||
### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup)
|
||||
|
||||
- 19^th^
|
||||
- H~2~O
|
||||
|
||||
### [\<ins>](https://github.com/markdown-it/markdown-it-ins)
|
||||
|
||||
++Inserted text++
|
||||
|
||||
### [\<mark>](https://github.com/markdown-it/markdown-it-mark)
|
||||
|
||||
==Marked text==
|
||||
|
||||
### [Footnotes](https://github.com/markdown-it/markdown-it-footnote)
|
||||
|
||||
Footnote 1 link[^first].
|
||||
|
||||
Footnote 2 link[^second].
|
||||
|
||||
Inline footnote^[Text of inline footnote] definition.
|
||||
|
||||
Duplicated footnote reference[^second].
|
||||
|
||||
[^first]: Footnote **can have markup**
|
||||
|
||||
and multiple paragraphs.
|
||||
|
||||
[^second]: Footnote text.
|
||||
|
||||
### [Definition lists](https://github.com/markdown-it/markdown-it-deflist)
|
||||
|
||||
Term 1
|
||||
|
||||
: Definition 1
|
||||
with lazy continuation.
|
||||
|
||||
Term 2 with _inline markup_
|
||||
|
||||
: Definition 2
|
||||
|
||||
{ some code, part of Definition 2 }
|
||||
|
||||
Third paragraph of definition 2.
|
||||
|
||||
_Compact style:_
|
||||
|
||||
Term 1
|
||||
~ Definition 1
|
||||
|
||||
Term 2
|
||||
~ Definition 2a
|
||||
~ Definition 2b
|
||||
|
||||
### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr)
|
||||
|
||||
This is HTML abbreviation example.
|
||||
|
||||
It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.
|
||||
|
||||
\*[HTML]: Hyper Text Markup Language
|
||||
|
||||
### [Custom containers](https://github.com/markdown-it/markdown-it-container)
|
||||
|
||||
::: warning
|
||||
_here be dragons_
|
||||
:::
|
Loading…
x
Reference in New Issue
Block a user