├── .husky ├── .gitignore ├── pre-commit └── commit-msg ├── apps ├── web │ ├── src │ │ ├── modules │ │ │ ├── index.ts │ │ │ └── server │ │ │ │ ├── index.ts │ │ │ │ └── docs │ │ │ │ ├── index.ts │ │ │ │ ├── getDocsFolder.ts │ │ │ │ ├── getMetaFile.ts │ │ │ │ ├── getParsedDocPaths.ts │ │ │ │ ├── getAllDocs.ts │ │ │ │ ├── getAllMDXFilePaths.ts │ │ │ │ ├── getDoc.ts │ │ │ │ ├── getDocPaginationNavigator.ts │ │ │ │ └── getDocLinks.ts │ │ ├── components │ │ │ ├── Modals │ │ │ │ ├── index.ts │ │ │ │ └── ExitIntent │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── styles.ts │ │ │ ├── Docs │ │ │ │ ├── index.ts │ │ │ │ ├── LinkTree │ │ │ │ │ ├── Link │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styles.ts │ │ │ │ │ ├── Tree │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── MDXComponents │ │ │ │ │ ├── styles.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── CodeBox │ │ │ │ │ │ ├── styles.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ └── PaginationNavigator │ │ │ │ │ ├── styles.ts │ │ │ │ │ └── index.tsx │ │ │ ├── Icons │ │ │ │ ├── Spinner │ │ │ │ │ ├── styles.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── File.tsx │ │ │ │ ├── CodeSandbox.tsx │ │ │ │ ├── Menu.tsx │ │ │ │ ├── Clipboard.tsx │ │ │ │ ├── Package.tsx │ │ │ │ ├── Checkmark.tsx │ │ │ │ └── GitHub.tsx │ │ │ ├── Header │ │ │ │ ├── styles.ts │ │ │ │ └── index.tsx │ │ │ ├── LibraryVersion │ │ │ │ ├── index.tsx │ │ │ │ └── styles.ts │ │ │ ├── ExternalLink │ │ │ │ └── index.tsx │ │ │ ├── FixedHeader │ │ │ │ ├── index.tsx │ │ │ │ └── styles.ts │ │ │ ├── Layout │ │ │ │ ├── Box │ │ │ │ │ └── index.ts │ │ │ │ ├── OuterClickArea │ │ │ │ │ └── index.ts │ │ │ │ ├── RootContainer │ │ │ │ │ └── index.ts │ │ │ │ ├── Title │ │ │ │ │ └── index.ts │ │ │ │ ├── Background │ │ │ │ │ └── index.ts │ │ │ │ ├── Separator │ │ │ │ │ └── index.ts │ │ │ │ ├── Description │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── Button │ │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── CodeEditorPresenter │ │ │ │ ├── IntelliSenses │ │ │ │ │ ├── preload │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── Groups │ │ │ │ │ │ │ └── First.tsx │ │ │ │ │ ├── renderer │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── Groups │ │ │ │ │ │ │ └── First.tsx │ │ │ │ │ ├── ipcs │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── Groups │ │ │ │ │ │ │ └── First.tsx │ │ │ │ │ ├── main │ │ │ │ │ │ ├── Groups │ │ │ │ │ │ │ ├── Second.tsx │ │ │ │ │ │ │ ├── Fourth.tsx │ │ │ │ │ │ │ ├── Third.tsx │ │ │ │ │ │ │ └── First.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── styles.ts │ │ │ │ └── index.tsx │ │ │ ├── Footer │ │ │ │ ├── index.tsx │ │ │ │ └── styles.ts │ │ │ ├── Head │ │ │ │ └── index.tsx │ │ │ └── InstallationBox │ │ │ │ ├── styles.ts │ │ │ │ └── index.tsx │ │ ├── templates │ │ │ ├── index.ts │ │ │ ├── Home │ │ │ │ ├── index.tsx │ │ │ │ └── Features │ │ │ │ │ ├── styles.ts │ │ │ │ │ └── index.tsx │ │ │ └── Docs │ │ │ │ ├── index.tsx │ │ │ │ └── styles.ts │ │ ├── shared │ │ │ ├── utils │ │ │ │ ├── getPublicPath.ts │ │ │ │ ├── unslugify.ts │ │ │ │ ├── index.ts │ │ │ │ ├── queueTimeouts.ts │ │ │ │ ├── createDebounce.ts │ │ │ │ ├── getType.ts │ │ │ │ └── toObjectTree.ts │ │ │ ├── constants │ │ │ │ ├── tabs.ts │ │ │ │ ├── index.ts │ │ │ │ ├── sizes.ts │ │ │ │ ├── commands.ts │ │ │ │ ├── library.ts │ │ │ │ ├── meta.ts │ │ │ │ └── codes.ts │ │ │ └── types │ │ │ │ └── index.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useDisclosure.ts │ │ │ ├── useMatchMedia.ts │ │ │ └── usePackageManagerSelection.ts │ │ ├── pages │ │ │ ├── index.tsx │ │ │ ├── _document.tsx │ │ │ ├── _app.tsx │ │ │ └── docs │ │ │ │ └── [...doc].tsx │ │ └── styles │ │ │ ├── keyframes.ts │ │ │ ├── index.ts │ │ │ ├── global.ts │ │ │ └── lib │ │ │ └── react-syntax-highlighter │ │ │ └── theme.ts │ ├── .eslintrc.json │ ├── public │ │ ├── blur.jpg │ │ ├── favicon.ico │ │ ├── social.png │ │ ├── sparkles.png │ │ ├── abstract-background.jpg │ │ ├── bidirectional-arrows.svg │ │ ├── file.svg │ │ ├── codesandbox.svg │ │ ├── link.svg │ │ ├── pattern.svg │ │ └── package.svg │ ├── docs │ │ ├── meta.json │ │ ├── createInterprocess │ │ │ ├── ipcs.mdx │ │ │ ├── ipcMain.mdx │ │ │ ├── ipcRenderer.mdx │ │ │ ├── introduction.mdx │ │ │ └── exposeApiToGlobalWindow.mdx │ │ ├── getting-started │ │ │ ├── requirements.mdx │ │ │ ├── installation.mdx │ │ │ ├── overview.mdx │ │ │ └── create-a-interprocess.mdx │ │ └── code-splitting │ │ │ ├── combineIpcs.mdx │ │ │ └── createIpcSlice.mdx │ ├── next-env.d.ts │ ├── tsconfig.json │ ├── next.config.mjs │ ├── package.json │ └── README.md └── desktop │ ├── index.d.ts │ ├── .eslintrc.json │ ├── src │ ├── shared │ │ └── ipcs │ │ │ ├── main │ │ │ └── index.ts │ │ │ ├── renderer │ │ │ └── index.ts │ │ │ └── index.ts │ ├── preload │ │ └── index.ts │ ├── main │ │ ├── ipc │ │ │ └── index.ts │ │ └── index.ts │ └── renderer │ │ ├── index.ts │ │ └── index.html │ ├── tsconfig.json │ ├── electron.vite.config.ts │ ├── package.json │ └── ensureTypesWorking.ts ├── commitlint.config.js ├── packages ├── interprocess │ ├── .eslintrc.json │ ├── index.d.ts │ ├── src │ │ ├── index.ts │ │ ├── factories │ │ │ ├── index.ts │ │ │ ├── createIpcSlice.ts │ │ │ ├── createRendererInvokers.ts │ │ │ ├── createInterprocess.ts │ │ │ ├── createMainHandlers.ts │ │ │ ├── createRemoveHandlers.ts │ │ │ ├── createMainInvokers.ts │ │ │ ├── createApiToGlobalWindowExposer.ts │ │ │ └── createRendererHandlers.ts │ │ ├── utils │ │ │ ├── ipcs.ts │ │ │ ├── is.ts │ │ │ └── combineIpcs.ts │ │ ├── store.ts │ │ ├── ipcs │ │ │ └── main.ts │ │ └── types │ │ │ └── index.ts │ ├── .npmignore │ ├── tsconfig.json │ ├── CHANGELOG.md │ ├── rollup.config.js │ ├── package.json │ └── README.md ├── ts-config │ ├── package.json │ ├── next.json │ └── base.json └── eslint-config │ ├── package.json │ └── index.js ├── .github ├── FUNDING.yml └── workflows │ ├── release-npm.yml │ └── gh-page.yml ├── .editorconfig ├── turbo.json ├── .changeset ├── config.json └── README.md ├── .gitignore ├── LICENSE ├── package.json └── README.md /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /apps/web/src/modules/index.ts: -------------------------------------------------------------------------------- 1 | export * from './server' 2 | -------------------------------------------------------------------------------- /apps/web/src/modules/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from './docs' 2 | -------------------------------------------------------------------------------- /apps/desktop/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/web/src/components/Modals/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ExitIntent' 2 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@daltonmenezes/eslint-config" 3 | } 4 | -------------------------------------------------------------------------------- /apps/desktop/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@daltonmenezes/eslint-config" 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/src/templates/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Docs' 2 | export * from './Home' 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] } 2 | -------------------------------------------------------------------------------- /packages/interprocess/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@daltonmenezes/eslint-config" 3 | } 4 | -------------------------------------------------------------------------------- /packages/interprocess/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/web/src/shared/utils/getPublicPath.ts: -------------------------------------------------------------------------------- 1 | export const getPublicPath = (assetUrl: string) => assetUrl 2 | -------------------------------------------------------------------------------- /apps/web/public/blur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonmenezes/interprocess/HEAD/apps/web/public/blur.jpg -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonmenezes/interprocess/HEAD/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /apps/web/public/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonmenezes/interprocess/HEAD/apps/web/public/social.png -------------------------------------------------------------------------------- /apps/web/public/sparkles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonmenezes/interprocess/HEAD/apps/web/public/sparkles.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: daltonmenezes 2 | patreon: daltonmenezes 3 | issuehunt: daltonmenezes/interprocess?tab=idle 4 | -------------------------------------------------------------------------------- /packages/interprocess/src/index.ts: -------------------------------------------------------------------------------- 1 | export { combineIpcs } from './utils/combineIpcs' 2 | export * from './factories' 3 | -------------------------------------------------------------------------------- /apps/web/src/shared/utils/unslugify.ts: -------------------------------------------------------------------------------- 1 | export function unslugify(slug: string) { 2 | return slug.replace(/\-/g, ' ') 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/docs/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "order": [ 3 | "getting-started", 4 | "createInterprocess", 5 | "code-splitting" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/public/abstract-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daltonmenezes/interprocess/HEAD/apps/web/public/abstract-background.jpg -------------------------------------------------------------------------------- /apps/web/src/shared/constants/tabs.ts: -------------------------------------------------------------------------------- 1 | export const tabs = { 2 | fileNames: ['ipcs.ts', 'main.ts', 'preload.ts', 'renderer.ts'], 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/src/components/Docs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PaginationNavigator' 2 | export * from './MDXComponents' 3 | export * from './LinkTree' 4 | -------------------------------------------------------------------------------- /apps/web/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './usePackageManagerSelection' 2 | export * from './useDisclosure' 3 | export * from './useMatchMedia' 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { HomeTemplate } from 'templates' 2 | 3 | export default function App() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /packages/ts-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@daltonmenezes/ts-config", 4 | "version": "1.0.0", 5 | "license": "MIT" 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/docs/createInterprocess/ipcs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: ipcs 3 | --- 4 | 5 | # ipcs 6 | An object containing the IPC handlers 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/interprocess/src/factories/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createApiToGlobalWindowExposer' 2 | export * from './createInterprocess' 3 | export * from './createIpcSlice' 4 | -------------------------------------------------------------------------------- /packages/interprocess/src/utils/ipcs.ts: -------------------------------------------------------------------------------- 1 | export const IPC = { 2 | INTERNAL: { 3 | SYNC_AVAILABLE_RENDERER_IPCS: '@interprocess:syncAvailableRendererIpcs', 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /packages/interprocess/src/store.ts: -------------------------------------------------------------------------------- 1 | const availableRendererIpcChannels = new Set() 2 | 3 | export const store = { 4 | main: { 5 | availableRendererIpcChannels, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/src/components/Icons/Spinner/styles.ts: -------------------------------------------------------------------------------- 1 | import { styled, animations } from 'styles' 2 | 3 | export const SVG = styled('svg', { 4 | animation: `${animations.spin} 1s linear infinite`, 5 | }) 6 | -------------------------------------------------------------------------------- /apps/web/src/shared/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './commands' 2 | export * from './library' 3 | export * from './sizes' 4 | export * from './codes' 5 | export * from './meta' 6 | export * from './tabs' 7 | -------------------------------------------------------------------------------- /apps/web/src/shared/constants/sizes.ts: -------------------------------------------------------------------------------- 1 | import { config } from 'styles' 2 | 3 | export const sizes = { 4 | breakpoints: { 5 | mobile: `${config.media.bp4.replace(/\D/g, '')}`, 6 | }, 7 | } as const 8 | -------------------------------------------------------------------------------- /packages/interprocess/.npmignore: -------------------------------------------------------------------------------- 1 | /src 2 | rollup.config.js 3 | tsconfig.json 4 | .eslintrc* 5 | .editorconfig 6 | commitlint.config.js 7 | rollup.config.js 8 | /.husky 9 | .turbo 10 | .CHANGELOG.md 11 | -------------------------------------------------------------------------------- /packages/interprocess/src/factories/createIpcSlice.ts: -------------------------------------------------------------------------------- 1 | import type { IPCFactoryProps } from '../types' 2 | 3 | export function createIpcSlice>(props: T) { 4 | return props 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/docs/getting-started/requirements.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Requirements 3 | order: 2 4 | --- 5 | 6 | # Requirements 7 | 8 | - Node.js >= 14.0.0 9 | - Electron >= 12.0.0 10 | - TypeScript >= 4.8.0 11 | -------------------------------------------------------------------------------- /apps/web/src/components/Header/styles.ts: -------------------------------------------------------------------------------- 1 | import { BaseLayout } from 'components/Layout' 2 | import { styled } from 'styles' 3 | 4 | export const Layout = styled(BaseLayout, { 5 | alignItems: 'center', 6 | }) 7 | -------------------------------------------------------------------------------- /packages/interprocess/src/utils/is.ts: -------------------------------------------------------------------------------- 1 | export function isMainProcess() { 2 | return process.type === 'browser' 3 | } 4 | 5 | export function isRendererProcess() { 6 | return process.type === 'renderer' 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/web/src/shared/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createDebounce' 2 | export * from './queueTimeouts' 3 | export * from './getPublicPath' 4 | export * from './toObjectTree' 5 | export * from './unslugify' 6 | export * from './getType' 7 | -------------------------------------------------------------------------------- /apps/web/src/shared/constants/commands.ts: -------------------------------------------------------------------------------- 1 | import { library } from './library' 2 | 3 | export const commands = { 4 | npm: `npm i ${library.name}`, 5 | pnpm: `pnpm i ${library.name}`, 6 | yarn: `yarn add ${library.name}`, 7 | } as const 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /apps/web/src/components/Icons/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './CodeSandbox' 2 | export * from './Checkmark' 3 | export * from './Clipboard' 4 | export * from './Spinner' 5 | export * from './Package' 6 | export * from './GitHub' 7 | export * from './Menu' 8 | export * from './File' 9 | -------------------------------------------------------------------------------- /apps/web/src/components/LibraryVersion/index.tsx: -------------------------------------------------------------------------------- 1 | import { library } from 'shared/constants' 2 | 3 | import { LibraryVersionContainer } from './styles' 4 | 5 | export function LibraryVersion() { 6 | return v{library.version} 7 | } 8 | -------------------------------------------------------------------------------- /packages/ts-config/next.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./base.json", 3 | "include": [ 4 | "next-env.d.ts", 5 | "**/*.ts", 6 | "**/*.tsx" 7 | ], 8 | "compilerOptions": { 9 | "module": "esnext", 10 | "jsx": "preserve", 11 | "noEmit": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/desktop/src/shared/ipcs/main/index.ts: -------------------------------------------------------------------------------- 1 | import { createIpcSlice } from 'interprocess' 2 | 3 | export const getPingIpcSlice = createIpcSlice({ 4 | main: { 5 | async getPing(_, data: 'ping') { 6 | return `from renderer: ${data} on main process` 7 | }, 8 | }, 9 | }) 10 | -------------------------------------------------------------------------------- /apps/web/src/components/ExternalLink/index.tsx: -------------------------------------------------------------------------------- 1 | import { AnchorHTMLAttributes, PropsWithChildren } from 'react' 2 | 3 | export function ExternalLink( 4 | props: PropsWithChildren> 5 | ) { 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/src/modules/server/docs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getDocPaginationNavigator' 2 | export * from './getAllMDXFilePaths' 3 | export * from './getDocLinks' 4 | export * from './getParsedDocPaths' 5 | export * from './getMetaFile' 6 | export * from './getAllDocs' 7 | export * from './getDoc' 8 | -------------------------------------------------------------------------------- /apps/desktop/src/shared/ipcs/renderer/index.ts: -------------------------------------------------------------------------------- 1 | import { createIpcSlice } from 'interprocess' 2 | 3 | export const getPongIpcSlice = createIpcSlice({ 4 | renderer: { 5 | async getPong(_, data: 'pong') { 6 | return `from main: ${data} on renderer process` 7 | }, 8 | }, 9 | }) 10 | -------------------------------------------------------------------------------- /apps/web/public/bidirectional-arrows.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/web/src/modules/server/docs/getDocsFolder.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | 3 | import normalize from 'normalize-path' 4 | 5 | import packageJSON from '../../../../package.json' 6 | 7 | export function getDocsFolder() { 8 | return normalize(resolve(packageJSON?.docsFolder || 'docs')) 9 | } 10 | -------------------------------------------------------------------------------- /apps/desktop/src/shared/ipcs/index.ts: -------------------------------------------------------------------------------- 1 | import { combineIpcs } from 'interprocess' 2 | 3 | import { getPongIpcSlice } from './renderer' 4 | import { getPingIpcSlice } from './main' 5 | 6 | export const { ipcMain, ipcRenderer, exposeApiToGlobalWindow } = combineIpcs( 7 | getPongIpcSlice, 8 | getPingIpcSlice 9 | ) 10 | -------------------------------------------------------------------------------- /apps/web/docs/getting-started/installation.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | order: 3 4 | --- 5 | 6 | # Installation 7 | 8 | ### npm 9 | ```bash 10 | npm i interprocess 11 | ``` 12 | 13 | ### pnpm 14 | ```bash 15 | pnpm i interprocess 16 | ``` 17 | 18 | ### yarn 19 | ```bash 20 | yarn add interprocess 21 | ``` 22 | -------------------------------------------------------------------------------- /apps/desktop/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@daltonmenezes/ts-config/base.json", 3 | "include": [ 4 | "index.d.ts", 5 | "**/*.ts", 6 | "**/*.tsx" 7 | ], 8 | "compilerOptions": { 9 | "module": "esnext", 10 | "jsx": "preserve", 11 | "noEmit": true, 12 | "baseUrl": "./src" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/src/components/FixedHeader/index.tsx: -------------------------------------------------------------------------------- 1 | import { HeaderContent, HeaderContainer } from './styles' 2 | 3 | export function FixedHeader({ children }: { children: React.ReactNode }) { 4 | return ( 5 | 6 | {children} 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@daltonmenezes/ts-config/base.json", 3 | "include": [ 4 | "next-env.d.ts", 5 | "**/*.ts", 6 | "**/*.tsx" 7 | ], 8 | "compilerOptions": { 9 | "module": "esnext", 10 | "jsx": "preserve", 11 | "noEmit": true, 12 | "baseUrl": "./src" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": ["dist/**", ".next/**"] 7 | }, 8 | "lint": { 9 | "outputs": [] 10 | }, 11 | "dev": { 12 | "cache": false 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/src/components/Docs/LinkTree/Link/index.tsx: -------------------------------------------------------------------------------- 1 | import NextLink from 'next/link' 2 | 3 | import { Button, ButtonProps } from './styles' 4 | 5 | export function Link({ href, ...restOfProps }: ButtonProps) { 6 | return ( 7 | 8 | 77 | 78 |

