Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement keyword annotation feature #34

Merged
merged 6 commits into from
Apr 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@
"@zag-js/solid": "^0.2.10",
"@zag-js/toast": "^0.2.14",
"@zag-js/tooltip": "^0.2.13",
"monaco-editor": "0.33.0",
"axios": "^1.3.4",
"ethers": "^6.2.3",
"highlight.js": "^11.7.0",
"install": "^0.13.0",
"lodash": "^4.17.21",
"monaco-editor": "0.33.0",
"motion": "^10.15.5",
"npm": "^9.6.1",
"postcss-import": "^15.1.0",
"rehype-highlight": "^6.0.0",
"remark-directive": "^2.0.1",
"remark-directive-rehype": "^0.4.2",
"remark-gfm": "^3.0.1",
"solid-icons": "^1.0.4",
"solid-js": "^1.6.11",
Expand Down
4 changes: 2 additions & 2 deletions src/assets/css/mrakdown.css → src/assets/css/markdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@apply prose dark:prose-invert max-w-none text-light-secondary dark:text-dark-secondary;
@apply prose-pre:bg-light-tertiary/10 prose-pre:dark:bg-black/50;
@apply prose-pre:text-light-secondary prose-pre:dark:text-dark-secondary;
@apply prose-code:before:content-none prose-code:after:content-none;
@apply prose-code:before:content-none prose-code:after:content-none prose-code:text-light-secondary prose-code:dark:text-dark-secondary;
@apply prose-blockquote:before:content-none;
@apply prose-h1:border-b prose-h1:border-light-border dark:prose-h1:border-dark-border prose-h1:pb-4 prose-h1:text-3xl;

Expand All @@ -12,7 +12,7 @@
h4,
h5,
h6 {
@apply text-light-headline dark:text-dark-headline;
@apply text-light-secondary dark:text-dark-secondary;
}

:where(code):not(:where([class~='not-prose'] *)) {
Expand Down
22 changes: 22 additions & 0 deletions src/components/AnnotationView/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import url(../../assets/css/markdown.css);

.annotation-view {
@apply rounded-xl border-light-border dark:border-dark-border border p-0;

.hover-card-arrow {
@apply border-light-border dark:border-dark-border border relative rounded-sm;
clip-path: polygon(0 0, 0 12px, 12px 0);
}
}


.annotation-content {
@apply markdown;
@apply text-sm prose-p:my-4 !important;

h1, h2, h3, h4, h5, h6 {
@apply mt-0;
}
}


75 changes: 75 additions & 0 deletions src/components/AnnotationView/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { createResource, createSignal, ParentComponent, Show, Suspense } from 'solid-js';
import HoverCard from '~/components/HoverCard';
import './index.css';
import { BiRegularBookmarks } from 'solid-icons/bi';
import { useNavigate } from '@solidjs/router';
import { keywordAnnotationList } from '~/content/keyword-annotation';

interface Props {
id: string;
}

const AnnotationView: ParentComponent<Props> = (props) => {
const [open, setOpen] = createSignal(false);
const go = useNavigate();
const [res] = createResource(
() => open(),
async (open) => {
if (open) {
try {
const { keywordAnnotationList } = await import('~/content/keyword-annotation');
const key = (props.id ?? props.children).toLowerCase();
const annotation = keywordAnnotationList[key];
const content = await annotation.content();
return { content: content.default, url: annotation.url };
} catch (e) {
throw Error('Fetch data error');
}
}
},
);

const jumpUrl = () => {
const url = res()?.url;
if (url === undefined) return;
if (url.startsWith('http')) {
window.open(url);
} else {
go(url);
}
};

return (
<HoverCard
arrowSize={12}
positioning={{ gutter: 2 }}
class="annotation-view"
onOpenChange={(open) => setOpen(open)}
arrow={true}
content={
<div class="min-w-96 max-w-md">
<div class="font-bold text-lg px-4 py-2 border-b border-light-border dark:border-dark-border flex items-center">
<BiRegularBookmarks class="mr-1" />
{props.children}
</div>
<div class="mx-6 my-4">
<Suspense fallback="Loading...">
<Show when={res.error === undefined} keyed fallback="Unable to display content">
<article class="annotation-content">{res()?.content}</article>
</Show>
</Suspense>
</div>
</div>
}
>
<div
onClick={jumpUrl}
class="font-bold underline decoration-dotted underline-offset-4 hover:cursor-pointer px-1"
>
{props.children}
</div>
</HoverCard>
);
};

export default AnnotationView;
13 changes: 11 additions & 2 deletions src/components/CourseCore/CourseExplorer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {
} from 'solid-icons/fa';
import { useNavigate } from '@solidjs/router';
import { Portal } from 'solid-js/web';
import '~/assets/css/mrakdown.css';
import '~/assets/css/markdown.css';
import '~/assets/css/github-code.css';
import Catalogue from '~/components/CourseCore/CourseExplorer/Catalogue';
import ToolBox from '~/components/CourseCore/CourseExplorer/ToolBox';
import Header from '~/components/CourseCore/CourseExplorer/Header';
import Tooltip from '~/components/Tooltip';
import AnnotationView from '~/components/AnnotationView';

const Index: Component = () => {
const context = useCourseContext();
Expand Down Expand Up @@ -87,7 +88,15 @@ const Index: Component = () => {
'max-w-none': articleLooseLayout(),
}}
>
<article class="markdown">{context.article?.()?.({})}</article>
<article class="markdown">
{context.article?.()?.({
components: {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
term: (props) => <AnnotationView {...props} />,
},
})}
</article>
<Show when={context.isUnderWayChapter()} keyed>
<div class="pt-8 mt-8 border-t border-light-border dark:border-dark-border flex">
<button
Expand Down
9 changes: 7 additions & 2 deletions src/components/HoverCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createMemo, createUniqueId, JSX, ParentComponent, Show } from 'solid-js
import { Portal } from 'solid-js/web';
import * as hoverCard from '@zag-js/hover-card';
import { normalizeProps, useMachine } from '@zag-js/solid';
import { PositioningOptions } from '@zag-js/popper';

