mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 01:09:54 +08:00
init: the first public commit for AFFiNE
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
"projectName": "Ligo-Virgo",
|
||||||
|
"projectOwner": "toeverything",
|
||||||
|
"repoType": "github",
|
||||||
|
"repoHost": "https://github.com",
|
||||||
|
"files": [
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"imageSize": 100,
|
||||||
|
"commit": false,
|
||||||
|
"commitConvention": "angular",
|
||||||
|
"contributorsPerLine": 7,
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"login": "darkskygit",
|
||||||
|
"name": "DarkSky",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/25152247?v=4",
|
||||||
|
"profile": "https://darksky.eu.org/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "tzhangchi",
|
||||||
|
"name": "Chi Zhang",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/5910926?v=4",
|
||||||
|
"profile": "https://zhangchi.blog.csdn.net/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "alt1o",
|
||||||
|
"name": "alt1o",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/21084335?v=4",
|
||||||
|
"profile": "https://github.com/alt1o",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "DiamondThree",
|
||||||
|
"name": "Diamond",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/24630517?v=4",
|
||||||
|
"profile": "https://github.com/DiamondThree",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "lawvs",
|
||||||
|
"name": "Whitewater",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/18554747?v=4",
|
||||||
|
"profile": "https://lawvs.github.io/profile/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "zuoxiaodong0815",
|
||||||
|
"name": "zuoxiaodong0815",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/53252747?v=4",
|
||||||
|
"profile": "https://github.com/zuoxiaodong0815",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "SaikaSakura",
|
||||||
|
"name": "SaikaSakura",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/11530942?v=4",
|
||||||
|
"profile": "https://github.com/SaikaSakura",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "QiShaoXuan",
|
||||||
|
"name": "Qi",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/22772830?v=4",
|
||||||
|
"profile": "https://github.com/QiShaoXuan",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "tuluffy",
|
||||||
|
"name": "tuluffy",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/26808339?v=4",
|
||||||
|
"profile": "https://tuluffy.github.io/angular.github.io/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Austaras",
|
||||||
|
"name": "Austaras",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/15013925?v=4",
|
||||||
|
"profile": "https://shockwave.me/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "uptonking",
|
||||||
|
"name": "Jin Yao",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/11391549?v=4",
|
||||||
|
"profile": "https://github.com/uptonking?tab=repositories&type=source",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
module.exports = {
|
||||||
|
types: [
|
||||||
|
{ value: 'feat', name: 'feat 🍄: add new features' },
|
||||||
|
{ value: 'fix', name: 'fix 🐛: fix bug' },
|
||||||
|
{ value: 'docs', name: 'docs 📄: modify documentation, comments' },
|
||||||
|
{
|
||||||
|
value: 'refactor',
|
||||||
|
name: 'refactor 🎸: code refactoring, pay attention to distinguish it from features and fixes',
|
||||||
|
},
|
||||||
|
{ value: 'perf', name: 'perf ⚡: improve performance' },
|
||||||
|
{ value: 'test', name: 'test 👀: add a test' },
|
||||||
|
{
|
||||||
|
value: 'tool',
|
||||||
|
name: 'tool 🚗: Development tool changes (build, scaffolding tools, etc.)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'style',
|
||||||
|
name: 'style ✂: Modifications to code formatting do not affect logic',
|
||||||
|
},
|
||||||
|
{ value: 'revert', name: 'revert 🌝: version rollback' },
|
||||||
|
{
|
||||||
|
value: 'editor',
|
||||||
|
name: 'editor 🔧: editor configuration modification',
|
||||||
|
},
|
||||||
|
{ value: 'update', name: 'update ⬆: third-party library upgrade' },
|
||||||
|
],
|
||||||
|
scopes: [
|
||||||
|
{ name: 'selection' },
|
||||||
|
{ name: 'whiteboard' },
|
||||||
|
{ name: 'point' },
|
||||||
|
{ name: 'group' },
|
||||||
|
{ name: 'page' },
|
||||||
|
{ name: 'component' },
|
||||||
|
{ name: 'config' },
|
||||||
|
{ name: 'others' },
|
||||||
|
],
|
||||||
|
|
||||||
|
// it needs to match the value for field type. Eg.: 'fix'
|
||||||
|
/*
|
||||||
|
scopeOverrides: {
|
||||||
|
fix: [
|
||||||
|
{name: 'merge'},
|
||||||
|
{name: 'style'}
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
// override the messages, de faults are as follows
|
||||||
|
messages: {
|
||||||
|
type: 'Choose a type of your submission:',
|
||||||
|
scope: 'Choose a scope (optional):',
|
||||||
|
// used if allowCustomScopes is true
|
||||||
|
customScope: 'Denote the SCOPE of this change:',
|
||||||
|
subject: 'Brief description:\n',
|
||||||
|
body: 'Detailed description, use "|" newline (optional):\n',
|
||||||
|
breaking: 'Incompatibility specification (optional):\n',
|
||||||
|
footer: 'Associate closed issues, for example: #31, #34 (optional):\n',
|
||||||
|
confirmCommit: 'Are you sure to commit?',
|
||||||
|
},
|
||||||
|
|
||||||
|
allowCustomScopes: true,
|
||||||
|
allowBreakingChanges: ['Added', 'Repair'],
|
||||||
|
|
||||||
|
// limit subject length
|
||||||
|
subjectLimit: 100,
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# use for download icon from figma
|
||||||
|
FIGMA_TOKEN
|
||||||
|
NODE_ENV
|
||||||
|
AFFINE_FEATURE_FLAG_TOKEN
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
**/webpack.config.js
|
||||||
|
**/jest.config.js
|
||||||
|
**/node_modules/**
|
||||||
|
.github/**
|
||||||
+287
@@ -0,0 +1,287 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"ignorePatterns": ["**/*"],
|
||||||
|
"plugins": ["@nrwl/nx", "react", "filename-rules", "import", "prettier"],
|
||||||
|
"parserOptions": {
|
||||||
|
"project": ["./tsconfig.base.json"]
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "warn",
|
||||||
|
"@nrwl/nx/enforce-module-boundaries": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"enforceBuildableLibDependency": true,
|
||||||
|
"allow": [],
|
||||||
|
"depConstraints": [
|
||||||
|
{
|
||||||
|
"sourceTag": "library:utils",
|
||||||
|
"onlyDependOnLibsWithTags": ["library:utils"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "datasource:jwt",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"datasource:remote-kv",
|
||||||
|
"datasource:jwt-rpc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "datasource:db-services",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"datasource:jwt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "datasource:hooks",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"datasource:jwt",
|
||||||
|
"datasource:db-services"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "datasource:http",
|
||||||
|
"onlyDependOnLibsWithTags": ["library:utils"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "datasource:state",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"library:feature-flags",
|
||||||
|
"datasource:http",
|
||||||
|
"datasource:jwt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "components:common",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"components:icons",
|
||||||
|
"components:ui"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "components:editor-core",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"library:feature-flags",
|
||||||
|
"datasource:db-services",
|
||||||
|
"datasource:state",
|
||||||
|
"datasource:commands",
|
||||||
|
"datasource:jwt",
|
||||||
|
"components:ui",
|
||||||
|
"components:common",
|
||||||
|
"components:icons"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "components:editor-blocks",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"library:feature-flags",
|
||||||
|
"components:common",
|
||||||
|
"components:editor-core",
|
||||||
|
"framework:editor",
|
||||||
|
"datasource:db-services",
|
||||||
|
"components:ui",
|
||||||
|
"components:icons"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "components:editor-plugins",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"library:utils",
|
||||||
|
"components:common",
|
||||||
|
"components:editor-core",
|
||||||
|
"framework:editor",
|
||||||
|
"components:editor-blocks",
|
||||||
|
"datasource:db-services",
|
||||||
|
"components:ui",
|
||||||
|
"components:icons",
|
||||||
|
"library:feature-flags"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "components:ui",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"components:icons",
|
||||||
|
"library:utils"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "framework:editor",
|
||||||
|
"onlyDependOnLibsWithTags": [
|
||||||
|
"components:editor-core"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceTag": "*",
|
||||||
|
"onlyDependOnLibsWithTags": ["*"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"allowCircularSelfDependency": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filename-rules/match": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
".tsx": "PascalCase",
|
||||||
|
".ts": "kebab-case",
|
||||||
|
".json": "kebab-case",
|
||||||
|
"": "kebab-case"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"group": ["lodash"],
|
||||||
|
"message": "Forbid direct import of lodash, use @toeverything/utils"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": ["lodash-es"],
|
||||||
|
"message": "Forbid direct import of lodash-es, use @toeverything/utils"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": ["@mui/material", "@mui/material/*"],
|
||||||
|
"message": "Forbid direct import of @mui/material, use @toeverything/components/ui"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/naming-convention": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"selector": [
|
||||||
|
"property",
|
||||||
|
"parameterProperty",
|
||||||
|
"accessor",
|
||||||
|
"enumMember"
|
||||||
|
],
|
||||||
|
"format": ["strictCamelCase"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": ["property", "accessor"],
|
||||||
|
"modifiers": ["private"],
|
||||||
|
"format": ["strictCamelCase"],
|
||||||
|
"leadingUnderscore": "require"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": ["method"],
|
||||||
|
"modifiers": ["public"],
|
||||||
|
"format": ["strictCamelCase"]
|
||||||
|
},
|
||||||
|
// Private methods
|
||||||
|
{
|
||||||
|
"selector": ["method"],
|
||||||
|
"modifiers": ["private"],
|
||||||
|
"format": ["strictCamelCase"],
|
||||||
|
"leadingUnderscore": "require"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": ["method"],
|
||||||
|
"modifiers": ["protected"],
|
||||||
|
"format": ["strictCamelCase"],
|
||||||
|
"leadingUnderscore": "require"
|
||||||
|
},
|
||||||
|
// Top Level Methods
|
||||||
|
// const func: Function
|
||||||
|
// allow PascalCase for react components
|
||||||
|
{
|
||||||
|
"selector": ["variable"],
|
||||||
|
"modifiers": ["global"],
|
||||||
|
"types": ["function"],
|
||||||
|
"format": ["strictCamelCase", "StrictPascalCase"],
|
||||||
|
"leadingUnderscore": "allow"
|
||||||
|
},
|
||||||
|
// function something() { }
|
||||||
|
{
|
||||||
|
"selector": ["function"],
|
||||||
|
"modifiers": ["global"],
|
||||||
|
"format": ["strictCamelCase"],
|
||||||
|
"leadingUnderscore": "require"
|
||||||
|
},
|
||||||
|
// export const func: Function
|
||||||
|
{
|
||||||
|
"selector": ["variable"],
|
||||||
|
"modifiers": ["exported"],
|
||||||
|
"types": ["function"],
|
||||||
|
"format": ["strictCamelCase", "StrictPascalCase"],
|
||||||
|
"leadingUnderscore": "forbid"
|
||||||
|
},
|
||||||
|
// export function something() { }
|
||||||
|
{
|
||||||
|
"selector": ["function"],
|
||||||
|
"modifiers": ["exported"],
|
||||||
|
"format": ["strictCamelCase", "StrictPascalCase"],
|
||||||
|
"leadingUnderscore": "forbid"
|
||||||
|
},
|
||||||
|
// Top Level Variables
|
||||||
|
{
|
||||||
|
"selector": ["variable"],
|
||||||
|
"modifiers": ["global", "const"],
|
||||||
|
"types": ["boolean", "string", "number"],
|
||||||
|
"format": ["UPPER_CASE"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": ["variable"],
|
||||||
|
"modifiers": ["global"],
|
||||||
|
"format": ["strictCamelCase"],
|
||||||
|
"leadingUnderscore": "require"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": ["variable"],
|
||||||
|
"modifiers": ["exported"],
|
||||||
|
"format": ["strictCamelCase"]
|
||||||
|
},
|
||||||
|
// types, enums
|
||||||
|
{
|
||||||
|
"selector": "typeLike",
|
||||||
|
"format": ["PascalCase"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selector": "variableLike",
|
||||||
|
"format": ["strictCamelCase"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"react/self-closing-comp": "warn",
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"selector": ":matches(PropertyDefinition)[accessibility!='private'][accessibility!='protected'][key.name!='constructor']",
|
||||||
|
"message": "Use private instead, please implement getter or setXxx for external read and write requirements"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"extends": ["plugin:@nrwl/nx/typescript"],
|
||||||
|
"rules": {
|
||||||
|
"prefer-const": "warn",
|
||||||
|
"no-console": ["warn", { "allow": ["warn", "error"] }],
|
||||||
|
"@typescript-eslint/ban-ts-comment": "warn",
|
||||||
|
"@typescript-eslint/no-empty-interface": "warn",
|
||||||
|
"@typescript-eslint/no-empty-function": "warn",
|
||||||
|
// https://github.com/nrwl/nx/issues/10445
|
||||||
|
"react/jsx-key": "error",
|
||||||
|
"import/no-default-export": "warn",
|
||||||
|
"import/no-duplicates": "warn",
|
||||||
|
"max-lines": [
|
||||||
|
"warn",
|
||||||
|
{ "max": 300, "skipComments": true, "skipBlankLines": true }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"extends": ["plugin:@nrwl/nx/javascript"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# About code owners
|
||||||
|
|
||||||
|
# You can use a CODEOWNERS file to define individuals or teams that are responsible for code in a repository.
|
||||||
|
|
||||||
|
# See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||||
|
|
||||||
|
|
||||||
|
# =================
|
||||||
|
# configs
|
||||||
|
# =================
|
||||||
|
|
||||||
|
.* @darkskygit
|
||||||
|
babel.config.json @darkskygit
|
||||||
|
nx.json @darkskygit
|
||||||
|
tsconfig.base.json @darkskygit
|
||||||
|
**/project.json @darkskygit
|
||||||
|
**/tsconfig.json @darkskygit
|
||||||
|
**/tsconfig.*.json @darkskygit
|
||||||
|
**/.babelrc @darkskygit
|
||||||
|
**/babel.config.js @darkskygit
|
||||||
|
**/.* @darkskygit
|
||||||
|
|
||||||
|
# =================
|
||||||
|
# components
|
||||||
|
# =================
|
||||||
|
|
||||||
|
# editor-core
|
||||||
|
libs/components/editor-core @lawvs
|
||||||
|
|
||||||
|
# editor-blocks
|
||||||
|
|
||||||
|
# group block
|
||||||
|
libs/components/editor-blocks/src/blocks/group @lawvs
|
||||||
|
# todo block
|
||||||
|
libs/components/editor-blocks/src/blocks/todo @lawvs
|
||||||
|
|
||||||
|
libs/framework/virgo @SaikaSakura
|
||||||
|
|
||||||
|
# =================
|
||||||
|
# datasource
|
||||||
|
# =================
|
||||||
|
|
||||||
|
# feature flags
|
||||||
|
libs/datasource/feature-flags @lawvs
|
||||||
|
|
||||||
|
# jwt
|
||||||
|
libs/datasource/jwt @darkskygit
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
# Cancels all previous workflow runs for pull requests that have not completed.
|
||||||
|
# See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
||||||
|
concurrency:
|
||||||
|
# The concurrency group contains the workflow name and the branch name for
|
||||||
|
# pull requests or the commit hash for any other events.
|
||||||
|
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [16]
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
# TODO Remove the next line after cleaning all errors
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Use pnpm
|
||||||
|
uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 7
|
||||||
|
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
# https://github.com/actions/setup-node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install node modules
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
if: always()
|
||||||
|
run: pnpm run lint:with-cache
|
||||||
|
|
||||||
|
# - name: Check
|
||||||
|
# if: always()
|
||||||
|
# run: pnpm run check
|
||||||
|
|
||||||
|
# - name: Format Check
|
||||||
|
# if: always()
|
||||||
|
# run: pnpm run format:ci
|
||||||
|
|
||||||
|
# - name: Build
|
||||||
|
# run: pnpm run build
|
||||||
|
|
||||||
|
# - name: Test
|
||||||
|
# run: pnpm run test
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
name: Build Lisa
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
# Cancels all previous workflow runs for pull requests that have not completed.
|
||||||
|
# See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
||||||
|
concurrency:
|
||||||
|
# The concurrency group contains the workflow name and the branch name for
|
||||||
|
# pull requests or the commit hash for any other events.
|
||||||
|
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
NAMESPACE: toeverything
|
||||||
|
LISA_IMAGE_NAME: lisa
|
||||||
|
IMAGE_TAG: canary-${{ github.sha }}
|
||||||
|
IMAGE_TAG_LATEST: nightly-latest
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ligo-virgo:
|
||||||
|
runs-on: self-hosted
|
||||||
|
environment: development
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels) for Docker (lisa)
|
||||||
|
id: meta_lisa
|
||||||
|
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.LISA_IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE_TAG }}
|
||||||
|
${{ env.IMAGE_TAG_LATEST }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image (lisa)
|
||||||
|
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: ${{ github.ref == 'refs/heads/master' && true || false }}
|
||||||
|
tags: ${{ steps.meta_lisa.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta_lisa.outputs.labels }}
|
||||||
|
target: lisa
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# compiled output
|
||||||
|
*dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
testem.log
|
||||||
|
.pnpm-debug.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System Files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# env
|
||||||
|
*.env.local
|
||||||
|
*.local.env
|
||||||
|
.history
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
# npx lint-staged
|
||||||
|
|
||||||
|
pnpm run lint:with-cache
|
||||||
|
pnpm run check
|
||||||
|
pnpm run format:ci
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
# Show just the current branch in Git
|
||||||
|
# See https://stackoverflow.com/questions/1417957/show-just-the-current-branch-in-git/1418022#1418022
|
||||||
|
current_branch=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
|
||||||
|
default_branch="master"
|
||||||
|
|
||||||
|
if test $current_branch != $default_branch; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm run type:check
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
registry=https://registry.npmjs.org
|
||||||
|
engine-strict=true
|
||||||
|
auto-install-peers=true
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Add files here to ignore them from prettier formatting
|
||||||
|
|
||||||
|
/dist
|
||||||
|
/coverage
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
# Editoe assets
|
||||||
|
apps/editoe/src/app/assets/svg
|
||||||
|
|
||||||
|
# Automatically generated from Figma
|
||||||
|
libs/components/icons/src/auto-icons
|
||||||
|
libs/components/common/src/lib/icon
|
||||||
|
|
||||||
|
# DevOps
|
||||||
|
.github/**
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 4,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"mikestead.dotenv",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"visualstudioexptteam.vscodeintellicode",
|
||||||
|
"nrwl.angular-console",
|
||||||
|
"firsttris.vscode-jest-runner",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"streetsidesoftware.code-spell-checker"
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+41
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnSaveMode": "file",
|
||||||
|
"prettier.prettierPath": "./node_modules/prettier",
|
||||||
|
"cSpell.words": [
|
||||||
|
"Backlinks",
|
||||||
|
"blockdb",
|
||||||
|
"booktitle",
|
||||||
|
"Cascader",
|
||||||
|
"clsx",
|
||||||
|
"cssmodule",
|
||||||
|
"datasource",
|
||||||
|
"fflate",
|
||||||
|
"groq",
|
||||||
|
"howpublished",
|
||||||
|
"immer",
|
||||||
|
"inbook",
|
||||||
|
"incollection",
|
||||||
|
"inproceedings",
|
||||||
|
"Kanban",
|
||||||
|
"keyval",
|
||||||
|
"ligo",
|
||||||
|
"lozad",
|
||||||
|
"mastersthesis",
|
||||||
|
"nrwl",
|
||||||
|
"phdthesis",
|
||||||
|
"pnpm",
|
||||||
|
"reindex",
|
||||||
|
"ROOTNODE",
|
||||||
|
"techreport",
|
||||||
|
"tldr",
|
||||||
|
"tldraw",
|
||||||
|
"tldtaw",
|
||||||
|
"toeverything",
|
||||||
|
"Unstyled",
|
||||||
|
"unversioned",
|
||||||
|
"uuidv",
|
||||||
|
"webm"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
:80 {
|
||||||
|
reverse_proxy /api/* keck:3001
|
||||||
|
@websockets {
|
||||||
|
path /collaboration/*
|
||||||
|
}
|
||||||
|
reverse_proxy @websockets keck:3000
|
||||||
|
reverse_proxy /* lisa:3001 {
|
||||||
|
header_up Host lisa:3001
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http://lisa:3001 {
|
||||||
|
root /* ./dist
|
||||||
|
|
||||||
|
file_server {
|
||||||
|
precompressed br
|
||||||
|
}
|
||||||
|
|
||||||
|
encode {
|
||||||
|
zstd
|
||||||
|
gzip 9
|
||||||
|
}
|
||||||
|
|
||||||
|
@notStatic {
|
||||||
|
not path /*.css
|
||||||
|
not path /*.js
|
||||||
|
not path /*.png
|
||||||
|
not path /*.jpg
|
||||||
|
not path /*.svg
|
||||||
|
not path /*.ttf
|
||||||
|
not path /*.eot
|
||||||
|
not path /*.woff
|
||||||
|
not path /*.woff2
|
||||||
|
}
|
||||||
|
|
||||||
|
handle @notStatic {
|
||||||
|
try_files {path} /index.html
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
FROM node:16-alpine as builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
RUN apk add g++ make python3 git
|
||||||
|
RUN npm i -g pnpm@7 && pnpm i --frozen-lockfile --store=node_modules/.pnpm-store && pnpm run build
|
||||||
|
|
||||||
|
FROM node:16-alpine as relocate
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /app/dist/apps/ligo-virgo ./dist
|
||||||
|
COPY --from=builder /app/Caddyfile ./
|
||||||
|
RUN rm ./dist/*.txt
|
||||||
|
|
||||||
|
# =============
|
||||||
|
# lisa image
|
||||||
|
# =============
|
||||||
|
FROM caddy:2.4.6-alpine as lisa
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=relocate /app .
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["caddy", "run"]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) Toeverything Technology (Hangzhou) Co., Ltd. and its affiliates.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# AFFiNE
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
|
|
||||||
|
[](#contributors-)
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
|
Workspace for AFFiNE
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Clone the repo
|
||||||
|
git clone git@github.com:toeverything/AFFiNE.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Once cloned, switch to the master branch and navigate to the folder by typing `cd AFFiNE` and then running the following commands:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Install all project dependencies
|
||||||
|
npm i -g pnpm
|
||||||
|
pnpm i
|
||||||
|
|
||||||
|
# Start the project
|
||||||
|
pnpm start
|
||||||
|
open http://localhost:4200/
|
||||||
|
```
|
||||||
|
|
||||||
|
This project uses pnpm for package management and is built based on nx. It is recommended to install the [nx console](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console) plugin to create dependencies
|
||||||
|
|
||||||
|
**If it is development, you can add environment variables in the project directory .env.local file**
|
||||||
|
|
||||||
|
```
|
||||||
|
NODE_ENV=development
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
1. Create react dependency library: `pnpm run add:library`
|
||||||
|
2. Create react components: `pnpm run add:components`
|
||||||
|
3. Create a data source: `pnpm run add:datasource`
|
||||||
|
4. Unit testing: `pnpm test`
|
||||||
|
5. Compile specific components
|
||||||
|
- `pnpm build/test/lint `project name
|
||||||
|
- Project name reference workspace.json
|
||||||
|
6. Create react/node program: use nx console
|
||||||
|
7. If you need to use the git cz function, please install it globally first commitizen `npm install -g commitizen conventional-changelog conventional-changelog-cli`
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Have a look at [the examples to see AFFiNE in action](https://app.affine.pro/).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
- Generic functional components (such as ui components) are placed in `libs/components/common`
|
||||||
|
- components within common are not allowed to reference _components_ except utils and dependencies
|
||||||
|
- Common components can reference each other
|
||||||
|
- Business components are placed in `libs/components`
|
||||||
|
- The data source component is placed in `libs/datasource` - api request code, schema, etc. belong to the data source
|
||||||
|
Please see [CONTRIBUTING](/docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [how-to-write-css-in-affine.md](/docs/how-to-write-css-in-affine.md)
|
||||||
|
- [how-to-add-ui-component-in-affine.md](/docs/how-to-add-ui-component-in-affine.md)
|
||||||
|
- [how-to-customize-rollup-config.md](docs/how-to-customize-rollup-config.md)
|
||||||
|
- [how-to-auto-download-figma-assets-in-affine.md](docs/how-to-auto-download-figma-assets-in-affine.md)
|
||||||
|
- [affine-icons-user-guide.md](docs/affine-icons-user-guide.md)
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
|
||||||
|
|
||||||
|
[Discuss AFFiNE on GitHub](https://github.com/toeverything/AFFiNE/discussions)
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://darksky.eu.org/"><img src="https://avatars.githubusercontent.com/u/25152247?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DarkSky</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=darkskygit" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=darkskygit" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/tzhangchi"><img src="https://avatars.githubusercontent.com/u/5910926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chi Zhang</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=tzhangchi" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=tzhangchi" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/alt1o"><img src="https://avatars.githubusercontent.com/u/21084335?v=4?s=100" width="100px;" alt=""/><br /><sub><b>alt1o</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=alt1o" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=alt1o" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/DiamondThree"><img src="https://avatars.githubusercontent.com/u/24630517?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Diamond</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=DiamondThree" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=DiamondThree" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://lawvs.github.io/profile/"><img src="https://avatars.githubusercontent.com/u/18554747?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Whitewater</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=lawvs" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=lawvs" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/zuoxiaodong0815"><img src="https://avatars.githubusercontent.com/u/53252747?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zuoxiaodong0815</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=zuoxiaodong0815" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=zuoxiaodong0815" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/SaikaSakura"><img src="https://avatars.githubusercontent.com/u/11530942?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SaikaSakura</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=SaikaSakura" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=SaikaSakura" title="Documentation">📖</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/QiShaoXuan"><img src="https://avatars.githubusercontent.com/u/22772830?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Qi</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=QiShaoXuan" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=QiShaoXuan" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://tuluffy.github.io/angular.github.io/"><img src="https://avatars.githubusercontent.com/u/26808339?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tuluffy</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=tuluffy" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=tuluffy" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://shockwave.me/"><img src="https://avatars.githubusercontent.com/u/15013925?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austaras</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=Austaras" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=Austaras" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/uptonking?tab=repositories&type=source"><img src="https://avatars.githubusercontent.com/u/11391549?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jin Yao</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=uptonking" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=uptonking" title="Documentation">📖</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
AFFiNE is distributed under the terms of MIT license.
|
||||||
|
|
||||||
|
See LICENSE for details.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@nrwl/react/babel",
|
||||||
|
{
|
||||||
|
"runtime": "automatic"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# This file is used by:
|
||||||
|
# 1. autoprefixer to adjust CSS to support the below specified browsers
|
||||||
|
# 2. babel preset-env to adjust included polyfills
|
||||||
|
#
|
||||||
|
# For additional information regarding the format and rule options, please see:
|
||||||
|
# https://github.com/browserslist/browserslist#queries
|
||||||
|
#
|
||||||
|
# If you need to support different browsers in production, you may tweak the list below.
|
||||||
|
|
||||||
|
last 1 Chrome version
|
||||||
|
last 1 Firefox version
|
||||||
|
last 2 Edge major versions
|
||||||
|
last 2 Safari major version
|
||||||
|
last 2 iOS major versions
|
||||||
|
Firefox ESR
|
||||||
|
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
module.exports = {
|
||||||
|
displayName: 'ligo-virgo',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
transform: {
|
||||||
|
'node_modules\\/.+\\.js$': 'jest-esm-transformer',
|
||||||
|
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
|
||||||
|
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
|
coverageDirectory: '../../coverage/apps/ligo-virgo',
|
||||||
|
transformIgnorePatterns: [],
|
||||||
|
};
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "ligo-virgo",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"description": "",
|
||||||
|
"main": "jest.config.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "AFFiNE <developer@affine.pro>",
|
||||||
|
"dependencies": {
|
||||||
|
"@mui/icons-material": "^5.8.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"firebase": "^9.8.4",
|
||||||
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
|
"webpack": "^5.73.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"sourceRoot": "apps/ligo-virgo/src",
|
||||||
|
"projectType": "application",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "@nrwl/web:webpack",
|
||||||
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"defaultConfiguration": "production",
|
||||||
|
"options": {
|
||||||
|
"compiler": "babel",
|
||||||
|
"outputPath": "dist/apps/ligo-virgo",
|
||||||
|
"index": "apps/ligo-virgo/src/index.html",
|
||||||
|
"baseHref": "/",
|
||||||
|
"main": "apps/ligo-virgo/src/index.tsx",
|
||||||
|
"polyfills": "apps/ligo-virgo/src/polyfills.ts",
|
||||||
|
"tsConfig": "apps/ligo-virgo/tsconfig.app.json",
|
||||||
|
"assets": ["apps/ligo-virgo/src/assets"],
|
||||||
|
"styles": [],
|
||||||
|
"scripts": [],
|
||||||
|
"webpackConfig": "apps/ligo-virgo/webpack.config.js"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "apps/ligo-virgo/src/environments/environment.ts",
|
||||||
|
"with": "apps/ligo-virgo/src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"namedChunks": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"generateIndexHtml": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"executor": "@nrwl/web:dev-server",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "ligo-virgo:build:development",
|
||||||
|
"hmr": true,
|
||||||
|
"proxyConfig": "apps/ligo-virgo/proxy.conf.json",
|
||||||
|
"open": true
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "ligo-virgo:build:production",
|
||||||
|
"hmr": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nrwl/linter:eslint",
|
||||||
|
"outputs": ["{options.outputFile}"],
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["apps/ligo-virgo/**/*.{ts,tsx,js,jsx}"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"outputs": ["coverage/apps/ligo-virgo"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "apps/ligo-virgo/jest.config.js",
|
||||||
|
"passWithNoTests": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"check": {
|
||||||
|
"executor": "./tools/executors/tsCheck:tsCheck"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": ["app:ligo-virgo"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"/api": {
|
||||||
|
"target": "https://nightly.affine.pro/",
|
||||||
|
"secure": false,
|
||||||
|
"changeOrigin": true
|
||||||
|
},
|
||||||
|
"/collaboration": {
|
||||||
|
"target": "https://canary.affine.pro",
|
||||||
|
"ws": true,
|
||||||
|
"changeOrigin": true,
|
||||||
|
"secure": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,60 @@
|
|||||||
|
import { AsyncBlock } from '@toeverything/framework/virgo';
|
||||||
|
import { isDev } from '@toeverything/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ported from https://github.com/vuejs/core/blob/main/packages/runtime-core/src/customFormatter.ts
|
||||||
|
* See [Custom Object Formatters in Chrome DevTools](https://docs.google.com/document/d/1FTascZXT9cxfetuPRT2eXPQKXui4nWFivUnS_335T3U)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const isAsyncBlock = (x: unknown): x is AsyncBlock => {
|
||||||
|
return x instanceof AsyncBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function initCustomFormatter() {
|
||||||
|
if (!isDev || typeof window === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bannerStyle = {
|
||||||
|
style: 'color: #eee; background: #3F6FDB; margin-right: 5px; padding: 2px; border-radius: 4px',
|
||||||
|
};
|
||||||
|
const typeStyle = {
|
||||||
|
style: 'color: #eee; background: #DB6D56; margin-right: 5px; padding: 2px; border-radius: 4px',
|
||||||
|
};
|
||||||
|
|
||||||
|
// custom formatter for Chrome
|
||||||
|
// https://www.mattzeunert.com/2016/02/19/custom-chrome-devtools-object-formatters.html
|
||||||
|
const formatter = {
|
||||||
|
header(obj: unknown, config = { expand: false }) {
|
||||||
|
if (!isAsyncBlock(obj) || config.expand) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'div',
|
||||||
|
{},
|
||||||
|
['span', bannerStyle, 'AsyncBlock'],
|
||||||
|
['span', typeStyle, obj.type],
|
||||||
|
// @ts-expect-error Debug at development environment
|
||||||
|
`${JSON.stringify(obj.raw_data.properties)}`,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
hasBody(obj: unknown) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
body(obj: unknown) {
|
||||||
|
return ['object', { object: obj, config: { expand: true } }];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
if ((window as any).devtoolsFormatters) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(window as any).devtoolsFormatters.push(formatter);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(window as any).devtoolsFormatters = [formatter];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initCustomFormatter();
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true,
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// This file can be replaced during build by using the `fileReplacements` array.
|
||||||
|
// When building for production, this file is replaced with `environment.prod.ts`.
|
||||||
|
|
||||||
|
export const environment = {
|
||||||
|
production: false,
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!-- local dev index.html -->
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" href="https://app.affine.pro/favicon.ico" />
|
||||||
|
<title>Affine | Local Dev Environment</title>
|
||||||
|
<script>
|
||||||
|
window.global = window;
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/* eslint-disable filename-rules/match */
|
||||||
|
import { StrictMode } from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { ThemeProvider } from '@toeverything/components/ui';
|
||||||
|
import { FeatureFlagsProvider } from '@toeverything/datasource/feature-flags';
|
||||||
|
|
||||||
|
import './custom-formatter';
|
||||||
|
import { LigoVirgoRoutes } from './pages';
|
||||||
|
import './styles.css';
|
||||||
|
|
||||||
|
const container = document.getElementById('root');
|
||||||
|
if (!container) {
|
||||||
|
throw new Error('No root container found');
|
||||||
|
}
|
||||||
|
const root = createRoot(container);
|
||||||
|
root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<BrowserRouter>
|
||||||
|
<ThemeProvider>
|
||||||
|
<FeatureFlagsProvider>
|
||||||
|
<LigoVirgoRoutes />
|
||||||
|
</FeatureFlagsProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { styled } from '@toeverything/components/ui';
|
||||||
|
import { SettingsSidebar, LayoutHeader } from '@toeverything/components/layout';
|
||||||
|
|
||||||
|
export function LigoVirgoRootContainer() {
|
||||||
|
return (
|
||||||
|
<StyledRootContainer id="idAppRoot">
|
||||||
|
<StyledContentContainer>
|
||||||
|
<LayoutHeader />
|
||||||
|
<StyledMainContainer>
|
||||||
|
<Outlet />
|
||||||
|
</StyledMainContainer>
|
||||||
|
</StyledContentContainer>
|
||||||
|
<SettingsSidebar />
|
||||||
|
</StyledRootContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledMainContainer = styled('div')({
|
||||||
|
flex: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledRootContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
height: '100vh',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledContentContainer = styled('div')({
|
||||||
|
flex: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
|
});
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import Agenda from './agenda';
|
||||||
|
import { WorkspaceContainer } from './workspace';
|
||||||
|
import Recent from './recent';
|
||||||
|
import Search from './search';
|
||||||
|
import Settings from './settings';
|
||||||
|
import Shared from './shared';
|
||||||
|
import Starred from './starred';
|
||||||
|
import { Login } from './account';
|
||||||
|
import { PageNotFound } from './status/page-not-found';
|
||||||
|
import { WorkspaceNotFound } from './status/workspace-not-found';
|
||||||
|
import { RoutePrivate } from './RoutePrivate';
|
||||||
|
import { RoutePublicAutoLogin } from './RoutePublicAutoLogin';
|
||||||
|
import { Tools } from './tools';
|
||||||
|
import { Templates } from './templates';
|
||||||
|
import { LigoVirgoRootContainer } from './AppContainer';
|
||||||
|
import { UIPage } from './ui';
|
||||||
|
|
||||||
|
export function LigoVirgoRoutes() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<LigoVirgoRootContainer />}>
|
||||||
|
<Route path="/error/404" element={<PageNotFound />} />
|
||||||
|
<Route
|
||||||
|
path="/error/workspace"
|
||||||
|
element={<WorkspaceNotFound />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route path="/agenda/*" element={<Agenda />} />
|
||||||
|
<Route path="/recent" element={<Recent />} />
|
||||||
|
<Route path="/search" element={<Search />} />
|
||||||
|
<Route path="/settings" element={<Settings />} />
|
||||||
|
<Route path="/shared" element={<Shared />} />
|
||||||
|
<Route path="/started" element={<Starred />} />
|
||||||
|
<Route path="/templates" element={<Templates />} />
|
||||||
|
<Route path="/ui" element={<UIPage />} />
|
||||||
|
<Route
|
||||||
|
path="/:workspace_id/*"
|
||||||
|
element={
|
||||||
|
<RoutePrivate>
|
||||||
|
<WorkspaceContainer />
|
||||||
|
</RoutePrivate>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/" element={<Navigate to="/login" replace />} />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
{/* put public routes here; header and sidebar are disabled here */}
|
||||||
|
<Route>
|
||||||
|
<Route path="/tools/*" element={<Tools />} />
|
||||||
|
<Route
|
||||||
|
path="/login"
|
||||||
|
element={
|
||||||
|
<RoutePublicAutoLogin>
|
||||||
|
<Login />
|
||||||
|
</RoutePublicAutoLogin>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="/" element={<Navigate to="/login" replace />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Navigate, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { PageLoading, Error } from '@toeverything/components/account';
|
||||||
|
import { useUserAndSpaces } from '@toeverything/datasource/state';
|
||||||
|
|
||||||
|
export type RoutePrivateProps = {
|
||||||
|
children: JSX.Element;
|
||||||
|
unauthorizedRedirectTo?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A routing component that cannot be accessed without logging in, and can only be accessed after logging in.
|
||||||
|
*/
|
||||||
|
export function RoutePrivate({
|
||||||
|
children,
|
||||||
|
unauthorizedRedirectTo = '/login',
|
||||||
|
}: RoutePrivateProps) {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
const { user, loading } = useUserAndSpaces();
|
||||||
|
|
||||||
|
if (user == null && loading) {
|
||||||
|
return <PageLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return (
|
||||||
|
<Navigate
|
||||||
|
to={unauthorizedRedirectTo}
|
||||||
|
state={{ from: pathname }}
|
||||||
|
replace={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Navigate, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { PageLoading } from '@toeverything/components/account';
|
||||||
|
import { useUserAndSpaces } from '@toeverything/datasource/state';
|
||||||
|
|
||||||
|
export type RouteUnauthorizedOnlyProps = {
|
||||||
|
children: JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routing components that are accessible without logging in and inaccessible after logging in will automatically jump to the specified route authorizedRedirectTo
|
||||||
|
*/
|
||||||
|
export function RoutePublicAutoLogin({ children }: RouteUnauthorizedOnlyProps) {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
const { user, loading, currentSpaceId } = useUserAndSpaces();
|
||||||
|
|
||||||
|
if (user == null && loading) {
|
||||||
|
return <PageLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSpaceId) {
|
||||||
|
return (
|
||||||
|
<Navigate
|
||||||
|
to={`/${currentSpaceId}`}
|
||||||
|
state={{ from: pathname }}
|
||||||
|
replace={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { Login } from '@toeverything/components/account';
|
||||||
|
|
||||||
|
export { Login };
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function AgendaCalendar() {
|
||||||
|
return <span>AgendaCalendar</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
import style9 from 'style9';
|
||||||
|
|
||||||
|
import { MuiBox as Box } from '@toeverything/components/ui';
|
||||||
|
|
||||||
|
const styles = style9.create({
|
||||||
|
container: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function AgendaRootContainer() {
|
||||||
|
return (
|
||||||
|
<Box className={styles('container')}>
|
||||||
|
<Outlet />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function AgendaHome() {
|
||||||
|
return <span>AgendaHome</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Routes, Route } from 'react-router-dom';
|
||||||
|
|
||||||
|
import Container from './container';
|
||||||
|
import Calendar from './calendar';
|
||||||
|
import Tasks from './tasks';
|
||||||
|
import Today from './today';
|
||||||
|
import Home from './home';
|
||||||
|
|
||||||
|
export default function AgendaContainer() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Container />}>
|
||||||
|
<Route path="/calendar" element={<Calendar />} />
|
||||||
|
<Route path="/tasks" element={<Tasks />} />
|
||||||
|
<Route path="/today" element={<Today />} />
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function AgendaTasks() {
|
||||||
|
return <span>AgendaTasks</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function AgendaToday() {
|
||||||
|
return <span>AgendaToday</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { LigoVirgoRoutes } from './AppRoutes';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Recent() {
|
||||||
|
return <span>Recent</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Search() {
|
||||||
|
return <span>Search</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Settings() {
|
||||||
|
return <span>Settings</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Shared() {
|
||||||
|
return <span>Shared</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Starred() {
|
||||||
|
return <span>Starred</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Error } from '@toeverything/components/account';
|
||||||
|
|
||||||
|
export function PageNotFound() {
|
||||||
|
return <Error clearOnClick={true} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PageNotFound;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Error } from '@toeverything/components/account';
|
||||||
|
|
||||||
|
export function WorkspaceNotFound() {
|
||||||
|
return (
|
||||||
|
<Error
|
||||||
|
subTitle="No workspace is found, please contact the admin"
|
||||||
|
action1Text="Login or Register"
|
||||||
|
clearOnClick={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WorkspaceNotFound;
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { styled, ListButton } from '@toeverything/components/ui';
|
||||||
|
import { TemplateData } from './template-data';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { AffineEditor } from '@toeverything/components/affine-editor';
|
||||||
|
const TemplatesContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
border: '1px solid #E2E7ED',
|
||||||
|
borderRadius: '5px',
|
||||||
|
margin: '0 auto',
|
||||||
|
'.sidebar': {
|
||||||
|
width: '240px',
|
||||||
|
display: 'flex',
|
||||||
|
borderRight: '1px solid #E2E7ED',
|
||||||
|
flexDirection: 'column',
|
||||||
|
color: 'rgba(55, 53, 47, 0.65)',
|
||||||
|
background: 'rgb(247, 246, 243)',
|
||||||
|
padding: '12px',
|
||||||
|
},
|
||||||
|
'.preview-template': {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
'.sidebar-title': {
|
||||||
|
borderBottom: '1px solid #E2E7ED',
|
||||||
|
},
|
||||||
|
'.sidebar-template-type': {
|
||||||
|
height: '600px',
|
||||||
|
overflowY: 'scroll',
|
||||||
|
ul: {},
|
||||||
|
'ul li': {
|
||||||
|
paddingLeft: '10px',
|
||||||
|
height: '32px',
|
||||||
|
lineHeight: '32px',
|
||||||
|
listStyle: 'none',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
'&:hover': {
|
||||||
|
background: '#eee',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'.btn-use-this-template': {
|
||||||
|
background: '#eee',
|
||||||
|
color: '#fff',
|
||||||
|
':hover': {
|
||||||
|
background: '#ccc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface ITemplateProps {
|
||||||
|
handleClickUseThisTemplate?: () => void;
|
||||||
|
}
|
||||||
|
function Templates(props: ITemplateProps) {
|
||||||
|
const handle_click_use_this_template = () => {
|
||||||
|
props.handleClickUseThisTemplate();
|
||||||
|
};
|
||||||
|
const { workspace_id, page_id } = useParams();
|
||||||
|
return (
|
||||||
|
<TemplatesContainer>
|
||||||
|
<div className="sidebar">
|
||||||
|
<div className="sidebar-title">
|
||||||
|
<ListButton
|
||||||
|
className="btn-use-this-template"
|
||||||
|
content="Use this template"
|
||||||
|
onClick={handle_click_use_this_template}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="sidebar-template-type">
|
||||||
|
{TemplateData.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
{item.name}
|
||||||
|
<ul>
|
||||||
|
{item.subList.map((item, index) => {
|
||||||
|
return <li key={index}>{item.name}</li>;
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="preview-template">
|
||||||
|
{page_id && (
|
||||||
|
<AffineEditor
|
||||||
|
workspace={workspace_id}
|
||||||
|
rootBlockId={page_id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TemplatesContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Templates };
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
export const TemplateData = [
|
||||||
|
{
|
||||||
|
name: 'Design',
|
||||||
|
subList: [
|
||||||
|
{ name: '🚘 Roadmap' },
|
||||||
|
{ name: '🔬 User Research Database' },
|
||||||
|
{ name: '🎒 Design Tasks' },
|
||||||
|
{ name: '✏️ Meeting Notes' },
|
||||||
|
{ name: '🖋️ Design System' },
|
||||||
|
{ name: '🎯 Company goals' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Student',
|
||||||
|
subList: [
|
||||||
|
{ name: '✏️ Class Notes' },
|
||||||
|
{ name: '🏗 Job Applications' },
|
||||||
|
{ name: '⚖️ Grade Calculator' },
|
||||||
|
{ name: '🏡 Club Homepage' },
|
||||||
|
{ name: '📚 Reading List' },
|
||||||
|
{ name: '📜 Thesis Planning' },
|
||||||
|
{ name: '📍 Cornell Notes System' },
|
||||||
|
{ name: '📇 Personal CRM' },
|
||||||
|
{ name: '✌️ Roommate Space' },
|
||||||
|
{ name: '💸 Simple Budget' },
|
||||||
|
{ name: '📄 Syllabus' },
|
||||||
|
{ name: '🏠 Classroom Home' },
|
||||||
|
{ name: '📋 Lesson Plans' },
|
||||||
|
{ name: '🗓 Course Schedule' },
|
||||||
|
{ name: '👋 Class Directory' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Engineering',
|
||||||
|
subList: [
|
||||||
|
{ name: '🎒 To-Do' },
|
||||||
|
{ name: '🚘 Roadmap' },
|
||||||
|
{ name: '📓 Engineering Wiki' },
|
||||||
|
{ name: '📎 Docs' },
|
||||||
|
{ name: '✏️ Meeting Notes' },
|
||||||
|
{ name: '🎯 Company goals' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Human resources',
|
||||||
|
subList: [
|
||||||
|
{ name: '💼 Job Board' },
|
||||||
|
{ name: '✏️ Meeting Notes' },
|
||||||
|
{ name: '🚂 New Hire Onboarding' },
|
||||||
|
{ name: '📮 Applicant Tracker' },
|
||||||
|
{ name: '🏠 Company Home' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Marketing',
|
||||||
|
subList: [
|
||||||
|
{ name: '🎨 Brand Assets' },
|
||||||
|
{ name: '✏️ Meeting Notes' },
|
||||||
|
{ name: '🎤 Media List' },
|
||||||
|
{ name: '📆 Content Calendar' },
|
||||||
|
{ name: '🎟️ Mood Board' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Personal',
|
||||||
|
subList: [
|
||||||
|
{ name: '📌 Quick Note' },
|
||||||
|
{ name: '🏠 Personal Home' },
|
||||||
|
{ name: '✔️ Task List' },
|
||||||
|
{ name: '🖊️ Journal' },
|
||||||
|
{ name: '📚 Reading List' },
|
||||||
|
{ name: '🏔️ Goals' },
|
||||||
|
{ name: '✈️ Travel Planner' },
|
||||||
|
{ name: '✏️ Blog Post' },
|
||||||
|
{ name: '📔 Simple Notebook' },
|
||||||
|
{ name: '👟 Habit Tracker' },
|
||||||
|
{ name: '🧭 Life Wiki' },
|
||||||
|
{ name: '👔 Resume' },
|
||||||
|
{ name: '📥 Job Applications' },
|
||||||
|
{ name: '📕 Weekly Agenda' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Other',
|
||||||
|
subList: [
|
||||||
|
{ name: '️📝 Meeting Notes' },
|
||||||
|
{ name: '📄 Docs' },
|
||||||
|
{ name: '🏠 Team Home' },
|
||||||
|
{ name: '☑️ Team Tasks' },
|
||||||
|
{ name: '✔️ Task List' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Product management',
|
||||||
|
subList: [
|
||||||
|
{ name: '🚘 Roadmap' },
|
||||||
|
{ name: ' User Research Database' },
|
||||||
|
{ name: '📎 Docs' },
|
||||||
|
{ name: '✏️ Meeting Notes' },
|
||||||
|
{ name: '🏗 Product Wiki' },
|
||||||
|
{ name: '🎯 Company goals' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sales',
|
||||||
|
subList: [
|
||||||
|
{ name: '✏️ Meeting Notes' },
|
||||||
|
{ name: '👟 Sales CRM' },
|
||||||
|
{ name: '📕 Sales Wiki' },
|
||||||
|
{ name: '🎯 Competitive Analysis' },
|
||||||
|
{ name: '✌️ Sales Assets' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Support',
|
||||||
|
subList: [
|
||||||
|
{ name: '✌️ Team Directory' },
|
||||||
|
{ name: '❓ Product FAQs' },
|
||||||
|
{ name: '✏️ Meeting Notes' },
|
||||||
|
{ name: '🎒 Task List' },
|
||||||
|
{ name: '🚨 Help Center' },
|
||||||
|
{ name: '📎 Process Docs' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
|
export function Container() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { type FC, useRef } from 'react';
|
||||||
|
import * as uiIcons from '@toeverything/components/icons';
|
||||||
|
import { message, styled } from '@toeverything/components/ui';
|
||||||
|
import { copy } from './copy';
|
||||||
|
|
||||||
|
const IconBooth: FC<{ name: string; Icon: FC<any> }> = ({ name, Icon }) => {
|
||||||
|
const on_click = () => {
|
||||||
|
copy(`<${name} />`);
|
||||||
|
message.success({
|
||||||
|
content: 'Copied.',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<IconContainer title={name} onClick={on_click}>
|
||||||
|
<Icon />
|
||||||
|
<IconName>{name}</IconName>
|
||||||
|
</IconContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _icons = Object.entries(uiIcons).filter(([key]) => key !== 'timestamp');
|
||||||
|
|
||||||
|
export const Icons: FC = () => {
|
||||||
|
const ref = useRef<HTMLHeadingElement>(null);
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<h3 ref={ref}>Example:</h3>
|
||||||
|
<div>
|
||||||
|
<code>
|
||||||
|
{`import { TextIcon } from '@toeverything/components/ui'`};
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
<h3>{`Total: ${_icons.length}`}</h3>
|
||||||
|
<blockquote>Click to copy.</blockquote>
|
||||||
|
<p>{`Last Updated: ${new Date(
|
||||||
|
uiIcons.timestamp
|
||||||
|
).toLocaleString()}`}</p>
|
||||||
|
<hr />
|
||||||
|
<IconsContainer>
|
||||||
|
{_icons.map(([key, icon]) => {
|
||||||
|
return <IconBooth key={key} name={key} Icon={icon as FC} />;
|
||||||
|
})}
|
||||||
|
</IconsContainer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = styled('div')({
|
||||||
|
color: '#98ACBD',
|
||||||
|
padding: '20px',
|
||||||
|
});
|
||||||
|
|
||||||
|
const IconName = styled('div')({
|
||||||
|
width: '100%',
|
||||||
|
marginTop: '8px',
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
});
|
||||||
|
|
||||||
|
const IconContainer = styled('div')(({ theme }) => ({
|
||||||
|
width: '112px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
padding: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
textAlign: 'center',
|
||||||
|
'--color-0': theme.affine.palette.hover,
|
||||||
|
'--color-1': theme.affine.palette.icons,
|
||||||
|
|
||||||
|
'& svg:first-of-type': {
|
||||||
|
boxShadow: '0 0 6px #e0e6eb',
|
||||||
|
},
|
||||||
|
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: '#F5F7F8',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const IconsContainer = styled('div')({
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(auto-fill,112px)',
|
||||||
|
justifyContent: 'justify',
|
||||||
|
alignContent: 'start',
|
||||||
|
columnGap: '16px',
|
||||||
|
rowGap: '24px',
|
||||||
|
marginTop: '24px',
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const create_fake_element = (value: string) => {
|
||||||
|
const fake_element = document.createElement('textarea');
|
||||||
|
fake_element.style.position = 'fixed';
|
||||||
|
fake_element.style.top = '0';
|
||||||
|
fake_element.style.clipPath = "path('M0,0 L0,0')";
|
||||||
|
fake_element.setAttribute('readonly', '');
|
||||||
|
fake_element.value = value;
|
||||||
|
return fake_element;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const copy = (value: string) => {
|
||||||
|
const fake_element = create_fake_element(value);
|
||||||
|
document.body.appendChild(fake_element);
|
||||||
|
fake_element.select();
|
||||||
|
fake_element.setSelectionRange(0, fake_element.value.length);
|
||||||
|
document.execCommand('copy');
|
||||||
|
fake_element.remove();
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Icons } from './Icons';
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Routes, Route } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { Container } from './container';
|
||||||
|
import { Icons } from './icons';
|
||||||
|
|
||||||
|
export function Tools() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Container />}>
|
||||||
|
<Route path="/icons" element={<Icons />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const UIPage = () => {
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
This page is used to show ui components of Affine ~
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
|
import style9 from 'style9';
|
||||||
|
|
||||||
|
import { MuiBox as Box } from '@toeverything/components/ui';
|
||||||
|
|
||||||
|
const styles = style9.create({
|
||||||
|
container: {
|
||||||
|
display: 'flex',
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function WorkspaceRootContainer() {
|
||||||
|
return (
|
||||||
|
<Box className={styles('container')}>
|
||||||
|
<Outlet />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { useUserAndSpaces } from '@toeverything/datasource/state';
|
||||||
|
import { services, TemplateFactory } from '@toeverything/datasource/db-service';
|
||||||
|
|
||||||
|
export function WorkspaceHome() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { workspace_id } = useParams();
|
||||||
|
const { user } = useUserAndSpaces();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const navigate_to_user_initial_page = async () => {
|
||||||
|
const recent_pages = await services.api.userConfig.getRecentPages(
|
||||||
|
workspace_id,
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
const user_initial_page_id =
|
||||||
|
await services.api.userConfig.getUserInitialPage(
|
||||||
|
workspace_id,
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
if (recent_pages.length === 0) {
|
||||||
|
await services.api.editorBlock.copyTemplateToPage(
|
||||||
|
workspace_id,
|
||||||
|
user_initial_page_id,
|
||||||
|
TemplateFactory.generatePageTemplateByGroupKeys({
|
||||||
|
name: null,
|
||||||
|
groupKeys: ['todolist'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
navigate(`/${workspace_id}/${user_initial_page_id}`);
|
||||||
|
};
|
||||||
|
navigate_to_user_initial_page();
|
||||||
|
}, [navigate, user.id, workspace_id]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { memo, useEffect } from 'react';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
|
import { AffineBoard } from '@toeverything/components/affine-board';
|
||||||
|
import { useUserAndSpaces } from '@toeverything/datasource/state';
|
||||||
|
import { services } from '@toeverything/datasource/db-service';
|
||||||
|
|
||||||
|
const MemoAffineBoard = memo(AffineBoard, (prev, next) => {
|
||||||
|
return prev.rootBlockId === next.rootBlockId;
|
||||||
|
});
|
||||||
|
|
||||||
|
type WhiteboardProps = {
|
||||||
|
workspace: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Whiteboard = (props: WhiteboardProps) => {
|
||||||
|
const { page_id } = useParams();
|
||||||
|
const { user } = useUserAndSpaces();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!user?.id || !props.workspace) return;
|
||||||
|
const update_recent_pages = async () => {
|
||||||
|
// TODO: deal with it temporarily
|
||||||
|
await services.api.editorBlock.getWorkspaceDbBlock(
|
||||||
|
props.workspace,
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
update_recent_pages();
|
||||||
|
}, [user, props.workspace]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MemoAffineBoard workspace={props.workspace} rootBlockId={page_id} />
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import style9 from 'style9';
|
||||||
|
import {
|
||||||
|
MuiBox as Box,
|
||||||
|
MuiButton as Button,
|
||||||
|
MuiCollapse as Collapse,
|
||||||
|
MuiIconButton as IconButton,
|
||||||
|
} from '@toeverything/components/ui';
|
||||||
|
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
||||||
|
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
|
||||||
|
|
||||||
|
import { services } from '@toeverything/datasource/db-service';
|
||||||
|
import { NewpageIcon } from '@toeverything/components/common';
|
||||||
|
import {
|
||||||
|
usePageTree,
|
||||||
|
useCalendarHeatmap,
|
||||||
|
} from '@toeverything/components/layout';
|
||||||
|
|
||||||
|
const styles = style9.create({
|
||||||
|
ligoButton: {
|
||||||
|
textTransform: 'none',
|
||||||
|
},
|
||||||
|
newPage: {
|
||||||
|
color: '#B6C7D3',
|
||||||
|
width: '26px',
|
||||||
|
fontSize: '18px',
|
||||||
|
textAlign: 'center',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CollapsiblePageTreeProps = {
|
||||||
|
title?: string;
|
||||||
|
initialOpen?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CollapsiblePageTree(props: CollapsiblePageTreeProps) {
|
||||||
|
const { className, style, children, title, initialOpen = true } = props;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { workspace_id, page_id } = useParams();
|
||||||
|
|
||||||
|
const { handleAddPage } = usePageTree();
|
||||||
|
const { addPageToday } = useCalendarHeatmap();
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(initialOpen);
|
||||||
|
|
||||||
|
const create_page = useCallback(async () => {
|
||||||
|
if (page_id) {
|
||||||
|
const newPage = await services.api.editorBlock.create({
|
||||||
|
workspace: workspace_id,
|
||||||
|
type: 'page' as const,
|
||||||
|
});
|
||||||
|
|
||||||
|
await handleAddPage(newPage.id);
|
||||||
|
addPageToday();
|
||||||
|
|
||||||
|
navigate(`/${workspace_id}/${newPage.id}`);
|
||||||
|
}
|
||||||
|
}, [addPageToday, handleAddPage, navigate, page_id, workspace_id]);
|
||||||
|
|
||||||
|
const [newPageBtnVisible, setNewPageBtnVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingRight: 1,
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => setNewPageBtnVisible(true)}
|
||||||
|
onMouseLeave={() => setNewPageBtnVisible(false)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
startIcon={
|
||||||
|
open ? <ArrowDropDownIcon /> : <ArrowRightIcon />
|
||||||
|
}
|
||||||
|
onClick={() => setOpen(prev => !prev)}
|
||||||
|
sx={{ color: '#566B7D', textTransform: 'none' }}
|
||||||
|
className={clsx(styles('ligoButton'), className)}
|
||||||
|
style={style}
|
||||||
|
disableElevation
|
||||||
|
disableRipple
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{newPageBtnVisible && (
|
||||||
|
<div
|
||||||
|
onClick={create_page}
|
||||||
|
className={clsx(styles('newPage'), className)}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
{children ? (
|
||||||
|
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||||
|
{children}
|
||||||
|
</Collapse>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CollapsiblePageTree;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/* eslint-disable filename-rules/match */
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { Page } from './index';
|
||||||
|
|
||||||
|
describe('App', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<Page workspace="default" />);
|
||||||
|
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have a greeting as the title', () => {
|
||||||
|
const { getByText } = render(<Page workspace="default" />);
|
||||||
|
|
||||||
|
expect(getByText(/Welcome ligo-virgo/gi)).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/* eslint-disable filename-rules/match */
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
import {
|
||||||
|
MuiBox as Box,
|
||||||
|
MuiCircularProgress as CircularProgress,
|
||||||
|
MuiDivider as Divider,
|
||||||
|
styled,
|
||||||
|
} from '@toeverything/components/ui';
|
||||||
|
import { AffineEditor } from '@toeverything/components/affine-editor';
|
||||||
|
import {
|
||||||
|
CalendarHeatmap,
|
||||||
|
PageTree,
|
||||||
|
Activities,
|
||||||
|
} from '@toeverything/components/layout';
|
||||||
|
import { CollapsibleTitle } from '@toeverything/components/common';
|
||||||
|
import {
|
||||||
|
useShowSpaceSidebar,
|
||||||
|
useUserAndSpaces,
|
||||||
|
} from '@toeverything/datasource/state';
|
||||||
|
import { services } from '@toeverything/datasource/db-service';
|
||||||
|
|
||||||
|
import { WorkspaceName } from './workspace-name';
|
||||||
|
import { CollapsiblePageTree } from './collapsible-page-tree';
|
||||||
|
import TemplatesPortal from './templates-portal';
|
||||||
|
import { useFlag } from '@toeverything/datasource/feature-flags';
|
||||||
|
|
||||||
|
type PageProps = {
|
||||||
|
workspace: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Page(props: PageProps) {
|
||||||
|
const { page_id } = useParams();
|
||||||
|
const { showSpaceSidebar, fixedDisplay, setSpaceSidebarVisible } =
|
||||||
|
useShowSpaceSidebar();
|
||||||
|
const { user } = useUserAndSpaces();
|
||||||
|
const templatesPortalFlag = useFlag('BooleanTemplatesPortal', false);
|
||||||
|
const dailyNotesFlag = useFlag('BooleanDailyNotes', false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!user?.id || !page_id) return;
|
||||||
|
const updateRecentPages = async () => {
|
||||||
|
// TODO: deal with it temporarily
|
||||||
|
await services.api.editorBlock.getWorkspaceDbBlock(
|
||||||
|
props.workspace,
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await services.api.userConfig.addRecentPage(
|
||||||
|
props.workspace,
|
||||||
|
user.id,
|
||||||
|
page_id
|
||||||
|
);
|
||||||
|
await services.api.editorBlock.clearUndoRedo(props.workspace);
|
||||||
|
};
|
||||||
|
update_recent_pages();
|
||||||
|
}, [user, props.workspace, page_id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LigoApp>
|
||||||
|
<LigoLeftContainer style={{ width: fixedDisplay ? '300px' : 0 }}>
|
||||||
|
<WorkspaceSidebar
|
||||||
|
style={{
|
||||||
|
opacity: !showSpaceSidebar && !fixedDisplay ? 0 : 1,
|
||||||
|
transform:
|
||||||
|
!showSpaceSidebar && !fixedDisplay
|
||||||
|
? 'translateX(-270px)'
|
||||||
|
: 'translateX(0px)',
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => setSpaceSidebarVisible(true)}
|
||||||
|
onMouseLeave={() => setSpaceSidebarVisible(false)}
|
||||||
|
>
|
||||||
|
<WorkspaceName />
|
||||||
|
<Divider light={true} sx={{ my: 1, margin: '6px 0px' }} />
|
||||||
|
<WorkspaceSidebarContent>
|
||||||
|
<div>
|
||||||
|
{templatesPortalFlag && <TemplatesPortal />}
|
||||||
|
{dailyNotesFlag && (
|
||||||
|
<div>
|
||||||
|
<CollapsibleTitle title="Daily Notes">
|
||||||
|
<CalendarHeatmap />
|
||||||
|
</CollapsibleTitle>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<CollapsibleTitle
|
||||||
|
title="Activities"
|
||||||
|
initialOpen={false}
|
||||||
|
>
|
||||||
|
<Activities></Activities>
|
||||||
|
</CollapsibleTitle>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<CollapsiblePageTree title="Page Tree">
|
||||||
|
{page_id ? <PageTree /> : null}
|
||||||
|
</CollapsiblePageTree>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WorkspaceSidebarContent>
|
||||||
|
</WorkspaceSidebar>
|
||||||
|
</LigoLeftContainer>
|
||||||
|
<LigoRightContainer>
|
||||||
|
<LigoEditorOuterContainer>
|
||||||
|
{page_id ? (
|
||||||
|
<AffineEditor
|
||||||
|
workspace={props.workspace}
|
||||||
|
rootBlockId={page_id}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</LigoEditorOuterContainer>
|
||||||
|
</LigoRightContainer>
|
||||||
|
</LigoApp>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LigoApp = styled('div')({
|
||||||
|
width: '100vw',
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
flex: '1 1 0%',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
margin: '10px 0',
|
||||||
|
});
|
||||||
|
|
||||||
|
const LigoRightContainer = styled('div')({
|
||||||
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
|
flex: 'auto',
|
||||||
|
});
|
||||||
|
|
||||||
|
const LigoEditorOuterContainer = styled('div')({
|
||||||
|
position: 'absolute',
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
overflowX: 'hidden',
|
||||||
|
overflowY: 'hidden',
|
||||||
|
});
|
||||||
|
|
||||||
|
const LigoLeftContainer = styled('div')({
|
||||||
|
flex: '0 0 auto',
|
||||||
|
});
|
||||||
|
|
||||||
|
const WorkspaceSidebar = styled('div')(({ hidden }) => ({
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 1,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: 300,
|
||||||
|
minWidth: 300,
|
||||||
|
height: '100%',
|
||||||
|
borderRadius: '0px 10px 10px 0px',
|
||||||
|
boxShadow: '0px 1px 10px rgba(152, 172, 189, 0.6)',
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
transitionProperty: 'left',
|
||||||
|
transitionDuration: '0.35s',
|
||||||
|
transitionTimingFunction: 'ease',
|
||||||
|
padding: '16px 12px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const WorkspaceSidebarContent = styled('div')({
|
||||||
|
flex: 'auto',
|
||||||
|
overflow: 'hidden auto',
|
||||||
|
});
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { styled } from '@toeverything/components/ui';
|
||||||
|
|
||||||
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
|
|
||||||
|
const handle_search = () => {
|
||||||
|
//@ts-ignore
|
||||||
|
virgo.plugins.plugins['search'].renderSearch();
|
||||||
|
};
|
||||||
|
const QuickFindPortalContainer = styled('div')({
|
||||||
|
position: 'relative',
|
||||||
|
marginLeft: '10px',
|
||||||
|
height: '22px',
|
||||||
|
lineHeight: '22px',
|
||||||
|
width: '220px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
color: '#4c6275',
|
||||||
|
fontSize: '14px',
|
||||||
|
paddingLeft: '20px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
':hover': {
|
||||||
|
backgroundColor: '#ccc',
|
||||||
|
},
|
||||||
|
'.shortcutIcon': {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '3px',
|
||||||
|
left: '0px',
|
||||||
|
fontSize: '16px!important',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function QuickFindPortal() {
|
||||||
|
return (
|
||||||
|
<QuickFindPortalContainer onClick={handle_search}>
|
||||||
|
<SearchIcon className="shortcutIcon" /> Quick Find
|
||||||
|
</QuickFindPortalContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QuickFindPortal;
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import {
|
||||||
|
styled,
|
||||||
|
MuiBox as Box,
|
||||||
|
MuiModal as Modal,
|
||||||
|
} from '@toeverything/components/ui';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Templates } from '../../templates';
|
||||||
|
import StarIcon from '@mui/icons-material/Star';
|
||||||
|
import { useNavigate } from 'react-router';
|
||||||
|
import { AsyncBlock } from '@toeverything/framework/virgo';
|
||||||
|
import { createEditor } from '@toeverything/components/affine-editor';
|
||||||
|
const TemplatePortalContainer = styled('div')({
|
||||||
|
position: 'relative',
|
||||||
|
marginLeft: '10px',
|
||||||
|
height: '22px',
|
||||||
|
lineHeight: '22px',
|
||||||
|
width: '220px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
color: '#4c6275',
|
||||||
|
fontSize: '14px',
|
||||||
|
paddingLeft: '20px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
':hover': {
|
||||||
|
backgroundColor: '#ccc',
|
||||||
|
},
|
||||||
|
'.shortcutIcon': {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '3px',
|
||||||
|
left: '0px',
|
||||||
|
fontSize: '16px!important',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '40%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: '80%',
|
||||||
|
height: '70%',
|
||||||
|
boxShadow: 0,
|
||||||
|
p: 0,
|
||||||
|
};
|
||||||
|
const maskStyle = {
|
||||||
|
background: 'rgba(0,0,0,0.5)',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
position: 'fixed',
|
||||||
|
};
|
||||||
|
function TemplatesPortal() {
|
||||||
|
const [open, set_open] = React.useState(false);
|
||||||
|
const handle_open = () => set_open(true);
|
||||||
|
const handle_close = () => set_open(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const get_default_workspace_id = () => {
|
||||||
|
return window.location.pathname.split('/')[1];
|
||||||
|
};
|
||||||
|
const handleClickUseThisTemplate = () => {
|
||||||
|
const block_editor = createEditor(get_default_workspace_id());
|
||||||
|
//@ts-ignore
|
||||||
|
block_editor.plugins
|
||||||
|
.getPlugin('page-toolbar')
|
||||||
|
//@ts-ignore 泛型处理
|
||||||
|
.addDailyNote()
|
||||||
|
.then((new_page: AsyncBlock) => {
|
||||||
|
handle_close();
|
||||||
|
const new_state =
|
||||||
|
`/${get_default_workspace_id()}/` + new_page.id;
|
||||||
|
navigate(new_state);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TemplatePortalContainer onClick={handle_open}>
|
||||||
|
<StarIcon className="shortcutIcon" /> Templates
|
||||||
|
</TemplatePortalContainer>
|
||||||
|
<Modal open={open} onClose={handle_close}>
|
||||||
|
<Box sx={style}>
|
||||||
|
<Templates
|
||||||
|
handleClickUseThisTemplate={handleClickUseThisTemplate}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TemplatesPortal;
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
import {
|
||||||
|
MuiButton as Button,
|
||||||
|
MuiSwitch as Switch,
|
||||||
|
styled,
|
||||||
|
MuiOutlinedInput as OutlinedInput,
|
||||||
|
} from '@toeverything/components/ui';
|
||||||
|
import { LogoIcon } from '@toeverything/components/icons';
|
||||||
|
import {
|
||||||
|
useUserAndSpaces,
|
||||||
|
useShowSpaceSidebar,
|
||||||
|
} from '@toeverything/datasource/state';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { services } from '@toeverything/datasource/db-service';
|
||||||
|
|
||||||
|
const WorkspaceContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
minHeight: 60,
|
||||||
|
padding: '12px 0px',
|
||||||
|
color: '#566B7D',
|
||||||
|
});
|
||||||
|
const LeftContainer = styled('div')({
|
||||||
|
flex: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
});
|
||||||
|
|
||||||
|
const LogoContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: 24,
|
||||||
|
minWidth: 24,
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledLogoIcon = styled(LogoIcon)(({ theme }) => {
|
||||||
|
return {
|
||||||
|
color: theme.affine.palette.primary,
|
||||||
|
width: '16px !important',
|
||||||
|
height: '16px !important',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const WorkspaceNameContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flex: 'auto',
|
||||||
|
width: '100px',
|
||||||
|
marginRight: '10px',
|
||||||
|
input: {
|
||||||
|
padding: '5px 10px',
|
||||||
|
},
|
||||||
|
span: {
|
||||||
|
width: '100%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const WorkspaceReNameContainer = styled('div')({
|
||||||
|
marginRight: '10px',
|
||||||
|
input: {
|
||||||
|
padding: '5px 10px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ToggleDisplayContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#3E6FDB',
|
||||||
|
padding: 6,
|
||||||
|
minWidth: 64,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const WorkspaceName = () => {
|
||||||
|
const { currentSpaceId } = useUserAndSpaces();
|
||||||
|
const { fixedDisplay, toggleSpaceSidebar } = useShowSpaceSidebar();
|
||||||
|
const [inRename, setInRename] = useState(false);
|
||||||
|
const [workspaceName, setWorkspaceName] = useState('');
|
||||||
|
|
||||||
|
const fetchWorkspaceName = useCallback(async () => {
|
||||||
|
if (!currentSpaceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = await services.api.userConfig.getWorkspaceName(
|
||||||
|
currentSpaceId
|
||||||
|
);
|
||||||
|
setWorkspaceName(name);
|
||||||
|
}, [currentSpaceId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchWorkspaceName();
|
||||||
|
}, [currentSpaceId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let unobserve: () => void;
|
||||||
|
const observe = async () => {
|
||||||
|
unobserve = await services.api.userConfig.observe(
|
||||||
|
{ workspace: currentSpaceId },
|
||||||
|
() => {
|
||||||
|
fetchWorkspaceName();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
observe();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unobserve?.();
|
||||||
|
};
|
||||||
|
}, [currentSpaceId, fetchWorkspaceName]);
|
||||||
|
|
||||||
|
const handleKeyDown = useCallback(
|
||||||
|
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
setInRename(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
services.api.userConfig.setWorkspaceName(
|
||||||
|
currentSpaceId,
|
||||||
|
e.currentTarget.value
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WorkspaceContainer>
|
||||||
|
<LeftContainer>
|
||||||
|
<LogoContainer>
|
||||||
|
<StyledLogoIcon />
|
||||||
|
</LogoContainer>
|
||||||
|
|
||||||
|
{inRename ? (
|
||||||
|
<WorkspaceReNameContainer>
|
||||||
|
<OutlinedInput
|
||||||
|
value={workspaceName}
|
||||||
|
onChange={handleChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onMouseLeave={() => setInRename(false)}
|
||||||
|
/>
|
||||||
|
</WorkspaceReNameContainer>
|
||||||
|
) : (
|
||||||
|
<WorkspaceNameContainer>
|
||||||
|
<span onClick={() => setInRename(true)}>
|
||||||
|
{workspaceName}
|
||||||
|
</span>
|
||||||
|
</WorkspaceNameContainer>
|
||||||
|
)}
|
||||||
|
</LeftContainer>
|
||||||
|
<ToggleDisplayContainer>
|
||||||
|
<span>{fixedDisplay ? 'ON' : 'OFF'}</span>
|
||||||
|
<Switch
|
||||||
|
checked={fixedDisplay}
|
||||||
|
onChange={toggleSpaceSidebar}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</ToggleDisplayContainer>
|
||||||
|
</WorkspaceContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/* eslint-disable filename-rules/match */
|
||||||
|
import { Routes, Route, useParams, Navigate } from 'react-router';
|
||||||
|
|
||||||
|
import { useUserAndSpaces } from '@toeverything/datasource/state';
|
||||||
|
|
||||||
|
import { WorkspaceRootContainer } from './Container';
|
||||||
|
import { Page } from './docs';
|
||||||
|
import { WorkspaceHome } from './Home';
|
||||||
|
import Labels from './labels';
|
||||||
|
import Pages from './pages';
|
||||||
|
import { Whiteboard } from './Whiteboard';
|
||||||
|
|
||||||
|
export function WorkspaceContainer() {
|
||||||
|
const { workspace_id } = useParams();
|
||||||
|
const { user, currentSpaceId } = useUserAndSpaces();
|
||||||
|
|
||||||
|
if (
|
||||||
|
user &&
|
||||||
|
![currentSpaceId, 'affine2vin277tcmafwq'].includes(workspace_id)
|
||||||
|
) {
|
||||||
|
// return <Navigate to={`/${currentSpaceId}`} replace={true} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<WorkspaceRootContainer />}>
|
||||||
|
<Route path="/labels" element={<Labels />} />
|
||||||
|
<Route path="/pages" element={<Pages />} />
|
||||||
|
<Route
|
||||||
|
path="/:page_id/whiteboard"
|
||||||
|
element={<Whiteboard workspace={workspace_id} />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/:page_id"
|
||||||
|
element={<Page workspace={workspace_id} />}
|
||||||
|
/>
|
||||||
|
<Route path="/" element={<WorkspaceHome />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function WorkspaceLabels() {
|
||||||
|
return <span>WorkspaceLabels</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function WorkspacePages() {
|
||||||
|
return <span>WorkspacePages</span>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// import { FC, useMemo, useState } from 'react';
|
||||||
|
// import { useParams } from 'react-router-dom';
|
||||||
|
// import { TDPage } from '@toeverything/framework/whiteboard';
|
||||||
|
// import {
|
||||||
|
// AffineWhiteboard,
|
||||||
|
// WhiteboardMeta,
|
||||||
|
// AffineEditorShape
|
||||||
|
// } from '@toeverything/components/affine-whiteboard';
|
||||||
|
|
||||||
|
// interface EditorShapeProps {
|
||||||
|
// blockIds: string | string[];
|
||||||
|
// point: [number, number];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const createEditorShape = (props: EditorShapeProps): AffineEditorShape => {
|
||||||
|
// const block_ids = Array.isArray(props.blockIds)
|
||||||
|
// ? props.blockIds
|
||||||
|
// : [props.blockIds];
|
||||||
|
// return {
|
||||||
|
// id: block_ids.join('_'),
|
||||||
|
// label: '',
|
||||||
|
// childIndex: 1,
|
||||||
|
// name: 'affine_editor',
|
||||||
|
// parentId: 'page',
|
||||||
|
// point: props.point,
|
||||||
|
// rotation: 0,
|
||||||
|
// size: [400, 200],
|
||||||
|
// style: {
|
||||||
|
// color: 'black',
|
||||||
|
// size: 'small',
|
||||||
|
// isFilled: false,
|
||||||
|
// dash: 'draw',
|
||||||
|
// scale: 1
|
||||||
|
// } as any,
|
||||||
|
// type: 'affineEditor',
|
||||||
|
// blockIds: block_ids
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const Whiteboard: FC = () => {
|
||||||
|
// const { workspace_id, page_id } = useParams();
|
||||||
|
// const [shapes, set_shapes] = useState<TDPage['shapes']>({});
|
||||||
|
// const meta = useMemo<WhiteboardMeta>(() => {
|
||||||
|
// return {
|
||||||
|
// workspace: workspace_id,
|
||||||
|
// rootBlockId: page_id
|
||||||
|
// };
|
||||||
|
// }, [workspace_id, page_id]);
|
||||||
|
|
||||||
|
// return page_id ? <AffineWhiteboard meta={meta} shapes={shapes} /> : null;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default Whiteboard;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||||
|
*
|
||||||
|
* See: https://github.com/zloirock/core-js#babel
|
||||||
|
*/
|
||||||
|
import 'core-js/stable';
|
||||||
|
import 'regenerator-runtime/runtime';
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
/* resset.dev • v5.0.2 */
|
||||||
|
|
||||||
|
/* # =================================================================
|
||||||
|
# Global selectors
|
||||||
|
# ================================================================= */
|
||||||
|
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-text-size-adjust: 100%; /* Prevent adjustments of font size after orientation changes in iOS */
|
||||||
|
word-break: normal;
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Tahoma,
|
||||||
|
PingFang SC, Microsoft Yahei, Arial, Hiragino Sans GB, sans-serif,
|
||||||
|
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
background-repeat: no-repeat; /* Set `background-repeat: no-repeat` to all elements and pseudo elements */
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
text-decoration: inherit; /* Inherit text-decoration and vertical align to ::before and ::after pseudo elements */
|
||||||
|
vertical-align: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
padding: 0; /* Reset `padding` and `margin` of all elements */
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* # =================================================================
|
||||||
|
# General elements
|
||||||
|
# ================================================================= */
|
||||||
|
|
||||||
|
hr {
|
||||||
|
overflow: visible; /* Show the overflow in Edge and IE */
|
||||||
|
height: 0; /* Add the correct box sizing in Firefox */
|
||||||
|
color: inherit; /* Correct border color in Firefox. */
|
||||||
|
}
|
||||||
|
|
||||||
|
details,
|
||||||
|
main {
|
||||||
|
display: block; /* Render the `main` element consistently in IE. */
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item; /* Add the correct display in all browsers */
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%; /* Set font-size to 80% in `small` elements */
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none; /* Add the correct display in IE */
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* Remove the bottom border in Chrome 57 */
|
||||||
|
/* Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari */
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent; /* Remove the gray background on active links in IE 10 */
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline-width: 0; /* Remove the outline when hovering in all browsers */
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
pre,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* Specify the font family of code elements */
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-size: 1em; /* Correct the odd `em` font sizing in all browsers */
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder; /* Add the correct font weight in Chrome, Edge, and Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://gist.github.com/unruthless/413930 */
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-color: inherit; /* Correct border color in all Chrome, Edge, and Safari. */
|
||||||
|
text-indent: 0; /* Remove text indentation in Chrome, Edge, and Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* # =================================================================
|
||||||
|
# Forms
|
||||||
|
# ================================================================= */
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='number']::-webkit-inner-spin-button,
|
||||||
|
[type='number']::-webkit-outer-spin-button {
|
||||||
|
height: auto; /* Correct the cursor style of increment and decrement buttons in Chrome */
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='search'] {
|
||||||
|
-webkit-appearance: textfield; /* Correct the odd appearance in Chrome and Safari */
|
||||||
|
outline-offset: -2px; /* Correct the outline style in Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='search']::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none; /* Remove the inner padding in Chrome and Safari on macOS */
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto; /* Internet Explorer 11+ */
|
||||||
|
resize: vertical; /* Specify textarea resizability */
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font: inherit; /* Specify font inheritance of form elements */
|
||||||
|
}
|
||||||
|
|
||||||
|
optgroup {
|
||||||
|
font-weight: bold; /* Restore the font weight unset by the previous rule */
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
overflow: visible; /* Address `overflow` set to `hidden` in IE 8/9/10/11 */
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none; /* Firefox 40+, Internet Explorer 11- */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply cursor pointer to button elements */
|
||||||
|
button,
|
||||||
|
[type='button'],
|
||||||
|
[type='reset'],
|
||||||
|
[type='submit'],
|
||||||
|
[role='button'] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove inner padding and border in Firefox 4+ */
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type='button']::-moz-focus-inner,
|
||||||
|
[type='reset']::-moz-focus-inner,
|
||||||
|
[type='submit']::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace focus style removed in the border reset above */
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type='button']::-moz-focus-inner,
|
||||||
|
[type='reset']::-moz-focus-inner,
|
||||||
|
[type='submit']::-moz-focus-inner {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
html [type='button'], /* Prevent a WebKit bug where (2) destroys native `audio` and `video`controls in Android 4 */
|
||||||
|
[type='reset'],
|
||||||
|
[type='submit'] {
|
||||||
|
-webkit-appearance: button; /* Correct the inability to style clickable types in iOS */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the default button styling in all browsers */
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
background-color: transparent;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus,
|
||||||
|
button:focus,
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style select like a standard input */
|
||||||
|
select {
|
||||||
|
-moz-appearance: none; /* Firefox 36+ */
|
||||||
|
-webkit-appearance: none; /* Chrome 41+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
select::-ms-expand {
|
||||||
|
display: none; /* Internet Explorer 11+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
select::-ms-value {
|
||||||
|
color: currentColor; /* Internet Explorer 11+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
border: 0; /* Correct `color` not being inherited in IE 8/9/10/11 */
|
||||||
|
color: inherit; /* Correct the color inheritance from `fieldset` elements in IE */
|
||||||
|
display: table; /* Correct the text wrapping in Edge and IE */
|
||||||
|
max-width: 100%; /* Correct the text wrapping in Edge and IE */
|
||||||
|
white-space: normal; /* Correct the text wrapping in Edge and IE */
|
||||||
|
max-width: 100%; /* Correct the text wrapping in Edge 18- and IE */
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
/* Correct the inability to style clickable types in iOS and Safari */
|
||||||
|
-webkit-appearance: button;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit; /* Change font properties to `inherit` in Chrome and Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace pointer cursor in disabled elements */
|
||||||
|
[disabled] {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* # =================================================================
|
||||||
|
# Specify media element style
|
||||||
|
# ================================================================= */
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none; /* Remove border when inside `a` element in IE 8/9/10 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the correct vertical alignment in Chrome, Firefox, and Opera */
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* # =================================================================
|
||||||
|
# Accessibility
|
||||||
|
# ================================================================= */
|
||||||
|
|
||||||
|
/* Specify the progress cursor of updating elements */
|
||||||
|
[aria-busy='true'] {
|
||||||
|
cursor: progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify the pointer cursor of trigger elements */
|
||||||
|
[aria-controls] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify the unstyled cursor of disabled, not-editable, or otherwise inoperable elements */
|
||||||
|
[aria-disabled='true'] {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||||
|
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.test.tsx",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.test.js",
|
||||||
|
"**/*.spec.jsx",
|
||||||
|
"**/*.test.jsx"
|
||||||
|
],
|
||||||
|
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
// "noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noImplicitAny": false
|
||||||
|
// "strictNullChecks": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.test.tsx",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.test.js",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.test.jsx",
|
||||||
|
"**/*.spec.jsx",
|
||||||
|
"**/*.d.ts"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||||
|
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const zlib = require('zlib');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const getNxWebpackConfig = require('@nrwl/react/plugins/webpack');
|
||||||
|
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
|
const CompressionPlugin = require('compression-webpack-plugin');
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
const Style9Plugin = require('style9/webpack');
|
||||||
|
|
||||||
|
const enableBundleAnalyzer = process.env.BUNDLE_ANALYZER;
|
||||||
|
|
||||||
|
module.exports = function (webpackConfig) {
|
||||||
|
const config = getNxWebpackConfig(webpackConfig);
|
||||||
|
|
||||||
|
const isProd = config.mode === 'production';
|
||||||
|
|
||||||
|
const style9 = {
|
||||||
|
test: /\.(tsx|ts|js|mjs|jsx)$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: Style9Plugin.loader,
|
||||||
|
options: {
|
||||||
|
minifyProperties: isProd,
|
||||||
|
incrementalClassnames: isProd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
config.experiments.topLevelAwait = true;
|
||||||
|
|
||||||
|
if (isProd) {
|
||||||
|
config.module.rules.unshift(style9);
|
||||||
|
} else {
|
||||||
|
config.module.rules.push(style9);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProd) {
|
||||||
|
config.entry = {
|
||||||
|
main: [...config.entry.main, ...config.entry.polyfills],
|
||||||
|
};
|
||||||
|
config.devtool = false;
|
||||||
|
config.output = {
|
||||||
|
...config.output,
|
||||||
|
filename: '[name].[contenthash:8].js',
|
||||||
|
chunkFilename: '[name].[chunkhash:8].js',
|
||||||
|
hashFunction: undefined,
|
||||||
|
};
|
||||||
|
config.optimization = {
|
||||||
|
nodeEnv: 'production',
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
terserOptions: {
|
||||||
|
ecma: 2020,
|
||||||
|
},
|
||||||
|
extractComments: true,
|
||||||
|
parallel: true,
|
||||||
|
}),
|
||||||
|
new CssMinimizerPlugin(),
|
||||||
|
],
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all',
|
||||||
|
cacheGroups: {
|
||||||
|
styles: {
|
||||||
|
name: 'styles',
|
||||||
|
type: 'css/mini-extract',
|
||||||
|
chunks: 'all',
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
test: /[\\/]node_modules[\\/](@authing|@?firebase)/,
|
||||||
|
name: 'auth',
|
||||||
|
priority: -5,
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
whiteboard: {
|
||||||
|
test: /(libs\/components\/board-|[\\/]node_modules[\\/]@tldraw)/,
|
||||||
|
name: 'whiteboard',
|
||||||
|
priority: -7,
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
editor: {
|
||||||
|
test: /(libs\/framework\/(ligo|virgo|editor)|[\\/]node_modules[\\/](@codemirror|@lezer|slate))/,
|
||||||
|
name: 'editor',
|
||||||
|
priority: -8,
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
test: /[\\/]node_modules[\\/](@mui|@emotion|react|katex)/,
|
||||||
|
name: 'ui',
|
||||||
|
priority: -9,
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
vender: {
|
||||||
|
test: /([\\/]node_modules[\\/]|polyfills|@nrwl)/,
|
||||||
|
name: 'vender',
|
||||||
|
priority: -10,
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
config.module.rules.unshift({
|
||||||
|
test: /\.css$/i,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
sourceMap: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
config.module.rules.unshift({
|
||||||
|
test: /\.scss$/i,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
sourceMap: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
config.module.rules.splice(6);
|
||||||
|
} else {
|
||||||
|
config.output = {
|
||||||
|
...config.output,
|
||||||
|
publicPath: '/',
|
||||||
|
};
|
||||||
|
|
||||||
|
const babelLoader = config.module.rules.find(
|
||||||
|
rule =>
|
||||||
|
typeof rule !== 'string' &&
|
||||||
|
rule.loader?.toString().includes('babel-loader')
|
||||||
|
);
|
||||||
|
if (babelLoader && typeof babelLoader !== 'string') {
|
||||||
|
babelLoader.options['plugins'] = [
|
||||||
|
...(babelLoader.options['plugins'] || []),
|
||||||
|
[require.resolve('babel-plugin-open-source')],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.plugins = [
|
||||||
|
...config.plugins.filter(
|
||||||
|
p => !(isProd && p instanceof MiniCssExtractPlugin)
|
||||||
|
),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
JWT_DEV: !isProd,
|
||||||
|
global: {},
|
||||||
|
}),
|
||||||
|
isProd &&
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
title: 'Affine - All In One Workos',
|
||||||
|
favicon: path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'./src/assets/images/favicon.ico'
|
||||||
|
), //favicon path
|
||||||
|
template: path.resolve(__dirname, './src/template.html'),
|
||||||
|
publicPath: '/',
|
||||||
|
}),
|
||||||
|
new Style9Plugin(),
|
||||||
|
isProd && new MiniCssExtractPlugin(),
|
||||||
|
isProd &&
|
||||||
|
new CompressionPlugin({
|
||||||
|
test: /\.(js|css|html|svg|ttf|woff)$/,
|
||||||
|
algorithm: 'brotliCompress',
|
||||||
|
filename: '[path][base].br',
|
||||||
|
compressionOptions: {
|
||||||
|
params: {
|
||||||
|
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
isProd &&
|
||||||
|
enableBundleAnalyzer &&
|
||||||
|
new BundleAnalyzerPlugin({ analyzerMode: 'static' }),
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
// Workaround for webpack infinite recompile errors
|
||||||
|
config.watchOptions = {
|
||||||
|
// followSymlinks: false,
|
||||||
|
ignored: ['**/*.css'],
|
||||||
|
};
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"babelrcRoots": ["*"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# AFFiNE CONTRIBUTING
|
||||||
|
|
||||||
|
Contributions are **welcome** and will be fully **credited**.
|
||||||
|
|
||||||
|
## **Requirements**
|
||||||
|
|
||||||
|
If the project maintainer has any additional requirements, you will find them listed here.
|
||||||
|
|
||||||
|
- Code Style [AFFiNE Code Guideline](./affine-code-guideline.md)
|
||||||
|
- Git Rules [AFFiNE Git Guideline ](./affine-git-guideline.md)
|
||||||
|
- • **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
|
||||||
|
|
||||||
|
**Happy coding**!
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# AFFiNE Code Guideline
|
||||||
|
|
||||||
|
| Item | Specification | Example |
|
||||||
|
| ----------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||||
|
| [Packages/Paths]() | aaa-bbb-ccc | ligo-virgo, editor-todo |
|
||||||
|
| [.tsx]() | PascalCase | AddPage.tsx |
|
||||||
|
| [.ts]() | kebab-case | file-export.ts |
|
||||||
|
| [.json]() | kebab-case | file-export.ts |
|
||||||
|
| [Domain File]() | OpenRules | xx._d.ts_ | _tsconfig.xx_.json | xx.spec .ts | .env.xx | yy-ds.ts |
|
||||||
|
| [Types]() | UpperCamelCase | WebEvent |
|
||||||
|
| [Enum variants]() | UpperCamelCase | Status{ Todo,Completed } |
|
||||||
|
| [Functions]() | lowerCamelCase | |
|
||||||
|
| [React Funciton Compoment]() | UpperCamelCase | function DocShare(){} |
|
||||||
|
| [React HOC]() | UpperCamelCase | function BussinessText(){} |
|
||||||
|
| [Function Parameter]() | lowerCamelCase | function searchByIdOrName(idOrname){ } |
|
||||||
|
| [Methods for external access]() | lowerCamelCase | public sayHello(){ }; |
|
||||||
|
| [Externally Accessible Variables (Variables)]() | lowerCamelCase | animal.sleepCount |
|
||||||
|
| [General constructors]() | constructor or with_more_details | |
|
||||||
|
| [Local variables]() | lowerCamelCase | const tableCollection = []; |
|
||||||
|
| [Statics]() | SCREAMING_SNAKE_CASE | GLOBAL_MESSAGES |
|
||||||
|
| [Constants](b) | SCREAMING_SNAKE_CASE | GLOBAL_CONFIG |
|
||||||
|
| [Type parameters]() | UpperCamelCase , usually a single capital letter: T | let a: Animal = new Animal() |
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# AFFiNE Git Guideline
|
||||||
|
|
||||||
|
# 1. Git Branch Name
|
||||||
|
|
||||||
|
- fix/
|
||||||
|
- feat/
|
||||||
|
|
||||||
|
# 2. **Commit message guidelines**
|
||||||
|
|
||||||
|
Affine uses [semantic-release](https://github.com/semantic-release/semantic-release) for automated version management and package publishing. For that to work, commitmessages need to be in the right format.
|
||||||
|
|
||||||
|
### **Atomic commits**
|
||||||
|
|
||||||
|
If possible, make [atomic commits](https://en.wikipedia.org/wiki/Atomic_commit), which means:
|
||||||
|
|
||||||
|
- a commit should contain exactly one self-contained functional change
|
||||||
|
- a functional change should be contained in exactly one commit
|
||||||
|
- a commit should not create an inconsistent state (such as test errors, linting errors, partial fix, feature with documentation etc...)
|
||||||
|
|
||||||
|
A complex feature can be broken down into multiple commits as long as each one keep a consistent state and consist of a self-contained change.
|
||||||
|
|
||||||
|
### **Commit message format**
|
||||||
|
|
||||||
|
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special format that includes a **type**, a **scope** and a **subject**:
|
||||||
|
|
||||||
|
`<type>(<scope>): <subject>
|
||||||
|
<BLANK LINE>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>`
|
||||||
|
|
||||||
|
The **header** is mandatory and the **scope** of the header is optional.
|
||||||
|
|
||||||
|
The **footer** can contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages).
|
||||||
|
|
||||||
|
### **Revert**
|
||||||
|
|
||||||
|
If the commit reverts a previous commit, it should begin with `revert:` , followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||||
|
|
||||||
|
### **Type**
|
||||||
|
|
||||||
|
The type must be one of the following:
|
||||||
|
| Type | Description |
|
||||||
|
| ----------- | ----------- |
|
||||||
|
| build | Changes that affect the build system or external | dependencies (example scopes: gulp, broccoli, npm) |
|
||||||
|
| ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) |
|
||||||
|
| docs | Documentation only changes |
|
||||||
|
| feat | A new feature |
|
||||||
|
| fix | A bug fix |
|
||||||
|
| perf | A code change that improves performance |
|
||||||
|
| refactor | A code change that neither fixes a bug nor adds a feature |
|
||||||
|
| style | Changes that do not affect the meaning of the code(white-space, formatting, missing semi-colons, etc) |
|
||||||
|
| test | Adding missing tests or correcting existing tests |
|
||||||
|
| chore | Changes to the build process or auxiliary tools | |
|
||||||
|
|
||||||
|
### **Subject**
|
||||||
|
|
||||||
|
The subject contains succinct description of the change:
|
||||||
|
|
||||||
|
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||||
|
- don't capitalize first letter
|
||||||
|
- no dot (.) at the end
|
||||||
|
|
||||||
|
### **Body**
|
||||||
|
|
||||||
|
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||||
|
|
||||||
|
### **Footer**
|
||||||
|
|
||||||
|
The footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit **Closes**.
|
||||||
|
|
||||||
|
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||||
|
|
||||||
|
### **Examples**
|
||||||
|
|
||||||
|
`fix(pencil): stop graphite breaking when too much pressure applied`
|
||||||
|
|
||||||
|
``feat(pencil): add 'graphiteWidth' option`
|
||||||
|
|
||||||
|
Fix #42`
|
||||||
|
|
||||||
|
`perf(pencil): remove graphiteWidth option`
|
||||||
|
|
||||||
|
BREAKING CHANGE: The graphiteWidth option has been removed.
|
||||||
|
|
||||||
|
The default graphite width of 10mm is always used for performance reasons.`
|
||||||
|
|
||||||
|
# 3. tracking-your-work-with-issues
|
||||||
|
|
||||||
|
[https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
## Affine Icons
|
||||||
|
|
||||||
|
> Abundant and colorful icon resource for free free use
|
||||||
|
|
||||||
|
Website: [http://localhost:4200/tools/icons](http://localhost:4200/tools/icons)
|
||||||
|
|
||||||
|
Figma: [Figma Affine Icons](https://www.figma.com/file/7pyx5gMz6CN0qSRADmScQ7/AFFINE?node-id=665%3A1734)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Tutorial
|
||||||
|
|
||||||
|
Affine defines a new component development specification in Figma, extends Affine UI Components based on MUI BASE and MUI SYSTEM, and supplements as needed https://github.com/toeverything/AFFiNE/tree/master/libs/components/ui , eg `src/libs/components/ui/src/button/base-button.ts`
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import ButtonUnstyled, {
|
||||||
|
buttonUnstyledClasses,
|
||||||
|
} from '@mui/base/ButtonUnstyled';
|
||||||
|
import { styled } from '../styled';
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
const blue = {
|
||||||
|
500: '#007FFF',
|
||||||
|
600: '#0072E5',
|
||||||
|
700: '#0059B2',
|
||||||
|
};
|
||||||
|
/* eslint-enable @typescript-eslint/naming-convention */
|
||||||
|
|
||||||
|
export const BaseButton = styled(ButtonUnstyled)`
|
||||||
|
font-family: IBM Plex Sans, sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
background-color: ${blue[500]};
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
color: white;
|
||||||
|
transition: all 150ms ease;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: ${blue[600]};
|
||||||
|
}
|
||||||
|
|
||||||
|
&.${buttonUnstyledClasses.active} {
|
||||||
|
background-color: ${blue[700]};
|
||||||
|
}
|
||||||
|
|
||||||
|
&.${buttonUnstyledClasses.focusVisible} {
|
||||||
|
box-shadow: 0 4px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 5px rgba(0, 127, 255, 0.5);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.${buttonUnstyledClasses.disabled} {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
export { BaseButton } from './base-button';
|
||||||
|
```
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
Create or edit the `.env.local` file in the project root directory, add `FIGMA_TOKEN`, you can refer to Generate ACCESS_TOKEN: https://www.figma.com/developers/api#access-tokens
|
||||||
|
|
||||||
|
```
|
||||||
|
FIGMA_TOKEN=your-figma-token
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute the command `nx run components-icons:figmaRes` to synchronize figma resources
|
||||||
|
|
||||||
|
figma icon resource address: https://www.figma.com/file/7pyx5gMz6CN0qSRADmScQ7/AFFINE?node-id=665%3A1734
|
||||||
|
|
||||||
|
### Icon Supplementary Style
|
||||||
|
|
||||||
|
Some icons downloaded directly have incorrect styles. You can add supplementary styles in `tools/executors/figmaRes/patch-styles.js`. The key is the name of the icon kebab-case.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## Single Component Rollup Config
|
||||||
|
|
||||||
|
If styles are used in a Component, `rollupConfig` under `project.json` needs to be modified to `libs/rollup.config.cjs`
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
[toc]
|
||||||
|
|
||||||
|
# Tutorial
|
||||||
|
|
||||||
|
1. MUI styled
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import type { MouseEventHandler, ReactNode } from 'react';
|
||||||
|
import { styled } from '@toeverything/components/ui';
|
||||||
|
|
||||||
|
const CardContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
border: '1px solid #E2E7ED',
|
||||||
|
borderRadius: '5px',
|
||||||
|
});
|
||||||
|
|
||||||
|
const CardContent = styled('div')({
|
||||||
|
margin: '23px 52px 24px 19px',
|
||||||
|
});
|
||||||
|
|
||||||
|
const CardActions = styled('div')({
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: '29px',
|
||||||
|
background: 'rgba(152, 172, 189, 0.1)',
|
||||||
|
borderRadius: '0px 0px 5px 5px',
|
||||||
|
padding: '6px 0 6px 19px',
|
||||||
|
fontSize: '12px',
|
||||||
|
fontWeight: '300',
|
||||||
|
color: '#98ACBD',
|
||||||
|
});
|
||||||
|
|
||||||
|
const PlusIcon = styled('div')({
|
||||||
|
marginRight: '9px',
|
||||||
|
fontWeight: '500',
|
||||||
|
lineHeight: 0,
|
||||||
|
'::before': {
|
||||||
|
content: '"+"',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Card = ({
|
||||||
|
children,
|
||||||
|
onAddItem,
|
||||||
|
}: {
|
||||||
|
children?: ReactNode,
|
||||||
|
onAddItem?: MouseEventHandler<HTMLDivElement>,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<CardContainer>
|
||||||
|
<CardContent>{children}</CardContent>
|
||||||
|
<CardActions onClick={onAddItem}>
|
||||||
|
<PlusIcon />
|
||||||
|
Add item
|
||||||
|
</CardActions>
|
||||||
|
</CardContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. import `*.scss`
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import styles from './tree-item.module.scss';
|
||||||
|
|
||||||
|
export const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
|
||||||
|
() => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
ref={wrapperRef}
|
||||||
|
className={cx(
|
||||||
|
styles['Wrapper'],
|
||||||
|
clone && styles['clone'],
|
||||||
|
ghost && styles['ghost'],
|
||||||
|
indicator && styles['indicator']
|
||||||
|
)}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
'--spacing': `${indentationWidth * depth}px`
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. import `*.css`,
|
||||||
|
|
||||||
|
```js
|
||||||
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
import 'codemirror/lib/codemirror';
|
||||||
|
```
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const { getJestProjects } = require('@nrwl/jest');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
projects: getJestProjects(),
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
const nxPreset = require('@nrwl/jest/preset');
|
||||||
|
|
||||||
|
module.exports = { ...nxPreset };
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@nrwl/react/babel",
|
||||||
|
{
|
||||||
|
"runtime": "automatic",
|
||||||
|
"useBuiltIns": "usage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": ["plugin:@nrwl/nx/react", "../../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# components-account
|
||||||
|
|
||||||
|
- components about user accounts
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `nx test components-account` to execute the unit tests via [Jest](https://jestjs.io).
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
displayName: 'components-account',
|
||||||
|
preset: '../../../jest.preset.js',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
|
coverageDirectory: '../../../coverage/libs/components/account',
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user