From 6621d60a767a69b2a6742a1ac479e044e99167b7 Mon Sep 17 00:00:00 2001 From: Mike Barkmin Date: Wed, 26 Oct 2022 11:23:52 +0200 Subject: [PATCH 1/3] refactor platforms Platforms are now in a separate folder. The core functionality of the web platforms is also moved out into the package shell. --- README.md | 12 +- package.json | 3 +- packages/hyperbook/helpers/make-env.ts | 8 +- packages/hyperbook/package.json | 2 +- packages/hyperbook/postbuild.mjs | 6 +- packages/hyperbook/setup.ts | 6 +- packages/provider/src/index.tsx | 11 + packages/shell/README.md | 9 + packages/shell/jest.config.js | 5 + packages/shell/package.json | 59 ++ .../shell/src}/Drawer.tsx | 53 +- .../shell/src}/Links.tsx | 49 +- .../shell/src}/Navigation.tsx | 50 +- .../shell/src/Shell.tsx | 52 +- packages/shell/src/index.css | 900 ++++++++++++++++++ packages/shell/src/index.ts | 2 + packages/shell/tsconfig.build.json | 8 + packages/shell/tsconfig.json | 4 + packages/toc/README.md | 9 + packages/toc/package.json | 50 + .../utils/toc.ts => packages/toc/src/Toc.tsx | 23 +- packages/toc/src/index.ts | 1 + packages/toc/tsconfig.build.json | 8 + packages/toc/tsconfig.json | 4 + .../vscode}/.eslintrc.json | 0 .../vscode}/.gitignore | 0 .../vscode}/.prettierrc | 0 .../vscode}/.vscodeignore | 0 .../vscode}/CHANGELOG.md | 0 .../vscode}/LICENSE.md | 0 .../vscode}/README.md | 0 .../vscode}/app/App.tsx | 0 .../vscode}/app/ErrorBoundary.tsx | 0 .../vscode}/app/global.d.ts | 0 .../vscode}/app/index.scss | 0 .../vscode}/app/index.tsx | 0 .../vscode}/app/tsconfig.json | 0 .../vscode}/assets/icons/Preview.svg | 0 .../assets/icons/PreviewOnRightPane_16x.svg | 0 .../icons/PreviewOnRightPane_16x_dark.svg | 0 .../vscode}/assets/icons/Preview_inverse.svg | 0 .../vscode}/assets/images/hyperbook-logo.png | Bin .../vscode}/esbuild.js | 0 .../vscode}/package.json | 2 +- .../vscode}/schemas/hyperbook.schema.json | 0 .../screenshots/auto-complete-archive.gif | Bin .../screenshots/auto-complete-book.gif | Bin .../screenshots/auto-complete-glossary.gif | Bin .../vscode}/screenshots/links.gif | Bin .../vscode}/screenshots/preview.png | Bin .../vscode}/screenshots/snippets.gif | Bin .../vscode}/snipptes/hyperbook.code-snippets | 0 .../vscode}/src/Constants.ts | 0 .../vscode}/src/Preview.ts | 31 +- .../vscode}/src/StatusBarItem.ts | 0 .../vscode}/src/extension.ts | 0 .../vscode}/src/html-template.ts | 0 .../vscode}/src/messages/messageTypes.ts | 0 .../vscode}/src/utils/dispose.ts | 0 .../vscode}/syntaxes/hyperbook.json | 0 .../vscode}/tsconfig.json | 0 .../vscode}/webpack.config.js | 0 .../simple => platforms/web}/CHANGELOG.md | 0 {templates/simple => platforms/web}/LICENSE | 0 {templates/simple => platforms/web}/README.md | 0 platforms/web/archives | 1 + platforms/web/book | 1 + platforms/web/books | 1 + .../simple => platforms/web}/esbuild.mjs | 0 platforms/web/glossary | 1 + platforms/web/hyperbook.json | 1 + .../simple => platforms/web}/next-env.d.ts | 0 .../simple => platforms/web}/package.json | 8 +- platforms/web/public | 1 + platforms/web/src/index.css | 14 + .../web}/src/pages/[[...page]].tsx | 19 +- .../web}/src/pages/_app.tsx | 8 +- .../web}/src/pages/_document.tsx | 0 .../web}/src/pages/api/save.ts | 0 .../web}/src/pages/glossary/[...term].tsx | 25 +- .../web}/src/pages/glossary/index.tsx | 6 +- .../web}/src/utils/files.ts | 0 .../web}/src/utils/hyperbook.ts | 0 .../web}/src/utils/navigation.ts | 28 - .../simple => platforms/web}/tsconfig.json | 0 pnpm-lock.yaml | 213 ++--- pnpm-workspace.yaml | 1 + scripts/setup.mjs | 47 + scripts/watcher.mjs | 5 +- templates/simple/.gitignore | 35 - templates/simple/src/colors.css | 187 ---- templates/simple/src/components/Toc.tsx | 15 - templates/simple/src/reset.css | 267 ------ templates/simple/src/styles.css | 439 --------- templates/simple/src/szh-menu.css | 269 ------ .../simple/src/utils/useMountTransition.tsx | 23 - templates/simple/src/utils/useOnScreen.tsx | 42 - templates/simple/src/utils/useOverflow.tsx | 26 - .../src/utils/usePreferesColorScheme.tsx | 31 - 99 files changed, 1469 insertions(+), 1612 deletions(-) create mode 100644 packages/shell/README.md create mode 100644 packages/shell/jest.config.js create mode 100644 packages/shell/package.json rename {templates/simple/src/components => packages/shell/src}/Drawer.tsx (63%) rename {templates/simple/src/components => packages/shell/src}/Links.tsx (72%) rename {templates/simple/src/components => packages/shell/src}/Navigation.tsx (71%) rename templates/simple/src/components/Layout.tsx => packages/shell/src/Shell.tsx (84%) create mode 100644 packages/shell/src/index.css create mode 100644 packages/shell/src/index.ts create mode 100644 packages/shell/tsconfig.build.json create mode 100644 packages/shell/tsconfig.json create mode 100644 packages/toc/README.md create mode 100644 packages/toc/package.json rename templates/simple/src/utils/toc.ts => packages/toc/src/Toc.tsx (56%) create mode 100644 packages/toc/src/index.ts create mode 100644 packages/toc/tsconfig.build.json create mode 100644 packages/toc/tsconfig.json rename {packages/vscode-extension => platforms/vscode}/.eslintrc.json (100%) rename {packages/vscode-extension => platforms/vscode}/.gitignore (100%) rename {packages/vscode-extension => platforms/vscode}/.prettierrc (100%) rename {packages/vscode-extension => platforms/vscode}/.vscodeignore (100%) rename {packages/vscode-extension => platforms/vscode}/CHANGELOG.md (100%) rename {packages/vscode-extension => platforms/vscode}/LICENSE.md (100%) rename {packages/vscode-extension => platforms/vscode}/README.md (100%) rename {packages/vscode-extension => platforms/vscode}/app/App.tsx (100%) rename {packages/vscode-extension => platforms/vscode}/app/ErrorBoundary.tsx (100%) rename {packages/vscode-extension => platforms/vscode}/app/global.d.ts (100%) rename {packages/vscode-extension => platforms/vscode}/app/index.scss (100%) rename {packages/vscode-extension => platforms/vscode}/app/index.tsx (100%) rename {packages/vscode-extension => platforms/vscode}/app/tsconfig.json (100%) rename {packages/vscode-extension => platforms/vscode}/assets/icons/Preview.svg (100%) rename {packages/vscode-extension => platforms/vscode}/assets/icons/PreviewOnRightPane_16x.svg (100%) rename {packages/vscode-extension => platforms/vscode}/assets/icons/PreviewOnRightPane_16x_dark.svg (100%) rename {packages/vscode-extension => platforms/vscode}/assets/icons/Preview_inverse.svg (100%) rename {packages/vscode-extension => platforms/vscode}/assets/images/hyperbook-logo.png (100%) rename {packages/vscode-extension => platforms/vscode}/esbuild.js (100%) rename {packages/vscode-extension => platforms/vscode}/package.json (97%) rename {packages/vscode-extension => platforms/vscode}/schemas/hyperbook.schema.json (100%) rename {packages/vscode-extension => platforms/vscode}/screenshots/auto-complete-archive.gif (100%) rename {packages/vscode-extension => platforms/vscode}/screenshots/auto-complete-book.gif (100%) rename {packages/vscode-extension => platforms/vscode}/screenshots/auto-complete-glossary.gif (100%) rename {packages/vscode-extension => platforms/vscode}/screenshots/links.gif (100%) rename {packages/vscode-extension => platforms/vscode}/screenshots/preview.png (100%) rename {packages/vscode-extension => platforms/vscode}/screenshots/snippets.gif (100%) rename {packages/vscode-extension => platforms/vscode}/snipptes/hyperbook.code-snippets (100%) rename {packages/vscode-extension => platforms/vscode}/src/Constants.ts (100%) rename {packages/vscode-extension => platforms/vscode}/src/Preview.ts (92%) rename {packages/vscode-extension => platforms/vscode}/src/StatusBarItem.ts (100%) rename {packages/vscode-extension => platforms/vscode}/src/extension.ts (100%) rename {packages/vscode-extension => platforms/vscode}/src/html-template.ts (100%) rename {packages/vscode-extension => platforms/vscode}/src/messages/messageTypes.ts (100%) rename {packages/vscode-extension => platforms/vscode}/src/utils/dispose.ts (100%) rename {packages/vscode-extension => platforms/vscode}/syntaxes/hyperbook.json (100%) rename {packages/vscode-extension => platforms/vscode}/tsconfig.json (100%) rename {packages/vscode-extension => platforms/vscode}/webpack.config.js (100%) rename {templates/simple => platforms/web}/CHANGELOG.md (100%) rename {templates/simple => platforms/web}/LICENSE (100%) rename {templates/simple => platforms/web}/README.md (100%) create mode 120000 platforms/web/archives create mode 120000 platforms/web/book create mode 120000 platforms/web/books rename {templates/simple => platforms/web}/esbuild.mjs (100%) create mode 120000 platforms/web/glossary create mode 120000 platforms/web/hyperbook.json rename {templates/simple => platforms/web}/next-env.d.ts (100%) rename {templates/simple => platforms/web}/package.json (90%) create mode 120000 platforms/web/public create mode 100644 platforms/web/src/index.css rename {templates/simple => platforms/web}/src/pages/[[...page]].tsx (90%) rename {templates/simple => platforms/web}/src/pages/_app.tsx (97%) rename {templates/simple => platforms/web}/src/pages/_document.tsx (100%) rename {templates/simple => platforms/web}/src/pages/api/save.ts (100%) rename {templates/simple => platforms/web}/src/pages/glossary/[...term].tsx (87%) rename {templates/simple => platforms/web}/src/pages/glossary/index.tsx (96%) rename {templates/simple => platforms/web}/src/utils/files.ts (100%) rename {templates/simple => platforms/web}/src/utils/hyperbook.ts (100%) rename {templates/simple => platforms/web}/src/utils/navigation.ts (88%) rename {templates/simple => platforms/web}/tsconfig.json (100%) create mode 100644 scripts/setup.mjs delete mode 100644 templates/simple/.gitignore delete mode 100644 templates/simple/src/colors.css delete mode 100644 templates/simple/src/components/Toc.tsx delete mode 100644 templates/simple/src/reset.css delete mode 100644 templates/simple/src/styles.css delete mode 100644 templates/simple/src/szh-menu.css delete mode 100644 templates/simple/src/utils/useMountTransition.tsx delete mode 100644 templates/simple/src/utils/useOnScreen.tsx delete mode 100644 templates/simple/src/utils/useOverflow.tsx delete mode 100644 templates/simple/src/utils/usePreferesColorScheme.tsx diff --git a/README.md b/README.md index 1d99ef07c..293fc9c56 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,21 @@ development server and edit the files in the website folder. ``` pnpm install -pnpm hyperbook:build +pnpm build pnpm website:dev en pnpm website:dev de ``` +## Platform + +If you want to work on a platform, run the following commands: + +``` +pnpm install +pnpm build +pnpm platform:web:dev +``` + ## Maintainer Mike Barkmin • [Twitter](https://twitter.com/mikebarkmin) • [GitHub](https://github.com/mikebarkmin/) diff --git a/package.json b/package.json index 6dcb2c54b..330429641 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,14 @@ "private": true, "scripts": { "preinstall": "npx only-allow pnpm", - "prepare": "husky install", + "prepare": "husky install && node scripts/setup.mjs", "dev": "node scripts/watcher.mjs", "build": "pnpm -r build", "lint": "pnpm -r lint", "hyperbook:build": "pnpm --filter hyperbook build", "version-packages": "changeset version", "release": "changeset publish", + "platform:web:dev": "pnpm --filter @platforms/web dev", "examples:setup": "cd examples && HYPERBOOK_LOCAL_DEV=1 node ../packages/hyperbook/dist/index.js setup", "examples:build": "cd examples && HYPERBOOK_LOCAL_DEV=1 node ../packages/hyperbook/dist/index.js build", "website:setup": "cd website && HYPERBOOK_LOCAL_DEV=1 node ../packages/hyperbook/dist/index.js setup", diff --git a/packages/hyperbook/helpers/make-env.ts b/packages/hyperbook/helpers/make-env.ts index 2130678a6..b503e890f 100644 --- a/packages/hyperbook/helpers/make-env.ts +++ b/packages/hyperbook/helpers/make-env.ts @@ -9,8 +9,8 @@ export function makeEnv() { "..", "..", "..", - "templates", - "simple", + "platforms", + "web", "node_modules" ); PATH = @@ -19,8 +19,8 @@ export function makeEnv() { "..", "..", "..", - "templates", - "simple", + "platforms", + "web", "node_modules", ".bin" ) + diff --git a/packages/hyperbook/package.json b/packages/hyperbook/package.json index 51bd7c942..e5b7df91a 100644 --- a/packages/hyperbook/package.json +++ b/packages/hyperbook/package.json @@ -53,6 +53,6 @@ }, "dependencies": { "@hyperbook/types": "workspace:*", - "hyperbook-simple-template": "workspace:*" + "@platforms/web": "workspace:*" } } diff --git a/packages/hyperbook/postbuild.mjs b/packages/hyperbook/postbuild.mjs index e4765c88c..ca1ba921a 100644 --- a/packages/hyperbook/postbuild.mjs +++ b/packages/hyperbook/postbuild.mjs @@ -3,17 +3,17 @@ import * as exportableManifest from "@pnpm/exportable-manifest"; async function postbuild() { await fs.cp( - "./node_modules/hyperbook-simple-template/dist", + "./node_modules/@platforms/web/dist", "./dist/templates/default/.hyperbook", { recursive: true } ); const manifest = await fs - .readFile("./node_modules/hyperbook-simple-template/package.json") + .readFile("./node_modules/@platforms/web/package.json") .then((f) => JSON.parse(f)); const publishManifest = await exportableManifest.createExportableManifest( - "./node_modules/hyperbook-simple-template", + "./node_modules/@platforms/web", manifest ); diff --git a/packages/hyperbook/setup.ts b/packages/hyperbook/setup.ts index 8475878d4..932e747ec 100644 --- a/packages/hyperbook/setup.ts +++ b/packages/hyperbook/setup.ts @@ -16,9 +16,9 @@ export async function runSetupProject(project: Project, rootProject?: Project) { const root = path.join(rootProject?.src || "", ".hyperbook"); rimraf.sync(projectRoot); - await makeDir(projectRoot); const filesPath = path.join(__dirname, "templates"); + await makeDir(projectRoot); await cpy("default/.hyperbook/**", projectRoot, { cwd: filesPath, followSymbolicLinks: false, @@ -32,8 +32,8 @@ export async function runSetupProject(project: Project, rootProject?: Project) { "..", "..", "..", - "templates", - "simple", + "platforms", + "web", "package.json" ) ) diff --git a/packages/provider/src/index.tsx b/packages/provider/src/index.tsx index 26507d7a8..08b2bae87 100644 --- a/packages/provider/src/index.tsx +++ b/packages/provider/src/index.tsx @@ -30,6 +30,7 @@ export type RootFolder = "public" | "book" | "glossary"; type HyperbookContextProps = { directives: Record>; Link: FC; + Head: FC<{ children?: ReactNode }>; env: "development" | "production"; router: { push: (path: string) => Promise; @@ -54,6 +55,7 @@ const HyperbookContext = createContext({ router: { push: async () => false, }, + Head: () => null, env: "production", saveFile: () => async () => {}, loadFile: () => async () => "", @@ -71,6 +73,7 @@ export type Element = { export type ProviderProps = { Link: HyperbookContextProps["Link"]; + Head?: HyperbookContextProps["Head"]; router: HyperbookContextProps["router"]; elements?: Element[]; children?: ReactNode; @@ -87,6 +90,7 @@ export const Provider: FC = ({ elements = [], children, Link, + Head = () => null, saveFile = () => async () => {}, loadFile = () => async () => "", router, @@ -119,6 +123,7 @@ export const Provider: FC = ({ directives: directiveComponents, router, Link, + Head, saveFile, loadFile, makeUrl, @@ -207,6 +212,12 @@ export const useLink = () => { return data.Link; }; +export const useHead = () => { + const data = useContext(HyperbookContext); + + return data.Head; +}; + export const useRouter = () => { const data = useContext(HyperbookContext); diff --git a/packages/shell/README.md b/packages/shell/README.md new file mode 100644 index 000000000..3c7518bcd --- /dev/null +++ b/packages/shell/README.md @@ -0,0 +1,9 @@ +# @hyperbook/shell + +## Installation + +```sh +yarn add @hyperbook/shell +# or +npm i @hyperbook/shell +``` diff --git a/packages/shell/jest.config.js b/packages/shell/jest.config.js new file mode 100644 index 000000000..8bcecd0b3 --- /dev/null +++ b/packages/shell/jest.config.js @@ -0,0 +1,5 @@ +const baseConfig = require("../../jest.config"); + +module.exports = { + ...baseConfig, +}; diff --git a/packages/shell/package.json b/packages/shell/package.json new file mode 100644 index 000000000..4084fc95c --- /dev/null +++ b/packages/shell/package.json @@ -0,0 +1,59 @@ +{ + "name": "@hyperbook/shell", + "version": "0.0.0", + "author": "Mike Barkmin", + "homepage": "https://github.com/openpatch/hyperbook#readme", + "license": "MIT", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.mjs", + "types": "dist/index.d.ts", + "typings": "dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "require": "./dist/index.cjs.js", + "default": "./dist/index.esm.mjs" + }, + "./index.css": "./dist/index.css" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/openpatch/hyperbook.git", + "directory": "packages/shell" + }, + "bugs": { + "url": "https://github.com/openpatch/hyperbook/issues" + }, + "scripts": { + "prebuild": "rimraf dist", + "version": "pnpm build", + "lint": "tsc --noEmit", + "test": "jest --env=jsdom --passWithNoTests", + "build": "pnpm build:pkg && pnpm build:types", + "build:pkg": "node ../../scripts/build.mjs", + "build:types": "tsc --project tsconfig.build.json --declaration --emitDeclarationOnly" + }, + "dependencies": { + "@hyperbook/provider": "workspace:*", + "@szhsin/react-menu": "3.2.0", + "classnames": "2.3.2", + "react-collapsed": "^3.4.0" + }, + "peerDependencies": { + "react": "18.x", + "react-dom": "18.x" + }, + "devDependencies": { + "@hyperbook/types": "workspace:*", + "@types/react": "18.0.15", + "@types/react-dom": "18.0.6", + "react": "18.2.0", + "react-dom": "18.2.0" + } +} diff --git a/templates/simple/src/components/Drawer.tsx b/packages/shell/src/Drawer.tsx similarity index 63% rename from templates/simple/src/components/Drawer.tsx rename to packages/shell/src/Drawer.tsx index 4dac45a54..0db837877 100644 --- a/templates/simple/src/components/Drawer.tsx +++ b/packages/shell/src/Drawer.tsx @@ -1,7 +1,28 @@ -import { useRef, useEffect } from "react"; +import { useRef, useEffect, useState, ReactNode, FC } from "react"; import { createPortal } from "react-dom"; import cn from "classnames"; -import { useMountTransition } from "../utils/useMountTransition"; + +export const useMountTransition = ( + isMounted: boolean, + unmountDelay: number +) => { + const [isTransitioning, setIsTransitioning] = useState(false); + + useEffect(() => { + let timeoutId: NodeJS.Timeout; + + if (isMounted && !isTransitioning) { + setIsTransitioning(true); + } else if (!isMounted && isTransitioning) { + timeoutId = setTimeout(() => setIsTransitioning(false), unmountDelay); + } + return () => { + clearTimeout(timeoutId); + }; + }, [unmountDelay, isMounted, isTransitioning]); + + return isTransitioning; +}; function createPortalRoot() { const drawerRoot = document.createElement("div"); @@ -10,11 +31,19 @@ function createPortalRoot() { return drawerRoot; } +export type DrawerProps = { + isOpen: boolean; + children: ReactNode; + onClose: () => void; + position?: "left" | "right"; + removeWhenClosed?: boolean; +}; + /* * Read the blog post here: * https://letsbuildui.dev/articles/building-a-drawer-component-with-react-portals */ -const Drawer = ({ +export const Drawer: FC = ({ isOpen, children, onClose, @@ -29,7 +58,7 @@ const Drawer = ({ useEffect(() => { portalRootRef.current = document.getElementById("drawer-root") || createPortalRoot(); - bodyRef.current = document.querySelector("body"); + bodyRef.current = document.querySelector("body") as HTMLBodyElement; bodyRef.current.appendChild(portalRootRef.current); const portal = portalRootRef.current; const bodyEl = bodyRef.current; @@ -45,10 +74,12 @@ const Drawer = ({ // Prevent page scrolling when the drawer is open useEffect(() => { const updatePageScroll = () => { - if (isOpen) { - bodyRef.current.style.overflow = "hidden"; - } else { - bodyRef.current.style.overflow = ""; + if (bodyRef.current) { + if (isOpen) { + bodyRef.current.style.overflow = "hidden"; + } else { + bodyRef.current.style.overflow = ""; + } } }; @@ -57,7 +88,7 @@ const Drawer = ({ // Allow Escape key to dismiss the drawer useEffect(() => { - const onKeyPress = (e) => { + const onKeyPress = (e: KeyboardEvent) => { if (e.key === "Escape") { onClose(); } @@ -89,8 +120,6 @@ const Drawer = ({
, - portalRootRef.current + portalRootRef.current as HTMLElement ); }; - -export default Drawer; diff --git a/templates/simple/src/components/Links.tsx b/packages/shell/src/Links.tsx similarity index 72% rename from templates/simple/src/components/Links.tsx rename to packages/shell/src/Links.tsx index cdeb1315e..65009def6 100644 --- a/templates/simple/src/components/Links.tsx +++ b/packages/shell/src/Links.tsx @@ -1,3 +1,4 @@ +import { RefObject, useLayoutEffect, useState } from "react"; import { useLink } from "@hyperbook/provider"; import { HyperbookJson, @@ -7,7 +8,47 @@ import { } from "@hyperbook/types"; import { Menu, MenuItem, SubMenu, MenuButton } from "@szhsin/react-menu"; import { Fragment, FC, useRef } from "react"; -import { useOnScreen } from "../utils/useOnScreen"; + +export const useOnScreen = ( + ref: RefObject, + containerRef: RefObject, + padding: number = 0 +) => { + const [isOnScreen, setOnScreen] = useState(true); + + const handleResize = (bounds: DOMRect) => () => { + if (containerRef.current) { + const containerBounds = containerRef.current.getBoundingClientRect(); + + if ( + containerBounds.width >= bounds.width + padding && + containerBounds.right <= window.innerWidth + ) { + setOnScreen(true); + } else { + setOnScreen(false); + } + } + }; + + useLayoutEffect(() => { + if (ref.current) { + const bounds = ref.current.getBoundingClientRect(); + + const h = handleResize(bounds); + + window.addEventListener("resize", h); + + h(); + + return () => { + window.removeEventListener("resize", h); + }; + } + }, [ref]); + + return isOnScreen; +}; export type LinksProps = { links: HyperbookJson["links"] }; @@ -56,8 +97,8 @@ const MenuLink: FC = ({ label, href, icon }) => { }; export const Links: FC = ({ links }) => { - const linkRef = useRef(); - const containerRef = useRef(); + const linkRef = useRef(null); + const containerRef = useRef(null); const isVisible = useOnScreen(linkRef, containerRef); return (
@@ -83,7 +124,7 @@ export const Links: FC = ({ links }) => { ) : (
- {links.map((link, i) => { + {links?.map((link, i) => { if ("links" in link) { return ( & { + hide?: boolean; + virtual?: boolean; + expanded?: boolean; + pages: PageProps[]; // md-files + sections: SectionProps[]; // folders +}; + +export type NavigationProps = { + next: PageProps | null; + previous: PageProps | null; + current: PageProps | null; + pages: PageProps[]; + sections: SectionProps[]; +}; type P = PageProps & Pick; const Page = ({ name, href, current }: P) => { + const Link = useLink(); return (
  • - - - {name} - + + {name}
  • ); @@ -32,6 +57,7 @@ const Section = ({ expanded, current, }: S) => { + const Link = useLink(); const isActive = current?.href.startsWith(href); const { getCollapseProps, getToggleProps, isExpanded } = useCollapse({ defaultExpanded: isActive || expanded, @@ -48,8 +74,8 @@ const Section = ({ {virtual ? null : isEmpty ? ( {name} ) : ( - - {name} + + {name} )} {virtual ? null : ( diff --git a/templates/simple/src/components/Layout.tsx b/packages/shell/src/Shell.tsx similarity index 84% rename from templates/simple/src/components/Layout.tsx rename to packages/shell/src/Shell.tsx index da92596d4..c383e03d5 100644 --- a/templates/simple/src/components/Layout.tsx +++ b/packages/shell/src/Shell.tsx @@ -1,27 +1,21 @@ -import { useLink } from "@hyperbook/provider"; -import Head from "next/head"; -import { Fragment, ReactNode, useState } from "react"; -import { getHyperbook } from "../utils/hyperbook"; -import { Navigation as NavigationProps, Page } from "../utils/navigation"; -import { Toc as TocProps } from "../utils/toc"; -import Drawer from "./Drawer"; +import { useLink, useMakeUrl, useHead, useConfig } from "@hyperbook/provider"; +import { FC, Fragment, ReactNode, useState } from "react"; +import { Toc, TocProps } from "@hyperbook/toc"; +import { Drawer } from "./Drawer"; import { Links } from "./Links"; -import { Navigation } from "./Navigation"; -import { Toc } from "./Toc"; +import { Navigation, NavigationProps, PageProps } from "./Navigation"; -const hyperbook = getHyperbook(); - -export type LayoutProps = { +export type ShellProps = { navigation: NavigationProps; toc?: TocProps; - page: Pick; - children: ReactNode; + page: Pick; + children?: ReactNode; }; const linkLicense = (license: string) => { const Link = useLink(); - let href: string; - let label: string; + let href: string | null = null; + let label: string | null = null; switch (license.toLowerCase()) { case "cc0": { href = "https://creativecommons.org/publicdomain/zero/1.0/"; @@ -71,25 +65,11 @@ const linkLicense = (license: string) => { return license; }; -const relativeUrl = (url: string) => { - const { basePath } = hyperbook; - if ( - process.env.NODE_ENV !== "production" && - basePath && - url.startsWith("/") - ) { - if (basePath.endsWith("/")) { - return basePath.slice(0, -1) + url; - } else { - return basePath + url; - } - } else { - return url; - } -}; - -export function Layout({ toc, navigation, page, children }: LayoutProps) { +export const Shell: FC = ({ toc, navigation, page, children }) => { const Link = useLink(); + const Head = useHead(); + const hyperbook = useConfig(); + const makeUrl = useMakeUrl(); const [isNavOpen, setIsNavOpen] = useState(false); const [isTocOpen, setIsTocOpen] = useState(false); return ( @@ -153,7 +133,7 @@ export function Layout({ toc, navigation, page, children }: LayoutProps) { {hyperbook.logo && (
    - Logo + Logo
    )}
    {hyperbook.name}
    @@ -226,4 +206,4 @@ export function Layout({ toc, navigation, page, children }: LayoutProps) {
    ); -} +}; diff --git a/packages/shell/src/index.css b/packages/shell/src/index.css new file mode 100644 index 000000000..475ee5ee0 --- /dev/null +++ b/packages/shell/src/index.css @@ -0,0 +1,900 @@ +:root { + --internal-color-error: #d26466; +} + +#drawer-root { + font-family: hyperbook-body, sans-serif; +} + +.main-grid * { + box-sizing: border-box; +} + +.main-grid { + background: var(--color-background); + color: var(--color-text); +} + +a { + color: var(--color-brand); +} + +ul, +ol, +li { + padding: 0; + margin: 0; +} + +.sidebar, +#mobile-sidebar { + background: var(--color-nav); + border-right-color: var(--color-nav-border); +} + +#toc-sidebar { + background: var(--color-nav); + border-left-color: var(--color-nav-border); +} + +.sidebar > .author, +#mobile-sidebar > .author { + color: var(--color-author-color); + background: var(--color-author-background); +} + +.sidebar > .author:hover, +#mobile-sidebar > .author:hover { + color: var(--color-brand); +} + +/* Three bars for burger menu */ +.toggle > .bar1, +.toggle > .bar2, +.toggle > .bar3 { + background-color: var(--color-brand-text); +} + +.inverted .toggle > .bar1, +.inverted .toggle > .bar2, +.inverted .toggle > .bar3 { + background-color: var(--color-brand); +} + +.toc-toggle > .bar1, +.toc-toggle > .bar2, +.toc-toggle > .bar3, +.toc-toggle > .bar4 { + background-color: var(--color-text); +} + +.toc-toggle { + background: var(--color-background); + border-color: var(--color-nav-border); +} + +header { + background: var(--color-brand); +} + +header.inverted { + background: var(--color-brand-text); +} + +.branding { + color: var(--color-brand-text); +} + +header.inverted > .branding { + color: var(--color-brand); +} + +.meta { + border-top-color: var(--color-spacer); +} + +.section > .name { + color: var(--color-text); + border-color: var(--color-spacer); +} + +.section .name.empty { + color: var(--color-text-deactivated); +} + +.section > .name:hover { + background: var(--color-spacer); +} + +.section > .name.empty:hover { + background: none; +} + +.section > .name.active { + background: var(--color-background); + color: var(--color-brand); +} + +.page { + color: var(--color-text); +} + +.page:hover { + background: var(--color-spacer); +} + +.page.active { + background: var(--color-background); + color: var(--color-brand); +} + +.section > .links { + border-left-color: var(--color-spacer); +} + +a.jump { + border-color: var(--color-brand); +} + +a.jump:hover { + background: var(--color-nav); +} + +a.jump { + text-decoration: none; + flex: 1; + text-align: center; + border-style: solid; + border-width: 1px; + padding: 8px 16px; + width: 100%; + border-radius: 8px; + box-shadow: rgba(0, 0, 0, 0.24) 0px 1px 2px; +} + +a.jump.next { + margin-left: 8px; +} + +a.jump.next::after { + content: " →"; +} + +a.jump.previous { + margin-right: 8px; +} + +a.jump.previous::before { + content: "← "; +} + +@media screen and (max-width: 800px) { + .jump-container { + flex-direction: column; + } + + a.jump.next { + margin-left: 0px; + margin-top: 16px; + } + + a.jump.previous { + margin-right: 0px; + margin-top: 16px; + } +} + +.drawer { + background: #fff; +} + +.border { + border-color: var(--color-spacer); +} + +.loading { + background: var(--color-nav); +} + +.glossary .terms { + border-left-color: var(--color-spacer); +} + +.main-grid { + font-family: hyperbook-body, sans-serif; + display: grid; + height: 100vh; + width: 100vw; + grid-template-columns: 300px 1fr; + grid-template-rows: 80px 1fr; + grid-template-areas: + "header header header" + "nav article article"; +} + +.sidebar, +#mobile-sidebar { + display: flex; + flex-direction: column; + grid-area: nav; + border-right-width: 1px; + height: calc(100vh - 80px); + border-right-style: solid; + overflow-y: auto; +} + +#toc-sidebar { + display: flex; + flex-direction: column; + width: 100%; + height: calc(100vh - 80px); + border-left-width: 1px; + border-left-style: solid; + overflow-y: auto; +} + +#mobile-sidebar { + width: 100%; +} + +.sidebar > nav, +#mobile-sidebar > nav, +#toc-sidebar > nav { + padding: 20px; + flex: 1; +} + +.sidebar > .author, +#mobile-sidebar > .author { + text-align: center; + text-decoration: none; + padding: 20px; +} + +.sidebar > .author > b, +#mobile-sidebar > .author > b { + font-weight: bold; +} + +.mobile-nav { + display: none; + margin-left: 16px; +} + +.mobile-nav .toggle { + font-size: 42px; + border: none; + background: none; + width: 40px; + height: 40px; +} +.toggle > .bar1, +.toggle > .bar2, +.toggle > .bar3 { + width: 35px; + height: 5px; + margin: 6px 0; + transition: 0.4s; +} + +.toc-toggle { + position: fixed; + top: 90px; + right: 20px; + border-radius: 50%; + height: 40px; + width: 40px; + border-style: solid; + border-width: 1px; + opacity: 0.7; + z-index: 1000; +} + +.toc-toggle:hover { + opacity: 1; +} + +.toc-toggle > .bar1, +.toc-toggle > .bar3 { + width: 20px; + height: 2px; + margin: 2px 3px; + transition: 0.4s; +} + +.toc-toggle > .bar2, +.toc-toggle > .bar4 { + width: 25px; + height: 2px; + margin: 2px 3px; + transition: 0.4s; +} + +/* Rotate first bar */ +.change .bar1 { + transform: rotate(-45deg) translate(-8px, 8px); +} + +/* Fade out the second bar */ +.change .bar2 { + opacity: 0; +} + +/* Rotate last bar */ +.change .bar3 { + transform: rotate(45deg) translate(-8px, -8px); +} + +@media screen and (max-width: 800px) { + .main-grid { + grid-template-columns: 1fr; + grid-template-rows: 80px 1fr; + grid-template-areas: + "header" + "article"; + } + + .sidebar { + display: none; + } + + .mobile-nav { + display: flex; + align-items: center; + justify-content: center; + } +} + +main { + padding: 20px 40px; + overflow-y: auto; +} + +article { + max-width: 980px; + margin: 0 auto; + grid-area: article; + line-height: 1.5; +} + +header { + display: flex; + grid-area: header; + align-items: center; + z-index: 1001; + box-shadow: rgba(0, 0, 0, 0.24) 0px 1px 4px; +} + +.branding { + text-decoration: none; + display: flex; + padding: 20px; + align-items: center; +} + +.custom-links { + padding: 20px; + flex: 1; + display: flex; + justify-content: flex-end; +} + +.custom-links .container { + display: inline-flex; +} + +.branding .logo { + display: flex; + align-items: center; + justify-content: center; + margin-right: 10px; + height: 40px; + width: auto; + min-width: 40px; +} + +.branding .logo img { + width: auto; + height: 100%; +} + +.branding .name { + font-size: 20px; + font-weight: bold; +} + +.jump-container { + display: flex; + max-width: 980px; + margin: 0 auto; + margin-top: 40px; +} + +.flex { + flex: 1; +} + +.meta { + display: flex; + flex-wrap: wrap; + border-top-style: solid; + border-top-width: 1px; + margin: 0 auto; + align-items: center; + justify-content: center; + gap: 16px; + margin-top: 30px; + padding-top: 30px; + max-width: 980px; +} + +.meta .edit-github { + text-decoration: none; + display: block; + flex: 1; +} + +.meta .vercel { + text-align: center; + flex: 1; +} + +.meta .copyright { + text-align: right; + flex: 1; +} + +.section ul, +.virtual-section ul, +nav > ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +nav li + li { + margin-top: 0px; +} + +.section, +.virtual-section { + display: flex; + flex-direction: column; +} + +.section { + margin-top: 8px; + margin-bottom: 8px; +} + +.section > .name { + display: flex; + text-decoration: none; + padding: 10px; + border-width: 1px; + border-style: solid; + width: 100%; + user-select: none; +} + +.section > .name > .label { + flex: 1; +} + +.section > .name > .toggle { + text-align: right; + background: none; + border: none; + font-size: 13px; +} + +.section .section { + margin-left: 16px; + margin-bottom: 0px; +} + +.page { + display: block; + padding: 10px; + text-decoration: none; +} + +.section > .links { + border-left-width: 1px; + border-left-style: solid; + padding-top: 6px; +} + +.drawer-container { + --transition-speed: 0.3s; +} + +.drawer { + width: 300px; + height: 100%; + overflow: auto; + position: fixed; + display: flex; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); + transition: transform var(--transition-speed) ease; + z-index: 1000; +} + +.drawer.left { + top: 80px; + left: 0; + transform: translateX(-105%); +} + +.drawer.right { + top: 80px; + right: 0; + transform: translateX(100%); +} + +.drawer-container.in.open .left, +.drawer-container.in.open .right { + transform: translateX(0); +} + +.backdrop { + visibility: hidden; + opacity: 0; + background: rgba(0, 0, 0, 0.5); + transition: opacity var(--transition-speed) ease, + visibility var(--transition-speed) ease; + width: 100%; + height: 100%; + top: 80px; + left: 0; + position: fixed; + pointer-events: none; + z-index: 0; +} + +.drawer-container.in.open .backdrop { + visibility: visible; + opacity: 1; + pointer-events: auto; + z-index: 999; +} + +.glossary .letter { + font-size: 24px; +} + +.glossary .term { + list-style-type: none; + text-decoration: none; +} + +.glossary .terms { + padding-left: 1em; + border-left-width: 1px; + border-left-style: solid; +} + +.pages { + margin-top: 30px; +} + +.border { + border-radius: 4px; + border-style: solid; + border-width: 1px; +} + +.loading { + display: flex; + align-items: center; + justify-content: center; + overflow: none; + font-size: 32px; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +nav.toc li { + margin-bottom: 8px; +} + +nav.toc li > a { + color: var(--color-text); + text-decoration: none; +} + +nav.toc li > a:hover { + color: var(--color-brand); +} + +nav.toc li.level-1 { + padding-left: 0px; +} + +nav.toc li.level-2 { + padding-left: 8px; +} + +nav.toc li.level-3 { + padding-left: 16px; +} + +nav.toc li.level-3 { + padding-left: 24px; +} + +/* + * szh-menu + */ + +.szh-menu { + margin: 0; + padding: 0; + list-style: none; + box-sizing: border-box; + width: max-content; + z-index: 100; + border: 1px solid rgba(0, 0, 0, 0.1); + background-color: var(--color-background); +} +.szh-menu:focus { + outline: none; +} +.szh-menu__arrow { + box-sizing: border-box; + width: 0.75rem; + height: 0.75rem; + background-color: var(--color-background); + border: 1px solid transparent; + border-left-color: rgba(0, 0, 0, 0.1); + border-top-color: rgba(0, 0, 0, 0.1); + z-index: -1; +} +.szh-menu__arrow--dir-left { + right: -0.375rem; + transform: translateY(-50%) rotate(135deg); +} +.szh-menu__arrow--dir-right { + left: -0.375rem; + transform: translateY(-50%) rotate(-45deg); +} +.szh-menu__arrow--dir-top { + bottom: -0.375rem; + transform: translateX(-50%) rotate(-135deg); +} +.szh-menu__arrow--dir-bottom { + top: -0.375rem; + transform: translateX(-50%) rotate(45deg); +} +.szh-menu__item { + cursor: pointer; +} +.szh-menu__item:focus { + outline: none; +} +.szh-menu__item--hover { + background-color: var(--color-nav); +} +.szh-menu__item--focusable { + cursor: default; + background-color: inherit; +} +.szh-menu__item--disabled { + cursor: default; + color: #aaa; +} +.szh-menu__item--anchor { + text-decoration: none; + color: var(--color-text); +} + +.szh-menu__group { + box-sizing: border-box; +} +.szh-menu__radio-group { + margin: 0; + padding: 0; + list-style: none; +} +.szh-menu__divider { + height: 1px; + margin: 0.5rem 0; + background-color: rgba(0, 0, 0, 0.12); +} + +.szh-menu-button::after { + content: "❯"; + transform: rotate(90deg); + font-size: 0.8rem; + position: absolute; + right: 1rem; +} + +.szh-menu-button svg { + width: auto; + height: 28px; + stroke: var(--color-brand-text); + fill: var(--color-brand-text); +} + +.szh-menu-button.icon::after { + content: ""; +} + +.szh-menu-button, +.szh-menu-link { + display: flex; + align-items: center; + position: relative; + box-sizing: border-box; + background: none; + border-style: solid; + border-color: transparent; + text-decoration: none; + padding: 8px; + border-width: 1px; + color: var(--color-brand-text); + border-radius: 0.25rem; + font-size: 1em; + font-weight: bold; +} + +.szh-menu-button { + padding-right: 2.5rem; +} + +.szh-menu-button.icon { + padding: 8px; +} + +.szh-menu-button:hover, +.szh-menu-link:hover { + border-color: var(--color-brand-text); +} + +.szh-menu { + user-select: none; + color: var(--color-text); + border: 1px solid var(--color-spacer); + border-radius: 0.25rem; + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.133), 0 0.6px 2px rgba(0, 0, 0, 0.1); + min-width: 10rem; +} +.szh-menu__item { + display: flex; + align-items: center; + position: relative; + gap: 8px; + padding: 0.75rem 1rem; +} + +.szh-menu__item > .label { + flex: 1; +} + +.szh-menu-container--itemTransition .szh-menu__item { + transition-property: background-color, color; + transition-duration: 0.15s; + transition-timing-function: ease-in-out; +} +.szh-menu__item--type-radio { + padding-left: 2.2rem; +} +.szh-menu__item--type-radio::before { + content: "○"; + position: absolute; + left: 0.8rem; + top: 0.55rem; + font-size: 0.8rem; +} +.szh-menu__item--type-radio.szh-menu__item--checked::before { + content: "●"; +} +.szh-menu__item--type-checkbox { + padding-left: 2.2rem; +} +.szh-menu__item--type-checkbox::before { + position: absolute; + left: 0.8rem; +} +.szh-menu__item--type-checkbox.szh-menu__item--checked::before { + content: "✔"; +} +.szh-menu__submenu > .szh-menu__item { + padding-right: 2.5rem; +} +.szh-menu__submenu > .szh-menu__item::after { + content: "❯"; + font-size: 0.8rem; + position: absolute; + right: 1rem; +} +.szh-menu__header { + color: #888; + font-size: 0.8rem; + padding: 0.2rem 1.5rem; + text-transform: uppercase; +} + +@keyframes szh-menu-show-slide-left { + from { + opacity: 0; + transform: translateX(0.5rem); + } +} +@keyframes szh-menu-hide-slide-left { + to { + opacity: 0; + transform: translateX(0.5rem); + } +} +@keyframes szh-menu-show-slide-right { + from { + opacity: 0; + transform: translateX(-0.5rem); + } +} +@keyframes szh-menu-hide-slide-right { + to { + opacity: 0; + transform: translateX(-0.5rem); + } +} +@keyframes szh-menu-show-slide-top { + from { + opacity: 0; + transform: translateY(0.5rem); + } +} +@keyframes szh-menu-hide-slide-top { + to { + opacity: 0; + transform: translateY(0.5rem); + } +} +@keyframes szh-menu-show-slide-bottom { + from { + opacity: 0; + transform: translateY(-0.5rem); + } +} +@keyframes szh-menu-hide-slide-bottom { + to { + opacity: 0; + transform: translateY(-0.5rem); + } +} +.szh-menu--state-opening.szh-menu--dir-left { + animation: szh-menu-show-slide-left 0.15s ease-out; +} + +.szh-menu--state-closing.szh-menu--dir-left { + animation: szh-menu-hide-slide-left 0.15s ease-in forwards; +} + +.szh-menu--state-opening.szh-menu--dir-right { + animation: szh-menu-show-slide-right 0.15s ease-out; +} + +.szh-menu--state-closing.szh-menu--dir-right { + animation: szh-menu-hide-slide-right 0.15s ease-in forwards; +} + +.szh-menu--state-opening.szh-menu--dir-top { + animation: szh-menu-show-slide-top 0.15s ease-out; +} + +.szh-menu--state-closing.szh-menu--dir-top { + animation: szh-menu-hide-slide-top 0.15s ease-in forwards; +} + +.szh-menu--state-opening.szh-menu--dir-bottom { + animation: szh-menu-show-slide-bottom 0.15s ease-out; +} + +.szh-menu--state-closing.szh-menu--dir-bottom { + animation: szh-menu-hide-slide-bottom 0.15s ease-in forwards; +} diff --git a/packages/shell/src/index.ts b/packages/shell/src/index.ts new file mode 100644 index 000000000..1dbb1d55b --- /dev/null +++ b/packages/shell/src/index.ts @@ -0,0 +1,2 @@ +import "./index.css"; +export * from "./Shell"; diff --git a/packages/shell/tsconfig.build.json b/packages/shell/tsconfig.build.json new file mode 100644 index 000000000..e84150858 --- /dev/null +++ b/packages/shell/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["src", "../../types"], + "compilerOptions": { + "rootDir": "src", + "declarationDir": "dist" + } +} diff --git a/packages/shell/tsconfig.json b/packages/shell/tsconfig.json new file mode 100644 index 000000000..f068abe3d --- /dev/null +++ b/packages/shell/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src", "../../types", "tests"] +} diff --git a/packages/toc/README.md b/packages/toc/README.md new file mode 100644 index 000000000..21f0e1964 --- /dev/null +++ b/packages/toc/README.md @@ -0,0 +1,9 @@ +# @hyperbook/toc + +## Installation + +```sh +yarn add @hyperbook/toc +# or +npm i @hyperbook/toc +``` diff --git a/packages/toc/package.json b/packages/toc/package.json new file mode 100644 index 000000000..58dca3e7e --- /dev/null +++ b/packages/toc/package.json @@ -0,0 +1,50 @@ +{ + "name": "@hyperbook/toc", + "version": "0.0.0", + "author": "Mike Barkmin", + "homepage": "https://github.com/openpatch/hyperbook#readme", + "license": "MIT", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.mjs", + "types": "dist/index.d.ts", + "typings": "dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "require": "./dist/index.cjs.js", + "default": "./dist/index.esm.mjs" + } + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/openpatch/hyperbook.git", + "directory": "packages/toc" + }, + "bugs": { + "url": "https://github.com/openpatch/hyperbook/issues" + }, + "scripts": { + "prebuild": "rimraf dist", + "version": "pnpm build", + "lint": "tsc --noEmit", + "build": "pnpm build:pkg && pnpm build:types", + "build:pkg": "node ../../scripts/build.mjs", + "build:types": "tsc --project tsconfig.build.json --declaration --emitDeclarationOnly" + }, + "peerDependencies": { + "react": "18.x", + "react-dom": "18.x" + }, + "devDependencies": { + "@types/react": "18.0.15", + "@types/react-dom": "18.0.6", + "react": "18.2.0", + "react-dom": "18.2.0" + } +} diff --git a/templates/simple/src/utils/toc.ts b/packages/toc/src/Toc.tsx similarity index 56% rename from templates/simple/src/utils/toc.ts rename to packages/toc/src/Toc.tsx index a29bcb0e0..bf0163bab 100644 --- a/templates/simple/src/utils/toc.ts +++ b/packages/toc/src/Toc.tsx @@ -11,16 +11,17 @@ const makeAnchor = (heading: string) => { return anchor; }; -export type Toc = { +export type TocProps = { headings: { level: number; label: string; anchor: string }[]; }; -export const getToc = (markdown: string): Toc => { +export const parseTocFromMarkdown = (markdown: string): TocProps => { + const reg = /(#{1,3})\s(.*)/; const headings = markdown .split("\n") - .filter((line) => line.match(/^\s*#{1,3}\s/)) + .filter((line) => line.match(reg)) .map((line) => { - const [, level, label] = line.match(/(#{1,3})\s(.*)/); + const [, level, label] = line.match(reg) as RegExpMatchArray; const anchor = makeAnchor(label); return { level: level.length, @@ -31,3 +32,17 @@ export const getToc = (markdown: string): Toc => { return { headings }; }; + +export const Toc = ({ headings }: TocProps) => { + return ( + + ); +}; diff --git a/packages/toc/src/index.ts b/packages/toc/src/index.ts new file mode 100644 index 000000000..ae679d4e6 --- /dev/null +++ b/packages/toc/src/index.ts @@ -0,0 +1 @@ +export * from "./Toc"; diff --git a/packages/toc/tsconfig.build.json b/packages/toc/tsconfig.build.json new file mode 100644 index 000000000..e84150858 --- /dev/null +++ b/packages/toc/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["src", "../../types"], + "compilerOptions": { + "rootDir": "src", + "declarationDir": "dist" + } +} diff --git a/packages/toc/tsconfig.json b/packages/toc/tsconfig.json new file mode 100644 index 000000000..f068abe3d --- /dev/null +++ b/packages/toc/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src", "../../types", "tests"] +} diff --git a/packages/vscode-extension/.eslintrc.json b/platforms/vscode/.eslintrc.json similarity index 100% rename from packages/vscode-extension/.eslintrc.json rename to platforms/vscode/.eslintrc.json diff --git a/packages/vscode-extension/.gitignore b/platforms/vscode/.gitignore similarity index 100% rename from packages/vscode-extension/.gitignore rename to platforms/vscode/.gitignore diff --git a/packages/vscode-extension/.prettierrc b/platforms/vscode/.prettierrc similarity index 100% rename from packages/vscode-extension/.prettierrc rename to platforms/vscode/.prettierrc diff --git a/packages/vscode-extension/.vscodeignore b/platforms/vscode/.vscodeignore similarity index 100% rename from packages/vscode-extension/.vscodeignore rename to platforms/vscode/.vscodeignore diff --git a/packages/vscode-extension/CHANGELOG.md b/platforms/vscode/CHANGELOG.md similarity index 100% rename from packages/vscode-extension/CHANGELOG.md rename to platforms/vscode/CHANGELOG.md diff --git a/packages/vscode-extension/LICENSE.md b/platforms/vscode/LICENSE.md similarity index 100% rename from packages/vscode-extension/LICENSE.md rename to platforms/vscode/LICENSE.md diff --git a/packages/vscode-extension/README.md b/platforms/vscode/README.md similarity index 100% rename from packages/vscode-extension/README.md rename to platforms/vscode/README.md diff --git a/packages/vscode-extension/app/App.tsx b/platforms/vscode/app/App.tsx similarity index 100% rename from packages/vscode-extension/app/App.tsx rename to platforms/vscode/app/App.tsx diff --git a/packages/vscode-extension/app/ErrorBoundary.tsx b/platforms/vscode/app/ErrorBoundary.tsx similarity index 100% rename from packages/vscode-extension/app/ErrorBoundary.tsx rename to platforms/vscode/app/ErrorBoundary.tsx diff --git a/packages/vscode-extension/app/global.d.ts b/platforms/vscode/app/global.d.ts similarity index 100% rename from packages/vscode-extension/app/global.d.ts rename to platforms/vscode/app/global.d.ts diff --git a/packages/vscode-extension/app/index.scss b/platforms/vscode/app/index.scss similarity index 100% rename from packages/vscode-extension/app/index.scss rename to platforms/vscode/app/index.scss diff --git a/packages/vscode-extension/app/index.tsx b/platforms/vscode/app/index.tsx similarity index 100% rename from packages/vscode-extension/app/index.tsx rename to platforms/vscode/app/index.tsx diff --git a/packages/vscode-extension/app/tsconfig.json b/platforms/vscode/app/tsconfig.json similarity index 100% rename from packages/vscode-extension/app/tsconfig.json rename to platforms/vscode/app/tsconfig.json diff --git a/packages/vscode-extension/assets/icons/Preview.svg b/platforms/vscode/assets/icons/Preview.svg similarity index 100% rename from packages/vscode-extension/assets/icons/Preview.svg rename to platforms/vscode/assets/icons/Preview.svg diff --git a/packages/vscode-extension/assets/icons/PreviewOnRightPane_16x.svg b/platforms/vscode/assets/icons/PreviewOnRightPane_16x.svg similarity index 100% rename from packages/vscode-extension/assets/icons/PreviewOnRightPane_16x.svg rename to platforms/vscode/assets/icons/PreviewOnRightPane_16x.svg diff --git a/packages/vscode-extension/assets/icons/PreviewOnRightPane_16x_dark.svg b/platforms/vscode/assets/icons/PreviewOnRightPane_16x_dark.svg similarity index 100% rename from packages/vscode-extension/assets/icons/PreviewOnRightPane_16x_dark.svg rename to platforms/vscode/assets/icons/PreviewOnRightPane_16x_dark.svg diff --git a/packages/vscode-extension/assets/icons/Preview_inverse.svg b/platforms/vscode/assets/icons/Preview_inverse.svg similarity index 100% rename from packages/vscode-extension/assets/icons/Preview_inverse.svg rename to platforms/vscode/assets/icons/Preview_inverse.svg diff --git a/packages/vscode-extension/assets/images/hyperbook-logo.png b/platforms/vscode/assets/images/hyperbook-logo.png similarity index 100% rename from packages/vscode-extension/assets/images/hyperbook-logo.png rename to platforms/vscode/assets/images/hyperbook-logo.png diff --git a/packages/vscode-extension/esbuild.js b/platforms/vscode/esbuild.js similarity index 100% rename from packages/vscode-extension/esbuild.js rename to platforms/vscode/esbuild.js diff --git a/packages/vscode-extension/package.json b/platforms/vscode/package.json similarity index 97% rename from packages/vscode-extension/package.json rename to platforms/vscode/package.json index 6b7442d45..4ec659e73 100644 --- a/packages/vscode-extension/package.json +++ b/platforms/vscode/package.json @@ -128,7 +128,7 @@ "compile": "npm-run-all compile:*", "compile:extension": "node ./esbuild.js", "compile:view": "webpack --mode development", - "compile:schema": "typescript-json-schema ../types/src/index.ts HyperbookJson --out ./schemas/hyperbook.schema.json --required", + "compile:schema": "typescript-json-schema ../../packages/types/src/index.ts HyperbookJson --out ./schemas/hyperbook.schema.json --required", "watch": "npm-run-all -p watch:*", "watch:view": "webpack --watch --mode development", "pretest": "pnpm compile && pnpm lint", diff --git a/packages/vscode-extension/schemas/hyperbook.schema.json b/platforms/vscode/schemas/hyperbook.schema.json similarity index 100% rename from packages/vscode-extension/schemas/hyperbook.schema.json rename to platforms/vscode/schemas/hyperbook.schema.json diff --git a/packages/vscode-extension/screenshots/auto-complete-archive.gif b/platforms/vscode/screenshots/auto-complete-archive.gif similarity index 100% rename from packages/vscode-extension/screenshots/auto-complete-archive.gif rename to platforms/vscode/screenshots/auto-complete-archive.gif diff --git a/packages/vscode-extension/screenshots/auto-complete-book.gif b/platforms/vscode/screenshots/auto-complete-book.gif similarity index 100% rename from packages/vscode-extension/screenshots/auto-complete-book.gif rename to platforms/vscode/screenshots/auto-complete-book.gif diff --git a/packages/vscode-extension/screenshots/auto-complete-glossary.gif b/platforms/vscode/screenshots/auto-complete-glossary.gif similarity index 100% rename from packages/vscode-extension/screenshots/auto-complete-glossary.gif rename to platforms/vscode/screenshots/auto-complete-glossary.gif diff --git a/packages/vscode-extension/screenshots/links.gif b/platforms/vscode/screenshots/links.gif similarity index 100% rename from packages/vscode-extension/screenshots/links.gif rename to platforms/vscode/screenshots/links.gif diff --git a/packages/vscode-extension/screenshots/preview.png b/platforms/vscode/screenshots/preview.png similarity index 100% rename from packages/vscode-extension/screenshots/preview.png rename to platforms/vscode/screenshots/preview.png diff --git a/packages/vscode-extension/screenshots/snippets.gif b/platforms/vscode/screenshots/snippets.gif similarity index 100% rename from packages/vscode-extension/screenshots/snippets.gif rename to platforms/vscode/screenshots/snippets.gif diff --git a/packages/vscode-extension/snipptes/hyperbook.code-snippets b/platforms/vscode/snipptes/hyperbook.code-snippets similarity index 100% rename from packages/vscode-extension/snipptes/hyperbook.code-snippets rename to platforms/vscode/snipptes/hyperbook.code-snippets diff --git a/packages/vscode-extension/src/Constants.ts b/platforms/vscode/src/Constants.ts similarity index 100% rename from packages/vscode-extension/src/Constants.ts rename to platforms/vscode/src/Constants.ts diff --git a/packages/vscode-extension/src/Preview.ts b/platforms/vscode/src/Preview.ts similarity index 92% rename from packages/vscode-extension/src/Preview.ts rename to platforms/vscode/src/Preview.ts index b9f2b726a..8fcd7c188 100644 --- a/packages/vscode-extension/src/Preview.ts +++ b/platforms/vscode/src/Preview.ts @@ -45,15 +45,30 @@ export default class Preview { } async getConfig() { - const config = vscode.workspace.fs - .readFile( - vscode.Uri.joinPath( - vscode.Uri.parse(vscode.workspace.rootPath || ""), - "hyperbook.json" - ) - ) - .toString(); + const startUri = vscode.window.activeTextEditor?.document.uri; + let config: string = "{}"; + if (startUri) { + async function findHyperbookJson( + dir: vscode.Uri + ): Promise { + const stat = await vscode.workspace.fs.stat( + vscode.Uri.joinPath(dir, "hyperbook.json") + ); + if (stat.type === vscode.FileType.File) { + return vscode.Uri.joinPath(dir, "hyperbook.json"); + } else { + return findHyperbookJson( + vscode.Uri.parse(dir.path.split("/").slice(0, -1).join("/")) + ); + } + } + + const uri = await findHyperbookJson(startUri); + if (uri !== null) { + config = vscode.workspace.fs.readFile(uri).toString(); + } + } return this.parseConfig(config); } diff --git a/packages/vscode-extension/src/StatusBarItem.ts b/platforms/vscode/src/StatusBarItem.ts similarity index 100% rename from packages/vscode-extension/src/StatusBarItem.ts rename to platforms/vscode/src/StatusBarItem.ts diff --git a/packages/vscode-extension/src/extension.ts b/platforms/vscode/src/extension.ts similarity index 100% rename from packages/vscode-extension/src/extension.ts rename to platforms/vscode/src/extension.ts diff --git a/packages/vscode-extension/src/html-template.ts b/platforms/vscode/src/html-template.ts similarity index 100% rename from packages/vscode-extension/src/html-template.ts rename to platforms/vscode/src/html-template.ts diff --git a/packages/vscode-extension/src/messages/messageTypes.ts b/platforms/vscode/src/messages/messageTypes.ts similarity index 100% rename from packages/vscode-extension/src/messages/messageTypes.ts rename to platforms/vscode/src/messages/messageTypes.ts diff --git a/packages/vscode-extension/src/utils/dispose.ts b/platforms/vscode/src/utils/dispose.ts similarity index 100% rename from packages/vscode-extension/src/utils/dispose.ts rename to platforms/vscode/src/utils/dispose.ts diff --git a/packages/vscode-extension/syntaxes/hyperbook.json b/platforms/vscode/syntaxes/hyperbook.json similarity index 100% rename from packages/vscode-extension/syntaxes/hyperbook.json rename to platforms/vscode/syntaxes/hyperbook.json diff --git a/packages/vscode-extension/tsconfig.json b/platforms/vscode/tsconfig.json similarity index 100% rename from packages/vscode-extension/tsconfig.json rename to platforms/vscode/tsconfig.json diff --git a/packages/vscode-extension/webpack.config.js b/platforms/vscode/webpack.config.js similarity index 100% rename from packages/vscode-extension/webpack.config.js rename to platforms/vscode/webpack.config.js diff --git a/templates/simple/CHANGELOG.md b/platforms/web/CHANGELOG.md similarity index 100% rename from templates/simple/CHANGELOG.md rename to platforms/web/CHANGELOG.md diff --git a/templates/simple/LICENSE b/platforms/web/LICENSE similarity index 100% rename from templates/simple/LICENSE rename to platforms/web/LICENSE diff --git a/templates/simple/README.md b/platforms/web/README.md similarity index 100% rename from templates/simple/README.md rename to platforms/web/README.md diff --git a/platforms/web/archives b/platforms/web/archives new file mode 120000 index 000000000..799bcdb72 --- /dev/null +++ b/platforms/web/archives @@ -0,0 +1 @@ +/var/home/mbarkmin/Sources/openpatch/hyperbook/vscode-full-preview/website/en/archives \ No newline at end of file diff --git a/platforms/web/book b/platforms/web/book new file mode 120000 index 000000000..89627c067 --- /dev/null +++ b/platforms/web/book @@ -0,0 +1 @@ +/var/home/mbarkmin/Sources/openpatch/hyperbook/vscode-full-preview/website/en/book \ No newline at end of file diff --git a/platforms/web/books b/platforms/web/books new file mode 120000 index 000000000..1df6adfe3 --- /dev/null +++ b/platforms/web/books @@ -0,0 +1 @@ +/var/home/mbarkmin/Sources/openpatch/hyperbook/vscode-full-preview/website/en/books \ No newline at end of file diff --git a/templates/simple/esbuild.mjs b/platforms/web/esbuild.mjs similarity index 100% rename from templates/simple/esbuild.mjs rename to platforms/web/esbuild.mjs diff --git a/platforms/web/glossary b/platforms/web/glossary new file mode 120000 index 000000000..27db7121e --- /dev/null +++ b/platforms/web/glossary @@ -0,0 +1 @@ +/var/home/mbarkmin/Sources/openpatch/hyperbook/vscode-full-preview/website/en/glossary \ No newline at end of file diff --git a/platforms/web/hyperbook.json b/platforms/web/hyperbook.json new file mode 120000 index 000000000..bdede1939 --- /dev/null +++ b/platforms/web/hyperbook.json @@ -0,0 +1 @@ +/var/home/mbarkmin/Sources/openpatch/hyperbook/vscode-full-preview/website/en/hyperbook.json \ No newline at end of file diff --git a/templates/simple/next-env.d.ts b/platforms/web/next-env.d.ts similarity index 100% rename from templates/simple/next-env.d.ts rename to platforms/web/next-env.d.ts diff --git a/templates/simple/package.json b/platforms/web/package.json similarity index 90% rename from templates/simple/package.json rename to platforms/web/package.json index e7a2f9dea..d8417eef3 100644 --- a/templates/simple/package.json +++ b/platforms/web/package.json @@ -1,5 +1,5 @@ { - "name": "hyperbook-simple-template", + "name": "@platforms/web", "version": "0.7.2", "private": true, "scripts": { @@ -24,16 +24,14 @@ "@hyperbook/element-youtube": "workspace:*", "@hyperbook/markdown": "workspace:*", "@hyperbook/provider": "workspace:*", + "@hyperbook/shell": "workspace:*", "@hyperbook/store": "workspace:*", "@hyperbook/styles": "workspace:*", - "@szhsin/react-menu": "3.2.0", - "classnames": "2.3.2", + "@hyperbook/toc": "workspace:*", "gray-matter": "4.0.3", "next": "12.3.1", "react": "18.2.0", - "react-collapsed": "3.4.0", "react-dom": "18.2.0", - "react-icons": "4.6.0", "react-redux": "8.0.4", "redux": "4.2.0", "redux-persist": "6.0.0" diff --git a/platforms/web/public b/platforms/web/public new file mode 120000 index 000000000..c2b3afbe1 --- /dev/null +++ b/platforms/web/public @@ -0,0 +1 @@ +/var/home/mbarkmin/Sources/openpatch/hyperbook/vscode-full-preview/website/en/public \ No newline at end of file diff --git a/platforms/web/src/index.css b/platforms/web/src/index.css new file mode 100644 index 000000000..35044f0cb --- /dev/null +++ b/platforms/web/src/index.css @@ -0,0 +1,14 @@ +html, +body { + overflow-x: hidden; + margin: 0; +} + +body { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +body::-webkit-scrollbar { + display: none; +} diff --git a/templates/simple/src/pages/[[...page]].tsx b/platforms/web/src/pages/[[...page]].tsx similarity index 90% rename from templates/simple/src/pages/[[...page]].tsx rename to platforms/web/src/pages/[[...page]].tsx index 96f916e6a..7c17546f4 100644 --- a/templates/simple/src/pages/[[...page]].tsx +++ b/platforms/web/src/pages/[[...page]].tsx @@ -2,13 +2,10 @@ import fs from "fs"; import matter from "gray-matter"; import { GetStaticPaths, GetStaticProps } from "next"; import path from "path"; -import { Layout } from "../components/Layout"; +import { Shell, ShellProps } from "@hyperbook/shell"; import { getAllFiles } from "../utils/files"; -import { - Navigation as NavigationProps, - getNavigation, -} from "../utils/navigation"; -import { getToc, Toc } from "../utils/toc"; +import { getNavigation } from "../utils/navigation"; +import { parseTocFromMarkdown, TocProps } from "@hyperbook/toc"; import { Markdown } from "@hyperbook/markdown"; import { useActivePageId, useLink } from "@hyperbook/provider"; import { getHyperbook } from "../utils/hyperbook"; @@ -16,8 +13,8 @@ import { Fragment } from "react"; type PageProps = { markdown: string; - navigation: NavigationProps; - toc: Toc; + navigation: ShellProps["navigation"]; + toc: TocProps; }; const hyperbook = getHyperbook(); @@ -29,7 +26,7 @@ export default function BookPage({ markdown, navigation, toc }: PageProps) { return ( - )}
    - + ); } @@ -83,7 +80,7 @@ export const getStaticProps: GetStaticProps< props: { locale: data?.lang || hyperbook.language, markdown: content, - toc: getToc(content), + toc: parseTocFromMarkdown(content), navigation, }, }; diff --git a/templates/simple/src/pages/_app.tsx b/platforms/web/src/pages/_app.tsx similarity index 97% rename from templates/simple/src/pages/_app.tsx rename to platforms/web/src/pages/_app.tsx index dfb7ef9e5..6f901f2b3 100644 --- a/templates/simple/src/pages/_app.tsx +++ b/platforms/web/src/pages/_app.tsx @@ -1,7 +1,5 @@ -import "../reset.css"; -import "../styles.css"; -import "../colors.css"; -import "../szh-menu.css"; +import "../index.css"; +import "@hyperbook/shell/index.css"; import "@hyperbook/markdown/katex.css"; import "@hyperbook/markdown/index.css"; import { Provider, ProviderProps } from "@hyperbook/provider"; @@ -32,6 +30,7 @@ import "@hyperbook/element-excalidraw/index.css"; import elementBitflow from "@hyperbook/element-bitflow"; import "@hyperbook/element-bitflow/index.css"; import Link from "next/link"; +import Head from "next/head"; import { Styles } from "@hyperbook/styles"; import { localStorage } from "@hyperbook/store"; import { useRouter } from "next/router"; @@ -75,6 +74,7 @@ export default function MyApp({ Component, pageProps }) { return ( - + ); } @@ -72,7 +67,7 @@ export const getStaticProps: GetStaticProps< const files = getAllFiles( path.join(process.env.root ?? process.cwd(), "book") ); - const pages: Page[] = []; + const pages: ShellProps["navigation"]["pages"] = []; for (const file of files) { const { content, data } = readFile(file); const r = new RegExp( @@ -109,7 +104,7 @@ export const getStaticProps: GetStaticProps< locale: data?.lang || hyperbook.language, term, markdown: content, - toc: getToc(content), + toc: parseTocFromMarkdown(content), navigation, hyperbook, }, diff --git a/templates/simple/src/pages/glossary/index.tsx b/platforms/web/src/pages/glossary/index.tsx similarity index 96% rename from templates/simple/src/pages/glossary/index.tsx rename to platforms/web/src/pages/glossary/index.tsx index 6ad3dc9f4..42b57038f 100644 --- a/templates/simple/src/pages/glossary/index.tsx +++ b/platforms/web/src/pages/glossary/index.tsx @@ -3,7 +3,7 @@ import fs from "fs"; import { getAllFiles } from "../../utils/files"; import matter from "gray-matter"; import chalk from "chalk"; -import { Layout } from "../../components/Layout"; +import { Shell } from "@hyperbook/shell"; import { getNavigation, Navigation } from "../../utils/navigation"; import path from "path"; import { useActivePageId, useLink } from "@hyperbook/provider"; @@ -23,7 +23,7 @@ export default function Glossary({ terms, navigation }: GlossaryProps) { const Link = useLink(); useActivePageId(); return ( - ))} - + ); } diff --git a/templates/simple/src/utils/files.ts b/platforms/web/src/utils/files.ts similarity index 100% rename from templates/simple/src/utils/files.ts rename to platforms/web/src/utils/files.ts diff --git a/templates/simple/src/utils/hyperbook.ts b/platforms/web/src/utils/hyperbook.ts similarity index 100% rename from templates/simple/src/utils/hyperbook.ts rename to platforms/web/src/utils/hyperbook.ts diff --git a/templates/simple/src/utils/navigation.ts b/platforms/web/src/utils/navigation.ts similarity index 88% rename from templates/simple/src/utils/navigation.ts rename to platforms/web/src/utils/navigation.ts index 64a5f0b45..a52608acf 100644 --- a/templates/simple/src/utils/navigation.ts +++ b/platforms/web/src/utils/navigation.ts @@ -5,34 +5,6 @@ import { getHyperbook } from "./hyperbook"; const hyperbook = getHyperbook(); -export type Page = { - name: string; - description?: string; - keywords?: string[]; - repo?: string; - hide?: boolean; - toc?: boolean; - index?: number; - isEmpty?: boolean; - href: string; -}; - -export type Section = Omit & { - hide?: boolean; - virtual?: boolean; - expanded?: boolean; - pages: Page[]; // md-files - sections: Section[]; // folders -}; - -export type Navigation = { - next: Page | null; - previous: Page | null; - current: Page | null; - pages: Page[]; - sections: Section[]; -}; - export const readFile = (filePath: string) => { let source: Buffer; try { diff --git a/templates/simple/tsconfig.json b/platforms/web/tsconfig.json similarity index 100% rename from templates/simple/tsconfig.json rename to platforms/web/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 59f39f540..6a0ab8fda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -312,6 +312,7 @@ importers: packages/hyperbook: specifiers: '@hyperbook/types': workspace:* + '@platforms/web': workspace:* '@pnpm/exportable-manifest': 4.0.1 '@types/archiver': 5.3.1 '@types/async-retry': 1.4.5 @@ -327,14 +328,13 @@ importers: cpy: 9.0.1 cross-spawn: 7.0.3 got: 12.5.2 - hyperbook-simple-template: workspace:* prompts: 2.4.2 rimraf: 3.0.2 tar: 6.1.11 update-check: 1.5.4 dependencies: '@hyperbook/types': link:../types - hyperbook-simple-template: link:../../templates/simple + '@platforms/web': link:../../platforms/web devDependencies: '@pnpm/exportable-manifest': 4.0.1 '@types/archiver': 5.3.1 @@ -439,6 +439,29 @@ importers: react: 18.2.0 react-dom: 18.2.0_react@18.2.0 + packages/shell: + specifiers: + '@hyperbook/provider': workspace:* + '@hyperbook/types': workspace:* + '@szhsin/react-menu': 3.2.0 + '@types/react': 18.0.15 + '@types/react-dom': 18.0.6 + classnames: 2.3.2 + react: 18.2.0 + react-collapsed: ^3.4.0 + react-dom: 18.2.0 + dependencies: + '@hyperbook/provider': link:../provider + '@szhsin/react-menu': 3.2.0_biqbaboplfbrettd7655fr4n2y + classnames: 2.3.2 + react-collapsed: 3.4.0_biqbaboplfbrettd7655fr4n2y + devDependencies: + '@hyperbook/types': link:../types + '@types/react': 18.0.15 + '@types/react-dom': 18.0.6 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + packages/store: specifiers: '@reduxjs/toolkit': 1.8.6 @@ -467,10 +490,22 @@ importers: react: 18.2.0 react-dom: 18.2.0_react@18.2.0 + packages/toc: + specifiers: + '@types/react': 18.0.15 + '@types/react-dom': 18.0.6 + react: 18.2.0 + react-dom: 18.2.0 + devDependencies: + '@types/react': 18.0.15 + '@types/react-dom': 18.0.6 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + packages/types: specifiers: {} - packages/vscode-extension: + platforms/vscode: specifiers: '@hyperbook/element-alert': workspace:* '@hyperbook/element-bitflow': workspace:* @@ -523,23 +558,23 @@ importers: webpack: 5.74.0 webpack-cli: 4.10.0 dependencies: - '@hyperbook/element-alert': link:../element-alert - '@hyperbook/element-bitflow': link:../element-bitflow - '@hyperbook/element-bookmarks': link:../element-bookmarks - '@hyperbook/element-collapsible': link:../element-collapsible - '@hyperbook/element-dl': link:../element-dl - '@hyperbook/element-excalidraw': link:../element-excalidraw - '@hyperbook/element-mermaid': link:../element-mermaid - '@hyperbook/element-protect': link:../element-protect - '@hyperbook/element-qr': link:../element-qr - '@hyperbook/element-struktog': link:../element-struktog - '@hyperbook/element-tabs': link:../element-tabs - '@hyperbook/element-term': link:../element-term - '@hyperbook/element-youtube': link:../element-youtube - '@hyperbook/markdown': link:../markdown - '@hyperbook/provider': link:../provider - '@hyperbook/store': link:../store - '@hyperbook/styles': link:../styles + '@hyperbook/element-alert': link:../../packages/element-alert + '@hyperbook/element-bitflow': link:../../packages/element-bitflow + '@hyperbook/element-bookmarks': link:../../packages/element-bookmarks + '@hyperbook/element-collapsible': link:../../packages/element-collapsible + '@hyperbook/element-dl': link:../../packages/element-dl + '@hyperbook/element-excalidraw': link:../../packages/element-excalidraw + '@hyperbook/element-mermaid': link:../../packages/element-mermaid + '@hyperbook/element-protect': link:../../packages/element-protect + '@hyperbook/element-qr': link:../../packages/element-qr + '@hyperbook/element-struktog': link:../../packages/element-struktog + '@hyperbook/element-tabs': link:../../packages/element-tabs + '@hyperbook/element-term': link:../../packages/element-term + '@hyperbook/element-youtube': link:../../packages/element-youtube + '@hyperbook/markdown': link:../../packages/markdown + '@hyperbook/provider': link:../../packages/provider + '@hyperbook/store': link:../../packages/store + '@hyperbook/styles': link:../../packages/styles fs-plus: 3.1.1 gray-matter: 4.0.3 react: 18.2.0 @@ -575,7 +610,7 @@ importers: webpack: 5.74.0_na2qyddqajkdtu6l4otwoergp4 webpack-cli: 4.10.0_webpack@5.74.0 - templates/simple: + platforms/web: specifiers: '@hyperbook/element-alert': workspace:* '@hyperbook/element-bitflow': workspace:* @@ -593,22 +628,20 @@ importers: '@hyperbook/markdown': workspace:* '@hyperbook/next-watch': workspace:* '@hyperbook/provider': workspace:* + '@hyperbook/shell': workspace:* '@hyperbook/store': workspace:* '@hyperbook/styles': workspace:* + '@hyperbook/toc': workspace:* '@hyperbook/types': workspace:* - '@szhsin/react-menu': 3.2.0 '@types/node': 18.11.7 '@types/react': 18.0.23 chalk: 5.1.2 - classnames: 2.3.2 esbuild: 0.15.12 glob: 8.0.3 gray-matter: 4.0.3 next: 12.3.1 react: 18.2.0 - react-collapsed: 3.4.0 react-dom: 18.2.0 - react-icons: 4.6.0 react-redux: 8.0.4 redux: 4.2.0 redux-persist: 6.0.0 @@ -630,16 +663,14 @@ importers: '@hyperbook/element-youtube': link:../../packages/element-youtube '@hyperbook/markdown': link:../../packages/markdown '@hyperbook/provider': link:../../packages/provider + '@hyperbook/shell': link:../../packages/shell '@hyperbook/store': link:../../packages/store '@hyperbook/styles': link:../../packages/styles - '@szhsin/react-menu': 3.2.0_biqbaboplfbrettd7655fr4n2y - classnames: 2.3.2 + '@hyperbook/toc': link:../../packages/toc gray-matter: 4.0.3 next: 12.3.1_biqbaboplfbrettd7655fr4n2y react: 18.2.0 - react-collapsed: 3.4.0_biqbaboplfbrettd7655fr4n2y react-dom: 18.2.0_react@18.2.0 - react-icons: 4.6.0_react@18.2.0 react-redux: 8.0.4_ugomhvxc6vgmj6cf3dom2qmdvu redux: 4.2.0 redux-persist: 6.0.0_react@18.2.0+redux@4.2.0 @@ -2453,7 +2484,7 @@ packages: /@types/archiver/5.3.1: resolution: {integrity: sha512-wKYZaSXaDvTZuInAWjCeGG7BEAgTWG2zZW0/f7IYFcoHB2X2d9lkVFnrOlXl3W6NrvO6Ml3FLLu8Uksyymcpnw==} dependencies: - '@types/glob': 7.2.0 + '@types/glob': 8.0.0 dev: true /@types/async-retry/1.4.5: @@ -2465,7 +2496,7 @@ packages: /@types/cross-spawn/6.0.2: resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} dependencies: - '@types/node': 18.8.3 + '@types/node': 18.11.7 dev: true /@types/debug/4.1.7: @@ -2496,13 +2527,6 @@ packages: resolution: {integrity: sha512-CWYnSRnun3CGbt6taXeVo2lCbuaj4mchVJ4UF/BdU5TSuIn3AmS13pGMwCsBUoehGbhZrBrpNJZSZI5EVilXww==} dev: true - /@types/glob/7.2.0: - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - dependencies: - '@types/minimatch': 3.0.5 - '@types/node': 18.11.7 - dev: true - /@types/glob/8.0.0: resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} dependencies: @@ -2594,10 +2618,6 @@ packages: resolution: {integrity: sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==} dev: true - /@types/node/18.8.3: - resolution: {integrity: sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==} - dev: true - /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true @@ -2625,18 +2645,32 @@ packages: /@types/prompts/2.4.1: resolution: {integrity: sha512-1Mqzhzi9W5KlooNE4o0JwSXGUDeQXKldbGn9NO4tpxwZbHXYd+WcKpCksG2lbhH7U9I9LigfsdVsP2QAY0lNPA==} dependencies: - '@types/node': 18.8.3 + '@types/node': 18.11.7 dev: true /@types/prop-types/15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + /@types/react-dom/18.0.6: + resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} + dependencies: + '@types/react': 18.0.23 + dev: true + /@types/react-dom/18.0.7: resolution: {integrity: sha512-HaXc+BbqAZE1RdsK3tC8SbkFy6UL2xF76lT9rQs5JkPrJg3rWA3Ou/Lhw3YJQzEDkBpmJ79nBsfnd05WrBd2QQ==} dependencies: '@types/react': 18.0.23 dev: true + /@types/react/18.0.15: + resolution: {integrity: sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.1 + dev: true + /@types/react/18.0.23: resolution: {integrity: sha512-R1wTULtCiJkudAN2DJGoYYySbGtOdzZyUWAACYinKdiQC8auxso4kLDUhQ7AJ2kh3F6A6z4v69U6tNY39hihVQ==} dependencies: @@ -2651,8 +2685,8 @@ packages: /@types/rimraf/3.0.2: resolution: {integrity: sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==} dependencies: - '@types/glob': 7.2.0 - '@types/node': 18.8.3 + '@types/glob': 8.0.0 + '@types/node': 18.11.7 dev: true /@types/scheduler/0.16.2: @@ -2669,7 +2703,7 @@ packages: /@types/tar/6.1.3: resolution: {integrity: sha512-YzDOr5kdAeqS8dcO6NTTHTMJ44MUCBDoLEIyPtwEn7PssKqUYL49R1iCVJPeiPzPlKi6DbH33eZkpeJ27e4vHg==} dependencies: - '@types/node': 18.8.3 + '@types/node': 18.11.7 minipass: 3.3.5 dev: true @@ -3249,7 +3283,7 @@ packages: es6-object-assign: 1.1.0 is-nan: 1.3.2 object-is: 1.1.5 - util: 0.12.4 + util: 0.12.5 dev: true /async-retry/1.3.3: @@ -3454,17 +3488,6 @@ packages: pako: 1.0.11 dev: true - /browserslist/4.21.3: - resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001418 - electron-to-chromium: 1.4.276 - node-releases: 2.0.6 - update-browserslist-db: 1.0.10_browserslist@4.21.3 - dev: true - /browserslist/4.21.4: resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3474,7 +3497,6 @@ packages: electron-to-chromium: 1.4.276 node-releases: 2.0.6 update-browserslist-db: 1.0.10_browserslist@4.21.4 - dev: false /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -3770,14 +3792,6 @@ packages: wrap-ansi: 6.2.0 dev: true - /cliui/7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - /cliui/8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -3918,7 +3932,7 @@ packages: peerDependencies: webpack: ^5.1.0 dependencies: - fast-glob: 3.2.11 + fast-glob: 3.2.12 glob-parent: 6.0.2 globby: 13.1.1 normalize-path: 3.0.0 @@ -4081,7 +4095,7 @@ packages: postcss-modules-scope: 3.0.0_postcss@8.4.14 postcss-modules-values: 4.0.0_postcss@8.4.14 postcss-value-parser: 4.2.0 - semver: 7.3.7 + semver: 7.3.8 webpack: 5.74.0_na2qyddqajkdtu6l4otwoergp4 dev: true @@ -5450,6 +5464,7 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 + dev: false /fast-glob/3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} @@ -7974,7 +7989,7 @@ packages: tty-browserify: 0.0.1 type-fest: 2.19.0 url: 0.11.0 - util: 0.12.4 + util: 0.12.5 vm-browserify: 1.1.2 webpack: 5.74.0_na2qyddqajkdtu6l4otwoergp4 dev: true @@ -8011,7 +8026,7 @@ packages: timers-browserify: 2.0.12 tty-browserify: 0.0.1 url: 0.11.0 - util: 0.12.4 + util: 0.12.5 vm-browserify: 1.1.2 dev: true @@ -8899,14 +8914,6 @@ packages: react: 18.2.0 dev: false - /react-icons/4.6.0_react@18.2.0: - resolution: {integrity: sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g==} - peerDependencies: - react: '*' - dependencies: - react: 18.2.0 - dev: false - /react-is/16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -9701,14 +9708,6 @@ packages: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true - /semver/7.3.7: - resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /semver/7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} @@ -10226,7 +10225,7 @@ packages: schema-utils: 3.1.1 serialize-javascript: 6.0.0 terser: 5.14.2 - webpack: 5.74.0_na2qyddqajkdtu6l4otwoergp4 + webpack: 5.74.0_esbuild@0.15.12 dev: true /terser/5.14.2: @@ -10322,7 +10321,7 @@ packages: chalk: 4.1.2 enhanced-resolve: 5.10.0 micromatch: 4.0.5 - semver: 7.3.7 + semver: 7.3.8 typescript: 4.8.4 webpack: 5.74.0_na2qyddqajkdtu6l4otwoergp4 dev: true @@ -10505,7 +10504,7 @@ packages: safe-stable-stringify: 2.4.0 ts-node: 10.9.1_zatg7gq4isvm3eqhbbwlqos4ve typescript: 4.6.4 - yargs: 17.5.1 + yargs: 17.6.0 transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -10720,17 +10719,6 @@ packages: engines: {node: '>= 0.8'} dev: false - /update-browserslist-db/1.0.10_browserslist@4.21.3: - resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.3 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - /update-browserslist-db/1.0.10_browserslist@4.21.4: resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true @@ -10740,7 +10728,6 @@ packages: browserslist: 4.21.4 escalade: 3.1.1 picocolors: 1.0.0 - dev: false /update-check/1.5.4: resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} @@ -10789,14 +10776,13 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /util/0.12.4: - resolution: {integrity: sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==} + /util/0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} dependencies: inherits: 2.0.4 is-arguments: 1.1.1 is-generator-function: 1.0.10 is-typed-array: 1.1.9 - safe-buffer: 5.2.1 which-typed-array: 1.1.8 dev: true @@ -11009,7 +10995,7 @@ packages: '@webassemblyjs/wasm-parser': 1.11.1 acorn: 8.8.0 acorn-import-assertions: 1.8.0_acorn@8.8.0 - browserslist: 4.21.3 + browserslist: 4.21.4 chrome-trace-event: 1.0.3 enhanced-resolve: 5.10.0 es-module-lexer: 0.9.3 @@ -11049,7 +11035,7 @@ packages: '@webassemblyjs/wasm-parser': 1.11.1 acorn: 8.8.0 acorn-import-assertions: 1.8.0_acorn@8.8.0 - browserslist: 4.21.3 + browserslist: 4.21.4 chrome-trace-event: 1.0.3 enhanced-resolve: 5.10.0 es-module-lexer: 0.9.3 @@ -11257,19 +11243,6 @@ packages: yargs-parser: 18.1.3 dev: true - /yargs/17.5.1: - resolution: {integrity: sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==} - engines: {node: '>=12'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - dev: true - /yargs/17.6.0: resolution: {integrity: sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==} engines: {node: '>=12'} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8e990cbec..1a3241f19 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,5 +2,6 @@ packages: # all packages in subdirs of packages/ and components/ - "packages/*" - "templates/*" + - "platforms/*" # exclude packages that are inside test directories - "!**/test/**" diff --git a/scripts/setup.mjs b/scripts/setup.mjs new file mode 100644 index 000000000..65dd1db30 --- /dev/null +++ b/scripts/setup.mjs @@ -0,0 +1,47 @@ +import fs from "fs"; +import path from "path"; +import os from "os"; +import rimraf from "rimraf"; + +async function makeSymlink(src, dst) { + const type = os.platform() == "win32" ? "junction" : null; + return new Promise((resolve, reject) => { + fs.symlink(src, dst, type, (err) => { + if (err) { + reject(); + } else { + resolve(); + } + }); + }); +} + +async function setup() { + rimraf.sync(path.join(process.cwd(), "platforms", "web", "book")); + await makeSymlink( + path.join(process.cwd(), "website", "en", "book"), + path.join(process.cwd(), "platforms", "web", "book") + ); + rimraf.sync(path.join(process.cwd(), "platforms", "web", "glossary")); + await makeSymlink( + path.join(process.cwd(), "website", "en", "glossary"), + path.join(process.cwd(), "platforms", "web", "glossary") + ); + rimraf.sync(path.join(process.cwd(), "platforms", "web", "archives")); + await makeSymlink( + path.join(process.cwd(), "website", "en", "archives"), + path.join(process.cwd(), "platforms", "web", "archives") + ); + rimraf.sync(path.join(process.cwd(), "platforms", "web", "public")); + await makeSymlink( + path.join(process.cwd(), "website", "en", "public"), + path.join(process.cwd(), "platforms", "web", "public") + ); + rimraf.sync(path.join(process.cwd(), "platforms", "web", "hyperbook.json")); + await makeSymlink( + path.join(process.cwd(), "website", "en", "hyperbook.json"), + path.join(process.cwd(), "platforms", "web", "hyperbook.json") + ); +} + +setup(); diff --git a/scripts/watcher.mjs b/scripts/watcher.mjs index 8678b0476..d6b3256df 100644 --- a/scripts/watcher.mjs +++ b/scripts/watcher.mjs @@ -10,8 +10,9 @@ const watcher = chokidar.watch( "packages/**/*.ts", "packages/**/*.tsx", "packages/**/*.css", - "templates/**/*.ts", - "templates/**/*.tsx", + "platforms/**/*.ts", + "platforms/**/*.css", + "platforms/**/*.tsx", ], { ignored: [ diff --git a/templates/simple/.gitignore b/templates/simple/.gitignore deleted file mode 100644 index 7eeb9934c..000000000 --- a/templates/simple/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -.vscode -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env.local -.env.development.local -.env.test.local -.env.production.local - -# vercel -.vercel diff --git a/templates/simple/src/colors.css b/templates/simple/src/colors.css deleted file mode 100644 index 51351ab16..000000000 --- a/templates/simple/src/colors.css +++ /dev/null @@ -1,187 +0,0 @@ -:root { - --internal-color-error: #d26466; -} - -html, -body { - background: var(--color-background); - color: var(--color-text); -} - -a { - color: var(--color-brand); -} - -.sidebar, -#mobile-sidebar { - background: var(--color-nav); - border-right-color: var(--color-nav-border); -} - -#toc-sidebar { - background: var(--color-nav); - border-left-color: var(--color-nav-border); -} - -.sidebar > .author, -#mobile-sidebar > .author { - color: var(--color-author-color); - background: var(--color-author-background); -} - -.sidebar > .author:hover, -#mobile-sidebar > .author:hover { - color: var(--color-brand); -} - -/* Three bars for burger menu */ -.toggle > .bar1, -.toggle > .bar2, -.toggle > .bar3 { - background-color: var(--color-brand-text); -} - -.inverted .toggle > .bar1, -.inverted .toggle > .bar2, -.inverted .toggle > .bar3 { - background-color: var(--color-brand); -} - -.toc-toggle > .bar1, -.toc-toggle > .bar2, -.toc-toggle > .bar3, -.toc-toggle > .bar4 { - background-color: var(--color-text); -} - -.toc-toggle { - background: var(--color-background); - border-color: var(--color-nav-border); -} - -header { - background: var(--color-brand); -} - -header.inverted { - background: var(--color-brand-text); -} - -.branding { - color: var(--color-brand-text); -} - -header.inverted > .branding { - color: var(--color-brand); -} - -.meta { - border-top-color: var(--color-spacer); -} - -.section > .name { - color: var(--color-text); - border-color: var(--color-spacer); -} - -.section .name.empty { - color: var(--color-text-deactivated); -} - -.section > .name:hover { - background: var(--color-spacer); -} - -.section > .name.empty:hover { - background: none; -} - -.section > .name.active { - background: var(--color-background); - color: var(--color-brand); -} - -.page { - color: var(--color-text); -} - -.page:hover { - background: var(--color-spacer); -} - -.page.active { - background: var(--color-background); - color: var(--color-brand); -} - -.section > .links { - border-left-color: var(--color-spacer); -} - -a.jump { - border-color: var(--color-brand); -} - -a.jump:hover { - background: var(--color-nav); -} - -a.jump { - text-decoration: none; - flex: 1; - text-align: center; - border-style: solid; - border-width: 1px; - padding: 8px 16px; - width: 100%; - border-radius: 8px; - box-shadow: rgba(0, 0, 0, 0.24) 0px 1px 2px; -} - -a.jump.next { - margin-left: 8px; -} - -a.jump.next::after { - content: " →"; -} - -a.jump.previous { - margin-right: 8px; -} - -a.jump.previous::before { - content: "← "; -} - -@media screen and (max-width: 800px) { - .jump-container { - flex-direction: column; - } - - a.jump.next { - margin-left: 0px; - margin-top: 16px; - } - - a.jump.previous { - margin-right: 0px; - margin-top: 16px; - } -} - -.drawer { - background: #fff; -} - -.border { - border-color: var(--color-spacer); -} - -.loading { - background: var(--color-nav); -} - -.glossary .terms { - border-left-color: var(--color-spacer); -} diff --git a/templates/simple/src/components/Toc.tsx b/templates/simple/src/components/Toc.tsx deleted file mode 100644 index 6b22c6a95..000000000 --- a/templates/simple/src/components/Toc.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Toc as TocProps } from "../utils/toc"; - -export const Toc = ({ headings }: TocProps) => { - return ( - - ); -}; diff --git a/templates/simple/src/reset.css b/templates/simple/src/reset.css deleted file mode 100644 index 0687c0d50..000000000 --- a/templates/simple/src/reset.css +++ /dev/null @@ -1,267 +0,0 @@ -/* - HTML5 Reset :: style.css - ---------------------------------------------------------- - We have learned much from/been inspired by/taken code where offered from: - - Eric Meyer :: http://meyerweb.com - HTML5 Doctor :: http://html5doctor.com - and the HTML5 Boilerplate :: http://html5boilerplate.com - --------------------------------------------------------------------------------*/ - -/* Let's default this puppy out --------------------------------------------------------------------------------*/ - -html, -body, -body div, -span, -object, -iframe, -h1, -h2, -h3, -h4, -h5, -h6, -p, -blockquote, -pre, -abbr, -address, -cite, -code, -del, -dfn, -em, -img, -ins, -kbd, -q, -samp, -small, -strong, -sub, -sup, -var, -b, -i, -dl, -dt, -dd, -ol, -ul, -li, -fieldset, -form, -label, -legend, -caption, -article, -aside, -figure, -footer, -header, -menu, -nav, -section, -time, -mark, -audio, -video, -details, -summary { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font-weight: normal; - vertical-align: baseline; - background: transparent; -} - -main, -article, -aside, -figure, -footer, -header, -nav, -section, -details, -summary { - display: block; -} - -/* Handle box-sizing while better addressing child elements: - http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */ -html { - box-sizing: border-box; -} - -*, -*:before, -*:after { - box-sizing: inherit; -} - -/* consider resetting the default cursor: https://gist.github.com/murtaugh/5247154 */ - -/* Responsive images and other embedded objects */ -/* if you don't have full control over `img` tags (if you have to overcome attributes), consider adding height: auto */ -img, -object, -embed { - max-width: 100%; -} - -/* - Note: keeping IMG here will cause problems if you're using foreground images as sprites. - In fact, it *will* cause problems with Google Maps' controls at small size. - If this is the case for you, try uncommenting the following: - -#map img { - max-width: none; -} -*/ - -/* force a vertical scrollbar to prevent a jumpy page */ -html { - overflow-y: scroll; -} - -blockquote, -q { - quotes: none; -} - -blockquote:before, -blockquote:after, -q:before, -q:after { - content: ""; - content: none; -} - -a { - margin: 0; - padding: 0; - font-size: 100%; - vertical-align: baseline; - background: transparent; -} - -del { - text-decoration: line-through; -} - -abbr[title], -dfn[title] { - border-bottom: 1px dotted #000; - cursor: help; -} - -hr { - display: block; - height: 1px; - border: 0; - border-top: 1px solid #ccc; - margin: 1em 0; - padding: 0; -} - -input, -select { - vertical-align: middle; -} - -input[type="radio"] { - vertical-align: text-bottom; -} -input[type="checkbox"] { - vertical-align: bottom; -} -.ie7 input[type="checkbox"] { - vertical-align: baseline; -} -.ie6 input { - vertical-align: text-bottom; -} - -select, -input, -textarea { - font: 99% sans-serif; -} - -small { - font-size: 85%; -} - -strong { - font-weight: bold; -} - -/* Make sure sup and sub don't mess with your line-heights http://gist.github.com/413930 */ -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; -} -sup { - top: -0.5em; -} -sub { - bottom: -0.25em; -} - -/* standardize any monospaced elements */ -pre, -code, -kbd, -samp { - font-family: monospace, sans-serif; -} - -/* hand cursor on clickable elements */ -.clickable, -label, -input[type="button"], -input[type="submit"], -input[type="file"], -button { - cursor: pointer; -} - -/* Webkit browsers add a 2px margin outside the chrome of form elements */ -button, -input, -select, -textarea { - margin: 0; -} - -/* make buttons play nice in IE */ -button, -input[type="button"] { - width: auto; - overflow: visible; -} - -/* scale images in IE7 more attractively */ -.ie7 img { - -ms-interpolation-mode: bicubic; -} - -/* prevent BG image flicker upon hover - (commented out as usage is rare, and the filter syntax messes with some pre-processors) -.ie6 html {filter: expression(document.execCommand("BackgroundImageCache", false, true));} -*/ - -/* let's clear some floats */ -.clearfix:after { - content: " "; - display: block; - clear: both; -} diff --git a/templates/simple/src/styles.css b/templates/simple/src/styles.css deleted file mode 100644 index 00d7dbfb6..000000000 --- a/templates/simple/src/styles.css +++ /dev/null @@ -1,439 +0,0 @@ -html, -body { - font-family: hyperbook-body, sans-serif; - overflow-x: hidden; -} - -body { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ -} - -body::-webkit-scrollbar { - display: none; -} - -.main-grid { - display: grid; - height: 100vh; - width: 100vw; - grid-template-columns: 300px 1fr; - grid-template-rows: 80px 1fr; - grid-template-areas: - "header header header" - "nav article article"; -} - -.sidebar, -#mobile-sidebar { - display: flex; - flex-direction: column; - grid-area: nav; - border-right-width: 1px; - height: calc(100vh - 80px); - border-right-style: solid; - overflow-y: auto; -} - -#toc-sidebar { - display: flex; - flex-direction: column; - width: 100%; - height: calc(100vh - 80px); - border-left-width: 1px; - border-left-style: solid; - overflow-y: auto; -} - -#mobile-sidebar { - width: 100%; -} - -.sidebar > nav, -#mobile-sidebar > nav, -#toc-sidebar > nav { - padding: 20px; - flex: 1; -} - -.sidebar > .author, -#mobile-sidebar > .author { - text-align: center; - text-decoration: none; - padding: 20px; -} - -.sidebar > .author > b, -#mobile-sidebar > .author > b { - font-weight: bold; -} - -.mobile-nav { - display: none; - margin-left: 16px; -} - -.mobile-nav .toggle { - font-size: 42px; - border: none; - background: none; - width: 40px; - height: 40px; -} -.toggle > .bar1, -.toggle > .bar2, -.toggle > .bar3 { - width: 35px; - height: 5px; - margin: 6px 0; - transition: 0.4s; -} - -.toc-toggle { - position: fixed; - top: 90px; - right: 20px; - border-radius: 50%; - height: 40px; - width: 40px; - border-style: solid; - border-width: 1px; - opacity: 0.7; - z-index: 1000; -} - -.toc-toggle:hover { - opacity: 1; -} - -.toc-toggle > .bar1, -.toc-toggle > .bar3 { - width: 20px; - height: 2px; - margin: 2px 3px; - transition: 0.4s; -} - -.toc-toggle > .bar2, -.toc-toggle > .bar4 { - width: 25px; - height: 2px; - margin: 2px 3px; - transition: 0.4s; -} - -/* Rotate first bar */ -.change .bar1 { - transform: rotate(-45deg) translate(-8px, 8px); -} - -/* Fade out the second bar */ -.change .bar2 { - opacity: 0; -} - -/* Rotate last bar */ -.change .bar3 { - transform: rotate(45deg) translate(-8px, -8px); -} - -@media screen and (max-width: 800px) { - .main-grid { - grid-template-columns: 1fr; - grid-template-rows: 80px 1fr; - grid-template-areas: - "header" - "article"; - } - - .sidebar { - display: none; - } - - .mobile-nav { - display: flex; - align-items: center; - justify-content: center; - } -} - -main { - padding: 20px 40px; - overflow-y: auto; -} - -article { - max-width: 980px; - margin: 0 auto; - grid-area: article; - line-height: 1.5; -} - -header { - display: flex; - grid-area: header; - align-items: center; - z-index: 1001; - box-shadow: rgba(0, 0, 0, 0.24) 0px 1px 4px; -} - -.branding { - text-decoration: none; - display: flex; - padding: 20px; - align-items: center; -} - -.custom-links { - padding: 20px; - flex: 1; - display: flex; - justify-content: flex-end; -} - -.custom-links .container { - display: inline-flex; -} - -.branding .logo { - display: flex; - align-items: center; - justify-content: center; - margin-right: 10px; - height: 40px; - width: auto; - min-width: 40px; -} - -.branding .logo img { - width: auto; - height: 100%; -} - -.branding .name { - font-size: 20px; - font-weight: bold; -} - -.jump-container { - display: flex; - max-width: 980px; - margin: 0 auto; - margin-top: 40px; -} - -.flex { - flex: 1; -} - -.meta { - display: flex; - flex-wrap: wrap; - border-top-style: solid; - border-top-width: 1px; - margin: 0 auto; - align-items: center; - justify-content: center; - gap: 16px; - margin-top: 30px; - padding-top: 30px; - max-width: 980px; -} - -.meta .edit-github { - text-decoration: none; - display: block; - flex: 1; -} - -.meta .vercel { - text-align: center; - flex: 1; -} - -.meta .copyright { - text-align: right; - flex: 1; -} - -.section ul, -.virtual-section ul, -nav > ul { - list-style-type: none; - margin: 0; - padding: 0; -} - -nav li + li { - margin-top: 0px; -} - -.section, -.virtual-section { - display: flex; - flex-direction: column; -} - -.section { - margin-top: 8px; - margin-bottom: 8px; -} - -.section > .name { - display: flex; - text-decoration: none; - padding: 10px; - border-width: 1px; - border-style: solid; - width: 100%; - user-select: none; -} - -.section > .name > .label { - flex: 1; -} - -.section > .name > .toggle { - text-align: right; - background: none; - border: none; - font-size: 13px; -} - -.section .section { - margin-left: 16px; - margin-bottom: 0px; -} - -.page { - display: block; - padding: 10px; - text-decoration: none; -} - -.section > .links { - border-left-width: 1px; - border-left-style: solid; - padding-top: 6px; -} - -.drawer-container { - --transition-speed: 0.3s; -} - -.drawer { - width: 300px; - height: 100%; - overflow: auto; - position: fixed; - display: flex; - box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); - transition: transform var(--transition-speed) ease; - z-index: 1000; -} - -.drawer.left { - top: 80px; - left: 0; - transform: translateX(-105%); -} - -.drawer.right { - top: 80px; - right: 0; - transform: translateX(100%); -} - -.drawer-container.in.open .left, -.drawer-container.in.open .right { - transform: translateX(0); -} - -.backdrop { - visibility: hidden; - opacity: 0; - background: rgba(0, 0, 0, 0.5); - transition: opacity var(--transition-speed) ease, - visibility var(--transition-speed) ease; - width: 100%; - height: 100%; - top: 80px; - left: 0; - position: fixed; - pointer-events: none; - z-index: 0; -} - -.drawer-container.in.open .backdrop { - visibility: visible; - opacity: 1; - pointer-events: auto; - z-index: 999; -} - -.glossary .letter { - font-size: 24px; -} - -.glossary .term { - list-style-type: none; - text-decoration: none; -} - -.glossary .terms { - padding-left: 1em; - border-left-width: 1px; - border-left-style: solid; -} - -.pages { - margin-top: 30px; -} - -.border { - border-radius: 4px; - border-style: solid; - border-width: 1px; -} - -.loading { - display: flex; - align-items: center; - justify-content: center; - overflow: none; - font-size: 32px; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} - -nav.toc li { - margin-bottom: 8px; -} - -nav.toc li > a { - color: var(--color-text); - text-decoration: none; -} - -nav.toc li > a:hover { - color: var(--color-brand); -} - -nav.toc li.level-1 { - padding-left: 0px; -} - -nav.toc li.level-2 { - padding-left: 8px; -} - -nav.toc li.level-3 { - padding-left: 16px; -} - -nav.toc li.level-3 { - padding-left: 24px; -} diff --git a/templates/simple/src/szh-menu.css b/templates/simple/src/szh-menu.css deleted file mode 100644 index 87322d313..000000000 --- a/templates/simple/src/szh-menu.css +++ /dev/null @@ -1,269 +0,0 @@ -@charset "UTF-8"; -.szh-menu { - margin: 0; - padding: 0; - list-style: none; - box-sizing: border-box; - width: max-content; - z-index: 100; - border: 1px solid rgba(0, 0, 0, 0.1); - background-color: var(--color-background); -} -.szh-menu:focus { - outline: none; -} -.szh-menu__arrow { - box-sizing: border-box; - width: 0.75rem; - height: 0.75rem; - background-color: var(--color-background); - border: 1px solid transparent; - border-left-color: rgba(0, 0, 0, 0.1); - border-top-color: rgba(0, 0, 0, 0.1); - z-index: -1; -} -.szh-menu__arrow--dir-left { - right: -0.375rem; - transform: translateY(-50%) rotate(135deg); -} -.szh-menu__arrow--dir-right { - left: -0.375rem; - transform: translateY(-50%) rotate(-45deg); -} -.szh-menu__arrow--dir-top { - bottom: -0.375rem; - transform: translateX(-50%) rotate(-135deg); -} -.szh-menu__arrow--dir-bottom { - top: -0.375rem; - transform: translateX(-50%) rotate(45deg); -} -.szh-menu__item { - cursor: pointer; -} -.szh-menu__item:focus { - outline: none; -} -.szh-menu__item--hover { - background-color: var(--color-nav); -} -.szh-menu__item--focusable { - cursor: default; - background-color: inherit; -} -.szh-menu__item--disabled { - cursor: default; - color: #aaa; -} -.szh-menu__item--anchor { - text-decoration: none; - color: var(--color-text); -} - -.szh-menu__group { - box-sizing: border-box; -} -.szh-menu__radio-group { - margin: 0; - padding: 0; - list-style: none; -} -.szh-menu__divider { - height: 1px; - margin: 0.5rem 0; - background-color: rgba(0, 0, 0, 0.12); -} - -.szh-menu-button::after { - content: "❯"; - transform: rotate(90deg); - font-size: 0.8rem; - position: absolute; - right: 1rem; -} - -.szh-menu-button svg { - width: auto; - height: 28px; - stroke: var(--color-brand-text); - fill: var(--color-brand-text); -} - -.szh-menu-button.icon::after { - content: ""; -} - -.szh-menu-button, -.szh-menu-link { - display: flex; - align-items: center; - position: relative; - box-sizing: border-box; - background: none; - border-style: solid; - border-color: transparent; - text-decoration: none; - padding: 8px; - border-width: 1px; - color: var(--color-brand-text); - border-radius: 0.25rem; - font-size: 1em; - font-weight: bold; -} - -.szh-menu-button { - padding-right: 2.5rem; -} - -.szh-menu-button.icon { - padding: 8px; -} - -.szh-menu-button:hover, -.szh-menu-link:hover { - border-color: var(--color-brand-text); -} - -.szh-menu { - user-select: none; - color: var(--color-text); - border: 1px solid var(--color-spacer); - border-radius: 0.25rem; - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.133), 0 0.6px 2px rgba(0, 0, 0, 0.1); - min-width: 10rem; -} -.szh-menu__item { - display: flex; - align-items: center; - position: relative; - gap: 8px; - padding: 0.75rem 1rem; -} - -.szh-menu__item > .label { - flex: 1; -} - -.szh-menu-container--itemTransition .szh-menu__item { - transition-property: background-color, color; - transition-duration: 0.15s; - transition-timing-function: ease-in-out; -} -.szh-menu__item--type-radio { - padding-left: 2.2rem; -} -.szh-menu__item--type-radio::before { - content: "○"; - position: absolute; - left: 0.8rem; - top: 0.55rem; - font-size: 0.8rem; -} -.szh-menu__item--type-radio.szh-menu__item--checked::before { - content: "●"; -} -.szh-menu__item--type-checkbox { - padding-left: 2.2rem; -} -.szh-menu__item--type-checkbox::before { - position: absolute; - left: 0.8rem; -} -.szh-menu__item--type-checkbox.szh-menu__item--checked::before { - content: "✔"; -} -.szh-menu__submenu > .szh-menu__item { - padding-right: 2.5rem; -} -.szh-menu__submenu > .szh-menu__item::after { - content: "❯"; - font-size: 0.8rem; - position: absolute; - right: 1rem; -} -.szh-menu__header { - color: #888; - font-size: 0.8rem; - padding: 0.2rem 1.5rem; - text-transform: uppercase; -} - -@keyframes szh-menu-show-slide-left { - from { - opacity: 0; - transform: translateX(0.5rem); - } -} -@keyframes szh-menu-hide-slide-left { - to { - opacity: 0; - transform: translateX(0.5rem); - } -} -@keyframes szh-menu-show-slide-right { - from { - opacity: 0; - transform: translateX(-0.5rem); - } -} -@keyframes szh-menu-hide-slide-right { - to { - opacity: 0; - transform: translateX(-0.5rem); - } -} -@keyframes szh-menu-show-slide-top { - from { - opacity: 0; - transform: translateY(0.5rem); - } -} -@keyframes szh-menu-hide-slide-top { - to { - opacity: 0; - transform: translateY(0.5rem); - } -} -@keyframes szh-menu-show-slide-bottom { - from { - opacity: 0; - transform: translateY(-0.5rem); - } -} -@keyframes szh-menu-hide-slide-bottom { - to { - opacity: 0; - transform: translateY(-0.5rem); - } -} -.szh-menu--state-opening.szh-menu--dir-left { - animation: szh-menu-show-slide-left 0.15s ease-out; -} - -.szh-menu--state-closing.szh-menu--dir-left { - animation: szh-menu-hide-slide-left 0.15s ease-in forwards; -} - -.szh-menu--state-opening.szh-menu--dir-right { - animation: szh-menu-show-slide-right 0.15s ease-out; -} - -.szh-menu--state-closing.szh-menu--dir-right { - animation: szh-menu-hide-slide-right 0.15s ease-in forwards; -} - -.szh-menu--state-opening.szh-menu--dir-top { - animation: szh-menu-show-slide-top 0.15s ease-out; -} - -.szh-menu--state-closing.szh-menu--dir-top { - animation: szh-menu-hide-slide-top 0.15s ease-in forwards; -} - -.szh-menu--state-opening.szh-menu--dir-bottom { - animation: szh-menu-show-slide-bottom 0.15s ease-out; -} - -.szh-menu--state-closing.szh-menu--dir-bottom { - animation: szh-menu-hide-slide-bottom 0.15s ease-in forwards; -} diff --git a/templates/simple/src/utils/useMountTransition.tsx b/templates/simple/src/utils/useMountTransition.tsx deleted file mode 100644 index f38b2bdf9..000000000 --- a/templates/simple/src/utils/useMountTransition.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect, useState } from "react"; - -export const useMountTransition = ( - isMounted: boolean, - unmountDelay: number -) => { - const [isTransitioning, setIsTransitioning] = useState(false); - - useEffect(() => { - let timeoutId: NodeJS.Timeout; - - if (isMounted && !isTransitioning) { - setIsTransitioning(true); - } else if (!isMounted && isTransitioning) { - timeoutId = setTimeout(() => setIsTransitioning(false), unmountDelay); - } - return () => { - clearTimeout(timeoutId); - }; - }, [unmountDelay, isMounted, isTransitioning]); - - return isTransitioning; -}; diff --git a/templates/simple/src/utils/useOnScreen.tsx b/templates/simple/src/utils/useOnScreen.tsx deleted file mode 100644 index 169be08d3..000000000 --- a/templates/simple/src/utils/useOnScreen.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { RefObject, useLayoutEffect, useState } from "react"; - -export const useOnScreen = ( - ref: RefObject, - containerRef: RefObject, - padding: number = 0 -) => { - const [isOnScreen, setOnScreen] = useState(true); - - const handleResize = (bounds: DOMRect) => () => { - if (containerRef.current) { - const containerBounds = containerRef.current.getBoundingClientRect(); - - if ( - containerBounds.width >= bounds.width + padding && - containerBounds.right <= window.innerWidth - ) { - setOnScreen(true); - } else { - setOnScreen(false); - } - } - }; - - useLayoutEffect(() => { - if (ref.current) { - const bounds = ref.current.getBoundingClientRect(); - - const h = handleResize(bounds); - - window.addEventListener("resize", h); - - h(); - - return () => { - window.removeEventListener("resize", h); - }; - } - }, [ref]); - - return isOnScreen; -}; diff --git a/templates/simple/src/utils/useOverflow.tsx b/templates/simple/src/utils/useOverflow.tsx deleted file mode 100644 index f8af05c5c..000000000 --- a/templates/simple/src/utils/useOverflow.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { RefObject, useLayoutEffect, useState } from "react"; - -export const useIsOverflow = (ref: RefObject) => { - const [isOverflow, setIsOverflow] = useState(undefined); - - useLayoutEffect(() => { - const { current } = ref; - - const trigger = () => { - const hasOverflow = current.scrollWidth > current.clientWidth; - console.log(current); - - setIsOverflow(hasOverflow); - }; - - if (current) { - if ("ResizeObserver" in window) { - new ResizeObserver(trigger).observe(current); - } - - trigger(); - } - }, [ref]); - - return isOverflow; -}; diff --git a/templates/simple/src/utils/usePreferesColorScheme.tsx b/templates/simple/src/utils/usePreferesColorScheme.tsx deleted file mode 100644 index 6f9a3ed95..000000000 --- a/templates/simple/src/utils/usePreferesColorScheme.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useState, useEffect } from "react"; - -export type ColorScheme = "light" | "dark"; - -export function usePrefersColorScheme(): ColorScheme { - const [preferredColorScheme, setPreferredColorScheme] = - useState("light"); - - useEffect(() => { - if (!window.matchMedia) { - setPreferredColorScheme("light"); - return; - } - - const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); - - setPreferredColorScheme(mediaQuery.matches ? "dark" : "light"); - - function onChange(event: MediaQueryListEvent): void { - setPreferredColorScheme(event.matches ? "dark" : "light"); - } - - mediaQuery.addEventListener("change", onChange); - - return () => { - mediaQuery.removeEventListener("change", onChange); - }; - }, []); - - return preferredColorScheme; -} From e8897d1f258c17a665996fb0d838da94b5b19638 Mon Sep 17 00:00:00 2001 From: Mike Barkmin Date: Thu, 27 Oct 2022 10:47:37 +0200 Subject: [PATCH 2/3] refactor filesystem operations into separate package --- packages/fs/README.md | 9 + packages/fs/package.json | 48 +++ packages/fs/src/index.ts | 366 ++++++++++++++++++ packages/fs/tsconfig.build.json | 8 + packages/fs/tsconfig.json | 4 + packages/hyperbook/build.ts | 14 +- packages/hyperbook/helpers/project.ts | 160 -------- packages/hyperbook/index.ts | 6 +- packages/hyperbook/package.json | 1 + packages/hyperbook/setup.ts | 8 +- packages/shell/src/Navigation.tsx | 42 +- packages/shell/src/Shell.tsx | 59 +-- packages/shell/src/index.css | 4 + packages/types/src/index.ts | 55 +++ platforms/web/books | 1 - platforms/web/package.json | 3 +- platforms/web/src/pages/[[...page]].tsx | 34 +- platforms/web/src/pages/_app.tsx | 5 +- .../web/src/pages/glossary/[...term].tsx | 58 +-- platforms/web/src/pages/glossary/index.tsx | 67 +--- platforms/web/src/utils/files.ts | 23 -- platforms/web/src/utils/hyperbook.ts | 6 - platforms/web/src/utils/navigation.ts | 151 -------- pnpm-lock.yaml | 56 +++ scripts/watcher.mjs | 10 +- website/en/book/elements/image.md | 44 --- 26 files changed, 666 insertions(+), 576 deletions(-) create mode 100644 packages/fs/README.md create mode 100644 packages/fs/package.json create mode 100644 packages/fs/src/index.ts create mode 100644 packages/fs/tsconfig.build.json create mode 100644 packages/fs/tsconfig.json delete mode 100644 packages/hyperbook/helpers/project.ts delete mode 120000 platforms/web/books delete mode 100644 platforms/web/src/utils/files.ts delete mode 100644 platforms/web/src/utils/hyperbook.ts delete mode 100644 platforms/web/src/utils/navigation.ts delete mode 100644 website/en/book/elements/image.md diff --git a/packages/fs/README.md b/packages/fs/README.md new file mode 100644 index 000000000..66ad974e7 --- /dev/null +++ b/packages/fs/README.md @@ -0,0 +1,9 @@ +# @hyperbook/fs + +## Installation + +```sh +yarn add @hyperbook/fs +# or +npm i @hyperbook/fs +``` diff --git a/packages/fs/package.json b/packages/fs/package.json new file mode 100644 index 000000000..54a65ec28 --- /dev/null +++ b/packages/fs/package.json @@ -0,0 +1,48 @@ +{ + "name": "@hyperbook/fs", + "version": "0.0.0", + "author": "Mike Barkmin", + "homepage": "https://github.com/openpatch/hyperbook#readme", + "license": "MIT", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.mjs", + "types": "dist/index.d.ts", + "typings": "dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "require": "./dist/index.cjs.js", + "default": "./dist/index.esm.mjs" + } + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/openpatch/hyperbook.git", + "directory": "packages/fs" + }, + "bugs": { + "url": "https://github.com/openpatch/hyperbook/issues" + }, + "scripts": { + "prebuild": "rimraf dist", + "version": "pnpm build", + "lint": "tsc --noEmit", + "build": "pnpm build:pkg && pnpm build:types", + "build:pkg": "node ../../scripts/build.mjs", + "build:types": "tsc --project tsconfig.build.json --declaration --emitDeclarationOnly" + }, + "dependencies": { + "chalk": "5.1.2", + "find-up": "^6.3.0", + "gray-matter": "4.0.3" + }, + "devDependencies": { + "@hyperbook/types": "workspace:*" + } +} diff --git a/packages/fs/src/index.ts b/packages/fs/src/index.ts new file mode 100644 index 000000000..4a727c39c --- /dev/null +++ b/packages/fs/src/index.ts @@ -0,0 +1,366 @@ +import fs from "fs/promises"; +import path from "path"; +import matter from "gray-matter"; +import chalk from "chalk"; +import { + HyperbookFrontmatter, + HyperbookJson, + Hyperproject, + HyperlibraryJson, + Language, + Link, + Navigation, + HyperbookPage, + HyperbookSection, + Glossary, +} from "@hyperbook/types"; +import { findUp } from "find-up"; + +const listFiles = async ( + root: string, + dir: string = "", + absolute: boolean = true +): Promise => { + const files = await fs.readdir(path.join(root, dir)); + return Promise.all( + files.flatMap(async (file) => { + const stat = await fs.stat(path.join(root, dir, file)); + if (stat.isDirectory()) { + return listFiles(root, path.join(dir, file), absolute); + } else if (file.endsWith(".md")) { + if (absolute) { + return [path.join(root, dir, file)]; + } else { + return [path.join(dir, file)]; + } + } + return []; + }) + ).then((f) => f.flat()); +}; + +export const readBook = async (root: string, absolute: boolean = true) => { + return listFiles(path.join(root, "book"), "", absolute); +}; + +export const readGlossary = async (root: string, absolute: boolean = true) => { + return listFiles(path.join(root, "glossary"), "", absolute); +}; + +export const makeGlossary = async (root: string): Promise => { + const files = await readGlossary(root); + const glossary: Glossary = {}; + + for (const file of files) { + const { content, data } = await readFile(file); + let name = path.basename(file, ".md"); + if (data.name) { + name = data.name; + } else { + console.log( + `\n${chalk.yellow( + `warn ` + )}- Glossary page ${file} does not specify a name. Defaulting to the filename ${name}.` + ); + } + + const letter = name[0].toUpperCase(); + if (!glossary[letter]) { + glossary[letter] = []; + } + + const relativePath = path + .relative(root, file) + .replace(/\.md$/, "") + .split("/"); + + glossary[letter].push({ + name, + href: "/" + relativePath.join("/"), + }); + } + + return glossary; +}; + +export const listPagesForTerm = async ( + root: string, + term: string +): Promise => { + const files = await readBook(root); + + const pages: HyperbookPage[] = []; + + for (const file of files) { + const { content, data } = await readFile(file); + const r = new RegExp(`:t\\[.*\\]\\{#${term}(\..*)?\\}|:t\\[${term}\\]`); + const m = content.match(r); + + if (m && !data.hide && data.name) { + const relativePath = path + .relative(root, file) + .replace(/\.md$/, "") + .split("/"); + const isIndex = relativePath[relativePath.length - 1] === "index"; + if (isIndex) { + relativePath.pop(); + } + pages.push({ + ...data, + href: "/" + relativePath.join("/"), + }); + } + } + + return pages; +}; + +export const readFile = async (file: string) => { + const source = await fs + .readFile(file) + .catch(() => fs.readFile(path.join(file, "index.md"))); + + const { content, data } = matter(source); + + return { content, data: data as HyperbookFrontmatter }; +}; + +export const readHyperbook = async (root: string) => { + return fs + .readFile(path.join(root, "hyperbook.json")) + .then((f) => f.toString()) + .then(JSON.parse) as Promise; +}; + +export const readHyperlibrary = async (root: string) => { + return fs + .readFile(path.join(root, "hyperlibrary.json")) + .then((f) => f.toString()) + .then(JSON.parse) as Promise; +}; + +export const findHyperbook = async (file: string) => { + return findUp("hyperbook.json", { + cwd: file, + } as any); +}; + +export const readProject = async ( + root: string, + libraryEntry?: HyperlibraryJson["library"][0] +): Promise => { + if (libraryEntry?.src) { + root = path.join(root, libraryEntry.src); + } + const hyperbookJson = await readHyperbook(root).catch(() => null); + if (hyperbookJson) { + return { + type: "book", + src: root, + basePath: libraryEntry?.basePath ?? hyperbookJson.basePath, + name: libraryEntry?.name ?? hyperbookJson.name, + icon: libraryEntry?.icon, + }; + } + + const hyperlibraryJson = await readHyperlibrary(root).catch(() => null); + if (hyperlibraryJson) { + return { + type: "library", + src: root, + basePath: libraryEntry?.basePath ?? hyperlibraryJson.basePath, + name: libraryEntry?.name ?? hyperlibraryJson.name, + projects: await Promise.all( + hyperlibraryJson.library.map((p) => + readProject(root, { + ...p, + basePath: path.join( + libraryEntry?.basePath ?? hyperlibraryJson.basePath ?? "", + p.basePath + ), + }) + ) + ), + icon: libraryEntry?.icon, + }; + } + + console.log( + `${chalk.red("Error")} - Missing book or library for path ${root}.` + ); + + throw Error(`Missing book or library for path ${root}`); +}; + +export const getProjectName = (project: Hyperproject, language?: Language) => { + let label = ""; + if (typeof project.name === "string") { + label = project.name; + } else { + if (language) { + label = project.name[language]; + } else { + label = Object.values(project.name)[0]; + } + if (!label) { + console.log( + chalk.red( + `You need to provide a name for language ${language} in ${project.src}` + ) + ); + throw Error(""); + } + } + return label; +}; + +export const makeLinkForHyperproject = async ( + project: Hyperproject, + language: Language = "en" +): Promise => { + const label = getProjectName(project, language); + + if (project.type === "library") { + return { + label, + links: await Promise.all( + project.projects.map((p) => makeLinkForHyperproject(p, language)) + ), + icon: project.icon, + }; + } else { + let href = project.basePath ?? "/"; + if (!href.startsWith("/")) { + href = "/" + href; + } + + if (href.length > 1 && href.endsWith("/")) { + href = href.slice(0, -1); + } + + return { + label, + href, + icon: project.icon, + }; + } +}; + +export const makeNavigationForHyperbook = async ( + root: string, + currPath: string = "/" +): Promise => { + const hyperbook = await readHyperbook(root); + + const getSectionsAndPages = async function ( + dirPath: string, + pageList: HyperbookPage[] = [] + ) { + const files = await fs.readdir(dirPath); + let arrayOfPages: HyperbookPage[] = []; + let arrayOfSections: HyperbookSection[] = []; + + for (const file of files) { + let p = path.join(dirPath, file); + let repo: string | null = null; + if (hyperbook.repo) { + repo = hyperbook.repo + "/" + path.relative(root, p); + } + + const stat = await fs.stat(p); + + if (stat.isDirectory()) { + const { pages, sections } = await getSectionsAndPages(p, pageList); + const { content, data } = await readFile(p); + const section: HyperbookSection = { + ...data, + href: "/" + path.relative(path.join(root, "book"), p), + isEmpty: content.trim() === "", + pages, + sections, + }; + if (repo) { + section.repo = repo + "/index.md"; + } + + arrayOfSections.push(section); + } else { + const { data } = await readFile(p); + if (p.endsWith(".md")) { + p = p.substring(0, p.length - 3); + if (path.relative(path.join(root, "book"), p) === "index") { + p = p.substring(0, p.length - 5); + } + if (!p.endsWith("index")) { + const page: HyperbookPage = { + ...data, + href: "/" + path.relative(path.join(root, "book"), p), + }; + if (repo) { + page.repo = repo; + } + + arrayOfPages.push(page); + } + } + } + } + + arrayOfPages = arrayOfPages.sort((a, b) => (a.name > b.name ? 1 : -1)); + arrayOfPages = arrayOfPages.sort((a, b) => { + const iIndex = a.index !== undefined ? a.index : 9999; + const eIndex = b.index !== undefined ? b.index : 9999; + return iIndex - eIndex; + }); + arrayOfSections = arrayOfSections.sort((a, b) => + a.name > b.name ? 1 : -1 + ); + arrayOfSections = arrayOfSections.sort((a, b) => { + const iIndex = a.index !== undefined ? a.index : 9999; + const eIndex = b.index !== undefined ? b.index : 9999; + return iIndex - eIndex; + }); + + return { pages: arrayOfPages, sections: arrayOfSections }; + }; + + const getPageList = ( + sections: HyperbookSection[], + pages: HyperbookPage[] + ): HyperbookPage[] => { + let pageList = [...pages]; + + for (const section of sections) { + pageList = [ + ...pageList, + section, + ...getPageList(section.sections, section.pages), + ]; + } + + return pageList; + }; + + const { sections, pages } = await getSectionsAndPages( + path.join(root, "book") + ); + + let pageList = getPageList(sections, pages); + + let i = pageList.findIndex((p) => p.href === currPath); + const current = pageList[i] || null; + + pageList = pageList.filter((p) => !p.isEmpty && !p.hide); + i = pageList.findIndex((p) => p.href === currPath); + + const next = pageList[i + 1] || null; + const previous = pageList[i - 1] || null; + + return { + next, + current, + previous, + sections, + pages, + }; +}; diff --git a/packages/fs/tsconfig.build.json b/packages/fs/tsconfig.build.json new file mode 100644 index 000000000..e84150858 --- /dev/null +++ b/packages/fs/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["src", "../../types"], + "compilerOptions": { + "rootDir": "src", + "declarationDir": "dist" + } +} diff --git a/packages/fs/tsconfig.json b/packages/fs/tsconfig.json new file mode 100644 index 000000000..13c8c9f56 --- /dev/null +++ b/packages/fs/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src", "../../types", "stories", "tests"] +} diff --git a/packages/hyperbook/build.ts b/packages/hyperbook/build.ts index c3b786d19..959b0b411 100644 --- a/packages/hyperbook/build.ts +++ b/packages/hyperbook/build.ts @@ -5,21 +5,21 @@ import readline from "readline"; import chalk from "chalk"; import { isSetup } from "./helpers/is-setup"; import { readHyperbook } from "./helpers/read-hyperbook"; -import { getProjectName, makeLinks, Project } from "./helpers/project"; +import { getProjectName, makeLinkForHyperproject } from "@hyperbook/fs"; import { runArchive } from "./archive"; import { makeDir } from "./helpers/make-dir"; import rimraf from "rimraf"; -import { Link } from "@hyperbook/types"; +import { Link, Hyperproject } from "@hyperbook/types"; import { makeEnv } from "./helpers/make-env"; export async function runBuildProject( - project: Project, - rootProject: Project, + project: Hyperproject, + rootProject: Hyperproject, out?: string ): Promise { if (project.type === "book") { - console.log(`${chalk.blue(`[${project.name}]`)} Building Book.`); const name = getProjectName(project); + console.log(`${chalk.blue(`[${name}]`)} Building Book.`); await runBuild(project.src, rootProject, project.basePath, name, out); } else { if (!out) { @@ -35,7 +35,7 @@ export async function runBuildProject( async function runBuild( root: string, - rootProject: Project, + rootProject: Hyperproject, basePath?: string, prefix?: string, out?: string @@ -76,7 +76,7 @@ module.exports = { const hyperbookJson = await readHyperbook(root); let link: Link | undefined = undefined; if (rootProject.type === "library") { - link = makeLinks(rootProject, hyperbookJson.language); + link = await makeLinkForHyperproject(rootProject, hyperbookJson.language); } if (link) { if (!hyperbookJson.links) { diff --git a/packages/hyperbook/helpers/project.ts b/packages/hyperbook/helpers/project.ts deleted file mode 100644 index cf1f79628..000000000 --- a/packages/hyperbook/helpers/project.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { - Language, - LanguageString, - HyperbookJson, - HyperlibraryJson, - Link, -} from "@hyperbook/types"; -import fs from "fs/promises"; -import chalk from "chalk"; -import path from "path"; - -export type Book = { - type: "book"; - src: string; - basePath?: string; - icon?: string; - name: string | LanguageString; - template?: string; -}; - -export type Library = { - type: "library"; - name: string | LanguageString; - basePath?: string; - icon?: string; - src: string; - projects: Project[]; -}; - -export type Project = Book | Library; - -export function getProjectName(project: Project, language?: Language) { - let label = ""; - if (typeof project.name === "string") { - label = project.name; - } else { - if (language) { - label = project.name[language]; - } else { - label = Object.values(project.name)[0]; - } - if (!label) { - console.log( - chalk.red( - `You need to provide a name for language ${language} in ${project.src}` - ) - ); - throw Error(""); - } - } - return label; -} - -export function makeLinks(project: Project, language: Language = "en"): Link { - const label = getProjectName(project, language); - - if (project.type === "library") { - return { - label, - links: project.projects.map((p) => makeLinks(p, language)), - icon: project.icon, - }; - } else { - let href = project.basePath ?? "/"; - if (!href.startsWith("/")) { - href = "/" + href; - } - - if (href.length > 1 && href.endsWith("/")) { - href = href.slice(0, -1); - } - - return { - label, - href, - icon: project.icon, - }; - } -} - -export async function collect( - root: string, - basePath?: string, - label?: string | LanguageString, - icon?: string -): Promise { - const hyperbookJson = await fs - .readFile(path.join(root, "hyperbook.json")) - .then((data) => JSON.parse(data.toString()) as HyperbookJson) - .catch((e) => { - if (e instanceof SyntaxError) { - console.error(e); - } - return null; - }); - - if (hyperbookJson) { - return { - type: "book", - src: root, - basePath: basePath ?? hyperbookJson.basePath, - template: hyperbookJson.template, - name: label ?? hyperbookJson.name, - icon, - }; - } - - const hyperlibraryJson = await fs - .readFile(path.join(root, "hyperlibrary.json")) - .then((data) => JSON.parse(data.toString()) as HyperlibraryJson) - .catch((e) => { - if (e instanceof SyntaxError) { - console.error(e); - } - return null; - }); - - if (hyperlibraryJson) { - const projects = await Promise.all( - hyperlibraryJson.library.map( - async ({ src, basePath: localBasePath, name, icon }) => { - if (!localBasePath) { - console.log( - chalk.red( - `Missing basePath for book ${name} in library ${path.join( - root, - "hyperlibrary.json" - )}` - ) - ); - } - return collect( - path.join(root, src), - path.join( - basePath ?? hyperlibraryJson.basePath ?? "", - localBasePath - ), - name, - icon - ); - } - ) - ); - - return { - type: "library", - name: label ?? hyperlibraryJson.name, - basePath: basePath ?? hyperlibraryJson.basePath, - src: root, - icon, - projects, - }; - } - - console.log( - `${chalk.red("Error")} - Missing book or library for path ${root}.` - ); - - throw Error(`Missing book or library for path ${root}`); -} diff --git a/packages/hyperbook/index.ts b/packages/hyperbook/index.ts index 453c02f9b..fc754ba47 100644 --- a/packages/hyperbook/index.ts +++ b/packages/hyperbook/index.ts @@ -8,7 +8,7 @@ import { runArchive } from "./archive"; import { runBuildProject } from "./build"; import { runDev } from "./dev"; import { getPkgManager } from "./helpers/get-pkg-manager"; -import { collect } from "./helpers/project"; +import { readProject } from "@hyperbook/fs"; import { runNew } from "./new"; import packageJson from "./package.json"; import { runSetupProject } from "./setup"; @@ -45,7 +45,7 @@ program .command("setup") .description("downloads the latest version of the template of a hyperbook") .action(async () => { - const rootProject = await collect(process.cwd()).catch(() => { + const rootProject = await readProject(process.cwd()).catch(() => { process.exit(1); }); @@ -58,7 +58,7 @@ program .command("build") .description("build a hyperbook") .action(async () => { - const rootProject = await collect(process.cwd()).catch(() => { + const rootProject = await readProject(process.cwd()).catch(() => { process.exit(1); }); await runBuildProject(rootProject, rootProject).catch(() => { diff --git a/packages/hyperbook/package.json b/packages/hyperbook/package.json index e5b7df91a..69dd0a1ba 100644 --- a/packages/hyperbook/package.json +++ b/packages/hyperbook/package.json @@ -52,6 +52,7 @@ "update-check": "1.5.4" }, "dependencies": { + "@hyperbook/fs": "workspace:*", "@hyperbook/types": "workspace:*", "@platforms/web": "workspace:*" } diff --git a/packages/hyperbook/setup.ts b/packages/hyperbook/setup.ts index 932e747ec..0584cee31 100644 --- a/packages/hyperbook/setup.ts +++ b/packages/hyperbook/setup.ts @@ -7,9 +7,13 @@ import { getOnline } from "./helpers/is-online"; import { makeDir } from "./helpers/make-dir"; import path from "path"; import { makeSymlink } from "./helpers/make-symlink"; -import { getProjectName, Project } from "./helpers/project"; +import { getProjectName } from "@hyperbook/fs"; +import { Hyperproject } from "@hyperbook/types"; -export async function runSetupProject(project: Project, rootProject?: Project) { +export async function runSetupProject( + project: Hyperproject, + rootProject?: Hyperproject +) { const name = getProjectName(project); console.log(`${chalk.blue(`[${name}]`)} Setup Project.`); const projectRoot = path.join(project.src, ".hyperbook"); diff --git a/packages/shell/src/Navigation.tsx b/packages/shell/src/Navigation.tsx index 3f1ddff8a..7f7f95512 100644 --- a/packages/shell/src/Navigation.tsx +++ b/packages/shell/src/Navigation.tsx @@ -1,35 +1,12 @@ import { useLink } from "@hyperbook/provider"; +import { + HyperbookPage, + HyperbookSection, + Navigation as NavigationProps, +} from "@hyperbook/types"; import useCollapse from "react-collapsed"; -export type PageProps = { - name: string; - description?: string; - keywords?: string[]; - repo?: string; - hide?: boolean; - toc?: boolean; - index?: number; - isEmpty?: boolean; - href: string; -}; - -export type SectionProps = Omit & { - hide?: boolean; - virtual?: boolean; - expanded?: boolean; - pages: PageProps[]; // md-files - sections: SectionProps[]; // folders -}; - -export type NavigationProps = { - next: PageProps | null; - previous: PageProps | null; - current: PageProps | null; - pages: PageProps[]; - sections: SectionProps[]; -}; - -type P = PageProps & Pick; +type P = HyperbookPage & Pick; const Page = ({ name, href, current }: P) => { const Link = useLink(); @@ -45,7 +22,7 @@ const Page = ({ name, href, current }: P) => { ); }; -type S = SectionProps & Pick; +type S = HyperbookSection & Pick; const Section = ({ isEmpty, @@ -58,7 +35,10 @@ const Section = ({ current, }: S) => { const Link = useLink(); - const isActive = current?.href.startsWith(href); + let isActive = false; + if (current?.href && href) { + isActive = current.href.startsWith(href); + } const { getCollapseProps, getToggleProps, isExpanded } = useCollapse({ defaultExpanded: isActive || expanded, }); diff --git a/packages/shell/src/Shell.tsx b/packages/shell/src/Shell.tsx index c383e03d5..2d8f3d7ae 100644 --- a/packages/shell/src/Shell.tsx +++ b/packages/shell/src/Shell.tsx @@ -1,14 +1,15 @@ import { useLink, useMakeUrl, useHead, useConfig } from "@hyperbook/provider"; import { FC, Fragment, ReactNode, useState } from "react"; import { Toc, TocProps } from "@hyperbook/toc"; +import { Navigation as NavigationProps, HyperbookPage } from "@hyperbook/types"; import { Drawer } from "./Drawer"; import { Links } from "./Links"; -import { Navigation, NavigationProps, PageProps } from "./Navigation"; +import { Navigation } from "./Navigation"; export type ShellProps = { navigation: NavigationProps; toc?: TocProps; - page: Pick; + page: HyperbookPage; children?: ReactNode; }; @@ -74,29 +75,31 @@ export const Shell: FC = ({ toc, navigation, page, children }) => { const [isTocOpen, setIsTocOpen] = useState(false); return ( - - {`${page.name} - ${hyperbook.name}`} - - {hyperbook.description && ( - <> - - - - )} - {page.description && ( - <> - - - - )} - {page.keywords && ( - - )} - + {page && ( + + {`${page.name} - ${hyperbook.name}`} + + {hyperbook.description && ( + <> + + + + )} + {page.description && ( + <> + + + + )} + {page.keywords && ( + + )} + + )}
    = ({ toc, navigation, page, children }) => { position="left" >
    - + {navigation && } Powered by Hyperbook @@ -172,7 +175,7 @@ export const Shell: FC = ({ toc, navigation, page, children }) => {
    {children}
    - {page.repo && ( + {page?.repo && ( ✎ GitHub diff --git a/packages/shell/src/index.css b/packages/shell/src/index.css index 475ee5ee0..654a7419b 100644 --- a/packages/shell/src/index.css +++ b/packages/shell/src/index.css @@ -6,6 +6,10 @@ font-family: hyperbook-body, sans-serif; } +#drawer-root * { + box-sizing: border-box; +} + .main-grid * { box-sizing: border-box; } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 9ffbaf926..f66524ac8 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -16,8 +16,56 @@ export type LinkWithHref = { export type Link = LinkWithHref | LinkWithLinks; +export type Navigation = { + next: HyperbookPage | null; + previous: HyperbookPage | null; + current: HyperbookPage | null; + pages: HyperbookPage[]; + sections: HyperbookSection[]; +}; + +export type Term = { + name: string; + href: string; +}; + +export type Glossary = Record; + export type Language = "de" | "en" | "fr" | "es" | "it" | "pt" | "nl"; +export type HyperbookPageFrontmatter = { + name: string; + lang?: Language; + description?: string; + keywords?: string[]; + index?: number; + hide?: boolean; + toc?: boolean; +}; + +export type HyperbookSectionFrontmatter = HyperbookPageFrontmatter & { + virtual?: boolean; + expanded?: boolean; +}; + +export type HyperbookPage = HyperbookPageFrontmatter & { + isEmpty?: boolean; + href?: string; + repo?: string; +}; + +export type HyperbookSection = HyperbookSectionFrontmatter & { + isEmpty?: boolean; + href?: string; + pages: HyperbookPage[]; + sections: HyperbookSection[]; + repo?: string; +}; + +export type HyperbookFrontmatter = + | HyperbookPageFrontmatter + | HyperbookSectionFrontmatter; + export type HyperbookJson = { name: string; description?: string; @@ -76,3 +124,10 @@ export type HyperlibraryJson = { }[]; basePath?: string; }; + +export type Hyperproject = { + name: string | LanguageString; + basePath?: string; + icon?: string; + src: string; +} & ({ type: "book" } | { type: "library"; projects: Hyperproject[] }); diff --git a/platforms/web/books b/platforms/web/books deleted file mode 120000 index 1df6adfe3..000000000 --- a/platforms/web/books +++ /dev/null @@ -1 +0,0 @@ -/var/home/mbarkmin/Sources/openpatch/hyperbook/vscode-full-preview/website/en/books \ No newline at end of file diff --git a/platforms/web/package.json b/platforms/web/package.json index d8417eef3..daaf3ed54 100644 --- a/platforms/web/package.json +++ b/platforms/web/package.json @@ -3,7 +3,7 @@ "version": "0.7.2", "private": true, "scripts": { - "build": "rimraf dist && node ./esbuild.mjs", + "build": "rimraf dist && tsc --project tsconfig.json --noEmit && node ./esbuild.mjs", "dev": "next dev", "next:dev": "next-hyperbook-watch", "next:build": "next build && next export" @@ -22,6 +22,7 @@ "@hyperbook/element-tabs": "workspace:*", "@hyperbook/element-term": "workspace:*", "@hyperbook/element-youtube": "workspace:*", + "@hyperbook/fs": "workspace:^0.0.0", "@hyperbook/markdown": "workspace:*", "@hyperbook/provider": "workspace:*", "@hyperbook/shell": "workspace:*", diff --git a/platforms/web/src/pages/[[...page]].tsx b/platforms/web/src/pages/[[...page]].tsx index 7c17546f4..3ef40d8eb 100644 --- a/platforms/web/src/pages/[[...page]].tsx +++ b/platforms/web/src/pages/[[...page]].tsx @@ -3,13 +3,16 @@ import matter from "gray-matter"; import { GetStaticPaths, GetStaticProps } from "next"; import path from "path"; import { Shell, ShellProps } from "@hyperbook/shell"; -import { getAllFiles } from "../utils/files"; -import { getNavigation } from "../utils/navigation"; import { parseTocFromMarkdown, TocProps } from "@hyperbook/toc"; import { Markdown } from "@hyperbook/markdown"; import { useActivePageId, useLink } from "@hyperbook/provider"; -import { getHyperbook } from "../utils/hyperbook"; import { Fragment } from "react"; +import { + makeNavigationForHyperbook, + readBook, + readFile, + readHyperbook, +} from "@hyperbook/fs"; type PageProps = { markdown: string; @@ -17,8 +20,6 @@ type PageProps = { toc: TocProps; }; -const hyperbook = getHyperbook(); - export default function BookPage({ markdown, navigation, toc }: PageProps) { const page = navigation.current; const Link = useLink(); @@ -61,21 +62,17 @@ export const getStaticProps: GetStaticProps< page: string[]; } > = async ({ params }) => { - let source: Buffer; - let filePath = path.join(process.env.root ?? process.cwd(), "book"); + const root = process.env.root ?? process.cwd(); + let filePath = path.join(root, "book"); let href = "/"; if (params.page) { filePath = path.join(filePath, ...params.page); href = "/" + path.join(...params.page); } - try { - source = fs.readFileSync(filePath + ".md"); - } catch (e) { - source = fs.readFileSync(path.join(filePath, "index") + ".md"); - } - const { content, data } = matter(source); + const { content, data } = await readFile(filePath + ".md"); - const navigation = await getNavigation(href); + const hyperbook = await readHyperbook(root); + const navigation = await makeNavigationForHyperbook(root, href); return { props: { locale: data?.lang || hyperbook.language, @@ -89,13 +86,12 @@ export const getStaticProps: GetStaticProps< export const getStaticPaths: GetStaticPaths<{ page: string[]; }> = async () => { - const files = getAllFiles( - path.join(process.env.root ?? process.cwd(), "book") - ); + const root = process.env.root ?? process.cwd(); + const files = await readBook(root); const paths = files.map((f) => { const relativePath = path - .relative(path.join(process.env.root ?? process.cwd(), "book"), f) - .replace(/\.mdx?$/, "") + .relative(path.join(root, "book"), f) + .replace(/\.md$/, "") .split("/"); const isIndex = relativePath[relativePath.length - 1] === "index"; if (isIndex) { diff --git a/platforms/web/src/pages/_app.tsx b/platforms/web/src/pages/_app.tsx index 6f901f2b3..fe0e34b76 100644 --- a/platforms/web/src/pages/_app.tsx +++ b/platforms/web/src/pages/_app.tsx @@ -34,10 +34,9 @@ import Head from "next/head"; import { Styles } from "@hyperbook/styles"; import { localStorage } from "@hyperbook/store"; import { useRouter } from "next/router"; -import { getHyperbook } from "../utils/hyperbook"; import { useEffect } from "react"; -const hb = getHyperbook(); +import hb from "../../hyperbook.json"; const MyLink: ProviderProps["Link"] = ({ href, children, ...props }) => { return ( @@ -75,7 +74,7 @@ export default function MyApp({ Component, pageProps }) { = async ({ params }) => { - let filePath = path.join(process.env.root ?? process.cwd(), "glossary"); - filePath = path.join(filePath, ...params.term); + const root = process.env.root ?? process.cwd(); + const filePath = path.join(root, "glossary", ...params.term); const href = "/glossary/" + path.join(...params.term); - const source = fs.readFileSync(filePath + ".md"); - const { content, data } = matter(source); - - const navigation = await getNavigation(href); + const { content, data } = await readFile(filePath + ".md"); - const files = getAllFiles( - path.join(process.env.root ?? process.cwd(), "book") - ); - const pages: ShellProps["navigation"]["pages"] = []; - for (const file of files) { - const { content, data } = readFile(file); - const r = new RegExp( - `:t\\[.*\\]\\{#${params.term}(\..*)?\\}|:t\\[${params.term}\\]` - ); - const m = content.match(r); - if (m && !data.hide && data.name) { - const relativePath = path - .relative(path.join(process.env.root ?? process.cwd(), "book"), file) - .replace(/\.mdx?$/, "") - .split("/"); - const isIndex = relativePath[relativePath.length - 1] === "index"; - if (isIndex) { - relativePath.pop(); - } - pages.push({ - ...data, - href: "/" + relativePath.join("/"), - }); - } - } + const navigation = await makeNavigationForHyperbook(root, href); + const hyperbook = await readHyperbook(root); + const pages = await listPagesForTerm(root, params.term[0]); const term: TermProps["term"] = { ...(data as TermProps["term"]), @@ -114,11 +89,12 @@ export const getStaticProps: GetStaticProps< export const getStaticPaths: GetStaticPaths<{ term: string[]; }> = async () => { - const files = getAllFiles("glossary"); + const root = process.env.root ?? process.cwd(); + const files = await readGlossary(root); const paths = files.map((f) => { const relativePath = path - .relative("glossary", f) - .replace(/\.mdx?$/, "") + .relative(path.join(root, "glossary"), f) + .replace(/\.md$/, "") .split("/"); return { diff --git a/platforms/web/src/pages/glossary/index.tsx b/platforms/web/src/pages/glossary/index.tsx index 42b57038f..bbf9ecaa8 100644 --- a/platforms/web/src/pages/glossary/index.tsx +++ b/platforms/web/src/pages/glossary/index.tsx @@ -1,25 +1,20 @@ import { GetStaticProps } from "next"; -import fs from "fs"; -import { getAllFiles } from "../../utils/files"; -import matter from "gray-matter"; -import chalk from "chalk"; import { Shell } from "@hyperbook/shell"; -import { getNavigation, Navigation } from "../../utils/navigation"; -import path from "path"; +import { Glossary as TGlossary } from "@hyperbook/types"; import { useActivePageId, useLink } from "@hyperbook/provider"; -import hyperbook from "../../../hyperbook.json"; - -export type Term = { - name: string; - href: string; -}; +import { ShellProps } from "@hyperbook/shell"; +import { + makeGlossary, + makeNavigationForHyperbook, + readHyperbook, +} from "@hyperbook/fs"; export type GlossaryProps = { - navigation: Navigation; - terms: Record; + navigation: ShellProps["navigation"]; + glossary: TGlossary; }; -export default function Glossary({ terms, navigation }: GlossaryProps) { +export default function Glossary({ glossary, navigation }: GlossaryProps) { const Link = useLink(); useActivePageId(); return ( @@ -30,11 +25,11 @@ export default function Glossary({ terms, navigation }: GlossaryProps) { }} >
    - {Object.keys(terms).map((letter) => ( + {Object.keys(glossary).map((letter) => (
    {letter}