interface HoverCardProps {
content: JSX.Element | ((close?: () => void) => JSX.Element);
Expand All @@ -12,11 +13,13 @@ interface HoverCardProps {
closeDelay?: number;
defaultOpen?: boolean;
onOpenChange?: (changed: boolean) => void;
positioning?: PositioningOptions;
}

const HoverCard: ParentComponent<HoverCardProps> = (props) => {
const [state, send] = useMachine(
hoverCard.machine({
positioning: props.positioning,
id: createUniqueId(),
openDelay: props.openDelay ?? 100,
defaultOpen: props.defaultOpen,
Expand All @@ -32,7 +35,9 @@ const HoverCard: ParentComponent<HoverCardProps> = (props) => {
});
return (
<>
<div {...api().triggerProps}>{props.children}</div>
<div class="inline-block" {...api().triggerProps}>
{props.children}
</div>
<Show when={api().isOpen}>
<Portal>
<div
Expand All @@ -46,7 +51,7 @@ const HoverCard: ParentComponent<HoverCardProps> = (props) => {
<div
{...api().arrowTipProps}
data-part="arrow"
class="data-[part=arrow]:!bg-light-background data-[part=arrow]:dark:!bg-dark-background"
class="hover-card-arrow data-[part=arrow]:!bg-light-background data-[part=arrow]:dark:!bg-dark-background"
/>
</div>
</Show>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Tooltip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const Tooltip: ParentComponent<Props> = (props) => {

const api = createMemo(() => tooltip.connect(state, send, normalizeProps));
return (
<div>
<>
<button {...api().triggerProps}>{props.children}</button>
<Show when={api().isOpen}>
<Portal>
Expand All @@ -46,7 +46,7 @@ const Tooltip: ParentComponent<Props> = (props) => {
</div>
</Portal>
</Show>
</div>
</>
);
};

Expand Down
9 changes: 4 additions & 5 deletions src/content/courses/basic-theory/chapter_1.mdx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import CellsAnimate from "~/content/courses/basic-theory/CellsAnimate.tsx";

# What is CKB?
To understand :term[CKB], we must leave behind all the complex concepts and capture the very essence: it's all about cells and the transformation of cells.

To understand `CKB`, we must leave behind all the complex concepts and capture the very essence: it's all about cells and the transformation of cells.

`Cell` is the basic unit of CKB, similar to a cell in the human body. All the cells constitute the general state of the entire CKB blockchain. When we initiate a transaction on the blockchain, thus making a state change, in the end, for CKB, it is nothing more than spending some cells while creating some new ones, no matter how complicated the transaction and the state change are. This process is the same as the [Bitcoin UTXO](https://developer.bitcoin.org/glossary.html?highlight=utxo).
:term[Cell] is the basic unit of CKB, similar to a cell in the human body. All the cells constitute the general state of the entire CKB blockchain. When we initiate a transaction on the blockchain, thus making a state change, in the end, for CKB, it is nothing more than spending some cells while creating some new ones, no matter how complicated the transaction and the state change are. This process is the same as the [Bitcoin UTXO](https://developer.bitcoin.org/glossary.html?highlight=utxo).

Unspent cells are `live` cells; spent cells are `dead` cells. So, a CKB chain keeps on spending and creating cells through transactions, just like the renewal and division of cells throughout the body.

<CellsAnimate/>
<CellsAnimate />

> In the CKB universe, countless cells are floating around.
> Cells store data that jointly form the general state of the chain.
Expand All @@ -17,7 +16,7 @@ Unlike traditional `UTXO`, a cell can store any type of data. Each cell has a fi
unformatted string. The string can be in whatever format you want, as long as you can interpret it.

For example, the string can be a hash, a text, a date, or even a piece of binary code that can be referenced by
other cells and run on-chain through the CKB virtual machine, `CKB-VM`.
other cells and run on-chain through the CKB virtual machine, :term[CKB-VM].

As simple as that, this is the so-called `smart contract` on CKB.

Expand Down
6 changes: 6 additions & 0 deletions src/content/keyword-annotation/cell.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
All data on Nervos CKB is stored in cells. Cells are the primary state units in CKB, within them users can include arbitrary states.

A cell has 4 fields: `capacity`, `data`, `type and lock`.

#### Reference
- [Cell doc](https://docs.nervos.org/docs/reference/cell)
4 changes: 4 additions & 0 deletions src/content/keyword-annotation/ckb-vm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CKB VM is a crypto-agnostic virtual machine, a RISC-V instruction set based VM for executing both on-chain and off-chain code.

#### Reference
- [CKB-VM RFC (Final)](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0003-ckb-vm/0003-ckb-vm.md)
8 changes: 8 additions & 0 deletions src/content/keyword-annotation/ckb.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
An abbreviation which can have different meanings depending on the context:

- `Common Knowledge Base` <br/> The layer 1 blockchain of the Nervos Network.
- `Common Knowledge Byte` <br/> The native token of the Nervos Common Knowledge Base.

#### Reference
- [A Common Knowledge Base for Crypto-Economy](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0002-ckb/0002-ckb.md)

16 changes: 16 additions & 0 deletions src/content/keyword-annotation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Annotation } from '~/types/course';

export const keywordAnnotationList: Record<string, Annotation> = {
ckb: {
url: 'https://docs.nervos.org/docs/basics/glossary/#ckb',
content: () => import('./ckb.mdx'),
},
cell: {
url: 'https://docs.nervos.org/docs/basics/glossary/#cell',
content: () => import('./cell.mdx'),
},
'ckb-vm': {
url: 'https://docs.nervos.org/docs/basics/glossary/#ckb-vm',
content: () => import('./ckb-vm.mdx'),
},
};
10 changes: 9 additions & 1 deletion src/types/course.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component } from 'solid-js';
import { Component, JSX } from 'solid-js';
import { MDXComponent } from 'solid-mdx/client';
import { TranslateResource } from '~/types';

Expand Down Expand Up @@ -49,3 +49,11 @@ export interface CourseChapter {
titleTranslate?: TranslateResource<string>;
articleTranslate?: TranslateResource<Component>;
}

export type AnnotationContent = () => Promise<{ default: JSX.Element }>;

export interface Annotation {
url?: string;
content: AnnotationContent;
contentTranslate?: TranslateResource<AnnotationContent>;
}
4 changes: 3 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import solid from 'solid-start/vite';
import { defineConfig } from 'vite';
import Markdown from 'vite-plugin-solid-markdown';
import remarkGfm from 'remark-gfm';
import remarkDirective from 'remark-directive';
import remarkDirectiveRehype from 'remark-directive-rehype';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import rehypeHighlight from 'rehype-highlight';

export default defineConfig({
plugins: [
Markdown({
remarkPlugins: [remarkGfm],
remarkPlugins: [remarkGfm, remarkDirective, remarkDirectiveRehype],
rehypePlugins: [rehypeHighlight],
}),
solid({ ssr: false, extensions: ['.mdx', '.md'] }),
Expand Down
Loading