mirror of
https://github.com/colanode/colanode.git
synced 2025-12-14 18:57:46 +01:00
Switch to npm workspaces and electron forge
This commit is contained in:
@@ -3,5 +3,6 @@ out
|
||||
tsconfig.base.json
|
||||
tsconfig.json
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
.env
|
||||
LICENSE.md
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.gitignore
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'@electron-toolkit/eslint-config-ts/recommended',
|
||||
'@electron-toolkit/eslint-config-prettier',
|
||||
],
|
||||
};
|
||||
16
apps/desktop/.eslintrc.json
Normal file
16
apps/desktop/.eslintrc.json
Normal file
@@ -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"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
electron_mirror=https://npmmirror.com/mirrors/electron/
|
||||
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
|
||||
shamefully-hoist=true
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 121 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
@@ -1,3 +0,0 @@
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
updaterCacheDirName: colanode-updater
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"appId": "com.colanode.desktop",
|
||||
"productName": "Colanode",
|
||||
"directories": {
|
||||
"buildResources": "build"
|
||||
},
|
||||
"files": [
|
||||
"!**/.vscode/*",
|
||||
"!src/*",
|
||||
"!electron.vite.config.{js,ts,mjs,cjs}",
|
||||
"!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}",
|
||||
"!{.env,.env.*,.npmrc,pnpm-lock.yaml}",
|
||||
"!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}"
|
||||
],
|
||||
"asarUnpack": ["resources/**"],
|
||||
"win": {
|
||||
"executableName": "Colanode",
|
||||
"nsis": {
|
||||
"artifactName": "${name}-${version}-setup.${ext}",
|
||||
"shortcutName": "${productName}",
|
||||
"uninstallDisplayName": "${productName}",
|
||||
"createDesktopShortcut": "always"
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
"entitlementsInherit": "build/entitlements.mac.plist",
|
||||
"extendInfo": {
|
||||
"NSCameraUsageDescription": "Application requests access to the device's camera.",
|
||||
"NSMicrophoneUsageDescription": "Application requests access to the device's microphone.",
|
||||
"NSDocumentsFolderUsageDescription": "Application requests access to the user's Documents folder.",
|
||||
"NSDownloadsFolderUsageDescription": "Application requests access to the user's Downloads folder."
|
||||
},
|
||||
"notarize": false
|
||||
},
|
||||
"dmg": {
|
||||
"artifactName": "${name}-${version}.${ext}"
|
||||
},
|
||||
"linux": {
|
||||
"target": ["AppImage", "snap", "deb"],
|
||||
"maintainer": "electronjs.org",
|
||||
"category": "Utility"
|
||||
},
|
||||
"appImage": {
|
||||
"artifactName": "${name}-${version}.${ext}"
|
||||
},
|
||||
"npmRebuild": false,
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
"url": "https://example.com/auto-updates"
|
||||
},
|
||||
"electronDownload": {
|
||||
"mirror": "https://npmmirror.com/mirrors/electron/"
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||
|
||||
export default defineConfig({
|
||||
main: {
|
||||
plugins: [
|
||||
externalizeDepsPlugin({
|
||||
exclude: ['@colanode/core', '@colanode/crdt'],
|
||||
}),
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: 'assets/**/*',
|
||||
dest: 'assets',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@/main': resolve('src/main'),
|
||||
'@/shared': resolve('src/shared'),
|
||||
},
|
||||
},
|
||||
},
|
||||
preload: {
|
||||
plugins: [
|
||||
externalizeDepsPlugin({
|
||||
exclude: ['@colanode/core', '@colanode/crdt'],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@/shared': resolve('src/shared'),
|
||||
},
|
||||
},
|
||||
},
|
||||
renderer: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@/renderer': resolve('src/renderer'),
|
||||
'@/shared': resolve('src/shared'),
|
||||
},
|
||||
},
|
||||
plugins: [react()],
|
||||
},
|
||||
});
|
||||
59
apps/desktop/forge.config.ts
Normal file
59
apps/desktop/forge.config.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { ForgeConfig } from '@electron-forge/shared-types';
|
||||
import { MakerSquirrel } from '@electron-forge/maker-squirrel';
|
||||
import { MakerZIP } from '@electron-forge/maker-zip';
|
||||
import { MakerDeb } from '@electron-forge/maker-deb';
|
||||
import { MakerRpm } from '@electron-forge/maker-rpm';
|
||||
import { VitePlugin } from '@electron-forge/plugin-vite';
|
||||
import { FusesPlugin } from '@electron-forge/plugin-fuses';
|
||||
import { FuseV1Options, FuseVersion } from '@electron/fuses';
|
||||
|
||||
const config: ForgeConfig = {
|
||||
packagerConfig: {
|
||||
asar: true,
|
||||
},
|
||||
rebuildConfig: {},
|
||||
makers: [
|
||||
new MakerSquirrel({}),
|
||||
new MakerZIP({}, ['darwin']),
|
||||
new MakerRpm({}),
|
||||
new MakerDeb({}),
|
||||
],
|
||||
plugins: [
|
||||
new VitePlugin({
|
||||
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
|
||||
// If you are familiar with Vite configuration, it will look really familiar.
|
||||
build: [
|
||||
{
|
||||
// `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
|
||||
entry: 'src/main.ts',
|
||||
config: 'vite.main.config.ts',
|
||||
target: 'main',
|
||||
},
|
||||
{
|
||||
entry: 'src/preload.ts',
|
||||
config: 'vite.preload.config.ts',
|
||||
target: 'preload',
|
||||
},
|
||||
],
|
||||
renderer: [
|
||||
{
|
||||
name: 'main_window',
|
||||
config: 'vite.renderer.config.ts',
|
||||
},
|
||||
],
|
||||
}),
|
||||
// Fuses are used to enable/disable various Electron functionality
|
||||
// at package time, before code signing the application
|
||||
new FusesPlugin({
|
||||
version: FuseVersion.V1,
|
||||
[FuseV1Options.RunAsNode]: false,
|
||||
[FuseV1Options.EnableCookieEncryption]: true,
|
||||
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
|
||||
[FuseV1Options.EnableNodeCliInspectArguments]: false,
|
||||
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
|
||||
[FuseV1Options.OnlyLoadAppFromAsar]: true,
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
4
apps/desktop/forge.env.d.ts
vendored
Normal file
4
apps/desktop/forge.env.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/// <reference types="@electron-forge/plugin-vite/forge-vite-env" />
|
||||
|
||||
declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string;
|
||||
declare const MAIN_WINDOW_VITE_NAME: string;
|
||||
17
apps/desktop/index.html
Normal file
17
apps/desktop/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Colanode</title>
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self' 'unsafe-inline' data: avatar: local-file: local-file-preview: asset:;"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/renderer.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,29 +3,49 @@
|
||||
"productName": "Colanode",
|
||||
"version": "1.0.0",
|
||||
"description": "Colanode desktop application",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "Hakan Shehu",
|
||||
"main": ".vite/build/main.js",
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
||||
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
|
||||
"typecheck": "pnpm run typecheck:node && pnpm run typecheck:web",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev --watch",
|
||||
"build": "pnpm run typecheck && electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "pnpm run build && electron-builder --dir",
|
||||
"build:win": "pnpm run build && electron-builder --win",
|
||||
"build:mac": "electron-vite build && electron-builder --mac",
|
||||
"build:linux": "electron-vite build && electron-builder --linux"
|
||||
"dev": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "eslint --ext .ts,.tsx .",
|
||||
"postinstall": "electron-rebuild"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^7.5.0",
|
||||
"@electron-forge/maker-deb": "^7.5.0",
|
||||
"@electron-forge/maker-rpm": "^7.5.0",
|
||||
"@electron-forge/maker-squirrel": "^7.5.0",
|
||||
"@electron-forge/maker-zip": "^7.5.0",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^7.5.0",
|
||||
"@electron-forge/plugin-fuses": "^7.5.0",
|
||||
"@electron-forge/plugin-vite": "^7.5.0",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@types/better-sqlite3": "^7.6.11",
|
||||
"@types/is-hotkey": "^0.1.10",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/unzipper": "^0.10.10",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"electron": "33.2.0",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.15",
|
||||
"vite": "^5.4.11",
|
||||
"vite-plugin-static-copy": "^2.1.0"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
"name": "Hakan Shehu",
|
||||
"email": "***REMOVED***"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@colanode/core": "workspace:*",
|
||||
"@colanode/crdt": "workspace:*",
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@colanode/core": "*",
|
||||
"@colanode/crdt": "*",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||
"@radix-ui/react-checkbox": "^1.1.2",
|
||||
"@radix-ui/react-collapsible": "^1.1.1",
|
||||
@@ -33,18 +53,16 @@
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-hover-card": "^1.1.2",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-popover": "^1.1.2",
|
||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||
"@radix-ui/react-scroll-area": "^1.2.1",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.1",
|
||||
"@radix-ui/react-toast": "^1.2.2",
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.4",
|
||||
"@radix-ui/react-visually-hidden": "^1.1.0",
|
||||
"@react-oauth/google": "^0.12.1",
|
||||
"@tanstack/react-query": "^5.59.20",
|
||||
"@tanstack/react-query": "^5.60.5",
|
||||
"@tiptap/core": "^2.9.1",
|
||||
"@tiptap/extension-blockquote": "^2.9.1",
|
||||
"@tiptap/extension-bold": "^2.9.1",
|
||||
@@ -53,9 +71,6 @@
|
||||
"@tiptap/extension-code-block-lowlight": "^2.9.1",
|
||||
"@tiptap/extension-document": "^2.9.1",
|
||||
"@tiptap/extension-dropcursor": "^2.9.1",
|
||||
"@tiptap/extension-hard-break": "^2.9.1",
|
||||
"@tiptap/extension-heading": "^2.9.1",
|
||||
"@tiptap/extension-highlight": "^2.9.1",
|
||||
"@tiptap/extension-horizontal-rule": "^2.9.1",
|
||||
"@tiptap/extension-italic": "^2.9.1",
|
||||
"@tiptap/extension-link": "^2.9.1",
|
||||
@@ -65,84 +80,38 @@
|
||||
"@tiptap/extension-paragraph": "^2.9.1",
|
||||
"@tiptap/extension-placeholder": "^2.9.1",
|
||||
"@tiptap/extension-strike": "^2.9.1",
|
||||
"@tiptap/extension-subscript": "^2.9.1",
|
||||
"@tiptap/extension-superscript": "^2.9.1",
|
||||
"@tiptap/extension-task-item": "^2.9.1",
|
||||
"@tiptap/extension-task-list": "^2.9.1",
|
||||
"@tiptap/extension-text": "^2.9.1",
|
||||
"@tiptap/extension-text-style": "^2.9.1",
|
||||
"@tiptap/extension-underline": "^2.9.1",
|
||||
"@tiptap/pm": "^2.9.1",
|
||||
"@tiptap/react": "^2.9.1",
|
||||
"@tiptap/suggestion": "^2.9.1",
|
||||
"axios": "^1.7.7",
|
||||
"better-sqlite3": "^11.5.0",
|
||||
"bufferutil": "^4.0.8",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"form-data": "^4.0.1",
|
||||
"cmdk": "^1.0.4",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"fractional-indexing-jittered": "^0.9.1",
|
||||
"is-hotkey": "^0.2.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
"kysely": "^0.27.4",
|
||||
"lodash": "^4.17.21",
|
||||
"lowlight": "^3.1.0",
|
||||
"lucide-react": "^0.453.0",
|
||||
"lucide-react": "^0.460.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"public-ip": "^7.0.1",
|
||||
"re-resizable": "^6.10.0",
|
||||
"re-resizable": "^6.10.1",
|
||||
"react": "^18.3.1",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.53.1",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-intersection-observer": "^9.13.1",
|
||||
"react-router-dom": "^6.27.0",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"react-virtualized-auto-sizer": "^1.0.24",
|
||||
"react-window": "^1.8.10",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-pattern": "^5.5.0",
|
||||
"unzipper": "^0.12.3",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^2.0.0",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@types/better-sqlite3": "^7.6.11",
|
||||
"@types/diff": "^5.2.3",
|
||||
"@types/is-hotkey": "^0.1.10",
|
||||
"@types/lodash": "^4.17.12",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/node": "^20.14.8",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/unzipper": "^0.10.10",
|
||||
"@types/ws": "^8.5.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"electron": "^31.0.2",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-vite": "^2.3.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"node-abi": "^3.71.0",
|
||||
"postcss": "^8.4.47",
|
||||
"prettier": "^3.3.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.8",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-static-copy": "^2.0.0"
|
||||
"utf-8-validate": "^5.0.10"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
import { app, shell, BrowserWindow, ipcMain, protocol } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils';
|
||||
import { app, BrowserWindow, ipcMain, protocol } from 'electron';
|
||||
import path from 'path';
|
||||
import { eventBus } from '@/shared/lib/event-bus';
|
||||
import { avatarService } from '@/main/services/avatar-service';
|
||||
import { fileService } from '@/main/services/file-service';
|
||||
import { assetService } from '@/main/services/asset-service';
|
||||
import { MutationMap } from '@/shared/mutations';
|
||||
import { MutationInput } from '@/shared/mutations';
|
||||
import { QueryMap } from '@/shared/queries';
|
||||
import { mutationService } from '@/main/services/mutation-service';
|
||||
import { queryService } from '@/main/services/query-service';
|
||||
import { QueryInput } from '@/shared/queries';
|
||||
import { CommandMap } from '@/shared/commands';
|
||||
import { CommandInput } from '@/shared/commands';
|
||||
import { commandService } from '@/main/services/command-service';
|
||||
import { databaseService } from '@/main/data/database-service';
|
||||
import { socketService } from '@/main/services/socket-service';
|
||||
import { syncService } from '@/main/services/sync-service';
|
||||
import { avatarService } from '@/main/services/avatar-service';
|
||||
import { fileService } from '@/main/services/file-service';
|
||||
import { MutationInput, MutationMap } from '@/shared/mutations';
|
||||
import { QueryInput, QueryMap } from '@/shared/queries';
|
||||
import { assetService } from '@/main/services/asset-service';
|
||||
import { radarService } from '@/main/services/radar-service';
|
||||
import { mutationService } from '@/main/services/mutation-service';
|
||||
import { queryService } from '@/main/services/query-service';
|
||||
import { CommandInput } from '@/shared/commands';
|
||||
import { commandService } from '@/main/services/command-service';
|
||||
import { CommandMap } from '@/shared/commands';
|
||||
|
||||
let subscriptionId: string | null = null;
|
||||
const icon = join(__dirname, '../assets/icon.png');
|
||||
|
||||
app.setName('Colanode');
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require('electron-squirrel-startup')) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
const createWindow = async (): Promise<void> => {
|
||||
const createWindow = async () => {
|
||||
await databaseService.init();
|
||||
assetService.checkAssets();
|
||||
socketService.init();
|
||||
@@ -32,31 +35,20 @@ const createWindow = async (): Promise<void> => {
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
fullscreen: true,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux' ? { icon } : {}),
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: false,
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
},
|
||||
});
|
||||
|
||||
mainWindow.on('ready-to-show', () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||
shell.openExternal(details.url);
|
||||
return { action: 'deny' };
|
||||
});
|
||||
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
const electronRendererUrl = process.env['ELECTRON_RENDERER_URL'];
|
||||
if (is.dev && electronRendererUrl) {
|
||||
mainWindow.loadURL(electronRendererUrl);
|
||||
// and load the index.html of the app.
|
||||
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
||||
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
|
||||
// Open the DevTools.
|
||||
mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
|
||||
mainWindow.loadFile(
|
||||
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)
|
||||
);
|
||||
}
|
||||
|
||||
if (subscriptionId === null) {
|
||||
@@ -95,45 +87,32 @@ const createWindow = async (): Promise<void> => {
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(() => {
|
||||
// Set app user model id for windows
|
||||
electronApp.setAppUserModelId('com.colanode.desktop');
|
||||
|
||||
// Default open or close DevTools by F12 in development
|
||||
// and ignore CommandOrControl + R in production.
|
||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
optimizer.watchWindowShortcuts(window);
|
||||
});
|
||||
|
||||
// IPC test
|
||||
ipcMain.on('ping', () => console.log('pong'));
|
||||
|
||||
createWindow();
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||
});
|
||||
});
|
||||
app.on('ready', createWindow);
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', () => {
|
||||
if (subscriptionId) {
|
||||
eventBus.unsubscribe(subscriptionId);
|
||||
subscriptionId = null;
|
||||
}
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
if (subscriptionId) {
|
||||
eventBus.unsubscribe(subscriptionId);
|
||||
subscriptionId = null;
|
||||
}
|
||||
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app"s specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
app.on('activate', () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and import them here.
|
||||
ipcMain.handle(
|
||||
'execute-mutation',
|
||||
async <T extends MutationInput>(
|
||||
@@ -6,6 +6,7 @@ import { appDatabaseMigrations } from '@/main/data/app/migrations';
|
||||
import { workspaceDatabaseMigrations } from '@/main/data/workspace/migrations';
|
||||
import { appDatabasePath, getWorkspaceDirectoryPath } from '@/main/utils';
|
||||
import SQLite from 'better-sqlite3';
|
||||
|
||||
class DatabaseService {
|
||||
private initPromise: Promise<void> | null = null;
|
||||
private readonly workspaceDatabases: Map<
|
||||
|
||||
@@ -45,17 +45,17 @@ class NodeService {
|
||||
await databaseService.getWorkspaceDatabase(userId);
|
||||
|
||||
await workspaceDatabase.transaction().execute(async (transaction) => {
|
||||
for (const input of inputs) {
|
||||
const model = registry.getModel(input.attributes.type);
|
||||
if (!model.schema.safeParse(input.attributes).success) {
|
||||
for (const inputItem of inputs) {
|
||||
const model = registry.getModel(inputItem.attributes.type);
|
||||
if (!model.schema.safeParse(inputItem.attributes).success) {
|
||||
throw new Error('Invalid attributes');
|
||||
}
|
||||
|
||||
let ancestors: Node[] = [];
|
||||
if (input.attributes.parentId) {
|
||||
if (inputItem.attributes.parentId) {
|
||||
const ancestorRows = await fetchNodeAncestors(
|
||||
transaction,
|
||||
input.attributes.parentId
|
||||
inputItem.attributes.parentId
|
||||
);
|
||||
ancestors = ancestorRows.map(mapNode);
|
||||
}
|
||||
@@ -68,19 +68,19 @@ class NodeService {
|
||||
ancestors
|
||||
);
|
||||
|
||||
if (!model.canCreate(context, input.attributes)) {
|
||||
if (!model.canCreate(context, inputItem.attributes)) {
|
||||
throw new Error('Insufficient permissions');
|
||||
}
|
||||
|
||||
const ydoc = new YDoc(input.id);
|
||||
ydoc.updateAttributes(input.attributes);
|
||||
const ydoc = new YDoc(inputItem.id);
|
||||
ydoc.updateAttributes(inputItem.attributes);
|
||||
|
||||
const createdAt = new Date().toISOString();
|
||||
const versionId = generateId(IdType.Version);
|
||||
|
||||
const changeData: LocalCreateNodeChangeData = {
|
||||
type: 'node_create',
|
||||
id: input.id,
|
||||
id: inputItem.id,
|
||||
state: ydoc.getEncodedState(),
|
||||
createdAt: createdAt,
|
||||
createdBy: context.userId,
|
||||
@@ -91,8 +91,8 @@ class NodeService {
|
||||
.insertInto('nodes')
|
||||
.returningAll()
|
||||
.values({
|
||||
id: input.id,
|
||||
attributes: JSON.stringify(input.attributes),
|
||||
id: inputItem.id,
|
||||
attributes: JSON.stringify(inputItem.attributes),
|
||||
state: ydoc.getState(),
|
||||
created_at: createdAt,
|
||||
created_by: context.userId,
|
||||
@@ -119,11 +119,11 @@ class NodeService {
|
||||
createdChangeIds.push(createdChange.id);
|
||||
}
|
||||
|
||||
if (input.upload) {
|
||||
if (inputItem.upload) {
|
||||
const createdUploadRow = await transaction
|
||||
.insertInto('uploads')
|
||||
.returningAll()
|
||||
.values(input.upload)
|
||||
.values(inputItem.upload)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (createdUploadRow) {
|
||||
@@ -137,11 +137,11 @@ class NodeService {
|
||||
}
|
||||
}
|
||||
|
||||
if (input.download) {
|
||||
if (inputItem.download) {
|
||||
const createdDownloadRow = await transaction
|
||||
.insertInto('downloads')
|
||||
.returningAll()
|
||||
.values(input.download)
|
||||
.values(inputItem.download)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (createdDownloadRow) {
|
||||
|
||||
@@ -44,7 +44,7 @@ class QueryService {
|
||||
}
|
||||
|
||||
private async checkForQueryChanges(event: Event): Promise<void> {
|
||||
if (event.type !== 'query_result_updated') {
|
||||
if (event.type === 'query_result_updated') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { workspaceService } from '@/main/services/workspace-service';
|
||||
import { deviceService } from '@/main/services/device-service';
|
||||
|
||||
// one minute
|
||||
const EVENT_LOOP_INTERVAL = 1000 * 5;
|
||||
const EVENT_LOOP_INTERVAL = 1000 * 60;
|
||||
|
||||
class SyncService {
|
||||
private initiated: boolean = false;
|
||||
|
||||
54
apps/desktop/src/preload.ts
Normal file
54
apps/desktop/src/preload.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
// See the Electron documentation for details on how to use preload scripts:
|
||||
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
|
||||
import { CommandMap } from '@/shared/commands';
|
||||
import { CommandInput } from '@/shared/commands';
|
||||
import { MutationMap } from '@/shared/mutations';
|
||||
import { MutationInput } from '@/shared/mutations';
|
||||
import { QueryInput, QueryMap } from '@/shared/queries';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { eventBus } from '@/shared/lib/event-bus';
|
||||
import { Event } from '@/shared/types/events';
|
||||
|
||||
contextBridge.exposeInMainWorld('colanode', {
|
||||
init: () => ipcRenderer.invoke('init'),
|
||||
logout: (accountId: string) => ipcRenderer.invoke('logout', accountId),
|
||||
|
||||
executeMutation: <T extends MutationInput>(
|
||||
input: T
|
||||
): Promise<MutationMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-mutation', input);
|
||||
},
|
||||
|
||||
executeQuery: <T extends QueryInput>(
|
||||
input: T
|
||||
): Promise<QueryMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-query', input);
|
||||
},
|
||||
|
||||
executeQueryAndSubscribe: <T extends QueryInput>(
|
||||
id: string,
|
||||
input: T
|
||||
): Promise<QueryMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-query-and-subscribe', id, input);
|
||||
},
|
||||
|
||||
unsubscribeQuery: (id: string): Promise<void> => {
|
||||
return ipcRenderer.invoke('unsubscribe-query', id);
|
||||
},
|
||||
|
||||
executeCommand: <T extends CommandInput>(
|
||||
input: T
|
||||
): Promise<CommandMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-command', input);
|
||||
},
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('eventBus', {
|
||||
subscribe: (callback: (event: Event) => void) => eventBus.subscribe(callback),
|
||||
unsubscribe: (id: string) => eventBus.unsubscribe(id),
|
||||
publish: (event: Event) => eventBus.publish(event),
|
||||
});
|
||||
|
||||
ipcRenderer.on('event', (_, event) => {
|
||||
eventBus.publish(event);
|
||||
});
|
||||
@@ -1,68 +0,0 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { electronAPI } from '@electron-toolkit/preload';
|
||||
import { eventBus } from '@/shared/lib/event-bus';
|
||||
import { MutationInput, MutationMap } from '@/shared/mutations';
|
||||
import { QueryInput, QueryMap } from '@/shared/queries';
|
||||
import { Event } from '@/shared/types/events';
|
||||
import { CommandMap } from '@/shared/commands';
|
||||
import { CommandInput } from '@/shared/commands';
|
||||
|
||||
// Use `contextBridge` APIs to expose Electron APIs to
|
||||
// renderer only if context isolation is enabled, otherwise
|
||||
// just add to the DOM global.
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI);
|
||||
contextBridge.exposeInMainWorld('colanode', {
|
||||
init: () => ipcRenderer.invoke('init'),
|
||||
logout: (accountId: string) => ipcRenderer.invoke('logout', accountId),
|
||||
|
||||
executeMutation: <T extends MutationInput>(
|
||||
input: T
|
||||
): Promise<MutationMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-mutation', input);
|
||||
},
|
||||
|
||||
executeQuery: <T extends QueryInput>(
|
||||
input: T
|
||||
): Promise<QueryMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-query', input);
|
||||
},
|
||||
|
||||
executeQueryAndSubscribe: <T extends QueryInput>(
|
||||
id: string,
|
||||
input: T
|
||||
): Promise<QueryMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-query-and-subscribe', id, input);
|
||||
},
|
||||
|
||||
unsubscribeQuery: (id: string): Promise<void> => {
|
||||
return ipcRenderer.invoke('unsubscribe-query', id);
|
||||
},
|
||||
|
||||
executeCommand: <T extends CommandInput>(
|
||||
input: T
|
||||
): Promise<CommandMap[T['type']]['output']> => {
|
||||
return ipcRenderer.invoke('execute-command', input);
|
||||
},
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('eventBus', {
|
||||
subscribe: (callback: (event: Event) => void) =>
|
||||
eventBus.subscribe(callback),
|
||||
unsubscribe: (id: string) => eventBus.unsubscribe(id),
|
||||
publish: (event: Event) => eventBus.publish(event),
|
||||
});
|
||||
|
||||
ipcRenderer.on('event', (_, event) => {
|
||||
eventBus.publish(event);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore (define in dts)
|
||||
window.electron = electronAPI;
|
||||
// @ts-ignore (define in dts)
|
||||
window.api = api;
|
||||
}
|
||||
1
apps/desktop/src/renderer.ts
Normal file
1
apps/desktop/src/renderer.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './renderer/root';
|
||||
1
apps/desktop/src/renderer/env.d.ts
vendored
1
apps/desktop/src/renderer/env.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,28 +1,23 @@
|
||||
import { ElectronAPI } from '@electron-toolkit/preload';
|
||||
import { EventBus } from '@/lib/event-bus';
|
||||
import { MutationMap, MutationInput } from '@/shared/mutations';
|
||||
import { QueryMap, QueryInput } from '@/shared/queries';
|
||||
import { CommandMap, CommandInput } from '@/shared/commands';
|
||||
import { CommandMap } from '@/shared/commands';
|
||||
import { QueryInput } from '@/shared/queries';
|
||||
import { MutationInput, MutationMap } from '@/shared/mutations';
|
||||
import { QueryMap } from '@/shared/queries';
|
||||
import { CommandInput } from '@/shared/commands';
|
||||
import { EventBus } from '@/shared/lib/event-bus';
|
||||
|
||||
interface ColanodeAPI {
|
||||
export interface ColanodeApi {
|
||||
init: () => Promise<void>;
|
||||
logout: (accountId: string) => Promise<void>;
|
||||
|
||||
executeMutation: <T extends MutationInput>(
|
||||
input: T
|
||||
) => Promise<MutationMap[T['type']]['output']>;
|
||||
|
||||
executeQuery: <T extends QueryInput>(
|
||||
input: T
|
||||
) => Promise<QueryMap[T['type']]['output']>;
|
||||
|
||||
executeQueryAndSubscribe: <T extends QueryInput>(
|
||||
id: string,
|
||||
input: T
|
||||
) => Promise<QueryMap[T['type']]['output']>;
|
||||
|
||||
unsubscribeQuery: (id: string) => Promise<void>;
|
||||
|
||||
executeCommand: <T extends CommandInput>(
|
||||
input: T
|
||||
) => Promise<CommandMap[T['type']]['output']>;
|
||||
@@ -30,8 +25,7 @@ interface ColanodeAPI {
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI;
|
||||
colanode: ColanodeAPI;
|
||||
colanode: ColanodeApi;
|
||||
eventBus: EventBus;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,23 @@
|
||||
{
|
||||
"files": [],
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "commonjs",
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../../packages/core/tsconfig.json" },
|
||||
{ "path": "../../packages/crdt/tsconfig.json" },
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.web.json" }
|
||||
{ "path": "../../packages/crdt/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||
"include": [
|
||||
"electron.vite.config.*",
|
||||
"src/main/**/*",
|
||||
"src/preload/**/*",
|
||||
"src/shared/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["electron-vite/node"],
|
||||
"paths": {
|
||||
"@/shared/*": ["./src/shared/*"],
|
||||
"@/main/*": ["./src/main/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
|
||||
"include": [
|
||||
"src/renderer/env.d.ts",
|
||||
"src/renderer/**/*",
|
||||
"src/preload/*.d.ts",
|
||||
"src/shared/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/renderer/*": ["./src/renderer/*"],
|
||||
"@/shared/*": ["./src/shared/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
35
apps/desktop/vite.main.config.ts
Normal file
35
apps/desktop/vite.main.config.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// https://vitejs.dev/config
|
||||
export default defineConfig(async () => {
|
||||
const { viteStaticCopy } = await import('vite-plugin-static-copy');
|
||||
return {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': '/src',
|
||||
},
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
external: [
|
||||
'better-sqlite3',
|
||||
'kysely',
|
||||
'unzipper',
|
||||
'mime-types',
|
||||
'node-gyp-build',
|
||||
'asynckit',
|
||||
],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: 'assets/**/*',
|
||||
dest: 'assets',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
10
apps/desktop/vite.preload.config.ts
Normal file
10
apps/desktop/vite.preload.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// https://vitejs.dev/config
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': '/src',
|
||||
},
|
||||
},
|
||||
});
|
||||
10
apps/desktop/vite.renderer.config.ts
Normal file
10
apps/desktop/vite.renderer.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// https://vitejs.dev/config
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': '/src',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -10,7 +10,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"compile": "tsc --noEmit -p tsconfig.json",
|
||||
"build": "pnpm run compile && tsup-node",
|
||||
"build": "npm run compile && tsup-node",
|
||||
"clean": "del-cli dist isolate tsconfig.tsbuildinfo",
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"dev": "nodemon --env-file .env dist/index.js"
|
||||
@@ -32,8 +32,8 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.675.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.675.0",
|
||||
"@colanode/core": "workspace:*",
|
||||
"@colanode/crdt": "workspace:*",
|
||||
"@colanode/core": "^1.0.0",
|
||||
"@colanode/crdt": "^1.0.0",
|
||||
"axios": "^1.7.7",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bullmq": "^5.21.1",
|
||||
|
||||
17797
package-lock.json
generated
Normal file
17797
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,10 @@
|
||||
"description": "Colanode monorepo",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.0.4+sha256.caa915eaae9d9aefccf50ee8aeda25a2f8684d8f9d5c6e367eaf176d97c1f89e",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"apps/*"
|
||||
],
|
||||
"author": "Hakan Shehu",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@colanode/core": "workspace:*",
|
||||
"@colanode/core": "^1.0.0",
|
||||
"diff": "^7.0.0",
|
||||
"js-base64": "^3.7.7",
|
||||
"yjs": "^13.6.20",
|
||||
|
||||
12609
pnpm-lock.yaml
generated
12609
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
Reference in New Issue
Block a user