diff --git a/lib/remark-custom-blocks.js b/lib/remark-custom-blocks.js new file mode 100644 index 00000000..ce563f33 --- /dev/null +++ b/lib/remark-custom-blocks.js @@ -0,0 +1,160 @@ +const spaceSeparated = require('space-separated-tokens'); + +function escapeRegExp(str) { + return str.replace(new RegExp(`[-[\\]{}()*+?.\\\\^$|/]`, 'g'), '\\$&'); +} + +const C_NEWLINE = '\n'; +const C_FENCE = '|'; + +function compilerFactory(nodeType) { + let text; + let title; + + return { + blockHeading(node) { + title = this.all(node).join(''); + return ''; + }, + blockBody(node) { + text = this.all(node) + .map(s => s.replace(/\n/g, '\n| ')) + .join('\n|\n| '); + return text; + }, + block(node) { + text = ''; + title = ''; + this.all(node); + if (title) { + return `[[${nodeType} | ${title}]]\n| ${text}`; + } else { + return `[[${nodeType}]]\n| ${text}`; + } + }, + }; +} + +module.exports = function blockPlugin(availableBlocks = {}) { + const pattern = Object.keys(availableBlocks).map(escapeRegExp).join('|'); + + if (!pattern) { + throw new Error('remark-custom-blocks needs to be passed a configuration object as option'); + } + + const regex = new RegExp(`\\[\@(${pattern})(?: *\\| *(.*))?\]\n`); + + function blockTokenizer(eat, value, silent) { + const now = eat.now(); + const keep = regex.exec(value); + if (!keep) return; + if (keep.index !== 0) return; + const [eaten, blockType, blockTitle] = keep; + + /* istanbul ignore if - never used (yet) */ + if (silent) return true; + + const linesToEat = []; + const content = []; + + let idx = 0; + while ((idx = value.indexOf(C_NEWLINE)) !== -1) { + const next = value.indexOf(C_NEWLINE, idx + 1); + // either slice until next NEWLINE or slice until end of string + const lineToEat = next !== -1 ? value.slice(idx + 1, next) : value.slice(idx + 1); + if (lineToEat[0] !== C_FENCE) break; + // remove leading `FENCE ` or leading `FENCE` + const line = lineToEat.slice(lineToEat.startsWith(`${C_FENCE} `) ? 2 : 1); + linesToEat.push(lineToEat); + content.push(line); + value = value.slice(idx + 1); + } + + const contentString = content.join(C_NEWLINE); + + const stringToEat = eaten + linesToEat.join(C_NEWLINE); + + const potentialBlock = availableBlocks[blockType]; + const titleAllowed = + potentialBlock.title && ['optional', 'required'].includes(potentialBlock.title); + const titleRequired = potentialBlock.title && potentialBlock.title === 'required'; + + if (titleRequired && !blockTitle) return; + if (!titleAllowed && blockTitle) return; + + const add = eat(stringToEat); + if (potentialBlock.details) { + potentialBlock.containerElement = 'details'; + potentialBlock.titleElement = 'summary'; + } + + const exit = this.enterBlock(); + const contents = { + type: `${blockType}CustomBlockBody`, + data: { + hName: potentialBlock.contentsElement ? potentialBlock.contentsElement : 'div', + hProperties: { + className: 'custom-block-body', + }, + }, + children: this.tokenizeBlock(contentString, now), + }; + exit(); + + const blockChildren = [contents]; + if (titleAllowed && blockTitle) { + const titleElement = potentialBlock.titleElement ? potentialBlock.titleElement : 'div'; + const titleNode = { + type: `${blockType}CustomBlockHeading`, + data: { + hName: titleElement, + hProperties: { + className: 'custom-block-heading', + }, + }, + children: this.tokenizeInline(blockTitle, now), + }; + + blockChildren.unshift(titleNode); + } + + const classList = spaceSeparated.parse(potentialBlock.classes || ''); + + return add({ + type: `${blockType}CustomBlock`, + children: blockChildren, + data: { + hName: potentialBlock.containerElement ? potentialBlock.containerElement : 'div', + hProperties: { + className: ['custom-block', ...classList], + }, + }, + }); + } + + const Parser = this.Parser; + + // Inject blockTokenizer + const blockTokenizers = Parser.prototype.blockTokenizers; + const blockMethods = Parser.prototype.blockMethods; + blockTokenizers.customBlocks = blockTokenizer; + blockMethods.splice(blockMethods.indexOf('fencedCode') + 1, 0, 'customBlocks'); + const Compiler = this.Compiler; + if (Compiler) { + const visitors = Compiler.prototype.visitors; + if (!visitors) return; + Object.keys(availableBlocks).forEach(key => { + const compiler = compilerFactory(key); + visitors[`${key}CustomBlock`] = compiler.block; + visitors[`${key}CustomBlockHeading`] = compiler.blockHeading; + visitors[`${key}CustomBlockBody`] = compiler.blockBody; + }); + } + // Inject into interrupt rules + const interruptParagraph = Parser.prototype.interruptParagraph; + const interruptList = Parser.prototype.interruptList; + const interruptBlockquote = Parser.prototype.interruptBlockquote; + interruptParagraph.splice(interruptParagraph.indexOf('fencedCode') + 1, 0, ['customBlocks']); + interruptList.splice(interruptList.indexOf('fencedCode') + 1, 0, ['customBlocks']); + interruptBlockquote.splice(interruptBlockquote.indexOf('fencedCode') + 1, 0, ['customBlocks']); +}; diff --git a/lib/remark-plugins.js b/lib/remark-plugins.js index ba12d39b..08012059 100644 --- a/lib/remark-plugins.js +++ b/lib/remark-plugins.js @@ -6,11 +6,10 @@ const emoji = require('remark-emoji'); const paragraphAlerts = require('./remark-paragraph-alerts'); const images = require('remark-images'); const unwrapImages = require('remark-unwrap-images'); -const normalizeHeadings = require('remark-normalize-headings'); const slug = require('remark-slug'); const headingID = require('remark-heading-id'); const sectionize = require('remark-sectionize'); -const customBlocks = require('remark-custom-blocks'); +const customBlocks = require('./remark-custom-blocks'); const externalLinks = require('remark-external-links'); const remarkPlugins = [ @@ -19,7 +18,6 @@ const remarkPlugins = [ memoize(emoji), memoize(images), memoize(unwrapImages), - memoize(normalizeHeadings), memoize(slug), memoize(headingID), memoize(sectionize), diff --git a/package.json b/package.json index c530e1ff..fbd65b8e 100755 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "babel-plugin-macros": "^2.8.0", "cache-manager": "^3.3.0", "cache-manager-fs-hash": "^0.0.9", + "capsize": "^1.1.0", "csvtojson": "^2.0.10", "docsearch.js": "^2.6.3", "fathom-client": "^3.0.0", @@ -64,7 +65,7 @@ "swr": "^0.2.3", "turndown": "^6.0.0", "typescript": "^3.9.7", - "unified-vscode": "^1.0.0-beta.0", + "unified-vscode": "^1.0.0-beta.1", "unist-builder": "^2.0.3", "unist-util-is": "^4.0.2", "unist-util-select": "^3.0.1", diff --git a/public/images/pages/authentication-sm.svg b/public/images/pages/authentication-sm.svg new file mode 100644 index 00000000..d6cf77ba --- /dev/null +++ b/public/images/pages/authentication-sm.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/pages/authentication.svg b/public/images/pages/authentication.svg new file mode 100644 index 00000000..116f820e --- /dev/null +++ b/public/images/pages/authentication.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/pages/build-an-app-sm.svg b/public/images/pages/build-an-app-sm.svg new file mode 100644 index 00000000..9ad2c8c1 --- /dev/null +++ b/public/images/pages/build-an-app-sm.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/pages/build-an-app.svg b/public/images/pages/build-an-app.svg new file mode 100644 index 00000000..6ca434e6 --- /dev/null +++ b/public/images/pages/build-an-app.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/pages/connect-sm.svg b/public/images/pages/connect-sm.svg new file mode 100644 index 00000000..df65f00d --- /dev/null +++ b/public/images/pages/connect-sm.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/pages/connect.svg b/public/images/pages/connect.svg new file mode 100644 index 00000000..a99463fd --- /dev/null +++ b/public/images/pages/connect.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/pages/counter-tutorial-sm.svg b/public/images/pages/counter-tutorial-sm.svg new file mode 100644 index 00000000..f80ed4f4 --- /dev/null +++ b/public/images/pages/counter-tutorial-sm.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/pages/counter-tutorial.svg b/public/images/pages/counter-tutorial.svg new file mode 100644 index 00000000..e0b78c7f --- /dev/null +++ b/public/images/pages/counter-tutorial.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/pages/data-storage-sm.svg b/public/images/pages/data-storage-sm.svg new file mode 100644 index 00000000..24f4f4a7 --- /dev/null +++ b/public/images/pages/data-storage-sm.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/pages/data-storage.svg b/public/images/pages/data-storage.svg new file mode 100644 index 00000000..320deaca --- /dev/null +++ b/public/images/pages/data-storage.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/pages/hello-world-sm.svg b/public/images/pages/hello-world-sm.svg new file mode 100644 index 00000000..315cea81 --- /dev/null +++ b/public/images/pages/hello-world-sm.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/pages/hello-world.svg b/public/images/pages/hello-world.svg new file mode 100644 index 00000000..b4e0691f --- /dev/null +++ b/public/images/pages/hello-world.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/pages/mining-sm.svg b/public/images/pages/mining-sm.svg new file mode 100644 index 00000000..3ad8d2be --- /dev/null +++ b/public/images/pages/mining-sm.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/pages/mining.svg b/public/images/pages/mining.svg new file mode 100644 index 00000000..f19961f9 --- /dev/null +++ b/public/images/pages/mining.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/pages/radiks-sm.svg b/public/images/pages/radiks-sm.svg new file mode 100644 index 00000000..8dc6ec7e --- /dev/null +++ b/public/images/pages/radiks-sm.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/pages/radiks.svg b/public/images/pages/radiks.svg new file mode 100644 index 00000000..97c035ce --- /dev/null +++ b/public/images/pages/radiks.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/pages/smart-contracts-sm.svg b/public/images/pages/smart-contracts-sm.svg new file mode 100644 index 00000000..3b917903 --- /dev/null +++ b/public/images/pages/smart-contracts-sm.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/pages/smart-contracts.svg b/public/images/pages/smart-contracts.svg new file mode 100644 index 00000000..7ef603e0 --- /dev/null +++ b/public/images/pages/smart-contracts.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/pages/todo-app-sm.svg b/public/images/pages/todo-app-sm.svg new file mode 100644 index 00000000..566d6cd2 --- /dev/null +++ b/public/images/pages/todo-app-sm.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/images/pages/todo-app.svg b/public/images/pages/todo-app.svg new file mode 100644 index 00000000..a101f43b --- /dev/null +++ b/public/images/pages/todo-app.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/common/config.ts b/src/common/config.ts deleted file mode 100644 index 5600f362..00000000 --- a/src/common/config.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const config = { - title: 'Blockstack', - description: 'Docs', - baseurl: '', - url: 'https://docs.blockstack.org', - repository: 'blockstack/docs.blockstack', - github_editme_path: 'blockstack/docs.blockstack/edit/master', - branch_url: 'master', - twitter: { - username: 'blockstack', - image: '/assets/img/twitter.png', - card: 'summary', - }, - footer: { - copyright: 'Blockstack Public Benefit Corp.', - }, -}; diff --git a/src/common/constants.ts b/src/common/constants.ts index 75827376..66831831 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1,6 +1,7 @@ export const SIDEBAR_WIDTH = 240; -export const TOC_WIDTH = 240; +export const TOC_WIDTH = 208; export const CONTENT_MAX_WIDTH = 1104; +export const PAGE_WIDTH = 1216; export const SHIKI_THEME = 'Material-Theme-Default'; export const THEME_STORAGE_KEY = 'theme'; diff --git a/src/common/data/clarity-ref.ts b/src/common/data/clarity-ref.ts index 6e512261..9d9792cc 100644 --- a/src/common/data/clarity-ref.ts +++ b/src/common/data/clarity-ref.ts @@ -23,7 +23,8 @@ const generateMarkdown = () => { let functions = ''; CLARITY_REFERENCE.functions.forEach(entry => { - functions += `### ${entry.name} + functions += ` +### ${entry.name} **Signature:** ${inlineCode(entry.signature)}\n @@ -37,12 +38,12 @@ ${entry.description} #### Example {#${slugify(entry.name)}-example} -${wrapInClarityTicks(entry.example)} +${wrapInClarityTicks(entry.example)}\n `; }); CLARITY_REFERENCE.keywords.forEach(entry => { - keywords += `### ${entry.name} + keywords += `\n### ${entry.name} **Output:** ${inlineCode(entry.output_type)} @@ -50,7 +51,7 @@ ${entry.description} #### Example {#${slugify(entry.name)}-example} -${wrapInClarityTicks(entry.example)} +${wrapInClarityTicks(entry.example)}\n `; }); diff --git a/src/common/hydrate-mdx.ts b/src/common/data/hydrate-mdx.ts similarity index 100% rename from src/common/hydrate-mdx.ts rename to src/common/data/hydrate-mdx.ts diff --git a/src/common/navigation.yaml b/src/common/navigation.yaml index 115a0662..7390b63f 100644 --- a/src/common/navigation.yaml +++ b/src/common/navigation.yaml @@ -5,14 +5,14 @@ sections: - path: /smart-contracts pages: - path: /overview - - path: /testing-contracts - path: /principals - - path: /running-testnet-node + - path: /running-a-testnet-node sections: - title: Tutorials pages: - path: /hello-world-tutorial - path: /counter-tutorial + - path: /testing-contracts - path: /signing-transactions - path: /mining # is an overview page @@ -57,6 +57,13 @@ sections: pages: - path: /install-api - path: /installing-memcached + - path: /stacks-wallet + pages: + - path: /overview + - path: /install + - path: /usage + - path: /security + - path: /troubleshooting - path: /naming-services pages: - path: /overview @@ -92,11 +99,12 @@ sections: - path: /contributing - title: References + usePageTitles: true pages: - - path: /clarity-reference - - path: /blockstack-cli-reference - - path: /stacks-blockchain-reference - - path: /stacks-rpc-api-reference + - path: /clarity-language + - path: /blockstack-cli + - path: /stacks-blockchain + - path: /stacks-rpc-api - path: /faqs - path: /glossary - path: /deploy-tips diff --git a/src/common/utils.ts b/src/common/utils/index.ts similarity index 86% rename from src/common/utils.ts rename to src/common/utils/index.ts index fc0bedf3..22550aa9 100644 --- a/src/common/utils.ts +++ b/src/common/utils/index.ts @@ -9,7 +9,7 @@ const camelToKebab = (string: string) => .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2') .toLowerCase(); -export const slugify = (string: string) => +export const slugify = (string: string): string => string .toLowerCase() .replace(/\s+/g, '-') // Replace spaces with - @@ -60,3 +60,12 @@ export const onlyText = (children: ReactNode): string => { return text.concat(newText); }, '') as string; }; + +const getTitleFromHeading = (headings?: any[]) => + headings?.length + ? typeof headings[0] === 'string' + ? headings[0] + : headings[0].content + : undefined; + +export const getTitle = ({ title, headings }): string => title || getTitleFromHeading(headings); diff --git a/src/components/clarity-ref.tsx b/src/components/clarity-ref.tsx index 9cea4cb5..1cd2aa13 100644 --- a/src/components/clarity-ref.tsx +++ b/src/components/clarity-ref.tsx @@ -1,19 +1,25 @@ import React from 'react'; import { MDXComponents } from '@components/mdx/mdx-components'; import { TableOfContents } from '@components/toc'; -import { hydrate } from '@common/hydrate-mdx'; +import { hydrate } from '@common/data/hydrate-mdx'; +import { space } from '@blockstack/ui'; export const ClarityKeywordReference = ({ content, headings }) => { return ( <> - + {hydrate(content, MDXComponents)} ); }; export const ClarityFunctionReference = ({ content, headings }) => ( <> - + {hydrate(content, MDXComponents)} ); diff --git a/src/components/cli-reference.tsx b/src/components/cli-reference.tsx index dbdfbc37..60b4ebc2 100644 --- a/src/components/cli-reference.tsx +++ b/src/components/cli-reference.tsx @@ -3,7 +3,7 @@ import { cliReferenceData } from '@common/../_data/cliRef'; import { MDXComponents } from '@components/mdx/mdx-components'; import { Grid, Box, color } from '@blockstack/ui'; import { border } from '@common/utils'; -import { hydrate } from '@common/hydrate-mdx'; +import { hydrate } from '@common/data/hydrate-mdx'; const styles = { maxWidth: '100%', diff --git a/src/components/content-wrapper.tsx b/src/components/content-wrapper.tsx index 8a543970..3c6ee3ac 100644 --- a/src/components/content-wrapper.tsx +++ b/src/components/content-wrapper.tsx @@ -4,7 +4,6 @@ import { Flex, FlexProps, space } from '@blockstack/ui'; const ContentWrapper: React.FC = props => ( ( +const Image = ({ + src, + isHovered, + size, + ...rest +}: BoxProps & { src?: string; isHovered?: boolean }) => ( + style={{ + willChange: 'transform', + }} + width="100%" + transition="all 0.2s cubic-bezier(0.23, 1, 0.32, 1)" + size={size} + {...rest} + > + + ); const Title = ({ children, ...props }: BoxProps) => ( @@ -39,6 +60,7 @@ const InlineCard = ({ page }) => { return ( { position="relative" {...bind} > - - + + + + - + <Title + width={['100%', '100%', 'unset']} + color={hover ? color('accent') : color('text-title')} + mb={space('extra-tight')} + > {page.title || page.headings[0]} - {page.tags?.length - ? page.tags.map(tag => ( + {page.tags?.length ? ( + + {page.tags.map(tag => ( { > {tag} - )) - : null} + ))} + + ) : null} {page.description} @@ -81,6 +120,31 @@ const InlineCard = ({ page }) => { ); }; +const GridCard: React.FC = ({ page, ...rest }) => { + const { hover, active, bind } = useTouchable({ + behavior: 'link', + }); + return ( + + + + + + + {page.title || page.headings[0]} + + {page.description} + + + + ); +}; + export const PageReference = ({ children }) => { const content = onlyText(children).trim(); const [variant, _paths] = content.includes('\n') ? content.split('\n') : ['default', content]; @@ -90,23 +154,18 @@ export const PageReference = ({ children }) => { const pages = paths.map(path => routes?.find(route => route.path === path)).filter(page => page); return ( {pages.map(page => - variant === 'inline' ? ( - - ) : ( - - - - {page.title || page.headings[0]} - {page.description} - - - - ) + variant === 'inline' ? : )} ); diff --git a/src/components/faq.tsx b/src/components/faq.tsx index f4587cf9..d8967135 100644 --- a/src/components/faq.tsx +++ b/src/components/faq.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { MDXComponents } from '@components/mdx'; import { Box, Flex, ChevronIcon, space, color } from '@blockstack/ui'; -import { hydrate } from '@common/hydrate-mdx'; +import { hydrate } from '@common/data/hydrate-mdx'; import { Accordion, AccordionItem, AccordionButton, AccordionPanel } from '@reach/accordion'; import { border } from '@common/utils'; import { slugify } from '@common/utils'; diff --git a/src/components/glossary.tsx b/src/components/glossary.tsx index 0922f1ae..c6a15ad5 100644 --- a/src/components/glossary.tsx +++ b/src/components/glossary.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Box, space } from '@blockstack/ui'; -import { hydrate } from '@common/hydrate-mdx'; +import { hydrate } from '@common/data/hydrate-mdx'; import { MDXComponents } from '@components/mdx/mdx-components'; import { slugify } from '@common/utils'; import { css } from '@styled-system/css'; diff --git a/src/components/header.tsx b/src/components/header.tsx index 7cc6195f..94be662b 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -1,25 +1,14 @@ import React from 'react'; -import { - Flex, - Box, - BlockstackIcon, - Stack, - color, - space, - ChevronIcon, - BoxProps, -} from '@blockstack/ui'; -import { Link, Text, LinkProps } from '@components/typography'; +import { Flex, Box, BlockstackIcon, Stack, color, space, BoxProps } from '@blockstack/ui'; +import { Link, Text } from '@components/typography'; import MenuIcon from 'mdi-react/MenuIcon'; import CloseIcon from 'mdi-react/CloseIcon'; import { useMobileMenuState } from '@common/hooks/use-mobile-menu'; -import GithubIcon from 'mdi-react/GithubIcon'; -import { IconButton } from '@components/icon-button'; -import routes from '@common/routes'; + import { css } from '@styled-system/css'; import NextLink from 'next/link'; -import { useRouter } from 'next/router'; import { ColorModeButton } from '@components/color-mode-button'; +import { PAGE_WIDTH } from '@common/constants'; const MenuButton = ({ ...rest }: any) => { const { isOpen, handleOpen, handleClose } = useMobileMenuState(); @@ -37,73 +26,6 @@ const MenuButton = ({ ...rest }: any) => { ); }; -const BreadCrumbs: React.FC = props => { - const router = useRouter(); - const [route, setRoute] = React.useState(undefined); - const [section, setSection] = React.useState(undefined); - React.useEffect(() => { - routes.forEach(_section => { - _section?.routes?.length && - _section.routes.forEach(_route => { - if (router.route === `/${_route.path}`) { - setSection(_section); - setRoute(_route); - } - }); - }); - }, [router.route]); - - return route && section ? ( - - - - Docs - - - - - - - - {section?.title} - - - - - - - - {route?.title || (route?.headings.length && route.headings[0])} - - - - ) : ( - - ); -}; - -const GithubButton = (props: LinkProps) => ( - - - Find us on GitHub - - - -); - const HeaderWrapper: React.FC = React.forwardRef((props, ref) => ( )); @@ -111,9 +33,23 @@ const HeaderWrapper: React.FC = React.forwardRef((props, ref) => ( const nav = [ { label: 'Developers', + chilren: [ + { + label: 'Documentation', + }, + { + label: 'GitHub', + }, + { + label: 'Papers', + }, + { + label: 'Discord', + }, + ], }, - { label: 'Run a node' }, - { label: 'Build on Blockstack' }, + { label: 'Testnet' }, + { label: 'Discover apps' }, ]; export const HEADER_HEIGHT = 132; @@ -158,7 +94,7 @@ const Header = ({ hideSubBar, ...rest }: any) => { backdropFilter: 'blur(5px)', }} height="72px" - maxWidth="1280px" + maxWidth={`${PAGE_WIDTH}px`} mx="auto" px={space(['extra-loose', 'extra-loose', 'base', 'base'])} {...rest} @@ -177,7 +113,7 @@ const Header = ({ hideSubBar, ...rest }: any) => { - + {nav.map(item => ( {item.label} @@ -186,7 +122,6 @@ const Header = ({ hideSubBar, ...rest }: any) => { - diff --git a/src/components/layouts/base-layout.tsx b/src/components/layouts/base-layout.tsx index 32b4a2ab..24b4d9f8 100644 --- a/src/components/layouts/base-layout.tsx +++ b/src/components/layouts/base-layout.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { Flex, Box, FlexProps, color, space, CloseIcon, Fade, Transition } from '@blockstack/ui'; import { SideNav } from '../side-nav'; -import { Header, HEADER_HEIGHT } from '../header'; +import { Header } from '../header'; import { Main } from '../main'; import { Footer } from '../footer'; import NotFoundPage from '@pages/404'; -import { SIDEBAR_WIDTH } from '@common/constants'; +import { PAGE_WIDTH, SIDEBAR_WIDTH } from '@common/constants'; import { useWatchActiveHeadingChange } from '@common/hooks/use-active-heading'; import { useLockBodyScroll } from '@common/hooks/use-lock-body-scroll'; import { useMobileMenuState } from '@common/hooks/use-mobile-menu'; @@ -144,7 +144,7 @@ const BaseLayout: React.FC<{ isHome?: boolean }> = ({ children }) => {
- + title || (headings?.length && headings[0].content); +import { useRouter } from 'next/router'; +import dynamic from 'next/dynamic'; +import { getTitle } from '@common/utils'; + +const Search = dynamic(() => import('@components/search')); const PageTop = props => { + const router = useRouter(); + const isHome = router.pathname === '/'; return ( -

{getTitle(props)}

+ +

{getTitle(props)}

+ {isHome ? ( + + + + ) : null} +
{props.description ? ( {props.description}{' '} @@ -18,7 +31,7 @@ const PageTop = props => { {props.experience ? {props.experience} : null} - {props.duration ? {props.duration} : null} + {!isHome && props.duration ? {props.duration} : null} ); diff --git a/src/components/mdx/components/code.tsx b/src/components/mdx/components/code.tsx index 2edf24eb..4cad6e12 100644 --- a/src/components/mdx/components/code.tsx +++ b/src/components/mdx/components/code.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import { Box, BoxProps, color, themeColor } from '@blockstack/ui'; +import { Box, BoxProps, color, space, themeColor } from '@blockstack/ui'; import { border } from '@common/utils'; import { css } from '@styled-system/css'; import { Text } from '@components/typography'; -import { useColorMode } from '@pages/_app'; + +const LINE_MINIMUM = 4; const getHighlightLineNumbers = (str: string): number[] | undefined => { if (!str) return; @@ -20,26 +21,27 @@ const getHighlightLineNumbers = (str: string): number[] | undefined => { return numbers; }; -export const Code: React.FC = React.forwardRef( - ({ children, highlight, ...rest }, ref) => { - const [mode] = useColorMode(); - const numbers = getHighlightLineNumbers(highlight); +const generateCssStylesForHighlightedLines = (numbers: number[] = []) => { + const record = {}; + const style = { + bg: 'var(--colors-highlight-line-bg)', + '&::before': { + borderRightColor: themeColor('ink.600'), + }, + }; + numbers.forEach(number => { + record[`&:nth-of-type(${number})`] = style; + }); + return record; +}; - const generateCssStylesForHighlightedLines = (numbers: number[] = []) => { - const record = {}; - const style = { - bg: 'var(--colors-highlight-line-bg)', - '&::before': { - borderRightColor: themeColor('ink.600'), - }, - }; - numbers.forEach(number => { - record[`&:nth-of-type(${number})`] = style; - }); - return record; - }; +export const Code: React.FC< + BoxProps & { highlight?: string; lang?: string; lines: number } +> = React.memo( + React.forwardRef(({ children, highlight, lang, lines, ...rest }, ref) => { + const numbers = getHighlightLineNumbers(highlight); return ( - + = React.forwardRe display: 'inline-block', ...generateCssStylesForHighlightedLines(numbers), }, + counterReset: 'line', + '& .token-line': { + '.comment': { + color: 'rgba(255,255,255,0.5) !important', + }, + display: 'flex', + fontSize: '14px', + + '&::before': + lines > LINE_MINIMUM && lang !== 'bash' + ? { + counterIncrement: 'line', + content: 'counter(line, decimal-leading-zero)', + display: 'grid', + placeItems: 'center', + color: themeColor('ink.400'), + mr: '16px', + width: '42px', + fontSize: '12px', + borderRight: '1px solid rgb(39,41,46)', + } + : {}, + }, + pr: space(['base-loose', 'base-loose', 'extra-loose', 'extra-loose']), + pl: + lines <= LINE_MINIMUM || lang === 'bash' + ? space(['base-loose', 'base-loose', 'base-loose', 'base-loose']) + : 'unset', })} {...rest} > @@ -60,7 +90,7 @@ export const Code: React.FC = React.forwardRe ); - } + }) ); const preProps = { diff --git a/src/components/mdx/components/heading.tsx b/src/components/mdx/components/heading.tsx index 077077dc..c76a3219 100644 --- a/src/components/mdx/components/heading.tsx +++ b/src/components/mdx/components/heading.tsx @@ -1,4 +1,4 @@ -import { Box, FlexProps, BoxProps, color, useClipboard, space } from '@blockstack/ui'; +import { Box, Flex, FlexProps, BoxProps, color, useClipboard, space } from '@blockstack/ui'; import React from 'react'; import LinkIcon from 'mdi-react/LinkVariantIcon'; @@ -10,7 +10,6 @@ import { Title } from '@components/typography'; import { css } from '@styled-system/css'; import { getHeadingStyles, baseTypeStyles } from '@components/mdx/typography'; import { useRouter } from 'next/router'; -import { HEADER_HEIGHT } from '@components/header'; import { Link } from '@components/mdx/components/link'; const LinkButton = React.memo(({ link, onClick, ...rest }: BoxProps & { link: string }) => { @@ -63,9 +62,16 @@ const AnchorOffset = ({ id }: BoxProps) => ) : null; const Hashtag = () => ( - + - +
); export const Heading = ({ as, children, id, ...rest }: FlexProps) => { @@ -107,9 +113,7 @@ export const Heading = ({ as, children, id, ...rest }: FlexProps) => { })} onClick={id && handleLinkClick} > - - {children} - + {children} {id && isActive && } {id && } diff --git a/src/components/mdx/md-contents.tsx b/src/components/mdx/md-contents.tsx index a947fa62..825eaf43 100644 --- a/src/components/mdx/md-contents.tsx +++ b/src/components/mdx/md-contents.tsx @@ -7,9 +7,9 @@ import { TableOfContents } from '@components/toc'; import { css } from '@styled-system/css'; import { TOC_WIDTH } from '@common/constants'; import { styleOverwrites } from '@components/mdx/styles'; -import dynamic from 'next/dynamic'; import { border } from '@common/utils'; import { useRouter } from 'next/router'; +import dynamic from 'next/dynamic'; const Search = dynamic(() => import('@components/search')); @@ -20,7 +20,11 @@ export const MDContents: React.FC = React.memo( return ( <> = React.memo( {children} {!isHome ? ( - - + + {headings?.length > 1 ? ( diff --git a/src/components/mdx/styles.tsx b/src/components/mdx/styles.tsx index 5978d026..826d1eb4 100644 --- a/src/components/mdx/styles.tsx +++ b/src/components/mdx/styles.tsx @@ -3,6 +3,7 @@ import { color, space, themeColor } from '@blockstack/ui'; import { createGlobalStyle } from 'styled-components'; import { getHeadingStyles } from '@components/mdx/typography'; import { border } from '@common/utils'; +import { getCapsizeStyles } from '@components/mdx/typography'; export const MdxOverrides = createGlobalStyle` html, body { @@ -11,6 +12,11 @@ html, body { @counter-style list { pad: "0"; } +img{ + //image-rendering: auto; + //image-rendering: crisp-edges; + //image-rendering: pixelated; +} .headroom { top: 0; left: 0; @@ -98,9 +104,6 @@ html, body { color: ${color('accent')}; } -pre, code{ -font-family: "Soehne Mono", "Fira Code", monospace; -} pre{ display: inline-block; } @@ -159,26 +162,6 @@ export const styleOverwrites = { '& + h4, & + h5, & + h6, & + blockquote, & + ul, & + ol': { mt: 0, }, - counterReset: 'line', - '& .token-line': { - '.comment': { - color: 'rgba(255,255,255,0.5) !important', - }, - display: 'flex', - fontSize: '14px', - '&::before': { - counterIncrement: 'line', - content: 'counter(line, decimal-leading-zero)', - display: 'grid', - placeItems: 'center', - color: themeColor('ink.400'), - mr: '16px', - width: '42px', - fontSize: '12px', - borderRight: '1px solid rgb(39,41,46)', - }, - pr: space(['base-loose', 'base-loose', 'extra-loose', 'extra-loose']), - }, boxShadow: 'none', }, }, @@ -187,21 +170,7 @@ export const styleOverwrites = { }, 'p, li, a': { display: 'inline-block', - fontSize: '16px', - lineHeight: '28px', - padding: '0.05px 0', - '::before': { - content: "''", - marginTop: '-0.5144886363636364em', - display: 'block', - height: 0, - }, - '::after': { - content: "''", - marginBottom: '-0.5144886363636364em', - display: 'block', - height: 0, - }, + ...getCapsizeStyles(16, 28), }, li: { display: 'list-item', @@ -244,6 +213,16 @@ export const styleOverwrites = { borderRadius: '12px', }, }, + '*:not(pre) code': { + fontFamily: '"Soehne Mono", "Fira Code", monospace', + // ...getCapsizeStyles(14, 24), + // padding: '3px 2px', + }, + 'pre code': { + fontFamily: '"Soehne Mono", "Fira Code", monospace', + fontSize: '14px', + lineHeight: '24px', + }, h2: { mt: '64px', '&, & > *': { diff --git a/src/components/mdx/typography.ts b/src/components/mdx/typography.ts index 06209a4f..9fe473a0 100644 --- a/src/components/mdx/typography.ts +++ b/src/components/mdx/typography.ts @@ -1,94 +1,54 @@ +import capsize from 'capsize'; + +const fontMetrics = { + capHeight: 718, + ascent: 1040, + descent: -234, + lineGap: 0, + unitsPerEm: 1000, +}; + export const baseTypeStyles = { letterSpacing: '-0.01em', - dispay: 'flex', + display: 'flex', fontFeatureSettings: `'ss01' on`, fontFamily: `'Soehne', Inter, sans-serif`, }; +export const getCapsizeStyles = (fontSize, leading) => + capsize({ + fontMetrics, + fontSize, + leading, + }); + const h1 = { fontWeight: 600, - fontSize: ['38px', '38px', '44px'], - lineHeight: '52px', - padding: '0.05px 0', - ':before': { - content: "''", - marginTop: ['-0.32188995215311006em', '-0.32188995215311006em', '-0.2284090909090909em'], - display: 'block', - height: 0, - }, - ':after': { - content: "''", - marginBottom: ['-0.32188995215311006em', '-0.32188995215311006em', '-0.22840909090909092em'], - display: 'block', - height: 0, - }, + ...getCapsizeStyles(36, 48), }; const h2 = { - fontWeight: 500, - fontSize: '27.5px', - lineHeight: '34px', - padding: '0.05px 0', - ':before': { - content: "''", - marginTop: '-0.25636363636363635em', - display: 'block', - height: 0, - }, - ':after': { - content: "''", - marginBottom: '-0.2563636363636364em', - display: 'block', - height: 0, - }, + fontWeight: 600, + ...getCapsizeStyles(24, 36), }; const h3 = { - fontWeight: 500, - fontSize: '22px', - lineHeight: '32px', - padding: '0.05px 0', - ':before': { - content: "''", - marginTop: '-0.3659090909090909em', - display: 'block', - height: 0, - }, - ':after': { - content: "''", - marginBottom: '-0.3659090909090909em', - display: 'block', - height: 0, - }, + fontWeight: 400, + ...getCapsizeStyles(20, 32), }; const h4 = { - fontWeight: 400, - fontSize: '19.25px', - lineHeight: '28px', - padding: '0.05px 0', - ':before': { - content: "''", - marginTop: '-0.36623376623376624em', - display: 'block', - height: 0, - }, - ':after': { - content: "''", - marginBottom: '-0.36623376623376624em', - display: 'block', - height: 0, - }, + fontWeight: 500, + ...getCapsizeStyles(18, 32), }; + const h5 = { fontWeight: 400, - fontSize: '16px', - lineHeight: '28px', + ...getCapsizeStyles(16, 26), }; const h6 = { fontWeight: 400, - fontSize: '14px', - lineHeight: '28px', + ...getCapsizeStyles(14, 20), }; const headings = { diff --git a/src/components/pagination.tsx b/src/components/pagination.tsx index 3aa99993..ed1a7fdc 100644 --- a/src/components/pagination.tsx +++ b/src/components/pagination.tsx @@ -2,69 +2,52 @@ import React from 'react'; import { Box, BoxProps, Flex, Grid, color, space, transition } from '@blockstack/ui'; import routes from '@common/routes'; import { useRouter } from 'next/router'; -import { MDXComponents } from '@components/mdx'; -import { border } from '@common/utils'; +import { border, getTitle } from '@common/utils'; import NextLink from 'next/link'; -import { Link } from '@components/typography'; +import { Caption, Text, Link } from '@components/typography'; import { useTouchable } from '@common/hooks/use-touchable'; +import { getHeadingStyles } from '@components/mdx/typography'; +import { css } from '@styled-system/css'; + +const FloatingLink: React.FC = ({ href }) => ( + + + +); + +const getCategory = (pathname: string) => { + const arr = pathname.split('/'); + if (arr.length > 1) { + return arr[1]; + } + return undefined; +}; const usePaginateRoutes = () => { const router = useRouter(); - const getRoute = route => router.pathname === `/${route.path}`; - const getSection = _section => _section.routes.find(getRoute); - const findSectionByTitle = item => item.title === section.title; + const category = getCategory(router.pathname); + const getSection = route => getCategory(route.path) === category; + const findCurrentRouteInArray = item => item.path === router.pathname; - return { - next: undefined, - prev: undefined, - }; + const sectionRoutes = routes.filter(getSection); - const section = routes.find(getSection); - - if (!section) + if (!sectionRoutes) return { next: undefined, prev: undefined, }; - const { routes: sectionRoutes } = section; - const sectionIndex: number = routes.findIndex(findSectionByTitle); - - const nextSection = routes[sectionIndex + 1]; - const prevSection = routes[sectionIndex - 1]; - - const isFirstSection = sectionIndex === 0; - const isLastSection = sectionIndex === routes.length - 1; - - const routeIndex: number = sectionRoutes.findIndex(getRoute); - - const isFirstRouteInSection = routeIndex === 0; - const isLastRouteInSection = routeIndex === sectionRoutes.length - 1; + const routeIndex: number = sectionRoutes.findIndex(findCurrentRouteInArray); - let next; - let prev; - - if (!isLastRouteInSection) { - next = sectionRoutes[routeIndex + 1]; - } - if (isLastRouteInSection && !isLastSection) { - next = nextSection?.routes?.length && nextSection?.routes[0]; - } - if (!isFirstRouteInSection) { - prev = sectionRoutes[routeIndex - 1]; - } - if (isFirstRouteInSection && !isFirstSection) { - prev = prevSection?.routes?.length && prevSection?.routes[0]; - } + const next = sectionRoutes[routeIndex + 1]; + const prev = sectionRoutes[routeIndex - 1]; return { next, prev }; }; const Description: React.FC = props => ( - - - + ); const Card: React.FC = ({ children, ...rest }) => { @@ -93,6 +76,34 @@ const Card: React.FC = ({ children, ...rest }) => { ); }; +const Pretitle: React.FC = props => ( + +); + +const Title: React.FC = ({ isHovered, ...props }) => ( + +); + const PrevCard: React.FC = React.memo(props => { const { prev } = usePaginateRoutes(); @@ -100,17 +111,9 @@ const PrevCard: React.FC = React.memo(props => { {({ hover, active }) => ( <> - - - - - - Previous - - - - {prev.title || prev.headings[0]} - + + Previous + {getTitle(prev)} {prev.description && {prev.description}} )} @@ -126,21 +129,9 @@ const NextCard: React.FC = React.memo(props => { {({ hover, active }) => ( <> - - - - - Next - - - - {next.title || next.headings[0]} - - + + Next + {getTitle(next)} {next.description && {next.description}} )} diff --git a/src/components/search.tsx b/src/components/search.tsx index 303a13b1..6fd6e016 100644 --- a/src/components/search.tsx +++ b/src/components/search.tsx @@ -90,7 +90,7 @@ export const SearchBox: React.FC = React.memo(props => { = ({ width = `${SIDEBAR_WIDTH}px`, @@ -21,7 +22,6 @@ const Wrapper: React.FC = ({ width={width} maxHeight={`calc(100vh - 60px)`} overflow="auto" - pb="62px" px={space('base')} top={0} pt="64px" @@ -41,15 +41,15 @@ const capitalize = s => { const convertToTitle = (path: string) => !path ? null : path === '/' ? 'Home' : capitalize(path.replace('/', '').replace(/-/g, ' ')); -const PageItem = ({ isActive, ...props }: any) => ( +const PageItem = ({ isActive, color: _color = color('text-caption'), ...props }: any) => ( ); @@ -57,39 +57,56 @@ const PageItem = ({ isActive, ...props }: any) => ( const SectionTitle: React.FC = props => ( ); +const getRoutePath = path => routes.find(route => route.path.endsWith(path)); + const ChildPages = ({ items, handleClick }: any) => - items.pages.map(page => { - const path = page.pages - ? `${page.path}${page.pages[0].path}` - : items.path - ? '/' + slugify(items.path) + page.path - : page.path; + items?.pages + ? items?.pages?.map(page => { + const path = page.pages + ? `${page.path}${page.pages[0].path}` + : items.path + ? '/' + slugify(items.path) + page.path + : page.path; - const routePath = routes.find(route => route.path.endsWith(path)); + const router = useRouter(); - return ( - - - handleClick(page) : undefined} as="a"> - {convertToTitle(page.path)} - - - - ); - }); + const routePath = routes.find(route => route.path.endsWith(path)); + + return ( + + + handleClick(page) : undefined} + as="a" + > + {convertToTitle(page.path)} + + + + ); + }) + : null; -const ChildSection = ({ sections }) => +const ChildSection: React.FC = ({ sections, ...rest }) => sections.map(section => { return ( - - {section.title} + + + {section.title} + ); @@ -104,25 +121,48 @@ const BackItem = props => ( ); -const NewNav = () => { +const Navigation = () => { const [selected, setSelected] = React.useState({ type: 'default', items: nav.sections, selected: undefined, }); + const router = useRouter(); + + React.useEffect(() => { + let currentSection = selected.items; + nav.sections.forEach(section => { + section.pages.forEach(page => { + if (page.pages) { + const pagesFound = page.pages.find(_page => { + return router.pathname.endsWith(page.path + _page.path); + }); + const sectionsFound = page?.sections?.find(_section => { + return _section.pages.find(_page => { + return router.pathname.endsWith(page.path + _page.path); + }); + }); + if (pagesFound || sectionsFound) { + currentSection = page; + } + } else { + return router.pathname.endsWith(page.path); + } + }); + }); + + if (selected.items !== currentSection) { + setSelected({ type: 'page', items: currentSection }); + } + }, []); + const handleClick = (page: any) => { if (page.pages) { setSelected({ type: 'page', items: page, }); - } else { - setSelected({ - type: 'default', - items: nav.sections, - selected: page.path, - }); } }; @@ -141,6 +181,7 @@ const NewNav = () => { {selected.items ? : null} {selected.items?.sections ? ( ({ ...section, path: selected.items.path, @@ -159,7 +200,7 @@ const NewNav = () => { ? { color: color('text-title'), mb: space('tight'), - fontSize: '16px', + fontSize: '18px', _hover: { color: color('accent'), cursor: 'pointer', @@ -167,9 +208,9 @@ const NewNav = () => { } : {}; return ( - + {section.title ? ( - + {section.title} { const path = page.pages ? `${page.path}${page.pages[0].path}` : section?.title - ? '/' + slugify(section?.title) + page.path + ? `/${slugify(section?.title)}${page.path}` : page.path; + const route = getRoutePath(path); + return ( @@ -195,10 +238,10 @@ const NewNav = () => { as="a" href={path} {...itemProps} - isActive={selected.selected === page.path} + isActive={router.pathname.endsWith(path)} onClick={() => handleClick(page)} > - {convertToTitle(page.path)} + {section.usePageTitles ? getTitle(route) : convertToTitle(page.path)} @@ -216,7 +259,7 @@ export const SideNav: React.FC = ({ }) => { return ( - + ); }; diff --git a/src/components/toc.tsx b/src/components/toc.tsx index e4b153c4..2304e6c0 100644 --- a/src/components/toc.tsx +++ b/src/components/toc.tsx @@ -63,12 +63,7 @@ export const TableOfContents = ({ } & BoxProps) => { return ( - + {!noLabel && ( diff --git a/src/components/typography.tsx b/src/components/typography.tsx index b8a02523..75ac0403 100644 --- a/src/components/typography.tsx +++ b/src/components/typography.tsx @@ -1,19 +1,17 @@ import * as React from 'react'; -import { Text as BaseText, BoxProps, color, FlexProps } from '@blockstack/ui'; -import { border } from '@common/utils'; +import { Text as BaseText, BoxProps, color } from '@blockstack/ui'; +import { getCapsizeStyles } from '@components/mdx/typography'; export const Text = React.forwardRef((props: BoxProps, ref) => ( - + )); export const Caption: React.FC = props => ( ); @@ -22,26 +20,6 @@ export const Title: React.FC = React.forwardRef((props, ref) => ( )); -export const SectionTitle: React.FC = props => ( - -); - -export const Pre = React.forwardRef((props: BoxProps, ref) => ( - <Text - fontFamily={`"Soehne Mono", "Fira Code", monospace`} - bg={color('bg-alt')} - borderRadius="3px" - px="extra-tight" - border={border()} - fontSize="12px" - ref={ref} - {...props} - style={{ - whiteSpace: 'nowrap', - }} - /> -)); - export type LinkProps = BoxProps & Partial<React.AnchorHTMLAttributes<HTMLAnchorElement>>; export const Link = React.forwardRef(({ _hover = {}, ...props }: LinkProps, ref) => ( diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 43b08079..66a0da9a 100755 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -41,28 +41,28 @@ export default class MyDocument extends Document<any> { font-family: 'Soehne Mono'; src: url('/static/fonts/soehne-mono-web-buch.otf') format('opentype'); font-weight: 400; - font-display: auto; + font-display: swap; font-style: normal; } @font-face { font-family: 'Soehne'; src: url('/static/fonts/soehne-web-buch.otf') format('opentype'); font-weight: 400; - font-display: auto; + font-display: swap; font-style: normal; } @font-face { font-family: 'Soehne'; src: url('/static/fonts/soehne-web-kraftig_1.otf') format('opentype'); font-weight: 500; - font-display: auto; + font-display: swap; font-style: normal; } @font-face { font-family: 'Soehne'; src: url('/static/fonts/soehne-web-halbfett_1.otf') format('opentype'); font-weight: 600; - font-display: auto; + font-display: swap; font-style: normal; } `, diff --git a/src/pages/authentication/building-todo-app.md b/src/pages/authentication/building-todo-app.md index 67d7d7e7..e21763a6 100644 --- a/src/pages/authentication/building-todo-app.md +++ b/src/pages/authentication/building-todo-app.md @@ -3,6 +3,9 @@ title: Building a Todo app description: Learn how to integrate authentication and data storage. tags: - tutorial +images: + large: /images/pages/todo-app.svg + sm: /images/pages/todo-app-sm.svg redirect_from: - /develop/zero_to_dapp_1.html - /browser/hello-blockstack.html diff --git a/src/pages/authentication/connect.md b/src/pages/authentication/connect.md index c5831ac1..d11fd9f8 100644 --- a/src/pages/authentication/connect.md +++ b/src/pages/authentication/connect.md @@ -1,10 +1,29 @@ --- -description: 'Guide to Blockstack Connect' +title: Blockstack Connect +description: Learn what Connect is and how to integrate it into an app. experience: beginners --- # Guide to Blockstack Connect +## Introduction + +Blockstack Connect is a JavaScript library for integrating Blockstack authentication and smart contracts into your app. + +The library empowers you to: + +- Register new users with a pre-built onboarding flow that quickly educates them as to the privacy benefits of using your app with Blockstack and provisions a "Secret Key" that secures their identity and data against the Stacks blockchain. +- Authenticate users when they return to your app using that same Secret Key. +- Prompt users to sign transactions with smart contracts as written in Clarity and published to the Stacks blockchain. + +## How does this compare to `blockstack.js`? + +Although [`blockstack.js`](https://github.com/blockstack/blockstack.js) can also be used to authenticate users, it implements the deprecated [Blockstack Browser](https://browser.blockstack.org/) and lacks any pre-built onboarding UI that educates users as to how your app is more secure for having implemented Blockstack. As such, we advise that you use `blockstack.js` for all other functionality apart from authentication, such as saving and retrieving user data with Gaia. + +## Start building with Blockstack Connect + +Head over to the [to-do app tutorial](/authentication/building-todo-app) to learn how to build apps with Blockstack Connect. + ## Installation With yarn: diff --git a/src/pages/authentication/overview.md b/src/pages/authentication/overview.md index 86b74fc5..69152dcb 100644 --- a/src/pages/authentication/overview.md +++ b/src/pages/authentication/overview.md @@ -1,6 +1,9 @@ --- title: Authentication description: Blockstack Auth provides single sign on and authentication without third parties or remote servers. +images: + large: /images/pages/authentication.svg + sm: /images/pages/authentication-sm.svg --- # Authentication protocol diff --git a/src/pages/authentication/profiles.md b/src/pages/authentication/profiles.md index 700f7453..81f93128 100644 --- a/src/pages/authentication/profiles.md +++ b/src/pages/authentication/profiles.md @@ -4,7 +4,7 @@ title: Guide to Blockstack Profiles # Guide to Blockstack Profiles -You can use `blockstack.js` to create and register an Blockstack username on the Stacks blockchain. +You can use `blockstack.js` to create and register a Blockstack username on the Stacks blockchain. This section describes the `Profile` object and contains the following topics: ## About profiles diff --git a/src/pages/build-an-app.md b/src/pages/build-an-app.md index 9b71338f..bff3caf4 100644 --- a/src/pages/build-an-app.md +++ b/src/pages/build-an-app.md @@ -1,13 +1,16 @@ --- title: Building decentralized apps description: Overview and guides for getting started building decentralized applications. +images: + large: /images/pages/build-an-app.svg + sm: /images/pages/build-an-app-sm.svg --- # Building decentralized apps Prefer to jump right in? Get started with this tutorial where you’ll create a decentralized to-do list app. -[[page-reference | inline]] +[@page-reference | inline] | /authentication/building-todo-app ## What are decentralized apps? @@ -44,13 +47,13 @@ To build your decentralized app, you’ll use authentication, data storage, data Like a regular app, yours will require user authentication and data storage — but decentralized. Get started with the documentation below or try the tutorial. -[[page-reference | inline]] +[@page-reference | inline] | /authentication/overview -[[page-reference | inline]] +[@page-reference | inline] | /data-storage/overview -[[page-reference | inline]] +[@page-reference | inline] | /authentication/building-todo-app ### Data indexing @@ -58,7 +61,7 @@ documentation below or try the tutorial. If you need to store and index data shared by multiple users, such as messages or a shared document, read the Radiks documentation. -[[page-reference | inline]] +[@page-reference | inline] | /data-indexing/overview ### Smart contracts @@ -66,11 +69,11 @@ documentation. You can use smart contracts to decentralize your app’s backend logic, making it open and permissionless. Smart contracts on Blockstack are written in the Clarity language. View the smart contracts documentation or get started with a tutorial. -[[page-reference | inline]] +[@page-reference | inline] | /smart-contracts/overview -[[page-reference | inline]] +[@page-reference | inline] | /smart-contracts/hello-world-tutorial -[[page-reference | inline]] +[@page-reference | inline] | /smart-contracts/counter-tutorial diff --git a/src/pages/data-indexing/overview.md b/src/pages/data-indexing/overview.md index 0dbedce1..0a4182c3 100644 --- a/src/pages/data-indexing/overview.md +++ b/src/pages/data-indexing/overview.md @@ -1,6 +1,9 @@ --- -title: Overview +title: Radiks description: Using Radiks you can build multi-player apps that index, store, and query user data. +images: + large: /images/pages/radiks.svg + sm: /images/pages/radiks-sm.svg --- # Radiks the data indexer diff --git a/src/pages/data-storage/overview.md b/src/pages/data-storage/overview.md index 8090b673..5263a724 100644 --- a/src/pages/data-storage/overview.md +++ b/src/pages/data-storage/overview.md @@ -1,6 +1,9 @@ --- title: A decentralized storage architecture -description: 'Storing user data with Blockstack' +description: Storing user data with Blockstack +images: + large: /images/pages/data-storage.svg + sm: /images/pages/data-storage-sm.svg --- # A decentralized storage architecture diff --git a/src/pages/index.md b/src/pages/index.md index fa162de0..03b43f4d 100644 --- a/src/pages/index.md +++ b/src/pages/index.md @@ -1,28 +1,21 @@ --- -title: 'Documentation' +title: Documentation +description: All you need to build decentralized apps and smart contracts. --- # Documentation -All you need to build decentralized apps and smart contracts - ## Get started -[[page-reference]] +[@page-reference | grid] | /build-an-app, /smart-contracts/overview, /mining ## Tutorials -[[page-reference]] -| /authentication/building-todo-app, /smart-contracts/hello-world-tutorial, /smart-contracts/counter-tutorial +[@page-reference | grid] +| /authentication/building-todo-app, /smart-contracts/counter-tutorial ## Explore -[[page-reference]] +[@page-reference | grid-small] | /ecosystem/overview, /ecosystem/stacks-token - -## Sample projects - -Showcase your sample project - -[Submit on GitHub](https://github.com/blockstack/docs.blockstack) diff --git a/src/pages/mining.md b/src/pages/mining.md index e58f08b1..62e57717 100644 --- a/src/pages/mining.md +++ b/src/pages/mining.md @@ -1,6 +1,9 @@ --- title: Mine Stacks Token description: Run a node, earn STX, and learn how Proof of Transfer (PoX) works +images: + large: /images/pages/mining.svg + sm: /images/pages/mining-sm.svg --- # Mine Stacks Token diff --git a/src/pages/references/blockstack-cli-reference.md b/src/pages/references/blockstack-cli.md similarity index 98% rename from src/pages/references/blockstack-cli-reference.md rename to src/pages/references/blockstack-cli.md index 653f2e5a..170cc7bb 100644 --- a/src/pages/references/blockstack-cli-reference.md +++ b/src/pages/references/blockstack-cli.md @@ -1,5 +1,5 @@ --- -title: Blockstack CLI Reference +title: Blockstack CLI --- export { convertBlockstackCLIUsageToMdx as getStaticProps } from '@common/data/cli-ref' diff --git a/src/pages/references/clarity-reference.md b/src/pages/references/clarity-language.md similarity index 97% rename from src/pages/references/clarity-reference.md rename to src/pages/references/clarity-language.md index bb42d0f8..81eae845 100644 --- a/src/pages/references/clarity-reference.md +++ b/src/pages/references/clarity-language.md @@ -1,5 +1,6 @@ --- -description: 'See a detailed list of all keywords and functions for the Clarity language.' +title: Clarity Language +description: See a detailed list of all keywords and functions for the Clarity language. --- export { convertClarityRefToMdx as getStaticProps } from '@common/data/clarity-ref' diff --git a/src/pages/references/faqs.md b/src/pages/references/faqs.md index 4ce5c9cf..697befa4 100644 --- a/src/pages/references/faqs.md +++ b/src/pages/references/faqs.md @@ -1,5 +1,6 @@ --- -description: 'Blockstack Network documentation' +title: FAQs +description: This is a comprehensive list of all the Blockstack FAQs. redirect_from: /org/voucherholder --- diff --git a/src/pages/references/glossary.md b/src/pages/references/glossary.md index 100d2697..7aacdceb 100644 --- a/src/pages/references/glossary.md +++ b/src/pages/references/glossary.md @@ -1,5 +1,5 @@ --- -description: 'Blockstack Network documentation' +description: A comprehensive list of terms used within the ecosystem. --- export { convertGlossaryToJson as getStaticProps } from '@common/data/glossary' diff --git a/src/pages/references/stacks-blockchain-reference.md b/src/pages/references/stacks-blockchain.md similarity index 97% rename from src/pages/references/stacks-blockchain-reference.md rename to src/pages/references/stacks-blockchain.md index 6739f79b..55733bfe 100644 --- a/src/pages/references/stacks-blockchain-reference.md +++ b/src/pages/references/stacks-blockchain.md @@ -1,5 +1,5 @@ --- -title: 'Stacks Blockchain APIs' +title: Stacks Blockchain APIs description: Interacting with the Stacks 2.0 Blockchain --- diff --git a/src/pages/references/stacks-rpc-api-reference.md b/src/pages/references/stacks-rpc-api.md similarity index 97% rename from src/pages/references/stacks-rpc-api-reference.md rename to src/pages/references/stacks-rpc-api.md index 4d50e64e..d7714647 100644 --- a/src/pages/references/stacks-rpc-api-reference.md +++ b/src/pages/references/stacks-rpc-api.md @@ -1,5 +1,6 @@ --- -description: 'Stacks Node RPC API Reference' +title: Stacks Node RPC API +description: Every running Stacks node exposes an RPC API, which allows you to interact with the underlying blockchain. --- # RPC API Reference diff --git a/src/pages/smart-contracts/counter-tutorial.md b/src/pages/smart-contracts/counter-tutorial.md index 1375e4af..fe2360ef 100644 --- a/src/pages/smart-contracts/counter-tutorial.md +++ b/src/pages/smart-contracts/counter-tutorial.md @@ -2,6 +2,11 @@ description: Learn how to write a simple smart contract in the Clarity language. experience: intermediate duration: 30 minutes +tags: + - tutorial +images: + large: /images/pages/counter-tutorial.svg + sm: /images/pages/counter-tutorial-sm.svg --- # Counter smart contract diff --git a/src/pages/smart-contracts/hello-world-tutorial.md b/src/pages/smart-contracts/hello-world-tutorial.md index 753f23f9..15db5f4a 100644 --- a/src/pages/smart-contracts/hello-world-tutorial.md +++ b/src/pages/smart-contracts/hello-world-tutorial.md @@ -1,14 +1,17 @@ --- +title: Hello, World! description: Get Started Writing Smart Contracts with Clarity +duration: 18 minutes +experience: beginners +images: + large: /images/pages/hello-world-app.svg + sm: /images/pages/hello-world-sm.svg --- # Hello, World! ## Overview -| Experience | | **Beginner** | -| Duration | | **18 minutes** | - In the world of smart contracts, everything is a blockchain transaction. You use tokens in your wallet to deploy a smart contract in a transaction, and each call to that contract after it's published is a transaction, too. That means that at each step, tokens are being exchanged as transaction fees. This tutorial introduces you to this mode of programming, which transforms blockchains into powerful state machines capable of executing complex logic. Clarity, Blockstack's smart contracting language, is based on LISP and uses its parenthesized notation. Clarity is an [interpreted language](https://en.wikipedia.org/wiki/Interpreted_language), and [decidable](https://en.wikipedia.org/wiki/Recursive_language). To learn more basics about the language, see the [Introduction to Clarity](overview) topic. diff --git a/src/pages/smart-contracts/overview.md b/src/pages/smart-contracts/overview.md index a7a9cea5..0bc2f66b 100644 --- a/src/pages/smart-contracts/overview.md +++ b/src/pages/smart-contracts/overview.md @@ -1,8 +1,12 @@ --- -description: 'Blockstack Clarity: Introduction' +title: Write smart contracts +description: Overview and guides for getting started with Clarity +images: + large: /images/pages/smart-contracts.svg + sm: /images/pages/smart-contracts-sm.svg --- -# Introduction to Clarity +# Write smart contracts Clarity is a programming language for writing smart contracts on the Stacks 2.0 blockchain. It supports programmatic control over digital assets. @@ -28,7 +32,7 @@ Not every decentralized application requires smart contracts, but Clarity unlock Clarity differs from most other smart contract languages in two essential ways: -- The language is interpreted and broadcasted on the blockchain as is (not compiled) +- The language is interpreted and broadcast on the blockchain as is (not compiled) - The language is decidable (not Turing complete) Using an interpreted language ensures that the executed code is human-readable and auditable. A decidable language like Clarity makes it possible to determine precisely which code is going to be executed, for any function. diff --git a/src/pages/smart-contracts/running-testnet-node.md b/src/pages/smart-contracts/running-a-testnet-node.md similarity index 100% rename from src/pages/smart-contracts/running-testnet-node.md rename to src/pages/smart-contracts/running-a-testnet-node.md diff --git a/src/pages/smart-contracts/testing-contracts.md b/src/pages/smart-contracts/testing-contracts.md index ca4c4fb0..84caf00b 100644 --- a/src/pages/smart-contracts/testing-contracts.md +++ b/src/pages/smart-contracts/testing-contracts.md @@ -1,14 +1,14 @@ --- +title: Testing Clarity code description: Learn to Test Clarity Contract Code with JavaScript and Mocha +experience: advanced +duration: 15 minutes --- -# Test Clarity Code +# Testing Clarity code ## Overview -| Experience | | **Advanced** | -| Duration | | **15 minutes** | - Clarity, Blockstack's smart contracting language, is based on [LISP](<https://en.wikipedia.org/wiki/Lisp_(programming_language)>). Clarity is an interpreted language, and [decidable](https://en.wikipedia.org/wiki/Recursive_language). In this tutorial, you will learn how to test Clarity and how use [Mocha](https://mochajs.org/) to test Clarity contracts while you develop them. - Have a working Clarity starter project @@ -138,15 +138,9 @@ Now, review the last test `should echo number` on your own and try to understand --- -With the completion of this tutorial, you ... +With the completion of this tutorial, you: - Created a working Clarity starter project - Understood how to test Clarity contracts Congratulations! - -## Where to go next - -- <a href="principals.html">Guide: Understanding principals</a> -- <a href="tutorial-counter.html">Next tutorial: Writing a counter smart contract</a> -- <a href="clarityRef.html">Clarity language reference</a> diff --git a/yarn.lock b/yarn.lock index c94ca4d3..b02c7043 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2905,6 +2905,13 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001093: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001107.tgz#809360df7a5b3458f627aa46b0f6ed6d5239da9a" integrity sha512-86rCH+G8onCmdN4VZzJet5uPELII59cUzDphko3thQFgAQG1RNa+sVLDoALIhRYmflo5iSIzWY3vu1XTWtNMQQ== +capsize@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/capsize/-/capsize-1.1.0.tgz#d70f918b5ecadc2390b2a3b3b672ced639907ed6" + integrity sha512-tV0weOXxQ9hsbqewiWfGQBXsGzSN/i/w9IGuH+JVySzGOhscTAzeK3N9ZAM3Kf89bWgYot80Za5fSXKi6ZynvA== + dependencies: + round-to "^4.1.0" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -8072,6 +8079,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +round-to@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/round-to/-/round-to-4.1.0.tgz#148d768d18b2f127f78e6648cb8b0a5943c416bf" + integrity sha512-H/4z+4QdWS82iMZ23+5St302Mv2jJws0hUvEogrD6gC8NN6Z5TalDtbg51owCrVy4V/4c8ePvwVLNtlhEfPo5g== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -9172,10 +9184,10 @@ unicode-property-aliases-ecmascript@^1.0.4: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== -unified-vscode@^1.0.0-beta.0: - version "1.0.0-beta.0" - resolved "https://registry.yarnpkg.com/unified-vscode/-/unified-vscode-1.0.0-beta.0.tgz#d09a7375943094ac7b1a782e98b404405343a0ff" - integrity sha512-DsTZZqOx85ycgNKzemttRW2oBJN/7ZXZzJnvbaTQ594IH8L5zARrdWFqZxEiCgHM7rpYjnE52SbKMheH4VwBFQ== +unified-vscode@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/unified-vscode/-/unified-vscode-1.0.0-beta.1.tgz#d80c220c119e77da60c81fea1e441a062507449d" + integrity sha512-YXjyTkJRCeePjFfz8HCZM0ZZmiuHMwb83B8jQZ922rrnMas77MSYT7S63MFA2vBTOxvEJzeKutuTjPvRYd/hfA== dependencies: cache-manager "^3.3.0" cache-manager-fs-hash "^0.0.9" @@ -9576,6 +9588,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +"vscode-textmate@git+https://github.com/octref/vscode-textmate.git": + version "4.0.1" + uid e65aabe2227febda7beaad31dd0fca1228c5ddf3 + resolved "git+https://github.com/octref/vscode-textmate.git#e65aabe2227febda7beaad31dd0fca1228c5ddf3" + "vscode-textmate@https://github.com/octref/vscode-textmate": version "4.0.1" resolved "https://github.com/octref/vscode-textmate#e65aabe2227febda7beaad31dd0fca1228c5ddf3"