Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Desktop #615

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/fs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,5 @@
"@types/mime-types": "^2.1.1",
"handlebars": "^4.7.7",
"vitest": "^0.27.0"
},
"type": "module"
}
}
1 change: 1 addition & 0 deletions packages/markdown/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"dependencies": {
"@hyperbook/provider": "workspace:*",
"react-markdown": "8.0.5",
"react-remark": "^2.1.0",
"rehype-highlight": "5.0.2",
"rehype-katex": "6.0.2",
"remark-directive": "2.0.1",
Expand Down
16 changes: 16 additions & 0 deletions platforms/desktop/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/electron",
"plugin:import/typescript"
],
"parser": "@typescript-eslint/parser"
}
89 changes: 89 additions & 0 deletions platforms/desktop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock
.DS_Store

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# Webpack
.webpack/

# Electron-Forge
out/
1 change: 1 addition & 0 deletions platforms/desktop/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node-linker=hoisted
43 changes: 43 additions & 0 deletions platforms/desktop/forge.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { ForgeConfig } from "@electron-forge/shared-types";
import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { MakerZIP } from "@electron-forge/maker-zip";
import { WebpackPlugin } from "@electron-forge/plugin-webpack";

import { mainConfig } from "./webpack.main.config";
import { rendererConfig } from "./webpack.renderer.config";

const config: ForgeConfig = {
packagerConfig: {
appBundleId: "org.openpatch.hyperbook",
name: "Hyperbook",
asar: {
// We must add native node modules to this option. Doing so ensures that
// the modules will be code-signed. (They still end up in the final
// app.asar file, but they will be code-signed.) Code signing these dylibs
// is required on macOS for the Node process to properly load them.
unpack: "*.{node,dll}",
},
},
rebuildConfig: {},
makers: [new MakerSquirrel({}), new MakerZIP({}, ["darwin", "linux"])],
plugins: [
new WebpackPlugin({
mainConfig,
renderer: {
config: rendererConfig,
entryPoints: [
{
html: "./src/index.html",
js: "./src/renderer.ts",
name: "main_window",
preload: {
js: "./src/preload.ts",
},
},
],
},
}),
],
};

export default config;
77 changes: 77 additions & 0 deletions platforms/desktop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"name": "desktop",
"productName": "Hyperbook",
"version": "0.0.1",
"description": "Hyperbook viewer for the desktop.",
"main": ".webpack/main",
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish",
"lint": "eslint --ext .ts,.tsx ."
},
"keywords": [],
"author": {
"name": "Mike Barkmin",
"email": "mike@barkmin.eu"
},
"license": "MIT",
"devDependencies": {
"@electron-forge/cli": "^6.0.4",
"@electron-forge/maker-deb": "^6.0.4",
"@electron-forge/maker-rpm": "^6.0.4",
"@electron-forge/maker-squirrel": "^6.0.4",
"@electron-forge/maker-zip": "^6.0.4",
"@electron-forge/plugin-webpack": "^6.0.4",
"@electron-forge/shared-types": "^6.0.4",
"@types/node": "18.11.18",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"@vercel/webpack-asset-relocator-loader": "1.7.3",
"css-loader": "^6.0.0",
"electron": "22.0.2",
"eslint": "^8.0.1",
"eslint-plugin-import": "^2.25.0",
"fork-ts-checker-webpack-plugin": "^7.2.13",
"node-loader": "^2.0.0",
"sass-loader": "13.2.0",
"style-loader": "^3.0.0",
"ts-loader": "^9.2.2",
"ts-node": "^10.0.0",
"typescript": "~4.5.4",
"webpack": "5.75.0"
},
"dependencies": {
"@hyperbook/element-alert": "workspace:*",
"@hyperbook/element-bitflow": "workspace:*",
"@hyperbook/element-bookmarks": "workspace:*",
"@hyperbook/element-collapsible": "workspace:*",
"@hyperbook/element-dl": "workspace:*",
"@hyperbook/element-excalidraw": "workspace:*",
"@hyperbook/element-mermaid": "workspace:*",
"@hyperbook/element-protect": "workspace:*",
"@hyperbook/element-qr": "workspace:*",
"@hyperbook/element-scratchblock": "workspace:*",
"@hyperbook/element-struktog": "workspace:*",
"@hyperbook/element-tabs": "workspace:*",
"@hyperbook/element-term": "workspace:*",
"@hyperbook/element-youtube": "workspace:*",
"@hyperbook/fs": "workspace:*",
"@hyperbook/markdown": "workspace:*",
"@hyperbook/provider": "workspace:*",
"@hyperbook/shell": "workspace:*",
"@hyperbook/store": "workspace:*",
"@hyperbook/styles": "workspace:*",
"@hyperbook/toc": "workspace:*",
"@hyperbook/types": "workspace:*",
"chokidar": "3.5.3",
"electron-squirrel-startup": "^1.0.0",
"electron-store": "^8.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"redux-persist-electron-storage": "^2.1.0"
}
}
147 changes: 147 additions & 0 deletions platforms/desktop/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { useEffect, useState } from "react";
import { Provider, ProviderProps } from "@hyperbook/provider";
import elementTab from "@hyperbook/element-tabs";
import elementAlert from "@hyperbook/element-alert";
import elementBitflow from "@hyperbook/element-bitflow";
import elementTerm from "@hyperbook/element-term";
import elementYoutube from "@hyperbook/element-youtube";
import elementProtect from "@hyperbook/element-protect";
import elementCollapsible from "@hyperbook/element-collapsible";
import elementScratchblock from "@hyperbook/element-scratchblock";
import elementDl from "@hyperbook/element-dl";
import elementBookmarks from "@hyperbook/element-bookmarks";
import elementStruktog from "@hyperbook/element-struktog";
import elementQr from "@hyperbook/element-qr";
import elementMermaid from "@hyperbook/element-mermaid";
import elementExcalidraw from "@hyperbook/element-excalidraw";
import { Markdown } from "@hyperbook/markdown";
import { Styles } from "@hyperbook/styles";
import { FileChangedEvent, ConfigChangeEvent, OpenFileEvent } from "./events";
import { ErrorBoundary } from "./ErrorBoundary";
import { Shell } from "@hyperbook/shell";
import { ipcRenderer } from "electron";
import createElectronStorage from "redux-persist-electron-storage";
import ElectronStore from "electron-store";