79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /apps/web/src/styles/index.ts: -------------------------------------------------------------------------------- 1 | import { createStitches, ScaleValue, CSS } from '@stitches/react' 2 | 3 | import { createCodeTheme } from './lib/react-syntax-highlighter/theme' 4 | import { createKeyframes } from './keyframes' 5 | 6 | export const { styled, css, globalCss, getCssText, keyframes, theme, config } = 7 | createStitches({ 8 | theme: { 9 | colors: { 10 | 'text-title': 'hsl(0, 0%, 100%)', 11 | 'text-base': 'rgba(255, 255, 255, 0.87)', 12 | 'text-support': 'rgba(255, 255, 255, 0.6)', 13 | 'shape-primary': '#101010', 14 | 'shape-secondary': '#1F1F1F', 15 | 'shape-tertiary': '#2F2F2F', 16 | 'shape-quaternary': '#3F3F3F', 17 | 'shape-quinary': '#4F4F4F', 18 | 'shape-senary': '#5F5F5F', 19 | 'accent-primary': '#ff7ac8', 20 | 'accent-secondary': '#50e1c7', 21 | 'border-primary': 'rgba(36, 36, 36, 0.547)', 22 | 'shadow-primary': '#00000047', 23 | 'shadow-secondary': 'rgba(0, 0, 0, 0.5)', 24 | 25 | 'text-gradient': 26 | '-webkit-linear-gradient(0deg, $accent-primary 0%, $accent-secondary 100%)', 27 | }, 28 | 29 | fonts: { 30 | default: `Roboto`, 31 | }, 32 | }, 33 | 34 | media: { 35 | bp1: `(min-width: 375px)`, 36 | bp2: `(min-width: 640px)`, 37 | bp3: `(min-width: 768px)`, 38 | bp4: `(min-width: 1024px)`, 39 | bp5: `(min-width: 1440px)`, 40 | }, 41 | 42 | utils: { 43 | paddingVertical: (value: ScaleValue<'space'> | string) => ({ 44 | paddingTop: value, 45 | paddingBottom: value, 46 | }), 47 | 48 | paddingHorizontal: (value: ScaleValue<'space'> | string) => ({ 49 | paddingLeft: value, 50 | paddingRight: value, 51 | }), 52 | 53 | marginVertical: (value: ScaleValue<'space'> | string) => ({ 54 | marginTop: value, 55 | marginBottom: value, 56 | }), 57 | 58 | marginHorizontal: (value: ScaleValue<'space'> | string) => ({ 59 | marginLeft: value, 60 | marginRight: value, 61 | }), 62 | }, 63 | }) 64 | 65 | export * from './lib/react-syntax-highlighter/theme' 66 | 67 | export const codeTheme = createCodeTheme({ 68 | primary: config.theme.colors['accent-primary'], 69 | secondary: config.theme.colors['accent-secondary'], 70 | }) 71 | 72 | export const animations = createKeyframes(keyframes) 73 | 74 | export type Mixin = CSS 75 | -------------------------------------------------------------------------------- /apps/web/src/styles/global.ts: -------------------------------------------------------------------------------- 1 | import { globalCss, animations } from '.' 2 | 3 | globalCss({ 4 | '*': { 5 | margin: 0, 6 | padding: 0, 7 | border: 'none', 8 | boxSizing: 'border-box', 9 | }, 10 | 11 | ':root': { 12 | fontSynthesis: 'none', 13 | textRendering: 'optimizeLegibility', 14 | '-webkit-font-smoothing': 'antialiased', 15 | '-moz-osx-font-smoothing': 'grayscale', 16 | '-webkit-text-size-adjust': '100%', 17 | colorScheme: 'dark', 18 | scrollBehavior: 'smooth', 19 | scrollbarGutter: 'stable', 20 | }, 21 | 22 | 'body, input, button, textarea, select': { 23 | color: '$text-base', 24 | background: 'transparent', 25 | fontFamily: '$default', 26 | }, 27 | 28 | 'h1, h2, h3, h4, h5, h6': { 29 | color: '$text-base', 30 | lineHeight: 1.1, 31 | }, 32 | 33 | a: { 34 | color: '$text-base', 35 | textDecoration: 'none', 36 | transition: 'all 0.2s ease', 37 | 38 | 'svg path': { 39 | transition: 'all 0.2s ease', 40 | }, 41 | 42 | '&:hover': { 43 | color: '$accent-primary', 44 | 45 | 'svg path': { 46 | fill: '$accent-primary', 47 | }, 48 | }, 49 | 50 | '&:active': { 51 | transform: 'scale(0.95)', 52 | }, 53 | }, 54 | 55 | li: { listStyle: 'none' }, 56 | 57 | button: { 58 | cursor: 'pointer', 59 | background: 'transparent', 60 | transition: 'all 0.2s ease', 61 | 62 | 'svg path': { 63 | transition: 'all 0.2s ease', 64 | }, 65 | 66 | '&:hover': { 67 | color: '$accent-primary', 68 | 69 | 'svg path': { 70 | fill: '$accent-primary', 71 | }, 72 | }, 73 | 74 | '&:active': { 75 | transform: 'scale(0.95)', 76 | }, 77 | }, 78 | 79 | body: { 80 | minWidth: '100vw', 81 | minHeight: '100vh', 82 | backgroundColor: '#000000', 83 | overflowX: 'hidden', 84 | }, 85 | 86 | '::-webkit-scrollbar': { 87 | width: '0.6rem', 88 | height: '0.6rem', 89 | marginRight: '10px', 90 | }, 91 | 92 | '::-webkit-scrollbar-corner': { 93 | background: 'none', 94 | border: 'none', 95 | }, 96 | 97 | '::-webkit-scrollbar-thumb': { 98 | backgroundColor: '$shape-tertiary', 99 | borderRadius: '3px', 100 | cursor: 'move', 101 | }, 102 | 103 | '::-webkit-scrollbar-track': { 104 | backgroundColor: 'transparent', 105 | border: 'none', 106 | }, 107 | 108 | '.animate': { 109 | opacity: 0, 110 | animation: `${animations.fadeIn} 1s ease forwards`, 111 | }, 112 | })() 113 | -------------------------------------------------------------------------------- /apps/web/src/components/CodeEditorPresenter/IntelliSenses/index.tsx: -------------------------------------------------------------------------------- 1 | import type { CSSProperties, Dispatch, SetStateAction } from 'react' 2 | import { useState, RefObject, useEffect } from 'react' 3 | 4 | import { IntelliSense } from '../styles' 5 | 6 | export interface TabProps { 7 | containerRef: RefObject 8 | text: string 9 | controller: { 10 | pause: () => void 11 | resume: () => void 12 | isFinished: boolean 13 | } 14 | } 15 | 16 | export interface ItemGroup { 17 | activeItem: number 18 | setStyles: Dispatch> 19 | } 20 | 21 | export interface IntelliSenseHandler { 22 | activeItem?: number 23 | activePopupGroup?: number 24 | } 25 | 26 | interface IntelliSenseTabProps extends TabProps { 27 | popupItemGroups: ((props: ItemGroup) => JSX.Element)[] 28 | handler: (handleIntelliSense: (props: IntelliSenseHandler) => void) => void 29 | } 30 | 31 | const intelliSenseTimeout = 1000 32 | 33 | const defaultStyles = { 34 | top: '3rem', 35 | left: '3.8rem', 36 | } 37 | 38 | export function IntelliSenseTab({ 39 | text, 40 | handler, 41 | controller, 42 | containerRef, 43 | popupItemGroups, 44 | }: IntelliSenseTabProps) { 45 | const [activeItem, setActiveItem] = useState(0) 46 | const [shouldDisplay, setShouldDisplay] = useState(false) 47 | const [activePopupGroup, setActivePopupGroup] = useState(0) 48 | const [styles, setStyles] = useState(defaultStyles) 49 | 50 | const ItemGroup = popupItemGroups[activePopupGroup] 51 | 52 | function handleIntelliSense({ 53 | activeItem, 54 | activePopupGroup, 55 | }: { 56 | activeItem?: number 57 | activePopupGroup?: number 58 | } = {}) { 59 | controller.pause() 60 | 61 | setActiveItem(activeItem || 0) 62 | setShouldDisplay(true) 63 | setActivePopupGroup(activePopupGroup || 0) 64 | 65 | setTimeout(() => { 66 | controller.resume() 67 | setShouldDisplay(false) 68 | setActiveItem(0) 69 | setStyles(defaultStyles) 70 | }, intelliSenseTimeout) 71 | } 72 | 73 | useEffect(() => { 74 | const container = containerRef.current 75 | 76 | container!.scrollTo(0, container!.scrollHeight) 77 | 78 | handler(handleIntelliSense) 79 | }, [text, containerRef.current]) 80 | 81 | useEffect(() => { 82 | if (controller.isFinished) { 83 | setActiveItem(0) 84 | setShouldDisplay(false) 85 | setActivePopupGroup(0) 86 | setStyles(defaultStyles) 87 | } 88 | }, [controller.isFinished]) 89 | 90 | return shouldDisplay ? ( 91 | 92 | 93 | 94 | ) : null 95 | } 96 | -------------------------------------------------------------------------------- /apps/desktop/ensureTypesWorking.ts: -------------------------------------------------------------------------------- 1 | import { combineIpcs, createInterprocess, createIpcSlice } from 'interprocess' 2 | 3 | //////////////////////////////////// 4 | ////////// ipcs ////////// 5 | //////////////////////////////////// 6 | 7 | const getPongSlice = createIpcSlice({ 8 | main: { 9 | async getPong(_, data: 'pong') { 10 | return `from renderer: ping ${data} on main process` 11 | }, 12 | }, 13 | }) 14 | 15 | const getPingSlice = createIpcSlice({ 16 | renderer: { 17 | async getPing(_, data: 'ping') { 18 | return `from main: ${data} pong on renderer process` as const 19 | }, 20 | }, 21 | }) 22 | 23 | const getHelloSlice = createIpcSlice({ 24 | main: { 25 | async getHello(_, data: 'pong') { 26 | return `${data}` 27 | }, 28 | }, 29 | }) 30 | 31 | // const { ipcMain, exposeApiToGlobalWindow } = combineIpcs( 32 | // getPongSlice, 33 | // getPingSlice, 34 | // getHelloSlice 35 | // ) 36 | 37 | const ipcs = createInterprocess({ 38 | main: { 39 | async getPing(_, data: 'ping') { 40 | return `from renderer: ${data} on main process` 41 | }, 42 | }, 43 | 44 | renderer: { 45 | async getPong(_, data: 'pong') { 46 | return `from main: ${data} on renderer process` 47 | }, 48 | }, 49 | }) 50 | 51 | export const { ipcMain, ipcRenderer, exposeApiToGlobalWindow } = ipcs 52 | 53 | //////////////////////////////////// 54 | ////////// main.ts ////////// 55 | //////////////////////////////////// 56 | 57 | function registerIPCHandlers(window: Electron.BrowserWindow) { 58 | ipcMain.handle.getPing(async (_, { getPing, data }) => { 59 | const result = await getPing(_, data) 60 | 61 | console.log(result) 62 | 63 | ipcMain.invoke.getPong(window, 'pong') 64 | ipcMain.remove.getPing() 65 | 66 | return result 67 | }) 68 | } 69 | 70 | //////////////////////////////////// 71 | ////////// preload.ts ////////// 72 | //////////////////////////////////// 73 | 74 | declare global { 75 | interface Window { 76 | [key]: typeof api 77 | } 78 | } 79 | 80 | const { key, api } = exposeApiToGlobalWindow({ 81 | apiKey: 'ensureApi', 82 | exposeAll: true, 83 | 84 | append: { 85 | world: 'world', 86 | sayHello(data: 'hello world!') { 87 | console.log(data) 88 | }, 89 | } as const, 90 | }) 91 | 92 | //////////////////////////////////// 93 | ////////// renderer.ts ////////// 94 | //////////////////////////////////// 95 | 96 | const { invoke, handle, remove, sayHello, world } = window.ensureApi 97 | 98 | invoke.getPing('ping') 99 | 100 | sayHello(`hello ${world}!`) 101 | 102 | handle.getPong(async (_, { getPong, data }) => { 103 | const result = await getPong(_, data) 104 | 105 | console.log(result) 106 | 107 | remove.getPong() 108 | 109 | return result 110 | }) 111 | -------------------------------------------------------------------------------- /apps/web/src/templates/Docs/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMDXComponents } from '@mdx-js/react' 2 | import { useEffect, useState } from 'react' 3 | import { MDXRemote } from 'next-mdx-remote' 4 | import { useRouter } from 'next/router' 5 | import Link from 'next/link' 6 | 7 | import { library, sizes } from 'shared/constants' 8 | import { useMatchMedia } from 'hooks' 9 | 10 | import { 11 | Title, 12 | Header, 13 | Section, 14 | Sidebar, 15 | Content, 16 | Article, 17 | MenuButton, 18 | } from './styles' 19 | 20 | import { 21 | Menu, 22 | Layout, 23 | GitHubIcon, 24 | DocLinkTree, 25 | FixedHeader, 26 | ExternalLink, 27 | LayoutSpacing, 28 | OuterClickArea, 29 | LibraryVersion, 30 | PaginationNavigator, 31 | } from 'components' 32 | 33 | import type { DocProps } from 'shared/types' 34 | 35 | export function DocsTemplate({ doc, links, pagination }: DocProps) { 36 | const route = useRouter() 37 | const MDXComponents = useMDXComponents() 38 | const [menuOpen, setMenuOpen] = useState(false) 39 | 40 | const matchesMobileWidth = useMatchMedia( 41 | `(max-width: ${sizes.breakpoints.mobile}px)` 42 | ) 43 | 44 | function handleMenuVisibility() { 45 | setMenuOpen(!menuOpen) 46 | } 47 | 48 | useEffect(() => { 49 | setMenuOpen(false) 50 | }, [matchesMobileWidth, route.asPath]) 51 | 52 | return ( 53 | 54 | 55 | 56 | 81 | 82 | 83 | 84 |
85 | 86 | 87 | 88 | 89 | {menuOpen && } 90 | 91 | 92 | {doc?.source && ( 93 |
94 | 95 |
96 | )} 97 | 98 | 99 |
100 |
101 | 102 | ) 103 | } 104 | -------------------------------------------------------------------------------- /.github/workflows/gh-page.yml: -------------------------------------------------------------------------------- 1 | name: Release GH Page 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: 7 | - "published" 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | 25 | - name: Detect package manager 26 | id: detect-package-manager 27 | run: | 28 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then 29 | echo "::set-output name=manager::yarn" 30 | echo "::set-output name=command::install" 31 | echo "::set-output name=runner::yarn" 32 | exit 0 33 | elif [ -f "${{ github.workspace }}/package.json" ]; then 34 | echo "::set-output name=manager::npm" 35 | echo "::set-output name=command::ci" 36 | echo "::set-output name=runner::npx --no-install" 37 | exit 0 38 | else 39 | echo "Unable to determine packager manager" 40 | exit 1 41 | fi 42 | 43 | - name: Setup Node 44 | uses: actions/setup-node@v3 45 | with: 46 | node-version: "18" 47 | cache: ${{ steps.detect-package-manager.outputs.manager }} 48 | 49 | - name: Setup Pages 50 | uses: actions/configure-pages@v2 51 | with: 52 | token: ${{ secrets.GITHUB_TOKEN }} 53 | static_site_generator: next 54 | generator_config_file: ./apps/web/next.config.mjs 55 | 56 | - name: Restore cache 57 | uses: actions/cache@v3 58 | with: 59 | path: | 60 | ./apps/web/.next/cache 61 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} 62 | restore-keys: | 63 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- 64 | 65 | - name: Install dependencies 66 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} 67 | 68 | - name: Build with Next.js 69 | working-directory: ./apps/web 70 | run: ${{ steps.detect-package-manager.outputs.runner }} next build 71 | 72 | - name: Static HTML export with Next.js 73 | working-directory: ./apps/web 74 | run: ${{ steps.detect-package-manager.outputs.runner }} next export 75 | 76 | - name: Upload artifact 77 | uses: actions/upload-pages-artifact@v1 78 | with: 79 | path: ./apps/web/out 80 | 81 | deploy: 82 | environment: 83 | name: github-pages 84 | url: ${{ steps.deployment.outputs.page_url }} 85 | runs-on: ubuntu-latest 86 | needs: build 87 | steps: 88 | - name: Deploy to GitHub Pages 89 | id: deployment 90 | uses: actions/deploy-pages@v1 91 | -------------------------------------------------------------------------------- /apps/web/docs/getting-started/create-a-interprocess.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create a interprocess 3 | order: 4 4 | --- 5 | 6 | # Create a interprocess 7 | 8 | Let's build something simple that can show you some of the interprocess' power! 9 | 10 |
11 | 12 | First, create the following folders at `src`: 13 | 14 |
15 | 16 | - `shared/ipcs` (this folder structure is optional) 17 | 18 |
19 | 20 | Then, create a file named as `index.ts` in the `ipcs` folder with the following content: 21 | 22 |
23 | 24 | ```ts 25 | import { createInterprocess } from 'interprocess' 26 | 27 | export const { ipcMain, ipcRenderer, exposeApiToGlobalWindow } = 28 | createInterprocess({ 29 | main: { 30 | async getPing(_, data: 'ping') { 31 | const message = `from renderer: ${data} on main process` 32 | 33 | console.log(message) 34 | 35 | return message 36 | }, 37 | }, 38 | 39 | renderer: { 40 | async getPong(_, data: 'pong') { 41 | const message = `from main: ${data} on renderer process` 42 | 43 | console.log(message) 44 | 45 | return message 46 | }, 47 | }, 48 | }) 49 | ``` 50 | 51 |
52 | 53 | On the **main process**: 54 | 55 |
56 | 57 | > ⚠️ Don't forget to add `sandbox: false` to the BrowserWindow because it's required to load the preload script properly! 58 | 59 |
60 | 61 | ```ts 62 | import { BrowserWindow, app } from 'electron' 63 | 64 | import { ipcMain } from 'shared/ipcs' 65 | 66 | const { handle, invoke } = ipcMain 67 | 68 | app.whenReady().then(() => { 69 | const mainWindow = new BrowserWindow({ 70 | webPreferences: { 71 | preload: path.join(__dirname, '../preload/index.js'), 72 | sandbox: false, // sandbox must be false 73 | }, 74 | }) 75 | 76 | handle.getPing() 77 | 78 | mainWindow.webContents.on('dom-ready', () => { 79 | invoke.getPong(mainWindow, 'pong') 80 | }) 81 | }) 82 | ``` 83 | 84 |
85 | 86 | In the **preload script**: 87 | 88 |
89 | 90 | ```ts 91 | import { exposeApiToGlobalWindow } from 'shared/ipcs' 92 | 93 | const { key, api } = exposeApiToGlobalWindow({ 94 | exposeAll: true, // expose handlers, invokers and removers 95 | }) 96 | 97 | declare global { 98 | interface Window { 99 | [key]: typeof api 100 | } 101 | } 102 | ``` 103 | 104 |
105 | 106 | On the **renderer process**: 107 | 108 |
109 | 110 | ```ts 111 | const { invoke, handle } = window.api 112 | 113 | invoke.getPing('ping') 114 | handle.getPong() 115 | ``` 116 |
117 | 118 | This is a simple way to work with interprocess, but there's a lot of more cool features you can take advantage, like `overrides`, `code splitting`, `invoker's response` (for renderer and main process 🎉) and more. Keep reading the docs to get the knowledge to masterize interprocess! 119 | 120 |
121 | 122 | Also, **you can take a look the following examples**: 123 | 124 |
125 | 126 | - [Executable](https://github.com/daltonmenezes/interprocess/tree/main/apps/desktop) 127 | - [CodeSandbox (simple)](https://codesandbox.io/s/simple-607b6h?file=/src/ipcs.ts) 128 | - [CodeSandbox (advanced)](https://codesandbox.io/s/advanced-4qh0xb?file=/src/ipcs/index.ts) 129 | 130 | -------------------------------------------------------------------------------- /packages/interprocess/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type IPCMainEvent = Electron.IpcMainInvokeEvent 2 | export type IPCRendererEvent = Electron.IpcRendererEvent 3 | export type BrowserWindow = Electron.BrowserWindow 4 | 5 | export type SyncAvailableIpcsOperation = 'add' | 'remove' 6 | 7 | export type IPCMain = IPC 8 | export type IPCRenderer = IPC 9 | 10 | export type String = T | Omit 11 | 12 | export type ProcessKeys

= keyof P extends string ? keyof P : never 13 | 14 | export type Merge = (U extends any ? (k: U) => void : never) extends ( 15 | k: infer I 16 | ) => void 17 | ? I 18 | : never 19 | 20 | export type TypeAssertion = Target extends Type 21 | ? Target 22 | : Fallback 23 | 24 | export type StringAssertion = TypeAssertion< 25 | Target, 26 | string, 27 | Fallback 28 | > 29 | 30 | export type NormalizeCombinedIPCs< 31 | T extends Object[], 32 | S extends Merge 33 | > = { 34 | [K in keyof S]: S[K] 35 | } 36 | 37 | export type IPC = { 38 | [Property in keyof Type]: Type[Property] extends ( 39 | event: Event, 40 | value: infer V 41 | ) => infer R 42 | ? Type[Property] 43 | : never 44 | } 45 | 46 | export type IPCFactoryProps> = { 47 | main?: { 48 | [K in keyof T['main']]?: (event: IPCMainEvent, args: any) => Promise 49 | } 50 | 51 | renderer?: { 52 | [K in keyof T['renderer']]?: ( 53 | event: IPCRendererEvent, 54 | args: any 55 | ) => Promise 56 | } 57 | } 58 | 59 | export type MainHandler = Target extends ( 60 | event: IPCMainEvent, 61 | value: infer V 62 | ) => infer R 63 | ? ( 64 | handler?: ( 65 | event: IPCMainEvent, 66 | handler: { [K in Key extends string ? Key : string]: Target } & { 67 | data: V 68 | }, 69 | ...rest: any[] 70 | ) => R 71 | ) => void 72 | : never 73 | 74 | export type RendererHandler = Target extends ( 75 | event: IPCRendererEvent, 76 | value: infer V 77 | ) => infer R 78 | ? ( 79 | handler?: ( 80 | event: IPCRendererEvent, 81 | handler: { [K in Key extends string ? Key : string]: Target } & { 82 | data: V 83 | }, 84 | ...rest: any[] 85 | ) => R 86 | ) => void 87 | : never 88 | 89 | export type APIHandlers = { 90 | handle: H 91 | invoke: I 92 | remove: R 93 | } 94 | 95 | export type APIExpose< 96 | H, 97 | I, 98 | R, 99 | S extends (handlers: APIHandlers) => any, 100 | Key, 101 | Append extends any 102 | > = { 103 | apiKey?: String 104 | exposeAll?: boolean 105 | append?: { 106 | [K in keyof Append]: Append[K] 107 | } 108 | override?: (handlers: APIHandlers) => ReturnType 109 | } 110 | 111 | export type API< 112 | Handle, 113 | Invoke, 114 | Remove, 115 | APIConfig extends Record 116 | > = APIExpose< 117 | Handle, 118 | Invoke, 119 | Remove, 120 | (handlers: APIHandlers) => any, 121 | '', 122 | APIConfig['append'] 123 | > 124 | -------------------------------------------------------------------------------- /apps/web/src/components/InstallationBox/index.tsx: -------------------------------------------------------------------------------- 1 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' 2 | import { Fragment, useState } from 'react' 3 | 4 | import { queueTimeouts } from 'shared/utils' 5 | import { commands } from 'shared/constants' 6 | 7 | import { Separator, CheckmarkIcon, ClipboardIcon, Spinner } from 'components' 8 | 9 | import { Button, PackageButton, PackageManagerList, ShellBox } from './styles' 10 | import { usePackageManagerSelection } from 'hooks' 11 | import { codeTheme } from 'styles' 12 | 13 | const clipboardStateIcons = { 14 | copying: () => , 15 | copied: () => , 16 | default: () => , 17 | } as const 18 | 19 | type ClipboardStateKeys = keyof typeof clipboardStateIcons 20 | type PackageManagerOptions = keyof typeof commands 21 | 22 | const defaultActivePackageManager = 'yarn' 23 | 24 | export function InstallationBox() { 25 | const [clipboardState, setClipboardState] = 26 | useState('default') 27 | 28 | const { activePackageManager, updateActivePackageManager } = 29 | usePackageManagerSelection( 30 | defaultActivePackageManager 31 | ) 32 | 33 | const installCommand = commands[activePackageManager] 34 | 35 | const availablePackagesManagers = Object.keys( 36 | commands 37 | ) as PackageManagerOptions[] 38 | 39 | function copyToClipboard() { 40 | navigator.clipboard 41 | .writeText(installCommand) 42 | .then(() => setClipboardState('copying')) 43 | .then(() => 44 | queueTimeouts( 45 | { 46 | delay: 1000, 47 | callback: () => setClipboardState('copied'), 48 | }, 49 | { 50 | delay: 1000, 51 | callback: () => setClipboardState('default'), 52 | } 53 | ) 54 | ) 55 | } 56 | 57 | return ( 58 | <> 59 | 60 | {availablePackagesManagers.map( 61 | (packageManagerName, index, packageManagers) => { 62 | const isLastItem = index === packageManagers.length - 1 63 | 64 | return ( 65 | 66 |

  • 67 | 70 | updateActivePackageManager(packageManagerName) 71 | } 72 | > 73 | {packageManagerName} 74 | 75 |
  • 76 | 77 | {!isLastItem && } 78 | 79 | ) 80 | } 81 | )} 82 | 83 | 84 | 85 | 86 | {installCommand} 87 | 88 | 89 | 96 | 97 | 98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /apps/web/src/components/CodeEditorPresenter/styles.ts: -------------------------------------------------------------------------------- 1 | import { Button } from 'components/Docs/MDXComponents/CodeBox/styles' 2 | import { ExternalLink as _ExternalLink } from 'components/ExternalLink' 3 | import { ButtonStyles } from 'components/Layout' 4 | 5 | import { styled, animations, Mixin } from 'styles' 6 | import { BoxStyles } from '../Layout/Box' 7 | 8 | const SeparatorStyles: Mixin = { 9 | borderBottom: '1px solid $border-primary', 10 | } 11 | 12 | export const CodeContainer = styled('div', { 13 | ...BoxStyles, 14 | 15 | width: '100%', 16 | height: '100%', 17 | maxHeight: 400, 18 | minHeight: 400, 19 | paddingTop: '0.5rem', 20 | overflowY: 'auto', 21 | position: 'relative', 22 | fontSize: '1.1rem', 23 | flexDirection: 'column', 24 | animation: `${animations.reveal} 1s ease-in-out`, 25 | 26 | '.indiana-scroll-container': { 27 | marginRight: '1rem', 28 | display: 'inline-block', 29 | }, 30 | 31 | pre: { 32 | width: '100%', 33 | height: '100%', 34 | }, 35 | }) 36 | 37 | export const MenuContainer = styled('aside', { 38 | ...SeparatorStyles, 39 | 40 | display: 'flex', 41 | gap: '0.5rem', 42 | paddingHorizontal: '0.5rem', 43 | alignItems: 'center', 44 | 45 | '-webkit-transform': 'translate3d(0, 0, 0)', 46 | 47 | ul: { 48 | display: 'inline-flex', 49 | alignItems: 'center', 50 | gap: '0.2rem', 51 | }, 52 | 53 | button: { 54 | paddingHorizontal: '0.5rem', 55 | }, 56 | 57 | '@bp4': { 58 | gap: '1rem', 59 | paddingHorizontal: '1.4rem', 60 | 61 | ul: { 62 | gap: '1rem', 63 | }, 64 | }, 65 | }) 66 | 67 | export const MenuControls = styled('div', { 68 | display: 'flex', 69 | gap: '0.5rem', 70 | }) 71 | 72 | export const CodeContent = styled('div', { 73 | paddingHorizontal: '1rem', 74 | minWidth: '100%', 75 | height: '100vh', 76 | paddingBottom: '1rem', 77 | paddingTop: '0.5rem', 78 | marginTop: 0, 79 | overflow: 'auto', 80 | position: 'relative', 81 | 82 | [`&:hover > ${Button}, &:focus-within > ${Button}`]: { 83 | opacity: 1, 84 | pointerEvents: 'all', 85 | }, 86 | 87 | '@bp4': { 88 | marginTop: '0.5rem', 89 | paddingHorizontal: '1.4rem', 90 | }, 91 | }) 92 | 93 | export const TabButton = styled('button', { 94 | display: 'flex', 95 | alignItems: 'center', 96 | gap: '0.5rem', 97 | justifyContent: 'center', 98 | fontSize: '1rem', 99 | paddingVertical: '0.6rem', 100 | color: '$text-support', 101 | borderBottom: '2px solid transparent', 102 | 103 | svg: { 104 | width: 12, 105 | height: 12, 106 | }, 107 | 108 | variants: { 109 | active: { 110 | true: { 111 | color: '$accent-secondary', 112 | borderBottom: '2px solid $accent-primary', 113 | }, 114 | }, 115 | }, 116 | 117 | '@bp4': { 118 | svg: { 119 | width: 15, 120 | height: 15, 121 | }, 122 | }, 123 | }) 124 | 125 | export const IntelliSense = styled('div', { 126 | ...BoxStyles, 127 | 128 | flexDirection: 'column', 129 | left: '3.8rem', 130 | borderRadius: 6, 131 | boxShadow: '4px 4px 15px 5px $colors$shadow-secondary', 132 | minWidth: 200, 133 | minHeight: 80, 134 | position: 'absolute', 135 | zIndex: 2, 136 | overflow: 'hidden', 137 | animation: `${animations.fadeIn} 0.2s ease-in-out`, 138 | 139 | ul: { 140 | display: 'flex', 141 | flexDirection: 'column', 142 | gap: '0.5rem', 143 | }, 144 | 145 | li: { 146 | display: 'flex', 147 | alignItems: 'center', 148 | gap: '0.5rem', 149 | color: '$accent-secondary', 150 | paddingHorizontal: '1rem', 151 | paddingVertical: '0.2rem', 152 | 153 | svg: { 154 | width: 16, 155 | height: 16, 156 | }, 157 | }, 158 | }) 159 | 160 | export const CodeSandboxContainer = styled('div', { 161 | ...SeparatorStyles, 162 | 163 | display: 'flex', 164 | justifyContent: 'center', 165 | gap: '0.5rem', 166 | paddingHorizontal: '0.5rem', 167 | paddingBottom: '0.5rem', 168 | 169 | svg: { 170 | width: 15, 171 | height: 15, 172 | }, 173 | 174 | '@bp4': { 175 | justifyContent: 'flex-end', 176 | paddingHorizontal: '1rem', 177 | }, 178 | }) 179 | 180 | export const ExternalLink = styled(_ExternalLink, { 181 | ...ButtonStyles, 182 | 183 | fontSize: '0.9rem', 184 | 185 | '&:hover': { 186 | cursor: 'pointer', 187 | }, 188 | }) 189 | -------------------------------------------------------------------------------- /apps/web/src/components/Modals/ExitIntent/styles.ts: -------------------------------------------------------------------------------- 1 | import * as Dialog from '@radix-ui/react-dialog' 2 | 3 | import { getPublicPath } from 'shared/utils' 4 | 5 | import { styled, animations } from 'styles' 6 | 7 | export const Overlay = styled(Dialog.Overlay, { 8 | position: 'fixed', 9 | minWidth: '100vw', 10 | minHeight: '100vh', 11 | inset: 0, 12 | background: 'rgba(0, 0, 0, 0.75)', 13 | zIndex: 100, 14 | }) 15 | 16 | export const Container = styled(Dialog.Content, { 17 | display: 'flex', 18 | flexDirection: 'column', 19 | alignItems: 'center', 20 | textAlign: 'center', 21 | width: '100%', 22 | height: '100%', 23 | top: '50%', 24 | left: '50%', 25 | position: 'fixed', 26 | backdropFilter: 'blur(10px)', 27 | transform: 'translate(-50%, -50%)', 28 | overflowX: 'hidden', 29 | zIndex: 9999, 30 | 31 | '@bp4': { 32 | flexDirection: 'row', 33 | justifyContent: 'space-between', 34 | width: '90%', 35 | height: '35.1875rem', 36 | maxWidth: '59.125rem', 37 | borderRadius: 10, 38 | gap: 45.5, 39 | border: '1px solid $colors$shape-tertiary', 40 | boxShadow: '8px 2px 60px -22px $colors$shape-quinary', 41 | overflow: 'hidden', 42 | }, 43 | }) 44 | 45 | export const Content = styled('div', { 46 | display: 'flex', 47 | flex: 1, 48 | flexDirection: 'column', 49 | width: '100%', 50 | height: '100%', 51 | 52 | '@bp4': { 53 | display: 'flex', 54 | flexDirection: 'row', 55 | width: 'auto', 56 | }, 57 | }) 58 | 59 | export const ContentGroup = styled('div', { 60 | display: 'flex', 61 | flexDirection: 'column', 62 | alignItems: 'center', 63 | width: '100%', 64 | 65 | textAlign: 'center', 66 | 67 | paddingHorizontal: '1.5rem', 68 | paddingTop: 'calc(4rem + 1.911rem)', 69 | paddingBottom: '3.10375rem', 70 | 71 | gap: 16, 72 | 73 | p: { 74 | fontSize: '0.875rem', 75 | fontWeight: 400, 76 | textAlign: 'center', 77 | lineHeight: '1.4rem', 78 | 79 | color: '$text-support', 80 | }, 81 | 82 | a: { 83 | marginTop: 16, 84 | }, 85 | 86 | button: { 87 | fontSize: '1rem', 88 | }, 89 | 90 | 'button, a': { 91 | width: '100%', 92 | }, 93 | 94 | '@bp4': { 95 | flex: 1, 96 | width: '100%', 97 | alignItems: 'flex-start', 98 | textAlign: 'left', 99 | marginLeft: '1.46875rem', 100 | 101 | p: { 102 | textAlign: 'left', 103 | }, 104 | }, 105 | }) 106 | 107 | export const Title = styled(Dialog.Title, { 108 | display: 'inline-block', 109 | maxWidth: '20.1875rem', 110 | 111 | fontSize: '1.5rem', 112 | fontWeight: 700, 113 | lineHeight: '1.875rem', 114 | 115 | color: '$text-title', 116 | 117 | span: { 118 | textTransform: 'capitalize', 119 | }, 120 | 121 | strong: { 122 | '&:nth-of-type(odd)': { 123 | color: '$accent-secondary', 124 | }, 125 | 126 | '&:nth-of-type(even)': { 127 | color: '$accent-primary', 128 | }, 129 | }, 130 | 131 | img: { 132 | width: '1.5625rem', 133 | marginLeft: '0.5rem', 134 | }, 135 | 136 | '@bp4': { 137 | maxWidth: '33.3125rem', 138 | 139 | fontSize: '2.5rem', 140 | fontWeight: 700, 141 | textAlign: 'left', 142 | lineHeight: '3.125rem', 143 | 144 | img: { 145 | width: '2.5rem', 146 | marginLeft: '0.5rem', 147 | }, 148 | }, 149 | }) 150 | 151 | export const CloseButton = styled(Dialog.Close, { 152 | fontSize: '1.2rem', 153 | lineHeight: 0, 154 | color: '$text-title', 155 | background: 'transparent', 156 | transform: 'scaleX(1.4)', 157 | top: '1.5rem', 158 | right: '1.5rem', 159 | border: 0, 160 | cursor: 'pointer', 161 | position: 'absolute', 162 | transition: 'color 0.2s ease-in-out', 163 | 164 | '&:hover, &:focus-within': { 165 | color: '$accent-primary', 166 | }, 167 | }) 168 | 169 | export const Tag = styled('h2', { 170 | fontSize: '0.875rem', 171 | fontWeight: 700, 172 | lineHeight: '1.4rem', 173 | textTransform: 'uppercase', 174 | 175 | color: '$accent-primary', 176 | 177 | padding: '4px 8px', 178 | 179 | borderRadius: 3, 180 | 181 | variants: { 182 | type: { 183 | outlined: { 184 | border: '1px solid $accent-primary', 185 | }, 186 | }, 187 | }, 188 | 189 | defaultVariants: { 190 | type: 'outlined', 191 | }, 192 | }) 193 | 194 | export const Image = styled('div', { 195 | display: 'flex', 196 | width: '100%', 197 | height: '3.125rem', 198 | zIndex: -1, 199 | 200 | backgroundImage: `url("${getPublicPath('/abstract-background.jpg')}")`, 201 | backgroundPosition: 'center center', 202 | willChange: 'background', 203 | '-webkit-transform': 'translate3d(0)', 204 | animation: `${animations.backgroundCover} 40s linear infinite alternate`, 205 | 206 | '@bp4': { 207 | maxWidth: '20rem', 208 | minHeight: '100%', 209 | }, 210 | }) 211 | -------------------------------------------------------------------------------- /apps/web/src/components/CodeEditorPresenter/index.tsx: -------------------------------------------------------------------------------- 1 | import { useWindupString, WindupChildren, textFromChildren } from 'windups' 2 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' 3 | import React, { useCallback, useEffect, useRef, useState } from 'react' 4 | import ScrollableContainer from 'react-indiana-drag-scroll' 5 | 6 | import { codes, tabs } from 'shared/constants' 7 | 8 | import { Button, CodeSandboxIcon, FileIcon, Separator } from 'components' 9 | 10 | import { RendererIntelliSenseTab } from './IntelliSenses/renderer' 11 | import { PreloadIntelliSenseTab } from './IntelliSenses/preload' 12 | import { IpcsIntelliSenseTab } from './IntelliSenses/ipcs' 13 | import { MainIntelliSenseTab } from './IntelliSenses/main' 14 | 15 | import { 16 | TabButton, 17 | CodeContent, 18 | MenuControls, 19 | ExternalLink, 20 | MenuContainer, 21 | CodeContainer, 22 | CodeSandboxContainer, 23 | } from './styles' 24 | 25 | import { CodeBox } from 'components/Docs/MDXComponents/CodeBox' 26 | 27 | const tabsData = [ 28 | { 29 | code: codes.ipcs, 30 | IntelliSense: IpcsIntelliSenseTab, 31 | }, 32 | { 33 | code: codes.main, 34 | IntelliSense: MainIntelliSenseTab, 35 | }, 36 | { 37 | code: codes.preload, 38 | IntelliSense: PreloadIntelliSenseTab, 39 | }, 40 | { 41 | code: codes.renderer, 42 | IntelliSense: RendererIntelliSenseTab, 43 | }, 44 | ] 45 | 46 | export function CodeEditorPresenterSection() { 47 | const [activeTab, setActiveTab] = useState(0) 48 | const [tabsAlreadyVisited, setTabsAlreadyVisited] = useState([]) 49 | 50 | const Tab = tabsData[activeTab] 51 | const isActiveTabAlreadyVisited = tabsAlreadyVisited.includes(activeTab) 52 | 53 | const [typewrittenText, controls] = useWindupString(Tab.code.finalState, { 54 | pace: () => 1, 55 | 56 | onFinished() { 57 | if (!isActiveTabAlreadyVisited) { 58 | setTabsAlreadyVisited((prevState) => [...prevState, activeTab]) 59 | } 60 | 61 | setTimeout( 62 | () => { 63 | containerRef.current?.scrollTo(0, 0) 64 | }, 65 | isActiveTabAlreadyVisited ? 0 : 700 66 | ) 67 | }, 68 | }) 69 | 70 | const containerRef = useRef(null) 71 | 72 | useEffect(() => { 73 | const container = containerRef.current 74 | 75 | if (isActiveTabAlreadyVisited || !container) { 76 | return controls.skip() 77 | } 78 | 79 | container.scrollTo(0, container.scrollHeight) 80 | }, [typewrittenText]) 81 | 82 | const handleRemoveTabFromVisitedList = useCallback(() => { 83 | setTabsAlreadyVisited((prevState) => 84 | prevState.filter((item) => item !== activeTab) 85 | ) 86 | }, [activeTab]) 87 | 88 | return ( 89 | 90 | 91 | 95 | 96 | Simple 97 | 98 | 99 | 103 | 104 | Advanced 105 | 106 | 107 | 108 | 109 | 110 | 117 | 118 | 128 | 129 | 130 | 131 |
      132 | {tabs.fileNames.map((fileName, index) => ( 133 | 134 | setActiveTab(index)} 136 | active={Boolean(activeTab === index)} 137 | > 138 | {fileName} 139 | 140 | 141 | {index !== tabsData.length - 1 && } 142 | 143 | ))} 144 |
    145 |
    146 |
    147 | 148 | 149 | {Tab.IntelliSense && ( 150 | 155 | )} 156 | 157 | 158 | {textFromChildren({typewrittenText})} 159 | 160 | 161 |
    162 | ) 163 | } 164 | -------------------------------------------------------------------------------- /apps/web/src/styles/lib/react-syntax-highlighter/theme.ts: -------------------------------------------------------------------------------- 1 | export function createCodeTheme({ 2 | primary, 3 | secondary, 4 | }: { 5 | primary: string 6 | secondary: string 7 | }) { 8 | return { 9 | 'code[class*="language-"]': { 10 | textAlign: 'left', 11 | graySpace: 'pre', 12 | wordSpacing: 'normal', 13 | wordBreak: 'normal', 14 | wordWrap: 'normal', 15 | color: '#eee', 16 | fontFamily: 'Roboto Mono, monospace', 17 | fontSize: '1em', 18 | lineHeight: '1.5em', 19 | MozTabSize: '4', 20 | OTabSize: '4', 21 | tabSize: '4', 22 | WebkitHyphens: 'none', 23 | MozHyphens: 'none', 24 | msHyphens: 'none', 25 | hyphens: 'none', 26 | }, 27 | 28 | 'pre[class*="language-"]': { 29 | textAlign: 'left', 30 | graySpace: 'pre', 31 | wordSpacing: 'normal', 32 | wordBreak: 'normal', 33 | wordWrap: 'normal', 34 | color: '#eee', 35 | fontFamily: 'Roboto Mono, monospace', 36 | fontSize: '1em', 37 | lineHeight: '1.5em', 38 | MozTabSize: '4', 39 | OTabSize: '4', 40 | tabSize: '4', 41 | WebkitHyphens: 'none', 42 | MozHyphens: 'none', 43 | msHyphens: 'none', 44 | hyphens: 'none', 45 | overflow: 'auto', 46 | position: 'relative', 47 | // margin: '0.5em 0', 48 | // padding: '1.25em 1em', 49 | }, 50 | 51 | 'code[class*="language-"]::-moz-selection': { 52 | background: '#363636', 53 | }, 54 | 55 | 'pre[class*="language-"]::-moz-selection': { 56 | background: '#363636', 57 | }, 58 | 59 | 'code[class*="language-"] ::-moz-selection': { 60 | background: '#363636', 61 | }, 62 | 63 | 'pre[class*="language-"] ::-moz-selection': { 64 | background: '#363636', 65 | }, 66 | 67 | 'code[class*="language-"]::selection': { 68 | background: '#363636', 69 | }, 70 | 71 | 'pre[class*="language-"]::selection': { 72 | background: '#363636', 73 | }, 74 | 75 | 'code[class*="language-"] ::selection': { 76 | background: '#363636', 77 | }, 78 | 79 | 'pre[class*="language-"] ::selection': { 80 | background: '#363636', 81 | }, 82 | 83 | ':not(pre) > code[class*="language-"]': { 84 | graySpace: 'normal', 85 | borderRadius: '0.2em', 86 | padding: '0.1em', 87 | }, 88 | 89 | '.language-css > code': { 90 | color: secondary, 91 | }, 92 | 93 | '.language-sass > code': { 94 | color: secondary, 95 | }, 96 | 97 | '.language-scss > code': { 98 | color: secondary, 99 | }, 100 | 101 | '[class*="language-"] .namespace': { 102 | Opacity: '0.7', 103 | }, 104 | 105 | atrule: { 106 | color: secondary, 107 | }, 108 | 109 | 'attr-name': { 110 | color: '#64ffa4', 111 | }, 112 | 113 | 'attr-value': { 114 | color: secondary, 115 | }, 116 | 117 | attribute: { 118 | color: secondary, 119 | }, 120 | 121 | boolean: { 122 | color: secondary, 123 | }, 124 | 125 | builtin: { 126 | color: '#ffcb6b', 127 | }, 128 | 129 | cdata: { 130 | color: primary, 131 | }, 132 | 133 | char: { 134 | color: primary, 135 | }, 136 | 137 | class: { 138 | color: '#ffcb6b', 139 | }, 140 | 141 | 'class-name': { 142 | color: primary, 143 | }, 144 | 145 | comment: { 146 | color: '#616161', 147 | }, 148 | 149 | constant: { 150 | color: secondary, 151 | }, 152 | 153 | deleted: { 154 | color: secondary, 155 | }, 156 | 157 | doctype: { 158 | color: '#616161', 159 | }, 160 | 161 | entity: { 162 | color: secondary, 163 | }, 164 | 165 | function: { 166 | color: primary, 167 | }, 168 | 169 | hexcode: { 170 | color: '#f2ff00', 171 | }, 172 | 173 | id: { 174 | color: secondary, 175 | fontWeight: 'bold', 176 | }, 177 | 178 | important: { 179 | color: secondary, 180 | fontWeight: 'bold', 181 | }, 182 | 183 | inserted: { 184 | color: primary, 185 | }, 186 | 187 | keyword: { 188 | color: secondary, 189 | }, 190 | 191 | number: { 192 | color: secondary, 193 | }, 194 | 195 | operator: { 196 | color: 'gray', 197 | }, 198 | 199 | prolog: { 200 | color: '#616161', 201 | }, 202 | 203 | property: { 204 | color: primary, 205 | }, 206 | 207 | 'pseudo-class': { 208 | color: secondary, 209 | }, 210 | 211 | 'pseudo-element': { 212 | color: secondary, 213 | }, 214 | 215 | punctuation: { 216 | color: 'gray', 217 | }, 218 | 219 | regex: { 220 | color: '#f2ff00', 221 | }, 222 | 223 | selector: { 224 | color: secondary, 225 | }, 226 | 227 | string: { 228 | color: '#77bddf', 229 | }, 230 | 231 | symbol: { 232 | color: secondary, 233 | }, 234 | 235 | tag: { 236 | color: secondary, 237 | }, 238 | 239 | unit: { 240 | color: secondary, 241 | }, 242 | 243 | url: { 244 | color: secondary, 245 | }, 246 | 247 | variable: { 248 | color: secondary, 249 | }, 250 | } as { [key: string]: React.CSSProperties } 251 | } 252 | -------------------------------------------------------------------------------- /packages/interprocess/README.md: -------------------------------------------------------------------------------- 1 |

    2 | 3 | preview 4 | 5 |

    6 | 7 |

    interprocess: 💬 A scalable and type-safe Electron IPC management tool with enhanced DX

    8 | 9 |

    10 | 11 | 12 | github url 13 | 14 | 15 | 16 | patreon url 17 | 18 | 19 | 20 | releases url 21 | 22 | 23 | 24 | license url 25 | 26 |

    27 | 28 | > Electron IPC is good, but difficult to maintain and scale, either because of the numerous channels you have to remember, or because of the inconsistent API between processes and the absence of inferred types of your channels and handlers. These are some of the things that interprocess comes to solve! 29 | 30 | # 💬 Features 31 | - 🚀 Best-in-class DX with a fully-typed API 32 | - 🧠 Enchanced and consistent API 33 | - 🔥 Type-safe and scalable 34 | - 🪄 Code splitting support 35 | - 🕸️ All edges connected (APIs to handle all processes) 36 | - 💖 `invoke` and `handle` methods in both processes with the same expected behavior 37 | 38 | # Requirements 39 | - Node.js >= 14.0.0 40 | - Electron >= 12.0.0 41 | - TypeScript >= 4.8.0 42 | 43 | # 💬 Installation 44 | In your terminal, run: 45 | ```bash 46 | yarn add interprocess 47 | 48 | # OR 49 | 50 | npm i interprocess 51 | ``` 52 | 53 | # 💬 Usage 54 | 55 | Let's build something simple that can show you some of the interprocess's power! 56 | 57 | First, create the following folders at `src`: 58 | 59 | - `shared/ipcs` (this folder structure is optional) 60 | 61 | Then, create a file named as `index.ts` in the `ipcs` folder with the following content: 62 | 63 | ```ts 64 | import { createInterprocess } from 'interprocess' 65 | 66 | export const { ipcMain, ipcRenderer, exposeApiToGlobalWindow } = 67 | createInterprocess({ 68 | main: { 69 | async getPing(_, data: 'ping') { 70 | const message = `from renderer: ${data} on main process` 71 | 72 | console.log(message) 73 | 74 | return message 75 | }, 76 | }, 77 | 78 | renderer: { 79 | async getPong(_, data: 'pong') { 80 | const message = `from main: ${data} on renderer process` 81 | 82 | console.log(message) 83 | 84 | return message 85 | }, 86 | }, 87 | }) 88 | ``` 89 | 90 | On the **main process**: 91 | 92 | > :warning: Don't forget to add `sandbox: false` to the BrowserWindow because it's required to load the preload script properly! 93 | 94 | ```ts 95 | import { BrowserWindow, app } from 'electron' 96 | 97 | import { ipcMain } from 'shared/ipcs' 98 | 99 | const { handle, invoke } = ipcMain 100 | 101 | app.whenReady().then(() => { 102 | const mainWindow = new BrowserWindow({ 103 | webPreferences: { 104 | preload: path.join(__dirname, '../preload/index.js'), 105 | sandbox: false, // sandbox must be false 106 | }, 107 | }) 108 | 109 | handle.getPing() 110 | 111 | mainWindow.webContents.on('dom-ready', () => { 112 | invoke.getPong(mainWindow, 'pong') 113 | }) 114 | }) 115 | ``` 116 | 117 | In the **preload script**: 118 | 119 | ```ts 120 | import { exposeApiToGlobalWindow } from 'shared/ipcs' 121 | 122 | const { key, api } = exposeApiToGlobalWindow({ 123 | exposeAll: true, // expose handlers, invokers and removers 124 | }) 125 | 126 | declare global { 127 | interface Window { 128 | [key]: typeof api 129 | } 130 | } 131 | ``` 132 | 133 | On the **renderer process**: 134 | 135 | ```ts 136 | const { invoke, handle } = window.api 137 | 138 | invoke.getPing('ping') 139 | handle.getPong() 140 | ``` 141 | This is a simple way to work with interprocess, but there's a lot of more cool features you can take advantage, like overrides, code splitting, invoker's response (for renderer and main process 🎉) and more. See the [Knowledge section for more](#-knowledge) 142 | 143 | # 💬 Knowledge 144 | - [Docs](https://interprocess.daltonmenezes.com/docs/getting-started/overview) 145 | 146 | - Examples 147 | - [Executable](https://github.com/daltonmenezes/interprocess/tree/main/apps/desktop) 148 | - [CodeSandbox (simple)](https://codesandbox.io/s/simple-607b6h?file=/src/ipcs.ts) 149 | - [CodeSandbox (advanced)](https://codesandbox.io/s/advanced-4qh0xb?file=/src/ipcs/index.ts) 150 | 151 | 152 | # 💬 Contributing 153 | > **Note**: contributions are always welcome, but always **ask first**, — please — before work on a PR. 154 | 155 | That said, there's a bunch of ways you can contribute to this project, like by: 156 | 157 | - :beetle: Reporting a bug 158 | - :page_facing_up: Improving the docs 159 | - :rotating_light: Sharing this project and recommending it to your friends 160 | - :dollar: Supporting this project on GitHub Sponsors or Patreon 161 | - :bug: Funding an issue on IssueHunt 162 | - :star2: Giving a star on this repository 163 | 164 | # License 165 | 166 | [MIT © Dalton Menezes](https://github.com/daltonmenezes/interprocess/blob/main/LICENSE) 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

    2 | 3 | preview 4 | 5 |

    6 | 7 |

    interprocess: 💬 A scalable and type-safe Electron IPC management tool with enhanced DX

    8 | 9 |

    10 | 11 | 12 | github url 13 | 14 | 15 | 16 | patreon url 17 | 18 | 19 | 20 | releases url 21 | 22 | 23 | 24 | license url 25 | 26 |

    27 | 28 | > Electron IPC is good, but difficult to maintain and scale, either because of the numerous channels you have to remember, or because of the inconsistent API between processes and the absence of inferred types of your channels and handlers. These are some of the things that interprocess comes to solve! 29 | 30 | # 💬 Features 31 | - 🚀 Best-in-class DX with a fully-typed API 32 | - 🧠 Enchanced and consistent API 33 | - 🔥 Type-safe and scalable 34 | - 🪄 Code splitting support 35 | - 🕸️ All edges connected (APIs to handle all processes) 36 | - 💖 `invoke` and `handle` methods in both processes with the same expected behavior 37 | 38 | # Requirements 39 | - Node.js >= 14.0.0 40 | - Electron >= 12.0.0 41 | - TypeScript >= 4.8.0 42 | 43 | # 💬 Installation 44 | In your terminal, run: 45 | - npm 46 | ```bash 47 | npm i interprocess 48 | ``` 49 | - pnpm 50 | ```bash 51 | pnpm i interprocess 52 | ``` 53 | - yarn 54 | ```bash 55 | yarn add interprocess 56 | ``` 57 | 58 | # 💬 Usage 59 | 60 | Let's build something simple that can show you some of the interprocess's power! 61 | 62 | First, create the following folders at `src`: 63 | 64 | - `shared/ipcs` (this folder structure is optional) 65 | 66 | Then, create a file named as `index.ts` in the `ipcs` folder with the following content: 67 | 68 | ```ts 69 | import { createInterprocess } from 'interprocess' 70 | 71 | export const { ipcMain, ipcRenderer, exposeApiToGlobalWindow } = 72 | createInterprocess({ 73 | main: { 74 | async getPing(_, data: 'ping') { 75 | const message = `from renderer: ${data} on main process` 76 | 77 | console.log(message) 78 | 79 | return message 80 | }, 81 | }, 82 | 83 | renderer: { 84 | async getPong(_, data: 'pong') { 85 | const message = `from main: ${data} on renderer process` 86 | 87 | console.log(message) 88 | 89 | return message 90 | }, 91 | }, 92 | }) 93 | ``` 94 | 95 | On the **main process**: 96 | 97 | > :warning: Don't forget to add `sandbox: false` to the BrowserWindow because it's required to load the preload script properly! 98 | 99 | ```ts 100 | import { BrowserWindow, app } from 'electron' 101 | 102 | import { ipcMain } from 'shared/ipcs' 103 | 104 | const { handle, invoke } = ipcMain 105 | 106 | app.whenReady().then(() => { 107 | const mainWindow = new BrowserWindow({ 108 | webPreferences: { 109 | preload: path.join(__dirname, '../preload/index.js'), 110 | sandbox: false, // sandbox must be false 111 | }, 112 | }) 113 | 114 | handle.getPing() 115 | 116 | mainWindow.webContents.on('dom-ready', () => { 117 | invoke.getPong(mainWindow, 'pong') 118 | }) 119 | }) 120 | ``` 121 | 122 | In the **preload script**: 123 | 124 | ```ts 125 | import { exposeApiToGlobalWindow } from 'shared/ipcs' 126 | 127 | const { key, api } = exposeApiToGlobalWindow({ 128 | exposeAll: true, // expose handlers, invokers and removers 129 | }) 130 | 131 | declare global { 132 | interface Window { 133 | [key]: typeof api 134 | } 135 | } 136 | ``` 137 | 138 | On the **renderer process**: 139 | 140 | ```ts 141 | const { invoke, handle } = window.api 142 | 143 | invoke.getPing('ping') 144 | handle.getPong() 145 | ``` 146 | This is a simple way to work with interprocess, but there's a lot of more cool features you can take advantage, like overrides, code splitting, invoker's response (for renderer and main process 🎉) and more. See the [Knowledge section for more](#-knowledge) 147 | 148 | # 💬 Knowledge 149 | - [Docs](https://interprocess.daltonmenezes.com/docs/getting-started/overview) 150 | 151 | - Examples 152 | - [Executable](https://github.com/daltonmenezes/interprocess/tree/main/apps/desktop) 153 | - [CodeSandbox (simple)](https://codesandbox.io/s/simple-607b6h?file=/src/ipcs.ts) 154 | - [CodeSandbox (advanced)](https://codesandbox.io/s/advanced-4qh0xb?file=/src/ipcs/index.ts) 155 | 156 | 157 | # 💬 Contributing 158 | > **Note**: contributions are always welcome, but always **ask first**, — please — before work on a PR. 159 | 160 | That said, there's a bunch of ways you can contribute to this project, like by: 161 | 162 | - :beetle: Reporting a bug 163 | - :page_facing_up: Improving the docs 164 | - :rotating_light: Sharing this project and recommending it to your friends 165 | - :dollar: Supporting this project on GitHub Sponsors or Patreon 166 | - :bug: Funding an issue on IssueHunt 167 | - :star2: Giving a star on this repository 168 | 169 | # License 170 | 171 | [MIT © Dalton Menezes](https://github.com/daltonmenezes/interprocess/blob/main/LICENSE) 172 | -------------------------------------------------------------------------------- /apps/web/src/shared/constants/codes.ts: -------------------------------------------------------------------------------- 1 | export const codes = { 2 | ipcs: { 3 | finalState: `import { createInterprocess } from 'interprocess' 4 | 5 | export const { exposeApiToGlobalWindow, ipcMain, ipcRenderer } = 6 | createInterprocess({ 7 | main: { 8 | async myIpcHandler(_, data: string) { 9 | // ... do something with the data when this handler is invoked by the renderer process 10 | return data.toUpperCase() 11 | }, 12 | }, 13 | 14 | renderer: { 15 | async myAnotherIpcHandler(_, data: string) { 16 | // ... do something with the data when this handler is invoked by the main process 17 | return data.toLowerCase() 18 | }, 19 | }, 20 | }) 21 | `, 22 | 23 | firstCheckToShowIntelliSense: `import { createInterprocess } from 'interprocess' 24 | 25 | export const { exposeApiToGlobalWindow, ipcMain, ipcRenderer } = 26 | createInterprocess({ 27 | `, 28 | 29 | secondCheckToShowIntelliSense: `import { createInterprocess } from 'interprocess' 30 | 31 | export const { exposeApiToGlobalWindow, ipcMain, ipcRenderer } = 32 | createInterprocess({ 33 | main: { 34 | async myIpcHandler(_, data: string) { 35 | // ... do something with the data when this handler is invoked by the renderer process 36 | return data.toUpperCase() 37 | }, 38 | }, 39 | 40 | `, 41 | }, 42 | 43 | main: { 44 | finalState: `// ... some imports 45 | import { ipcMain } from 'shared/ipcs' 46 | 47 | const { handle, invoke } = ipcMain 48 | 49 | app.whenReady().then(() => { 50 | const mainWindow = new BrowserWindow({ 51 | webPreferences: { 52 | preload: path.join(__dirname, '../preload/index.js'), 53 | sandbox: false, // sandbox must be false 54 | }, 55 | }) 56 | 57 | handle.myIpcHandler(async (_, { myIpcHandler, data }) => { 58 | const result = await myIpcHandler(_, data) 59 | 60 | return result 61 | }) 62 | // That could be handle.myIpcHandler() 63 | // if you don't need to do anything else 64 | // with the result here and it will call the handler directly 65 | 66 | mainWindow.webContents.on('dom-ready', () => { 67 | // invoke the myAnotherIpcHandler from renderer process 68 | invoke.myAnotherIpcHandler(mainWindow, 'LOWER-CASE IT FOR ME') 69 | }) 70 | }) 71 | `, 72 | 73 | firstCheckToShowIntelliSense: `// ... some imports 74 | import { ipcMain } from 'shared/ipcs' 75 | 76 | const { `, 77 | 78 | secondCheckToShowIntelliSense: `// ... some imports 79 | import { ipcMain } from 'shared/ipcs' 80 | 81 | const { handle, `, 82 | 83 | thirdCheckToShowIntelliSense: `// ... some imports 84 | import { ipcMain } from 'shared/ipcs' 85 | 86 | const { handle, invoke } = ipcMain 87 | 88 | app.whenReady().then(() => { 89 | const mainWindow = new BrowserWindow({ 90 | webPreferences: { 91 | preload: path.join(__dirname, '../preload/index.js'), 92 | sandbox: false, // sandbox must be false 93 | }, 94 | }) 95 | 96 | handle.`, 97 | 98 | fourthCheckToShowIntelliSense: `// ... some imports 99 | import { ipcMain } from 'shared/ipcs' 100 | 101 | const { handle, invoke } = ipcMain 102 | 103 | app.whenReady().then(() => { 104 | const mainWindow = new BrowserWindow({ 105 | webPreferences: { 106 | preload: path.join(__dirname, '../preload/index.js'), 107 | sandbox: false, // sandbox must be false 108 | }, 109 | }) 110 | 111 | handle.myIpcHandler(async (_, { `, 112 | 113 | fifthCheckToShowIntelliSense: `// ... some imports 114 | import { ipcMain } from 'shared/ipcs' 115 | 116 | const { handle, invoke } = ipcMain 117 | 118 | app.whenReady().then(() => { 119 | const mainWindow = new BrowserWindow({ 120 | webPreferences: { 121 | preload: path.join(__dirname, '../preload/index.js'), 122 | sandbox: false, // sandbox must be false 123 | }, 124 | }) 125 | 126 | handle.myIpcHandler(async (_, { myIpcHandler, `, 127 | 128 | sixthCheckToShowIntelliSense: `// ... some imports 129 | import { ipcMain } from 'shared/ipcs' 130 | 131 | const { handle, invoke } = ipcMain 132 | 133 | app.whenReady().then(() => { 134 | const mainWindow = new BrowserWindow({ 135 | webPreferences: { 136 | preload: path.join(__dirname, '../preload/index.js'), 137 | sandbox: false, // sandbox must be false 138 | }, 139 | }) 140 | 141 | handle.myIpcHandler(async (_, { myIpcHandler, data }) => { 142 | const result = await myIpcHandler(_, data) 143 | 144 | return result 145 | }) 146 | // That could be handle.myIpcHandler() 147 | // if you don't need to do anything else 148 | // with the result here and it will call the handler directly 149 | 150 | mainWindow.webContents.on('dom-ready', () => { 151 | // invoke the myAnotherIpcHandler from renderer process 152 | invoke.`, 153 | }, 154 | 155 | preload: { 156 | finalState: `import { exposeApiToGlobalWindow, ipcRenderer } from './ipcs' 157 | 158 | const { handle } = ipcRenderer 159 | 160 | // Call exposeApiToGlobalWindow with no arguments will expose only invokers to the api 161 | // If you want to expose all options, you can pass { exposeAll: true } object argument 162 | const { key, api } = exposeApiToGlobalWindow() 163 | 164 | // execute the myAnotherIpcHandler registration 165 | handle.myAnotherIpcHandler() 166 | 167 | declare global { 168 | interface Window { 169 | [key]: typeof api 170 | } 171 | } 172 | `, 173 | firstCheckToShowIntelliSense: `import { exposeApiToGlobalWindow, ipcRenderer } from './ipcs' 174 | 175 | const { handle } = ipcRenderer 176 | 177 | // Call exposeApiToGlobalWindow with no arguments will expose only invokers to the api 178 | // If you want to expose all options, you can pass { exposeAll: true } object argument 179 | const { key, api } = exposeApiToGlobalWindow() 180 | 181 | // execute the myAnotherIpcHandler registration 182 | handle.`, 183 | }, 184 | 185 | renderer: { 186 | finalState: `const { invoke } = window.api 187 | 188 | // invoke the myIpcHandler from main process 189 | invoke.myIpcHandler('upper-case me, please!') 190 | 191 | `, 192 | firstCheckToShowIntelliSense: `const { invoke } = window.api 193 | 194 | // invoke the myIpcHandler from main process 195 | invoke.`, 196 | }, 197 | } 198 | -------------------------------------------------------------------------------- /apps/web/src/templates/Docs/styles.ts: -------------------------------------------------------------------------------- 1 | import { styled } from 'styles' 2 | 3 | import { Title as _Title } from 'components/Layout/Title' 4 | import { getPublicPath } from 'shared/utils' 5 | 6 | export const Section = styled('section', { 7 | display: 'flex', 8 | flexDirection: 'row', 9 | flex: 1, 10 | height: '100%', 11 | width: '100%', 12 | paddingBottom: '1.5rem', 13 | paddingTop: '3.7rem', 14 | 15 | pre: { 16 | fontFamily: 'inherit', 17 | lineHeight: '1.5', 18 | textAlign: 'left', 19 | width: '100%', 20 | color: '$accent-secondary', 21 | }, 22 | 23 | '@bp4': { 24 | paddingTop: 0, 25 | }, 26 | }) 27 | 28 | export const MenuButton = styled('button', { 29 | display: 'flex', 30 | 31 | '@bp4': { 32 | display: 'none', 33 | }, 34 | 35 | variants: { 36 | active: { 37 | true: { 38 | 'svg g': { 39 | stroke: '$accent-secondary', 40 | }, 41 | }, 42 | }, 43 | }, 44 | }) 45 | 46 | export const Sidebar = styled('aside', { 47 | display: 'flex', 48 | flexDirection: 'column', 49 | flex: 1, 50 | width: '100%', 51 | maxWidth: '84%', 52 | height: '-webkit-fill-available', 53 | overflowY: 'auto', 54 | padding: '1.5rem', 55 | paddingTop: '3.3rem', 56 | fontSize: '1rem', 57 | lineHeight: '1.5rem', 58 | whiteSpace: 'pre-wrap', 59 | wordBreak: 'break-word', 60 | wordWrap: 'break-word', 61 | overflowWrap: 'break-word', 62 | hyphens: 'auto', 63 | top: '4.2rem', 64 | left: 0, 65 | zIndex: 999, 66 | position: 'fixed', 67 | 68 | background: '$shape-primary', 69 | border: '1px solid $border-primary', 70 | borderRadius: 15, 71 | borderTopLeftRadius: 0, 72 | borderBottomRightRadius: 0, 73 | boxShadow: '0 2px 2px 1px $colors$shadow-primary', 74 | 75 | ul: { 76 | display: 'flex', 77 | flexDirection: 'column', 78 | gap: '0.5rem', 79 | }, 80 | 81 | li: { 82 | display: 'flex', 83 | flexDirection: 'column', 84 | 85 | div: { 86 | display: 'flex', 87 | flexDirection: 'column', 88 | gap: '0.5rem', 89 | 90 | span: { 91 | textTransform: 'capitalize', 92 | fontSize: '1.1rem', 93 | fontWeight: 700, 94 | color: '$accent-secondary', 95 | 96 | '@bp4': { 97 | fontSize: '1.2rem', 98 | }, 99 | }, 100 | 101 | a: { 102 | transition: 'all 0.2s ease-in-out', 103 | overflow: 'hidden', 104 | textOverflow: 'ellipsis', 105 | whiteSpace: 'nowrap', 106 | maxWidth: '100%', 107 | }, 108 | 109 | '&:has(span) a': { 110 | marginLeft: '1rem', 111 | width: 'fit-content', 112 | }, 113 | 114 | '&:has(span + div) a': { 115 | marginLeft: 'unset', 116 | }, 117 | 118 | '&:has(span + div) div': { 119 | marginBottom: '0.5rem', 120 | }, 121 | 122 | 'ul li': { 123 | marginLeft: '1rem', 124 | 125 | span: { 126 | fontSize: '1rem', 127 | }, 128 | }, 129 | }, 130 | }, 131 | 132 | '@bp4': { 133 | display: 'flex', 134 | top: 0, 135 | left: 0, 136 | position: 'relative', 137 | height: 'auto', 138 | zIndex: 0, 139 | maxWidth: '220px', 140 | 141 | borderTopLeftRadius: 15, 142 | borderTopRightRadius: 0, 143 | borderBottomRightRadius: 0, 144 | }, 145 | 146 | transition: 'all 0.2s ease-in-out', 147 | 148 | variants: { 149 | visibility: { 150 | true: { 151 | transform: 'translateX(0)', 152 | }, 153 | 154 | false: { 155 | transform: 'translateX(-100%)', 156 | 157 | '@bp4': { 158 | transform: 'translateX(0)', 159 | minWidth: 'fit-content', 160 | }, 161 | }, 162 | }, 163 | }, 164 | }) 165 | 166 | export const Content = styled('div', { 167 | display: 'flex', 168 | flex: 1, 169 | flexDirection: 'column', 170 | justifyContent: 'space-between', 171 | padding: '1rem 1.5rem', 172 | gap: '1rem', 173 | backgroundColor: '$shape-primary', 174 | borderRadius: 15, 175 | border: '1px solid $border-primary', 176 | boxShadow: '0 2px 2px 1px $colors$shadow-primary', 177 | width: '100%', 178 | minHeight: '100%', 179 | 180 | '@bp4': { 181 | padding: '2rem 3rem', 182 | borderTopLeftRadius: 0, 183 | borderBottomLeftRadius: 0, 184 | borderLeft: '1px solid black', 185 | }, 186 | }) 187 | 188 | export const Header = styled('header', { 189 | display: 'flex', 190 | flex: 1, 191 | flexDirection: 'column', 192 | alignSelf: 'center', 193 | alignItems: 'center', 194 | minHeight: '100%', 195 | justifyContent: 'space-between', 196 | gap: '1rem', 197 | 198 | div: { 199 | display: 'flex', 200 | flexDirection: 'row', 201 | gap: '1rem', 202 | alignItems: 'center', 203 | }, 204 | 205 | 'div:first-of-type': { 206 | width: '100%', 207 | justifyContent: 'flex-start', 208 | }, 209 | 210 | 'div:last-of-type': { 211 | width: '100%', 212 | justifyContent: 'flex-end', 213 | }, 214 | 215 | a: { 216 | transition: 'filter 0.2s ease-in-out', 217 | 218 | '&:hover': { 219 | filter: 'brightness(0.7)', 220 | }, 221 | }, 222 | 223 | '@bp2': { 224 | flexDirection: 'row', 225 | gap: 0, 226 | 227 | div: { 228 | flexDirection: 'row', 229 | }, 230 | 231 | 'div:first-of-type': { 232 | width: 'fit-content', 233 | }, 234 | }, 235 | }) 236 | 237 | export const Title = styled(_Title, { 238 | fontSize: '1.3rem', 239 | letterSpacing: '0.1rem', 240 | textTransform: 'none', 241 | }) 242 | 243 | export const Article = styled('article', { 244 | display: 'flex', 245 | flexDirection: 'column', 246 | 247 | h1: { 248 | maxWidth: 'fit-content', 249 | fontSize: '1.7rem', 250 | letterSpacing: 0, 251 | transition: 'all 0.2s ease-in-out', 252 | }, 253 | 254 | 'h1:first-of-type:hover': { 255 | filter: 'brightness(0.7)', 256 | }, 257 | 258 | 'h1, h2, h3, h4, h5, h6': { 259 | marginVertical: '1rem', 260 | position: 'relative', 261 | 262 | '&:hover a::before': { 263 | content: '', 264 | display: 'block', 265 | width: 15, 266 | height: 15, 267 | zIndex: 1, 268 | top: '20%', 269 | right: 'calc(100% + 0.2rem)', 270 | background: `url(${getPublicPath('/link.svg')}) no-repeat center`, 271 | backgroundSize: 'contain', 272 | position: 'absolute', 273 | }, 274 | }, 275 | 276 | p: { 277 | fontSize: '1rem', 278 | color: '$text-support', 279 | textAlign: 'left', 280 | }, 281 | 282 | ul: {}, 283 | 284 | li: { 285 | marginLeft: '1rem', 286 | listStyleType: 'circle', 287 | }, 288 | 289 | strong: { 290 | color: '$accent-secondary', 291 | 292 | '&:nth-of-type(even)': { 293 | color: '$accent-primary', 294 | }, 295 | }, 296 | }) 297 | --------------------------------------------------------------------------------
    57 | 72 | 73 |
    74 | 75 | 76 | 77 | 78 | 79 |
    80 |