const electronStore = new ElectronStore();

export const App = () => {
const [state, setState] = useState<FileChangedEvent>();
const [config, setConfig] = useState<ConfigChangeEvent>();
const assetPath = state?.assetsPath;

useEffect(() => {
ipcRenderer.on("FILE_CHANGED", (_, arg: FileChangedEvent) => {
setState(arg);
});
ipcRenderer.on("CONFIG_CHANGE", (_, arg: ConfigChangeEvent) => {
setConfig(arg);
});

ipcRenderer.send("READY");
}, []);

if (!state) {
return <div>...</div>;
}

return (
<ErrorBoundary message="You have encountered an error.">
<Provider
config={config}
Link={({ href, ...props }) => {
href = href?.split("#")[0];
if (href?.startsWith("file:///")) {
return (
<a
href="#"
title={href}
onClick={() => {
ipcRenderer.send("OPEN_FILE", {
path: href as string,
rootFolder: "",
basePath: "",
} as OpenFileEvent);
}}
{...props}
/>
);
} else if (href?.startsWith("/")) {
let rootFolder = "";
if (!href.startsWith("/glossary")) {
rootFolder = "book";
}
return (
<a
href="#"
title={href}
onClick={() => {
ipcRenderer.send("OPEN_FILE", {
path: href as string,
rootFolder: rootFolder,
basePath: config?.basePath,
} as OpenFileEvent);
}}
{...props}
/>
);
}
return <a href={href} {...props} />;
}}
router={{
push: async () => true,
}}
storage={createElectronStorage({ electronStore })}
makeUrl={() => (p, rootFolder) => {
if (p?.startsWith("/")) {
if (rootFolder) {
return assetPath + rootFolder + p;
} else {
return assetPath + p;
}
} else {
return p || "";
}
}}
elements={[
elementTab,
elementAlert,
elementBitflow,
elementTerm,
elementYoutube,
elementCollapsible,
elementProtect,
elementDl,
elementBookmarks,
elementStruktog,
elementQr,
elementMermaid,
elementScratchblock,
elementExcalidraw,
]}
loadFile={() => async (path) => {
return fetch(path).then((res) => res.text());
}}
saveFile={() => async (path, content, rootFolder) => {
ipcRenderer.send("WRITE", {
type: "WRITE",
payload: {
content,
path,
rootFolder,
basePath: config?.basePath,
},
});
}}
getActivePageId={async () => state?.navigation?.current?.href || ""}
>
<Styles />
<Shell navigation={state?.navigation} toc={state?.toc}>
<ErrorBoundary message="You have a syntax error in your markdown file.">
<Markdown children={state?.content || ""} />
</ErrorBoundary>
</Shell>
</Provider>
</ErrorBoundary>
);
};
Loading