mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			copilot/ch
			...
			test-wip
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f195c7d1b6 | 
							
								
								
									
										7
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
.git
 | 
			
		||||
.idea
 | 
			
		||||
/bin
 | 
			
		||||
/dist
 | 
			
		||||
/docs
 | 
			
		||||
/npm-debug.log
 | 
			
		||||
node_modules
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*.{js,ts,tsx}]
 | 
			
		||||
charset = utf-8
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
indent_size = 4
 | 
			
		||||
indent_style = space
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 | 
			
		||||
[*.sh]
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
 | 
			
		||||
[{server,translation}.json]
 | 
			
		||||
charset = utf-8
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
indent_size = 2
 | 
			
		||||
indent_style = space
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 | 
			
		||||
[*.yml]
 | 
			
		||||
indent_size = 2
 | 
			
		||||
indent_style = space
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
							
								
								
									
										7
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
node_modules
 | 
			
		||||
dist
 | 
			
		||||
bin
 | 
			
		||||
docs
 | 
			
		||||
libraries
 | 
			
		||||
coverage
 | 
			
		||||
play
 | 
			
		||||
							
								
								
									
										212
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,212 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
    env: {
 | 
			
		||||
        browser: true,
 | 
			
		||||
        commonjs: true,
 | 
			
		||||
        es2021: true,
 | 
			
		||||
        node: true,
 | 
			
		||||
    },
 | 
			
		||||
    // plugins: ['prettier'], // to be activated
 | 
			
		||||
    extends: ['eslint:recommended', 'airbnb-base', 'plugin:jsonc/recommended-with-jsonc', 'prettier'],
 | 
			
		||||
    overrides: [
 | 
			
		||||
        {
 | 
			
		||||
            files: ['*.json', '*.json5', '*.jsonc'],
 | 
			
		||||
            parser: 'jsonc-eslint-parser',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            files: ['package.json'],
 | 
			
		||||
            parser: 'jsonc-eslint-parser',
 | 
			
		||||
            rules: {
 | 
			
		||||
                'jsonc/sort-keys': [
 | 
			
		||||
                    'off',
 | 
			
		||||
                    {
 | 
			
		||||
                        pathPattern: '^$',
 | 
			
		||||
                        order: [
 | 
			
		||||
                            'name',
 | 
			
		||||
                            'version',
 | 
			
		||||
                            'private',
 | 
			
		||||
                            'packageManager',
 | 
			
		||||
                            'description',
 | 
			
		||||
                            'type',
 | 
			
		||||
                            'keywords',
 | 
			
		||||
                            'homepage',
 | 
			
		||||
                            'bugs',
 | 
			
		||||
                            'license',
 | 
			
		||||
                            'author',
 | 
			
		||||
                            'contributors',
 | 
			
		||||
                            'funding',
 | 
			
		||||
                            'files',
 | 
			
		||||
                            'main',
 | 
			
		||||
                            'module',
 | 
			
		||||
                            'exports',
 | 
			
		||||
                            'unpkg',
 | 
			
		||||
                            'jsdelivr',
 | 
			
		||||
                            'browser',
 | 
			
		||||
                            'bin',
 | 
			
		||||
                            'man',
 | 
			
		||||
                            'directories',
 | 
			
		||||
                            'repository',
 | 
			
		||||
                            'publishConfig',
 | 
			
		||||
                            'scripts',
 | 
			
		||||
                            'peerDependencies',
 | 
			
		||||
                            'peerDependenciesMeta',
 | 
			
		||||
                            'optionalDependencies',
 | 
			
		||||
                            'dependencies',
 | 
			
		||||
                            'devDependencies',
 | 
			
		||||
                            'engines',
 | 
			
		||||
                            'config',
 | 
			
		||||
                            'overrides',
 | 
			
		||||
                            'pnpm',
 | 
			
		||||
                            'husky',
 | 
			
		||||
                            'lint-staged',
 | 
			
		||||
                            'eslintConfig',
 | 
			
		||||
                        ],
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies$',
 | 
			
		||||
                        order: { type: 'asc' },
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    globals: {
 | 
			
		||||
        $: true,
 | 
			
		||||
        jQuery: true,
 | 
			
		||||
        glob: true,
 | 
			
		||||
        log: true,
 | 
			
		||||
        EditorWatchdog: true,
 | 
			
		||||
        React: true,
 | 
			
		||||
        appState: true,
 | 
			
		||||
        ExcalidrawLib: true,
 | 
			
		||||
        elements: true,
 | 
			
		||||
        files: true,
 | 
			
		||||
        ReactDOM: true,
 | 
			
		||||
        // src\public\app\widgets\type_widgets\relation_map.js
 | 
			
		||||
        jsPlumb: true,
 | 
			
		||||
        panzoom: true,
 | 
			
		||||
        logError: true,
 | 
			
		||||
        // src\public\app\widgets\type_widgets\image.js
 | 
			
		||||
        WZoom: true,
 | 
			
		||||
        // \src\public\app\widgets\type_widgets\read_only_text.js
 | 
			
		||||
        renderMathInElement: true,
 | 
			
		||||
        // \src\public\app\widgets\type_widgets\editable_text.js
 | 
			
		||||
        BalloonEditor: true,
 | 
			
		||||
        FancytreeNode: true,
 | 
			
		||||
        CKEditorInspector: true,
 | 
			
		||||
        // \src\public\app\widgets\type_widgets\editable_code.js
 | 
			
		||||
        CodeMirror: true,
 | 
			
		||||
        // \src\public\app\services\resizer.js
 | 
			
		||||
        Split: true,
 | 
			
		||||
        // \src\public\app\services\content_renderer.js
 | 
			
		||||
        mermaid: true,
 | 
			
		||||
        // src\public\app\services\frontend_script_api.js
 | 
			
		||||
        dayjs: true,
 | 
			
		||||
        // \src\public\app\widgets\note_map.js
 | 
			
		||||
        ForceGraph: true,
 | 
			
		||||
        // \src\public\app\setup.js
 | 
			
		||||
        ko: true,
 | 
			
		||||
        syncInProgress: true,
 | 
			
		||||
        // src\public\app\services\utils.js
 | 
			
		||||
        logInfo: true,
 | 
			
		||||
        __non_webpack_require__: true,
 | 
			
		||||
        describe: true,
 | 
			
		||||
        it: true,
 | 
			
		||||
        expect: true
 | 
			
		||||
    },
 | 
			
		||||
    parserOptions: {
 | 
			
		||||
        ecmaVersion: 'latest',
 | 
			
		||||
        sourceType: 'module',
 | 
			
		||||
    },
 | 
			
		||||
    rules: {
 | 
			
		||||
        // eslint:recommended
 | 
			
		||||
        'no-unused-vars': 'off',
 | 
			
		||||
        'linebreak-style': 'off',
 | 
			
		||||
        'no-useless-escape': 'off',
 | 
			
		||||
        'no-empty': 'off',
 | 
			
		||||
        'no-constant-condition': 'off',
 | 
			
		||||
        'getter-return': 'off',
 | 
			
		||||
        'no-cond-assign': 'off',
 | 
			
		||||
        'no-async-promise-executor': 'off',
 | 
			
		||||
        'no-extra-semi': 'off',
 | 
			
		||||
        'no-inner-declarations': 'off',
 | 
			
		||||
 | 
			
		||||
        // prettier
 | 
			
		||||
        'prettier/prettier': ['off', { endOfLine: 'auto' }],
 | 
			
		||||
 | 
			
		||||
        // airbnb-base
 | 
			
		||||
        'no-console': 'off',
 | 
			
		||||
        'no-plusplus': 'off',
 | 
			
		||||
        'no-param-reassign': 'off',
 | 
			
		||||
        'global-require': 'off',
 | 
			
		||||
        'no-use-before-define': 'off',
 | 
			
		||||
        'no-await-in-loop': 'off',
 | 
			
		||||
        radix: 'off',
 | 
			
		||||
        'import/order': 'off',
 | 
			
		||||
        'import/no-extraneous-dependencies': 'off',
 | 
			
		||||
        'prefer-destructuring': 'off',
 | 
			
		||||
        'no-shadow': 'off',
 | 
			
		||||
        'no-new': 'off',
 | 
			
		||||
        'no-restricted-syntax': 'off',
 | 
			
		||||
        strict: 'off',
 | 
			
		||||
        'class-methods-use-this': 'off',
 | 
			
		||||
        'no-else-return': 'off',
 | 
			
		||||
        'import/no-dynamic-require': 'off',
 | 
			
		||||
        'no-underscore-dangle': 'off',
 | 
			
		||||
        'prefer-template': 'off',
 | 
			
		||||
        'consistent-return': 'off',
 | 
			
		||||
        'no-continue': 'off',
 | 
			
		||||
        'object-shorthand': 'off',
 | 
			
		||||
        'one-var': 'off',
 | 
			
		||||
        'prefer-const': 'off',
 | 
			
		||||
        'spaced-comment': 'off',
 | 
			
		||||
        'no-loop-func': 'off',
 | 
			
		||||
        'arrow-body-style': 'off',
 | 
			
		||||
 | 
			
		||||
        'guard-for-in': 'off',
 | 
			
		||||
        'no-return-assign': 'off',
 | 
			
		||||
        'dot-notation': 'off',
 | 
			
		||||
 | 
			
		||||
        'func-names': 'off',
 | 
			
		||||
        'import/no-useless-path-segments': 'off',
 | 
			
		||||
        'default-param-last': 'off',
 | 
			
		||||
        'prefer-arrow-callback': 'off',
 | 
			
		||||
        'no-unneeded-ternary': 'off',
 | 
			
		||||
        'no-return-await': 'off',
 | 
			
		||||
        'import/extensions': 'off',
 | 
			
		||||
 | 
			
		||||
        'no-var': 'off',
 | 
			
		||||
        'import/newline-after-import': 'off',
 | 
			
		||||
        'no-restricted-globals': 'off',
 | 
			
		||||
        'operator-assignment': 'off',
 | 
			
		||||
        'no-eval': 'off',
 | 
			
		||||
        'max-classes-per-file': 'off',
 | 
			
		||||
        'vars-on-top': 'off',
 | 
			
		||||
        'no-bitwise': 'off',
 | 
			
		||||
        'no-lonely-if': 'off',
 | 
			
		||||
        'no-multi-assign': 'off',
 | 
			
		||||
        'no-promise-executor-return': 'off',
 | 
			
		||||
        'no-empty-function': 'off',
 | 
			
		||||
        'import/no-unresolved': 'off',
 | 
			
		||||
        camelcase: 'off',
 | 
			
		||||
        eqeqeq: 'off',
 | 
			
		||||
        'lines-between-class-members': 'off',
 | 
			
		||||
        'import/no-cycle': 'off',
 | 
			
		||||
        'new-cap': 'off',
 | 
			
		||||
        'prefer-object-spread': 'off',
 | 
			
		||||
        'no-new-func': 'off',
 | 
			
		||||
        'no-unused-expressions': 'off',
 | 
			
		||||
        'lines-around-directive': 'off',
 | 
			
		||||
        'prefer-exponentiation-operator': 'off',
 | 
			
		||||
        'no-restricted-properties': 'off',
 | 
			
		||||
        'prefer-rest-params': 'off',
 | 
			
		||||
        'no-unreachable-loop': 'off',
 | 
			
		||||
        'no-alert': 'off',
 | 
			
		||||
        'no-useless-return': 'off',
 | 
			
		||||
        'no-nested-ternary': 'off',
 | 
			
		||||
        'prefer-regex-literals': 'off',
 | 
			
		||||
        'import/no-named-as-default-member': 'off',
 | 
			
		||||
        yoda: 'off',
 | 
			
		||||
        'no-script-url': 'off',
 | 
			
		||||
        'no-prototype-builtins':'off'
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										21
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							@@ -1,21 +0,0 @@
 | 
			
		||||
# Mark files as auto-generated to simplify reviews.
 | 
			
		||||
package-lock.json linguist-generated=true
 | 
			
		||||
**/package-lock.json linguist-generated=true
 | 
			
		||||
apps/server/src/assets/doc_notes/en/User[[:space:]]Guide/** linguist-generated
 | 
			
		||||
 | 
			
		||||
# Ignore from GitHub language stats.
 | 
			
		||||
apps/server/src/assets/doc_notes/en/User[[:space:]]Guide/**/*.html eol=lf
 | 
			
		||||
apps/server/src/assets/doc_notes/** linguist-vendored=true
 | 
			
		||||
apps/edit-docs/demo/** linguist-vendored=true
 | 
			
		||||
docs/** linguist-vendored=true
 | 
			
		||||
 | 
			
		||||
# Normalize line endings.
 | 
			
		||||
docs/**/*.md eol=lf
 | 
			
		||||
docs/**/*.json eol=lf
 | 
			
		||||
demo/**/*.html eol=lf
 | 
			
		||||
demo/**/*.json eol=lf
 | 
			
		||||
demo/**/*.svg eol=lf
 | 
			
		||||
demo/**/*.txt eol=lf
 | 
			
		||||
demo/**/*.js eol=lf
 | 
			
		||||
demo/**/*.css eol=lf
 | 
			
		||||
*.sh eol=lf
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,4 @@
 | 
			
		||||
# These are supported funding model platforms
 | 
			
		||||
 | 
			
		||||
github: [eliandoran]
 | 
			
		||||
custom: ["https://paypal.me/eliandoran"]
 | 
			
		||||
liberapay: ElianDoran
 | 
			
		||||
buy_me_a_coffee: eliandoran
 | 
			
		||||
github: [zadam]
 | 
			
		||||
custom: ["https://paypal.me/za4am"]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,18 +1,13 @@
 | 
			
		||||
name: Bug Report
 | 
			
		||||
description: Report a bug
 | 
			
		||||
type: "Bug"
 | 
			
		||||
title: "(Bug report) "
 | 
			
		||||
labels: "Type: Bug"
 | 
			
		||||
body:
 | 
			
		||||
- type: textarea
 | 
			
		||||
  attributes:
 | 
			
		||||
    label: Description
 | 
			
		||||
    description: A clear and concise description of the bug and any additional information.
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
- type: input
 | 
			
		||||
  attributes:
 | 
			
		||||
    label: TriliumNext Version
 | 
			
		||||
    description: What version of TriliumNext are you using?
 | 
			
		||||
    placeholder: 0.90.0-beta
 | 
			
		||||
    label: Trilium Version
 | 
			
		||||
    description: What version of Trilium are you using?
 | 
			
		||||
    placeholder: 0.57.0-beta
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
- type: dropdown
 | 
			
		||||
@@ -29,7 +24,7 @@ body:
 | 
			
		||||
- type: dropdown
 | 
			
		||||
  attributes:
 | 
			
		||||
    label: What is your setup?
 | 
			
		||||
    description: https://triliumnext.github.io/Docs/Wiki/quick-start.html
 | 
			
		||||
    description: https://github.com/zadam/trilium/wiki#choose-the-setup
 | 
			
		||||
    options:
 | 
			
		||||
      - Local (no sync)
 | 
			
		||||
      - Local + server sync
 | 
			
		||||
@@ -43,9 +38,15 @@ body:
 | 
			
		||||
    placeholder: "e.g. Windows 10 version 1909, macOS Catalina 10.15.7, or Ubuntu 20.04"
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
- type: textarea
 | 
			
		||||
  attributes:
 | 
			
		||||
    label: Description
 | 
			
		||||
    description: A clear and concise description of the bug and any additional information.
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
- type: textarea
 | 
			
		||||
  attributes:
 | 
			
		||||
    label: Error logs
 | 
			
		||||
    description: Please provide error logs, see [wiki page](https://triliumnext.github.io/Docs/Wiki/error-logs.html) for instructions on how to submit them.
 | 
			
		||||
    description: Please provide error logs, see [wiki page](https://github.com/zadam/trilium/wiki/Error-logs) for instructions on how to submit them.
 | 
			
		||||
  validations:
 | 
			
		||||
    required: false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,11 +1,12 @@
 | 
			
		||||
name: Feature Request
 | 
			
		||||
description: Ask for a new feature to be added
 | 
			
		||||
type: "Feature"
 | 
			
		||||
title: "(Feature request) "
 | 
			
		||||
labels: "Type: Enhancement"
 | 
			
		||||
body:
 | 
			
		||||
- type: textarea
 | 
			
		||||
  attributes:
 | 
			
		||||
    label: Describe feature
 | 
			
		||||
    description: A clear and concise description of what you want to be added.
 | 
			
		||||
    description: A clear and concise description of what you want to be added..
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
- type: textarea
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/ISSUE_TEMPLATE/task.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/ISSUE_TEMPLATE/task.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,10 +0,0 @@
 | 
			
		||||
name: Task
 | 
			
		||||
description: Create a new Task
 | 
			
		||||
type: "Task"
 | 
			
		||||
body:
 | 
			
		||||
- type: textarea
 | 
			
		||||
  attributes:
 | 
			
		||||
    label: Describe Task
 | 
			
		||||
    description: A clear and concise description of what the task is about.
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
							
								
								
									
										187
									
								
								.github/actions/build-electron/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										187
									
								
								.github/actions/build-electron/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,187 +0,0 @@
 | 
			
		||||
name: "Build Electron App"
 | 
			
		||||
description: "Builds and packages the Electron app for different platforms"
 | 
			
		||||
 | 
			
		||||
inputs:
 | 
			
		||||
  os:
 | 
			
		||||
    description: "One of the supported platforms: macos, linux, windows"
 | 
			
		||||
    required: true
 | 
			
		||||
  arch:
 | 
			
		||||
    description: "The architecture to build for: x64, arm64"
 | 
			
		||||
    required: true
 | 
			
		||||
  shell:
 | 
			
		||||
    description: "Which shell to use"
 | 
			
		||||
    required: true
 | 
			
		||||
  forge_platform:
 | 
			
		||||
    description: "The --platform to pass to Electron Forge"
 | 
			
		||||
    required: true
 | 
			
		||||
 | 
			
		||||
runs:
 | 
			
		||||
  using: composite
 | 
			
		||||
  steps:
 | 
			
		||||
  # Certificate setup
 | 
			
		||||
  - name: Import Apple certificates
 | 
			
		||||
    if: inputs.os == 'macos'
 | 
			
		||||
    uses: apple-actions/import-codesign-certs@v5
 | 
			
		||||
    with:
 | 
			
		||||
      p12-file-base64: ${{ env.APPLE_APP_CERTIFICATE_BASE64 }}
 | 
			
		||||
      p12-password: ${{ env.APPLE_APP_CERTIFICATE_PASSWORD }}
 | 
			
		||||
      keychain: build-app-${{ github.run_id }}
 | 
			
		||||
      keychain-password: ${{ github.run_id }}
 | 
			
		||||
 | 
			
		||||
  - name: Install Installer certificate
 | 
			
		||||
    if: inputs.os == 'macos'
 | 
			
		||||
    uses: apple-actions/import-codesign-certs@v5
 | 
			
		||||
    with:
 | 
			
		||||
      p12-file-base64: ${{ env.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
 | 
			
		||||
      p12-password: ${{ env.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
 | 
			
		||||
      keychain: build-installer-${{ github.run_id }}
 | 
			
		||||
      keychain-password: ${{ github.run_id }}
 | 
			
		||||
 | 
			
		||||
  - name: Verify certificates
 | 
			
		||||
    if: inputs.os == 'macos'
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    run: |
 | 
			
		||||
      echo "Available signing identities in app keychain:"
 | 
			
		||||
      security find-identity -v -p codesigning build-app-${{ github.run_id }}.keychain
 | 
			
		||||
 | 
			
		||||
      echo "Available signing identities in installer keychain:"
 | 
			
		||||
      security find-identity -v -p codesigning build-installer-${{ github.run_id }}.keychain
 | 
			
		||||
 | 
			
		||||
      # Make the keychains searchable
 | 
			
		||||
      security list-keychains -d user -s build-app-${{ github.run_id }}.keychain build-installer-${{ github.run_id }}.keychain $(security list-keychains -d user | tr -d '"')
 | 
			
		||||
      security default-keychain -s build-app-${{ github.run_id }}.keychain
 | 
			
		||||
      security unlock-keychain -p ${{ github.run_id }} build-app-${{ github.run_id }}.keychain
 | 
			
		||||
      security unlock-keychain -p ${{ github.run_id }} build-installer-${{ github.run_id }}.keychain
 | 
			
		||||
      security set-keychain-settings -t 3600 -l build-app-${{ github.run_id }}.keychain
 | 
			
		||||
      security set-keychain-settings -t 3600 -l build-installer-${{ github.run_id }}.keychain
 | 
			
		||||
 | 
			
		||||
  - name: Set up Python and other macOS dependencies
 | 
			
		||||
    if: ${{ inputs.os == 'macos' }}
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    run: |
 | 
			
		||||
      brew install python-setuptools
 | 
			
		||||
      brew install create-dmg
 | 
			
		||||
 | 
			
		||||
  - name: Install dependencies for RPM and Flatpak package building
 | 
			
		||||
    if: ${{ inputs.os == 'linux' }}
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    run: |
 | 
			
		||||
      sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils
 | 
			
		||||
      flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
 | 
			
		||||
      FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi)
 | 
			
		||||
      FLATPAK_VERSION='24.08'
 | 
			
		||||
      flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION
 | 
			
		||||
 | 
			
		||||
  - name: Update build info
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    run: pnpm run chore:update-build-info
 | 
			
		||||
 | 
			
		||||
  # Critical debugging configuration
 | 
			
		||||
  - name: Run electron-forge build with enhanced logging
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    env:
 | 
			
		||||
      # Pass through required environment variables for signing and notarization
 | 
			
		||||
      APPLE_TEAM_ID: ${{ env.APPLE_TEAM_ID }}
 | 
			
		||||
      APPLE_ID: ${{ env.APPLE_ID }}
 | 
			
		||||
      APPLE_ID_PASSWORD: ${{ env.APPLE_ID_PASSWORD }}
 | 
			
		||||
      WINDOWS_SIGN_EXECUTABLE: ${{ env.WINDOWS_SIGN_EXECUTABLE }}
 | 
			
		||||
      TRILIUM_ARTIFACT_NAME_HINT: TriliumNotes-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }}
 | 
			
		||||
      TARGET_ARCH: ${{ inputs.arch }}
 | 
			
		||||
    run: pnpm run --filter desktop electron-forge:make --arch=${{ inputs.arch }} --platform=${{ inputs.forge_platform }}
 | 
			
		||||
 | 
			
		||||
  # Add DMG signing step
 | 
			
		||||
  - name: Sign DMG
 | 
			
		||||
    if: inputs.os == 'macos'
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    run: |
 | 
			
		||||
      echo "Signing DMG file..."
 | 
			
		||||
      dmg_file=$(find ./apps/desktop/dist -name "*.dmg" -print -quit)
 | 
			
		||||
      if [ -n "$dmg_file" ]; then
 | 
			
		||||
        echo "Found DMG: $dmg_file"
 | 
			
		||||
        # Get the first valid signing identity from the keychain
 | 
			
		||||
        SIGNING_IDENTITY=$(security find-identity -v -p codesigning build-app-${{ github.run_id }}.keychain | grep "Developer ID Application" | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
 | 
			
		||||
        if [ -z "$SIGNING_IDENTITY" ]; then
 | 
			
		||||
          echo "Error: No valid Developer ID Application certificate found in keychain"
 | 
			
		||||
          exit 1
 | 
			
		||||
        fi
 | 
			
		||||
        echo "Using signing identity: $SIGNING_IDENTITY"
 | 
			
		||||
        # Sign the DMG
 | 
			
		||||
        codesign --force --sign "$SIGNING_IDENTITY" --options runtime --timestamp "$dmg_file"
 | 
			
		||||
        # Notarize the DMG
 | 
			
		||||
        xcrun notarytool submit "$dmg_file" --apple-id "$APPLE_ID" --password "$APPLE_ID_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait
 | 
			
		||||
        # Staple the notarization ticket
 | 
			
		||||
        xcrun stapler staple "$dmg_file"
 | 
			
		||||
      else
 | 
			
		||||
        echo "No DMG found to sign"
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
  - name: Verify code signing
 | 
			
		||||
    if: inputs.os == 'macos'
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    run: |
 | 
			
		||||
      echo "Verifying code signing for all artifacts..."
 | 
			
		||||
 | 
			
		||||
      # First check the .app bundle
 | 
			
		||||
      echo "Looking for .app bundle..."
 | 
			
		||||
      app_bundle=$(find ./apps/desktop/dist -name "*.app" -print -quit)
 | 
			
		||||
      if [ -n "$app_bundle" ]; then
 | 
			
		||||
        echo "Found app bundle: $app_bundle"
 | 
			
		||||
        echo "Verifying app bundle signing..."
 | 
			
		||||
        codesign --verify --deep --strict --verbose=2 "$app_bundle"
 | 
			
		||||
        echo "Displaying app bundle signing info..."
 | 
			
		||||
        codesign --display --verbose=2 "$app_bundle"
 | 
			
		||||
 | 
			
		||||
        echo "Checking entitlements..."
 | 
			
		||||
        codesign --display --entitlements :- "$app_bundle"
 | 
			
		||||
 | 
			
		||||
        echo "Checking notarization status..."
 | 
			
		||||
        xcrun stapler validate "$app_bundle" || echo "Warning: App bundle not notarized yet"
 | 
			
		||||
      else
 | 
			
		||||
        echo "No .app bundle found to verify"
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      # Then check DMG if it exists
 | 
			
		||||
      echo "Looking for DMG..."
 | 
			
		||||
      dmg_file=$(find ./apps/desktop/dist -name "*.dmg" -print -quit)
 | 
			
		||||
      if [ -n "$dmg_file" ]; then
 | 
			
		||||
        echo "Found DMG: $dmg_file"
 | 
			
		||||
        echo "Verifying DMG signing..."
 | 
			
		||||
        codesign --verify --deep --strict --verbose=2 "$dmg_file"
 | 
			
		||||
        echo "Displaying DMG signing info..."
 | 
			
		||||
        codesign --display --verbose=2 "$dmg_file"
 | 
			
		||||
 | 
			
		||||
        echo "Checking DMG notarization..."
 | 
			
		||||
        xcrun stapler validate "$dmg_file" || echo "Warning: DMG not notarized yet"
 | 
			
		||||
      else
 | 
			
		||||
        echo "No DMG found to verify"
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
      # Finally check ZIP if it exists
 | 
			
		||||
      echo "Looking for ZIP..."
 | 
			
		||||
      zip_file=$(find ./apps/desktop/dist -name "*.zip" -print -quit)
 | 
			
		||||
      if [ -n "$zip_file" ]; then
 | 
			
		||||
        echo "Found ZIP: $zip_file"
 | 
			
		||||
        echo "Note: ZIP files are not code signed, but their contents should be"
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
  - name: Sign the RPM
 | 
			
		||||
    if: inputs.os == 'linux'
 | 
			
		||||
    shell: ${{ inputs.shell }}
 | 
			
		||||
    run: |
 | 
			
		||||
      echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --import
 | 
			
		||||
 | 
			
		||||
      # Import the key into RPM for verification
 | 
			
		||||
      gpg --export -a > pubkey
 | 
			
		||||
      rpm --import pubkey
 | 
			
		||||
      rm pubkey
 | 
			
		||||
 | 
			
		||||
      # Sign the RPM
 | 
			
		||||
      rpm_file=$(find ./apps/desktop/upload -name "*.rpm" -print -quit)
 | 
			
		||||
      rpmsign --define "_gpg_name Trilium Notes Signing Key <triliumnotes@outlook.com>" --addsign "$rpm_file"
 | 
			
		||||
      rpm -Kv "$rpm_file"
 | 
			
		||||
 | 
			
		||||
      # Validate code signing
 | 
			
		||||
      if ! rpm -K "$rpm_file" | grep -q "digests signatures OK"; then
 | 
			
		||||
        echo .rpm file not signed
 | 
			
		||||
        exit 1
 | 
			
		||||
      fi
 | 
			
		||||
							
								
								
									
										33
									
								
								.github/actions/build-server/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								.github/actions/build-server/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,33 +0,0 @@
 | 
			
		||||
inputs:
 | 
			
		||||
  os:
 | 
			
		||||
    description: "One of the supported platforms: windows"
 | 
			
		||||
    required: true
 | 
			
		||||
  arch:
 | 
			
		||||
    description: "The architecture to build for: x64, arm64"
 | 
			
		||||
    required: true
 | 
			
		||||
runs:
 | 
			
		||||
  using: composite
 | 
			
		||||
  steps:
 | 
			
		||||
  - uses: pnpm/action-setup@v4
 | 
			
		||||
  - name: Set up node & dependencies
 | 
			
		||||
    uses: actions/setup-node@v6
 | 
			
		||||
    with:
 | 
			
		||||
      node-version: 24
 | 
			
		||||
      cache: "pnpm"
 | 
			
		||||
  - name: Install dependencies
 | 
			
		||||
    shell: bash
 | 
			
		||||
    run: pnpm install --frozen-lockfile
 | 
			
		||||
  - name: Run Linux server build
 | 
			
		||||
    env:
 | 
			
		||||
      MATRIX_ARCH: ${{ inputs.arch }}
 | 
			
		||||
    shell: bash
 | 
			
		||||
    run: |
 | 
			
		||||
      pnpm run chore:update-build-info
 | 
			
		||||
      pnpm run --filter server package
 | 
			
		||||
  - name: Prepare artifacts
 | 
			
		||||
    shell: bash
 | 
			
		||||
    run: |
 | 
			
		||||
      mkdir -p upload
 | 
			
		||||
      file=$(find ./apps/server/out -name '*.tar.xz' -print -quit)
 | 
			
		||||
      name=${{ github.ref_name }}
 | 
			
		||||
      cp "$file" "upload/TriliumNotes-Server-${name//\//-}-${{ inputs.os }}-${{ inputs.arch }}.tar.xz"
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
name: "Deploy to Cloudflare Pages"
 | 
			
		||||
description: "Deploys to Cloudflare Pages on either a temporary branch with preview comment, or on the production version if on the main branch."
 | 
			
		||||
inputs:
 | 
			
		||||
  project_name:
 | 
			
		||||
    description: "CloudFlare Pages project name"
 | 
			
		||||
  comment_body:
 | 
			
		||||
    description: "The message to display when deployment is ready"
 | 
			
		||||
    default: "Deployment is ready."
 | 
			
		||||
    required: false
 | 
			
		||||
  production_url:
 | 
			
		||||
    description: "The URL to mention as the production URL."
 | 
			
		||||
    required: true
 | 
			
		||||
  deploy_dir:
 | 
			
		||||
    description: "The directory from which to deploy."
 | 
			
		||||
    required: true
 | 
			
		||||
  cloudflare_api_token:
 | 
			
		||||
    description: "The Cloudflare API token to use for deployment."
 | 
			
		||||
    required: true
 | 
			
		||||
  cloudflare_account_id:
 | 
			
		||||
    description: "The Cloudflare account ID to use for deployment."
 | 
			
		||||
    required: true
 | 
			
		||||
  github_token:
 | 
			
		||||
    description: "The GitHub token to use for posting PR comments."
 | 
			
		||||
    required: true
 | 
			
		||||
runs:
 | 
			
		||||
  using: composite
 | 
			
		||||
  steps:
 | 
			
		||||
    # Install wrangler globally to avoid workspace issues
 | 
			
		||||
    - name: Install Wrangler
 | 
			
		||||
      shell: bash
 | 
			
		||||
      run: npm install -g wrangler
 | 
			
		||||
 | 
			
		||||
    # Deploy using Wrangler (use pre-installed wrangler)
 | 
			
		||||
    - name: Deploy to Cloudflare Pages
 | 
			
		||||
      id: deploy
 | 
			
		||||
      if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
 | 
			
		||||
      uses: cloudflare/wrangler-action@v3
 | 
			
		||||
      with:
 | 
			
		||||
        apiToken: ${{ inputs.cloudflare_api_token }}
 | 
			
		||||
        accountId: ${{ inputs.cloudflare_account_id }}
 | 
			
		||||
        command: pages deploy ${{ inputs.deploy_dir }} --project-name=${{ inputs.project_name}} --branch=${{ github.ref_name }}
 | 
			
		||||
        wranglerVersion: ''  # Use pre-installed version
 | 
			
		||||
 | 
			
		||||
    # Deploy preview for PRs
 | 
			
		||||
    - name: Deploy Preview to Cloudflare Pages
 | 
			
		||||
      id: preview-deployment
 | 
			
		||||
      if: github.event_name == 'pull_request'
 | 
			
		||||
      uses: cloudflare/wrangler-action@v3
 | 
			
		||||
      with:
 | 
			
		||||
        apiToken: ${{ inputs.cloudflare_api_token }}
 | 
			
		||||
        accountId: ${{ inputs.cloudflare_account_id }}
 | 
			
		||||
        command: pages deploy ${{ inputs.deploy_dir }} --project-name=${{ inputs.project_name}} --branch=pr-${{ github.event.pull_request.number }}
 | 
			
		||||
        wranglerVersion: ''  # Use pre-installed version
 | 
			
		||||
 | 
			
		||||
    # Post deployment URL as PR comment
 | 
			
		||||
    - name: Comment PR with Preview URL
 | 
			
		||||
      if: github.event_name == 'pull_request'
 | 
			
		||||
      uses: actions/github-script@v8
 | 
			
		||||
      env:
 | 
			
		||||
        COMMENT_BODY: ${{ inputs.comment_body }}
 | 
			
		||||
        PRODUCTION_URL: ${{ inputs.production_url }}
 | 
			
		||||
        PROJECT_NAME: ${{ inputs.project_name }}
 | 
			
		||||
      with:
 | 
			
		||||
        github-token: ${{ inputs.github_token }}
 | 
			
		||||
        script: |
 | 
			
		||||
          const prNumber = context.issue.number;
 | 
			
		||||
          // Construct preview URL based on Cloudflare Pages pattern
 | 
			
		||||
          const projectName = process.env.PROJECT_NAME;
 | 
			
		||||
          const previewUrl = `https://pr-${prNumber}.${projectName}.pages.dev`;
 | 
			
		||||
 | 
			
		||||
          // Check if we already commented
 | 
			
		||||
          const comments = await github.rest.issues.listComments({
 | 
			
		||||
            owner: context.repo.owner,
 | 
			
		||||
            repo: context.repo.repo,
 | 
			
		||||
            issue_number: prNumber
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          const customMessage = process.env.COMMENT_BODY;
 | 
			
		||||
          const botComment = comments.data.find(comment =>
 | 
			
		||||
            comment.user.type === 'Bot' &&
 | 
			
		||||
            comment.body.includes(customMessage)
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          const mainUrl = process.env.PRODUCTION_URL;
 | 
			
		||||
          const commentBody = `${customMessage}!\n\n🔗 Preview URL: ${previewUrl}\n📖 Production URL: ${mainUrl}\n\n✅ All checks passed\n\n_This preview will be updated automatically with new commits._`;
 | 
			
		||||
 | 
			
		||||
          if (botComment) {
 | 
			
		||||
            // Update existing comment
 | 
			
		||||
            await github.rest.issues.updateComment({
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              comment_id: botComment.id,
 | 
			
		||||
              body: commentBody
 | 
			
		||||
            });
 | 
			
		||||
          } else {
 | 
			
		||||
            // Create new comment
 | 
			
		||||
            await github.rest.issues.createComment({
 | 
			
		||||
              issue_number: prNumber,
 | 
			
		||||
              owner: context.repo.owner,
 | 
			
		||||
              repo: context.repo.repo,
 | 
			
		||||
              body: commentBody
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
							
								
								
									
										79
									
								
								.github/actions/report-size/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								.github/actions/report-size/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,79 +0,0 @@
 | 
			
		||||
name: 'Bundle size reporter'
 | 
			
		||||
description: 'Post bundle size difference compared to another branch'
 | 
			
		||||
inputs:
 | 
			
		||||
  branch:
 | 
			
		||||
    description: 'Branch to compare to'
 | 
			
		||||
    required: true
 | 
			
		||||
    default: 'main'
 | 
			
		||||
  paths:
 | 
			
		||||
    description:
 | 
			
		||||
      'Paths to json file bundle size report or folder containing bundles'
 | 
			
		||||
    required: true
 | 
			
		||||
    default: '/'
 | 
			
		||||
  onlyDiff:
 | 
			
		||||
    description: 'Report only different sizes'
 | 
			
		||||
    required: false
 | 
			
		||||
    default: 'false'
 | 
			
		||||
  filter:
 | 
			
		||||
    description: 'Regex filter based on file path'
 | 
			
		||||
    required: false
 | 
			
		||||
  unit:
 | 
			
		||||
    description: 'Size unit'
 | 
			
		||||
    required: false
 | 
			
		||||
    default: 'KB'
 | 
			
		||||
 | 
			
		||||
  # Comment inputs
 | 
			
		||||
  comment:
 | 
			
		||||
    description: 'Post comment'
 | 
			
		||||
    required: false
 | 
			
		||||
    default: 'true'
 | 
			
		||||
  header:
 | 
			
		||||
    description: 'Comment header'
 | 
			
		||||
    required: false
 | 
			
		||||
    default: 'Bundle size report'
 | 
			
		||||
  append:
 | 
			
		||||
    description: 'Append comment'
 | 
			
		||||
    required: false
 | 
			
		||||
    default: 'false'
 | 
			
		||||
  ghToken:
 | 
			
		||||
    description: 'Github token'
 | 
			
		||||
    required: false
 | 
			
		||||
 | 
			
		||||
runs:
 | 
			
		||||
  using: 'composite'
 | 
			
		||||
  steps:
 | 
			
		||||
    # Checkout branch to compare to [required]
 | 
			
		||||
    - name: Checkout base branch
 | 
			
		||||
      uses: actions/checkout@v5
 | 
			
		||||
      with:
 | 
			
		||||
        ref: ${{ inputs.branch }}
 | 
			
		||||
        path: br-base
 | 
			
		||||
        token: ${{ inputs.ghToken }}
 | 
			
		||||
 | 
			
		||||
    # Generate the bundle size difference report [required]
 | 
			
		||||
    - name: Generate report
 | 
			
		||||
      id: bundleSize
 | 
			
		||||
      uses: nejcm/bundle-size-reporter-action@v1.4.1
 | 
			
		||||
      with:
 | 
			
		||||
        paths: ${{ inputs.paths }}
 | 
			
		||||
        onlyDiff: ${{ inputs.onlyDiff }}
 | 
			
		||||
        filter: ${{ inputs.filter }}
 | 
			
		||||
        unit: ${{ inputs.unit }}
 | 
			
		||||
 | 
			
		||||
    # Post github action summary
 | 
			
		||||
    - name: Post summary
 | 
			
		||||
      if: ${{ steps.bundleSize.outputs.hasDifferences == 'true' }} # post only in case of changes
 | 
			
		||||
      run: |
 | 
			
		||||
        echo '${{ steps.bundleSize.outputs.summary }}' >> $GITHUB_STEP_SUMMARY
 | 
			
		||||
      shell: bash
 | 
			
		||||
 | 
			
		||||
    # Post github action comment
 | 
			
		||||
    - name: Post comment
 | 
			
		||||
      uses: marocchino/sticky-pull-request-comment@v2
 | 
			
		||||
      if: ${{ steps.bundleSize.outputs.hasDifferences == 'true' }} # post only in case of changes
 | 
			
		||||
      with:
 | 
			
		||||
        number: ${{ github.event.pull_request.number }}
 | 
			
		||||
        header: ${{ inputs.header }}
 | 
			
		||||
        append: ${{ inputs.append }}
 | 
			
		||||
        message: '${{ steps.bundleSize.outputs.summary }}'
 | 
			
		||||
        GITHUB_TOKEN: ${{ inputs.ghToken }}
 | 
			
		||||
							
								
								
									
										18
									
								
								.github/workflows/checks.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/checks.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,18 +0,0 @@
 | 
			
		||||
name: Checks
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
  pull_request_target:
 | 
			
		||||
    types: [synchronize]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  main:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check if PRs have conflicts
 | 
			
		||||
        uses: eps1lon/actions-label-merge-conflict@v3
 | 
			
		||||
        if: github.repository == ${{ vars.REPO_MAIN }}
 | 
			
		||||
        with:
 | 
			
		||||
          dirtyLabel: "merge-conflicts"
 | 
			
		||||
          repoToken: "${{ secrets.MERGE_CONFLICT_LABEL_PAT }}"
 | 
			
		||||
							
								
								
									
										71
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
# For most projects, this workflow file will not need changing; you simply need
 | 
			
		||||
# to commit it to your repository.
 | 
			
		||||
#
 | 
			
		||||
# You may wish to alter this file to override the set of languages analyzed,
 | 
			
		||||
# or to provide custom queries or build logic.
 | 
			
		||||
#
 | 
			
		||||
# ******** NOTE ********
 | 
			
		||||
# We have attempted to detect the languages in your repository. Please check
 | 
			
		||||
# the `language` matrix defined below to confirm you have the correct set of
 | 
			
		||||
# supported CodeQL languages.
 | 
			
		||||
#
 | 
			
		||||
name: "CodeQL"
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
  pull_request:
 | 
			
		||||
    # The branches below must be a subset of the branches above
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '37 4 * * 1'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  analyze:
 | 
			
		||||
    name: Analyze
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      actions: read
 | 
			
		||||
      contents: read
 | 
			
		||||
      security-events: write
 | 
			
		||||
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        language: [ 'javascript' ]
 | 
			
		||||
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
 | 
			
		||||
        # Learn more:
 | 
			
		||||
        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout repository
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
    # Initializes the CodeQL tools for scanning.
 | 
			
		||||
    - name: Initialize CodeQL
 | 
			
		||||
      uses: github/codeql-action/init@v1
 | 
			
		||||
      with:
 | 
			
		||||
        languages: ${{ matrix.language }}
 | 
			
		||||
        # If you wish to specify custom queries, you can do so here or in a config file.
 | 
			
		||||
        # By default, queries listed here will override any specified in a config file.
 | 
			
		||||
        # Prefix the list here with "+" to use these queries and those in the config file.
 | 
			
		||||
        # queries: ./path/to/local/query, your-org/your-repo/queries@main
 | 
			
		||||
 | 
			
		||||
    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
 | 
			
		||||
    # If this step fails, then you should remove it and run the build manually (see below)
 | 
			
		||||
    - name: Autobuild
 | 
			
		||||
      uses: github/codeql-action/autobuild@v1
 | 
			
		||||
 | 
			
		||||
    # ℹ️ Command-line programs to run using the OS shell.
 | 
			
		||||
    # 📚 https://git.io/JvXDl
 | 
			
		||||
 | 
			
		||||
    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
 | 
			
		||||
    #    and modify them (or add more) to build your code if your project
 | 
			
		||||
    #    uses a compiled language
 | 
			
		||||
 | 
			
		||||
    #- run: |
 | 
			
		||||
    #   make bootstrap
 | 
			
		||||
    #   make release
 | 
			
		||||
 | 
			
		||||
    - name: Perform CodeQL Analysis
 | 
			
		||||
      uses: github/codeql-action/analyze@v1
 | 
			
		||||
							
								
								
									
										100
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										100
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,100 +0,0 @@
 | 
			
		||||
# For most projects, this workflow file will not need changing; you simply need
 | 
			
		||||
# to commit it to your repository.
 | 
			
		||||
#
 | 
			
		||||
# You may wish to alter this file to override the set of languages analyzed,
 | 
			
		||||
# or to provide custom queries or build logic.
 | 
			
		||||
#
 | 
			
		||||
# ******** NOTE ********
 | 
			
		||||
# We have attempted to detect the languages in your repository. Please check
 | 
			
		||||
# the `language` matrix defined below to confirm you have the correct set of
 | 
			
		||||
# supported CodeQL languages.
 | 
			
		||||
#
 | 
			
		||||
name: "CodeQL Advanced"
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ "main" ]
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [ "main" ]
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '20 7 * * 0'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  analyze:
 | 
			
		||||
    name: Analyze (${{ matrix.language }})
 | 
			
		||||
    # Runner size impacts CodeQL analysis time. To learn more, please see:
 | 
			
		||||
    #   - https://gh.io/recommended-hardware-resources-for-running-codeql
 | 
			
		||||
    #   - https://gh.io/supported-runners-and-hardware-resources
 | 
			
		||||
    #   - https://gh.io/using-larger-runners (GitHub.com only)
 | 
			
		||||
    # Consider using larger runners or machines with greater resources for possible analysis time improvements.
 | 
			
		||||
    runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
 | 
			
		||||
    permissions:
 | 
			
		||||
      # required for all workflows
 | 
			
		||||
      security-events: write
 | 
			
		||||
 | 
			
		||||
      # required to fetch internal or private CodeQL packs
 | 
			
		||||
      packages: read
 | 
			
		||||
 | 
			
		||||
      # only required for workflows in private repositories
 | 
			
		||||
      actions: read
 | 
			
		||||
      contents: read
 | 
			
		||||
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
        - language: actions
 | 
			
		||||
          build-mode: none
 | 
			
		||||
        - language: javascript-typescript
 | 
			
		||||
          build-mode: none
 | 
			
		||||
        # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
 | 
			
		||||
        # Use `c-cpp` to analyze code written in C, C++ or both
 | 
			
		||||
        # Use 'java-kotlin' to analyze code written in Java, Kotlin or both
 | 
			
		||||
        # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
 | 
			
		||||
        # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
 | 
			
		||||
        # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
 | 
			
		||||
        # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
 | 
			
		||||
        # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout repository
 | 
			
		||||
      uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
    # Add any setup steps before running the `github/codeql-action/init` action.
 | 
			
		||||
    # This includes steps like installing compilers or runtimes (`actions/setup-node`
 | 
			
		||||
    # or others). This is typically only required for manual builds.
 | 
			
		||||
    # - name: Setup runtime (example)
 | 
			
		||||
    #   uses: actions/setup-example@v1
 | 
			
		||||
 | 
			
		||||
    # Initializes the CodeQL tools for scanning.
 | 
			
		||||
    - name: Initialize CodeQL
 | 
			
		||||
      uses: github/codeql-action/init@v4
 | 
			
		||||
      with:
 | 
			
		||||
        languages: ${{ matrix.language }}
 | 
			
		||||
        build-mode: ${{ matrix.build-mode }}
 | 
			
		||||
        # If you wish to specify custom queries, you can do so here or in a config file.
 | 
			
		||||
        # By default, queries listed here will override any specified in a config file.
 | 
			
		||||
        # Prefix the list here with "+" to use these queries and those in the config file.
 | 
			
		||||
 | 
			
		||||
        # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
 | 
			
		||||
        # queries: security-extended,security-and-quality
 | 
			
		||||
 | 
			
		||||
    # If the analyze step fails for one of the languages you are analyzing with
 | 
			
		||||
    # "We were unable to automatically build your code", modify the matrix above
 | 
			
		||||
    # to set the build mode to "manual" for that language. Then modify this step
 | 
			
		||||
    # to build your code.
 | 
			
		||||
    # ℹ️ Command-line programs to run using the OS shell.
 | 
			
		||||
    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
 | 
			
		||||
    - if: matrix.build-mode == 'manual'
 | 
			
		||||
      shell: bash
 | 
			
		||||
      run: |
 | 
			
		||||
        echo 'If you are using a "manual" build mode for one or more of the' \
 | 
			
		||||
          'languages you are analyzing, replace this with the commands to build' \
 | 
			
		||||
          'your code, for example:'
 | 
			
		||||
        echo '  make bootstrap'
 | 
			
		||||
        echo '  make release'
 | 
			
		||||
        exit 1
 | 
			
		||||
 | 
			
		||||
    - name: Perform CodeQL Analysis
 | 
			
		||||
      uses: github/codeql-action/analyze@v4
 | 
			
		||||
      with:
 | 
			
		||||
        category: "/language:${{matrix.language}}"
 | 
			
		||||
							
								
								
									
										74
									
								
								.github/workflows/deploy-docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										74
									
								
								.github/workflows/deploy-docs.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,74 +0,0 @@
 | 
			
		||||
name: Deploy Documentation
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  # Trigger on push to main branch
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
      - master  # Also support master branch
 | 
			
		||||
    # Only run when docs files change
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'docs/**'
 | 
			
		||||
      - 'apps/edit-docs/**'
 | 
			
		||||
      - 'packages/share-theme/**'
 | 
			
		||||
 | 
			
		||||
  # Allow manual triggering from Actions tab
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
  # Run on pull requests for preview deployments
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
      - master
 | 
			
		||||
    paths:
 | 
			
		||||
      - 'docs/**'
 | 
			
		||||
      - 'apps/edit-docs/**'
 | 
			
		||||
      - 'packages/share-theme/**'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build-and-deploy:
 | 
			
		||||
    name: Build and Deploy Documentation
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    timeout-minutes: 10
 | 
			
		||||
 | 
			
		||||
    # Required permissions for deployment
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      deployments: write
 | 
			
		||||
      pull-requests: write # For PR preview comments
 | 
			
		||||
      id-token: write # For OIDC authentication (if needed)
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout Repository
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
      - name: Setup pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node.js
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: '24'
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
 | 
			
		||||
      - name: Install Dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
 | 
			
		||||
      - name: Trigger build of documentation
 | 
			
		||||
        run: pnpm docs:build
 | 
			
		||||
 | 
			
		||||
      - name: Validate Built Site
 | 
			
		||||
        run: |
 | 
			
		||||
          test -f site/index.html || (echo "ERROR: site/index.html not found" && exit 1)
 | 
			
		||||
 | 
			
		||||
      - name: Deploy
 | 
			
		||||
        uses: ./.github/actions/deploy-to-cloudflare-pages
 | 
			
		||||
        if: github.repository == ${{ vars.REPO_MAIN }}
 | 
			
		||||
        with:
 | 
			
		||||
          project_name: "trilium-docs"
 | 
			
		||||
          comment_body: "📚 Documentation preview is ready"
 | 
			
		||||
          production_url: "https://docs.triliumnotes.org"
 | 
			
		||||
          deploy_dir: "site"
 | 
			
		||||
          cloudflare_api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
 | 
			
		||||
          cloudflare_account_id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
 | 
			
		||||
          github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
							
								
								
									
										128
									
								
								.github/workflows/dev.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										128
									
								
								.github/workflows/dev.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,128 +0,0 @@
 | 
			
		||||
name: Dev
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ main ]
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [ main ]
 | 
			
		||||
 | 
			
		||||
concurrency:
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  GHCR_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKERHUB_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: ${{ github.repository}}
 | 
			
		||||
  TEST_TAG: ${{ github.repository}}:test
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  pull-requests: write  # for PR comments
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  test_dev:
 | 
			
		||||
    name: Test development
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Set up node & dependencies
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 24
 | 
			
		||||
          cache: "pnpm"
 | 
			
		||||
      - run: pnpm install --frozen-lockfile
 | 
			
		||||
 | 
			
		||||
      - name: Typecheck
 | 
			
		||||
        run: pnpm typecheck
 | 
			
		||||
 | 
			
		||||
      - name: Run the unit tests
 | 
			
		||||
        run: pnpm run test:all
 | 
			
		||||
 | 
			
		||||
  build_docker:
 | 
			
		||||
    name: Build Docker image
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - test_dev
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
      - name: Update build info
 | 
			
		||||
        run: pnpm run chore:update-build-info
 | 
			
		||||
      - name: Trigger client build
 | 
			
		||||
        run: pnpm client:build
 | 
			
		||||
      - name: Send client bundle stats to RelativeCI
 | 
			
		||||
        if: false
 | 
			
		||||
        uses: relative-ci/agent-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          webpackStatsFile: ./apps/client/dist/webpack-stats.json
 | 
			
		||||
          key: ${{ secrets.RELATIVE_CI_CLIENT_KEY }}
 | 
			
		||||
      - name: Trigger server build
 | 
			
		||||
        run: pnpm run server:build
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: apps/server
 | 
			
		||||
          cache-from: type=gha
 | 
			
		||||
          cache-to: type=gha,mode=max
 | 
			
		||||
  test_docker:
 | 
			
		||||
    name: Check Docker build
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - build_docker
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
          - dockerfile: Dockerfile.alpine
 | 
			
		||||
          - dockerfile: Dockerfile
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
 | 
			
		||||
      - name: Update build info
 | 
			
		||||
        run: pnpm run chore:update-build-info
 | 
			
		||||
      - name: Trigger build
 | 
			
		||||
        run: pnpm server:build
 | 
			
		||||
 | 
			
		||||
      - name: Set IMAGE_NAME to lowercase
 | 
			
		||||
        run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set TEST_TAG to lowercase
 | 
			
		||||
        run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Build and export to Docker
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: apps/server
 | 
			
		||||
          file: apps/server/${{ matrix.dockerfile }}
 | 
			
		||||
          load: true
 | 
			
		||||
          tags: ${{ env.TEST_TAG }}
 | 
			
		||||
          cache-from: type=gha
 | 
			
		||||
          cache-to: type=gha,mode=max
 | 
			
		||||
 | 
			
		||||
      - name: Validate container run output
 | 
			
		||||
        run: |
 | 
			
		||||
          CONTAINER_ID=$(docker run -d --log-driver=journald --rm --name trilium_local ${{ env.TEST_TAG }})
 | 
			
		||||
          echo "Container ID: $CONTAINER_ID"
 | 
			
		||||
 | 
			
		||||
      - name: Wait for the healthchecks to pass
 | 
			
		||||
        uses: stringbean/docker-healthcheck-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          container: trilium_local
 | 
			
		||||
          wait-time: 50
 | 
			
		||||
          require-status: running
 | 
			
		||||
          require-healthy: true
 | 
			
		||||
 | 
			
		||||
      # Print the entire log of the container thus far, regardless if the healthcheck failed or succeeded
 | 
			
		||||
      - name: Print entire log
 | 
			
		||||
        if: always()
 | 
			
		||||
        run: journalctl -u docker CONTAINER_NAME=trilium_local --no-pager
 | 
			
		||||
							
								
								
									
										53
									
								
								.github/workflows/docker.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								.github/workflows/docker.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
name: Publish Docker image
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    tags: [v*]
 | 
			
		||||
jobs:
 | 
			
		||||
  push_to_registries:
 | 
			
		||||
    name: Push Docker image to multiple registries
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      packages: write
 | 
			
		||||
      contents: read
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v2
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v1
 | 
			
		||||
      - name: Docker meta
 | 
			
		||||
        id: meta
 | 
			
		||||
        uses: docker/metadata-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          images: |
 | 
			
		||||
            zadam/trilium
 | 
			
		||||
            ghcr.io/zadam/trilium
 | 
			
		||||
          tags: |
 | 
			
		||||
            type=semver,pattern={{version}}
 | 
			
		||||
            type=semver,pattern={{major}}.{{minor}}-latest
 | 
			
		||||
            type=match,pattern=(\d+.\d+).\d+\-beta,enable=${{ endsWith(github.ref, 'beta') }},group=1,suffix=-latest
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v1
 | 
			
		||||
        with:
 | 
			
		||||
          install: true
 | 
			
		||||
      - name: Log in to Docker Hub
 | 
			
		||||
        uses: docker/login-action@v1
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
      - name: Log in to GitHub Docker Registry
 | 
			
		||||
        uses: docker/login-action@v1
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.repository_owner }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
      - name: Create server-package.json
 | 
			
		||||
        run: cat package.json | grep -v electron > server-package.json
 | 
			
		||||
      - name: Build and Push
 | 
			
		||||
        uses: docker/build-push-action@v2.7.0
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
 | 
			
		||||
          push: true
 | 
			
		||||
          cache-from: type=registry,ref=zadam/trilium:buildcache
 | 
			
		||||
          cache-to: type=registry,ref=zadam/trilium:buildcache,mode=max
 | 
			
		||||
          tags: ${{ steps.meta.outputs.tags }}
 | 
			
		||||
							
								
								
									
										307
									
								
								.github/workflows/main-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										307
									
								
								.github/workflows/main-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,307 +0,0 @@
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - "main"
 | 
			
		||||
      - "feature/update**"
 | 
			
		||||
      - "feature/server_esm**"
 | 
			
		||||
    paths-ignore:
 | 
			
		||||
      - "docs/**"
 | 
			
		||||
      - "bin/**"
 | 
			
		||||
    tags:
 | 
			
		||||
      - "v*"
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  GHCR_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKERHUB_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: ${{ github.repository}}
 | 
			
		||||
  TEST_TAG: ${{ github.repository}}:test
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
  packages: write
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  test_docker:
 | 
			
		||||
    name: Check Docker build
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
          - dockerfile: Dockerfile.alpine
 | 
			
		||||
          - dockerfile: Dockerfile
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
      - name: Set IMAGE_NAME to lowercase
 | 
			
		||||
        run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set TEST_TAG to lowercase
 | 
			
		||||
        run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Set up node & dependencies
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 24
 | 
			
		||||
          cache: "pnpm"
 | 
			
		||||
 | 
			
		||||
      - name: Install npm dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
 | 
			
		||||
      - name: Install Playwright Browsers
 | 
			
		||||
        run: pnpm exec playwright install --with-deps
 | 
			
		||||
 | 
			
		||||
      - name: Run the TypeScript build
 | 
			
		||||
        run: pnpm run server:build
 | 
			
		||||
 | 
			
		||||
      - name: Build and export to Docker
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: apps/server
 | 
			
		||||
          file: apps/server/${{ matrix.dockerfile }}
 | 
			
		||||
          load: true
 | 
			
		||||
          tags: ${{ env.TEST_TAG }}
 | 
			
		||||
          cache-from: type=gha
 | 
			
		||||
          cache-to: type=gha,mode=max
 | 
			
		||||
 | 
			
		||||
      - name: Validate container run output
 | 
			
		||||
        run: |
 | 
			
		||||
          CONTAINER_ID=$(docker run -d --log-driver=journald --rm --network=host -e TRILIUM_PORT=8082 --volume ./apps/server/spec/db:/home/node/trilium-data --name trilium_local ${{ env.TEST_TAG }})
 | 
			
		||||
          echo "Container ID: $CONTAINER_ID"
 | 
			
		||||
 | 
			
		||||
      - name: Wait for the healthchecks to pass
 | 
			
		||||
        uses: stringbean/docker-healthcheck-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          container: trilium_local
 | 
			
		||||
          wait-time: 50
 | 
			
		||||
          require-status: running
 | 
			
		||||
          require-healthy: true
 | 
			
		||||
 | 
			
		||||
      - name: Run Playwright tests
 | 
			
		||||
        run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm --filter=server-e2e e2e
 | 
			
		||||
 | 
			
		||||
      - name: Upload Playwright trace
 | 
			
		||||
        if: failure()
 | 
			
		||||
        uses: actions/upload-artifact@v5
 | 
			
		||||
        with:
 | 
			
		||||
          name: Playwright trace (${{ matrix.dockerfile }})
 | 
			
		||||
          path: test-output/playwright/output
 | 
			
		||||
 | 
			
		||||
      - uses: actions/upload-artifact@v5
 | 
			
		||||
        if: ${{ !cancelled() }}
 | 
			
		||||
        with:
 | 
			
		||||
          name: Playwright report (${{ matrix.dockerfile }})
 | 
			
		||||
          path: playwright-report/
 | 
			
		||||
          retention-days: 30
 | 
			
		||||
 | 
			
		||||
      # Print the entire log of the container thus far, regardless if the healthcheck failed or succeeded
 | 
			
		||||
      - name: Print entire log
 | 
			
		||||
        if: always()
 | 
			
		||||
        run: |
 | 
			
		||||
          journalctl -u docker CONTAINER_NAME=trilium_local --no-pager
 | 
			
		||||
 | 
			
		||||
  build:
 | 
			
		||||
    name: Build Docker images
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
          - dockerfile: Dockerfile.alpine
 | 
			
		||||
            platform: linux/amd64
 | 
			
		||||
            image: ubuntu-latest
 | 
			
		||||
          - dockerfile: Dockerfile
 | 
			
		||||
            platform: linux/arm64
 | 
			
		||||
            image: ubuntu-24.04-arm
 | 
			
		||||
          - dockerfile: Dockerfile.legacy
 | 
			
		||||
            platform: linux/arm/v7
 | 
			
		||||
            image: ubuntu-24.04-arm
 | 
			
		||||
          - dockerfile: Dockerfile.legacy
 | 
			
		||||
            platform: linux/arm/v8
 | 
			
		||||
            image: ubuntu-24.04-arm
 | 
			
		||||
    runs-on: ${{ matrix.image }}
 | 
			
		||||
    needs:
 | 
			
		||||
      - test_docker
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
      attestations: write
 | 
			
		||||
      id-token: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Prepare
 | 
			
		||||
        run: |
 | 
			
		||||
          platform=${{ matrix.platform }}
 | 
			
		||||
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set IMAGE_NAME to lowercase
 | 
			
		||||
        run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set TEST_TAG to lowercase
 | 
			
		||||
        run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
 | 
			
		||||
 | 
			
		||||
      - name: Checkout repository
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Set up node & dependencies
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 24
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
 | 
			
		||||
      - name: Update build info
 | 
			
		||||
        run: pnpm run chore:update-build-info
 | 
			
		||||
 | 
			
		||||
      - name: Run the TypeScript build
 | 
			
		||||
        run: pnpm run server:build
 | 
			
		||||
 | 
			
		||||
      - name: Docker meta
 | 
			
		||||
        id: meta
 | 
			
		||||
        uses: docker/metadata-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          images: |
 | 
			
		||||
            ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}
 | 
			
		||||
            ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}
 | 
			
		||||
          tags: |
 | 
			
		||||
            type=ref,event=branch
 | 
			
		||||
            type=ref,event=tag
 | 
			
		||||
            type=sha
 | 
			
		||||
          flavor: |
 | 
			
		||||
            latest=false
 | 
			
		||||
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Login to GHCR
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.GHCR_REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Login to DockerHub
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.DOCKERHUB_REGISTRY }}
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKERHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and push by digest
 | 
			
		||||
        id: build
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: apps/server
 | 
			
		||||
          file: apps/server/${{ matrix.dockerfile }}
 | 
			
		||||
          platforms: ${{ matrix.platform }}
 | 
			
		||||
          labels: ${{ steps.meta.outputs.labels }}
 | 
			
		||||
          outputs: type=image,name=${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
 | 
			
		||||
 | 
			
		||||
      - name: Export digest
 | 
			
		||||
        run: |
 | 
			
		||||
          mkdir -p /tmp/digests
 | 
			
		||||
          digest="${{ steps.build.outputs.digest }}"
 | 
			
		||||
          touch "/tmp/digests/${digest#sha256:}"
 | 
			
		||||
 | 
			
		||||
      - name: Upload digest
 | 
			
		||||
        uses: actions/upload-artifact@v5
 | 
			
		||||
        with:
 | 
			
		||||
          name: digests-${{ env.PLATFORM_PAIR }}-${{ matrix.dockerfile }}
 | 
			
		||||
          path: /tmp/digests/*
 | 
			
		||||
          if-no-files-found: error
 | 
			
		||||
          retention-days: 1
 | 
			
		||||
 | 
			
		||||
  merge:
 | 
			
		||||
    name: Merge manifest lists
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - build
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Download digests
 | 
			
		||||
        uses: actions/download-artifact@v6
 | 
			
		||||
        with:
 | 
			
		||||
          path: /tmp/digests
 | 
			
		||||
          pattern: digests-*
 | 
			
		||||
          merge-multiple: true
 | 
			
		||||
      - name: Set IMAGE_NAME to lowercase
 | 
			
		||||
        run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set TEST_TAG to lowercase
 | 
			
		||||
        run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Docker meta
 | 
			
		||||
        id: meta
 | 
			
		||||
        uses: docker/metadata-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          images: |
 | 
			
		||||
            ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}
 | 
			
		||||
            ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}
 | 
			
		||||
          flavor: |
 | 
			
		||||
            latest=false
 | 
			
		||||
 | 
			
		||||
      - name: Login to GHCR
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.GHCR_REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Login to DockerHub
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.DOCKERHUB_REGISTRY }}
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKERHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Create manifest list and push
 | 
			
		||||
        working-directory: /tmp/digests
 | 
			
		||||
        run: |
 | 
			
		||||
          # Extract the branch or tag name from the ref
 | 
			
		||||
          REF_NAME=$(echo "${GITHUB_REF}" | sed 's/refs\/heads\///' | sed 's/refs\/tags\///')
 | 
			
		||||
 | 
			
		||||
          # Create and push the manifest list with both the branch/tag name and the commit SHA
 | 
			
		||||
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
 | 
			
		||||
            -t ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:${REF_NAME} \
 | 
			
		||||
            $(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
 | 
			
		||||
 | 
			
		||||
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
 | 
			
		||||
            -t ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${REF_NAME} \
 | 
			
		||||
            $(printf '${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
 | 
			
		||||
 | 
			
		||||
          # If the ref is a tag, also tag the image as stable as this is part of a 'release'
 | 
			
		||||
          # and only go in the `if` if there is NOT a `-` in the tag's name, due to tagging of `-alpha`, `-beta`, etc...
 | 
			
		||||
          if [[ "${GITHUB_REF}" == refs/tags/* && ! "${REF_NAME}" =~ - ]]; then
 | 
			
		||||
            # First create stable tags
 | 
			
		||||
            docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
 | 
			
		||||
              -t ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:stable \
 | 
			
		||||
              $(printf '${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
 | 
			
		||||
 | 
			
		||||
            docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
 | 
			
		||||
              -t ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:stable \
 | 
			
		||||
              $(printf '${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
 | 
			
		||||
 | 
			
		||||
            # Small delay to ensure stable tag is fully propagated
 | 
			
		||||
            sleep 5
 | 
			
		||||
 | 
			
		||||
            # Now update latest tags
 | 
			
		||||
            docker buildx imagetools create \
 | 
			
		||||
              -t ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
 | 
			
		||||
              ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:stable
 | 
			
		||||
 | 
			
		||||
            docker buildx imagetools create \
 | 
			
		||||
              -t ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
 | 
			
		||||
              ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:stable
 | 
			
		||||
 | 
			
		||||
          fi
 | 
			
		||||
 | 
			
		||||
      - name: Inspect image
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools inspect ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
 | 
			
		||||
          docker buildx imagetools inspect ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
 | 
			
		||||
							
								
								
									
										130
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										130
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,130 +0,0 @@
 | 
			
		||||
name: Nightly Release
 | 
			
		||||
on:
 | 
			
		||||
  # This can be used to automatically publish nightlies at UTC nighttime
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: "0 2 * * *" # run at 2 AM UTC
 | 
			
		||||
  # This can be used to allow manually triggering nightlies from the web interface
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - renovate/electron-forge*
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths:
 | 
			
		||||
      - .github/actions/build-electron/*
 | 
			
		||||
      - .github/workflows/nightly.yml
 | 
			
		||||
      - forge.config.ts
 | 
			
		||||
 | 
			
		||||
concurrency:
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  GITHUB_RELEASE_ID: 179589950
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: write
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  nightly-electron:
 | 
			
		||||
    if: github.repository == ${{ vars.REPO_MAIN }}
 | 
			
		||||
    name: Deploy nightly
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        arch: [x64, arm64]
 | 
			
		||||
        os:
 | 
			
		||||
          - name: macos
 | 
			
		||||
            image: macos-latest
 | 
			
		||||
            shell: bash
 | 
			
		||||
            forge_platform: darwin
 | 
			
		||||
          - name: linux
 | 
			
		||||
            image: ubuntu-22.04
 | 
			
		||||
            shell: bash
 | 
			
		||||
            forge_platform: linux
 | 
			
		||||
          - name: windows
 | 
			
		||||
            image: win-signing
 | 
			
		||||
            shell: cmd
 | 
			
		||||
            forge_platform: win32
 | 
			
		||||
    runs-on: ${{ matrix.os.image }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Set up node & dependencies
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 24
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
      - name: Update nightly version
 | 
			
		||||
        run: npm run chore:ci-update-nightly-version
 | 
			
		||||
      - name: Run the build
 | 
			
		||||
        uses: ./.github/actions/build-electron
 | 
			
		||||
        with:
 | 
			
		||||
          os: ${{ matrix.os.name }}
 | 
			
		||||
          arch: ${{ matrix.arch }}
 | 
			
		||||
          shell: ${{ matrix.os.shell }}
 | 
			
		||||
          forge_platform: ${{ matrix.os.forge_platform }}
 | 
			
		||||
        env:
 | 
			
		||||
          APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }}
 | 
			
		||||
          APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }}
 | 
			
		||||
          APPLE_INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
 | 
			
		||||
          APPLE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
 | 
			
		||||
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
 | 
			
		||||
          APPLE_ID: ${{ secrets.APPLE_ID }}
 | 
			
		||||
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
 | 
			
		||||
          WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
 | 
			
		||||
          GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
 | 
			
		||||
 | 
			
		||||
      - name: Publish release
 | 
			
		||||
        uses: softprops/action-gh-release@v2.4.1
 | 
			
		||||
        if: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
        with:
 | 
			
		||||
          make_latest: false
 | 
			
		||||
          prerelease: true
 | 
			
		||||
          draft: false
 | 
			
		||||
          fail_on_unmatched_files: true
 | 
			
		||||
          files: apps/desktop/upload/*.*
 | 
			
		||||
          tag_name: nightly
 | 
			
		||||
          name: Nightly Build
 | 
			
		||||
 | 
			
		||||
      - name: Publish artifacts
 | 
			
		||||
        uses: actions/upload-artifact@v5
 | 
			
		||||
        if: ${{ github.event_name == 'pull_request' }}
 | 
			
		||||
        with:
 | 
			
		||||
          name: TriliumNotes ${{ matrix.os.name }} ${{ matrix.arch }}
 | 
			
		||||
          path: apps/desktop/upload
 | 
			
		||||
 | 
			
		||||
  nightly-server:
 | 
			
		||||
    if: github.repository == ${{ vars.REPO_MAIN }}
 | 
			
		||||
    name: Deploy server nightly
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        arch: [x64, arm64]
 | 
			
		||||
        include:
 | 
			
		||||
          - arch: x64
 | 
			
		||||
            runs-on: ubuntu-22.04
 | 
			
		||||
          - arch: arm64
 | 
			
		||||
            runs-on: ubuntu-24.04-arm
 | 
			
		||||
    runs-on: ${{ matrix.runs-on }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
      - name: Run the build
 | 
			
		||||
        uses: ./.github/actions/build-server
 | 
			
		||||
        with:
 | 
			
		||||
          os: linux
 | 
			
		||||
          arch: ${{ matrix.arch }}
 | 
			
		||||
 | 
			
		||||
      - name: Publish release
 | 
			
		||||
        uses: softprops/action-gh-release@v2.4.1
 | 
			
		||||
        if: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
        with:
 | 
			
		||||
          make_latest: false
 | 
			
		||||
          prerelease: true
 | 
			
		||||
          draft: false
 | 
			
		||||
          fail_on_unmatched_files: true
 | 
			
		||||
          files: upload/*.*
 | 
			
		||||
          tag_name: nightly
 | 
			
		||||
          name: Nightly Build
 | 
			
		||||
							
								
								
									
										41
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,41 +0,0 @@
 | 
			
		||||
name: playwright
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
    paths-ignore:
 | 
			
		||||
      - "apps/website/**"
 | 
			
		||||
  pull_request:
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  actions: read
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  main:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
        with:
 | 
			
		||||
          filter: tree:0
 | 
			
		||||
          fetch-depth: 0
 | 
			
		||||
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 24
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
      - run: pnpm exec playwright install --with-deps
 | 
			
		||||
 | 
			
		||||
      - run: pnpm --filter server-e2e e2e
 | 
			
		||||
 | 
			
		||||
      - name: Upload test report
 | 
			
		||||
        if: failure()
 | 
			
		||||
        uses: actions/upload-artifact@v5
 | 
			
		||||
        with:
 | 
			
		||||
          name: e2e report
 | 
			
		||||
          path: apps/server-e2e/test-output
 | 
			
		||||
							
								
								
									
										20
									
								
								.github/workflows/release-winget.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								.github/workflows/release-winget.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,20 +0,0 @@
 | 
			
		||||
name: Release to winget
 | 
			
		||||
on:
 | 
			
		||||
  release:
 | 
			
		||||
    types: [ published ]
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
    inputs:
 | 
			
		||||
      release_tag:
 | 
			
		||||
        description: 'Git tag to release from'
 | 
			
		||||
        type: string
 | 
			
		||||
        required: true
 | 
			
		||||
jobs:
 | 
			
		||||
  release-winget:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Publish to WinGet
 | 
			
		||||
        uses: vedantmgoyal9/winget-releaser@main
 | 
			
		||||
        with:
 | 
			
		||||
          identifier: TriliumNext.Notes
 | 
			
		||||
          token: ${{ secrets.WINGET_PAT  }}
 | 
			
		||||
          release-tag:  ${{ github.event.inputs.release_tag || github.event.release.tag_name }}
 | 
			
		||||
							
								
								
									
										139
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										139
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,139 +0,0 @@
 | 
			
		||||
name: Release
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    tags:
 | 
			
		||||
      - "v*"
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: write
 | 
			
		||||
  discussions: write
 | 
			
		||||
concurrency:
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  make-electron:
 | 
			
		||||
    name: Make Electron
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        arch: [x64, arm64]
 | 
			
		||||
        os:
 | 
			
		||||
          - name: macos
 | 
			
		||||
            image: macos-latest
 | 
			
		||||
            shell: bash
 | 
			
		||||
            forge_platform: darwin
 | 
			
		||||
          - name: linux
 | 
			
		||||
            image: ubuntu-22.04
 | 
			
		||||
            shell: bash
 | 
			
		||||
            forge_platform: linux
 | 
			
		||||
          - name: windows
 | 
			
		||||
            image: win-signing
 | 
			
		||||
            shell: cmd
 | 
			
		||||
            forge_platform: win32
 | 
			
		||||
        # Exclude ARM64 Linux from default matrix to use native runner
 | 
			
		||||
        exclude:
 | 
			
		||||
          - arch: arm64
 | 
			
		||||
            os:
 | 
			
		||||
              name: linux
 | 
			
		||||
        # Add ARM64 Linux with native ubuntu-24.04-arm runner for better-sqlite3 compatibility
 | 
			
		||||
        include:
 | 
			
		||||
          - arch: arm64
 | 
			
		||||
            os:
 | 
			
		||||
              name: linux
 | 
			
		||||
              image: ubuntu-24.04-arm
 | 
			
		||||
              shell: bash
 | 
			
		||||
              forge_platform: linux
 | 
			
		||||
    runs-on: ${{ matrix.os.image }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Set up node & dependencies
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 24
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: pnpm install --frozen-lockfile
 | 
			
		||||
      - name: Run the build
 | 
			
		||||
        uses: ./.github/actions/build-electron
 | 
			
		||||
        with:
 | 
			
		||||
          os: ${{ matrix.os.name }}
 | 
			
		||||
          arch: ${{ matrix.arch }}
 | 
			
		||||
          shell: ${{ matrix.os.shell }}
 | 
			
		||||
          forge_platform: ${{ matrix.os.forge_platform }}
 | 
			
		||||
        env:
 | 
			
		||||
          APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }}
 | 
			
		||||
          APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }}
 | 
			
		||||
          APPLE_INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
 | 
			
		||||
          APPLE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
 | 
			
		||||
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
 | 
			
		||||
          APPLE_ID: ${{ secrets.APPLE_ID }}
 | 
			
		||||
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
 | 
			
		||||
          WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
 | 
			
		||||
          GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
 | 
			
		||||
 | 
			
		||||
      - name: Upload the artifact
 | 
			
		||||
        uses: actions/upload-artifact@v5
 | 
			
		||||
        with:
 | 
			
		||||
          name: release-desktop-${{ matrix.os.name }}-${{ matrix.arch }}
 | 
			
		||||
          path: apps/desktop/upload/*.*
 | 
			
		||||
 | 
			
		||||
  build_server:
 | 
			
		||||
    name: Build Linux Server
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        arch: [x64, arm64]
 | 
			
		||||
        include:
 | 
			
		||||
          - arch: x64
 | 
			
		||||
            runs-on: ubuntu-22.04
 | 
			
		||||
          - arch: arm64
 | 
			
		||||
            runs-on: ubuntu-24.04-arm
 | 
			
		||||
    runs-on: ${{ matrix.runs-on }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
      - name: Run the build
 | 
			
		||||
        uses: ./.github/actions/build-server
 | 
			
		||||
        with:
 | 
			
		||||
          os: linux
 | 
			
		||||
          arch: ${{ matrix.arch }}
 | 
			
		||||
 | 
			
		||||
      - name: Upload the artifact
 | 
			
		||||
        uses: actions/upload-artifact@v5
 | 
			
		||||
        with:
 | 
			
		||||
          name: release-server-linux-${{ matrix.arch }}
 | 
			
		||||
          path: upload/*.*
 | 
			
		||||
 | 
			
		||||
  publish_release:
 | 
			
		||||
    name: Publish release
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - make-electron
 | 
			
		||||
      - build_server
 | 
			
		||||
    steps:
 | 
			
		||||
      - run: mkdir upload
 | 
			
		||||
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
        with:
 | 
			
		||||
          sparse-checkout: |
 | 
			
		||||
            docs/Release Notes
 | 
			
		||||
 | 
			
		||||
      - name: Download all artifacts
 | 
			
		||||
        uses: actions/download-artifact@v6
 | 
			
		||||
        with:
 | 
			
		||||
          merge-multiple: true
 | 
			
		||||
          pattern: release-*
 | 
			
		||||
          path: upload
 | 
			
		||||
 | 
			
		||||
      - name: Publish stable release
 | 
			
		||||
        uses: softprops/action-gh-release@v2.4.1
 | 
			
		||||
        with:
 | 
			
		||||
          draft: false
 | 
			
		||||
          body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md
 | 
			
		||||
          fail_on_unmatched_files: true
 | 
			
		||||
          files: upload/*.*
 | 
			
		||||
          discussion_category_name: Releases
 | 
			
		||||
          make_latest: ${{ !contains(github.ref, 'rc') }}
 | 
			
		||||
          prerelease: ${{ contains(github.ref, 'rc') }}
 | 
			
		||||
          token: ${{ secrets.RELEASE_PAT }}
 | 
			
		||||
							
								
								
									
										11
									
								
								.github/workflows/unblock_signing.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/unblock_signing.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,11 +0,0 @@
 | 
			
		||||
name: Unblock signing
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  unblock-win-signing:
 | 
			
		||||
    runs-on: win-signing
 | 
			
		||||
    steps:
 | 
			
		||||
      - run: |
 | 
			
		||||
          cat ${{ vars.WINDOWS_SIGN_ERROR_LOG }}
 | 
			
		||||
          rm ${{ vars.WINDOWS_SIGN_ERROR_LOG }}
 | 
			
		||||
							
								
								
									
										51
									
								
								.github/workflows/website.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								.github/workflows/website.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,51 +0,0 @@
 | 
			
		||||
name: Deploy website
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
    paths:
 | 
			
		||||
      - "apps/website/**"
 | 
			
		||||
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths:
 | 
			
		||||
      - "apps/website/**"
 | 
			
		||||
 | 
			
		||||
  release:
 | 
			
		||||
    types: [ released ]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  build-and-deploy:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Build & deploy website
 | 
			
		||||
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      deployments: write
 | 
			
		||||
      pull-requests: write # For PR preview comments
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v5
 | 
			
		||||
      - uses: pnpm/action-setup@v4
 | 
			
		||||
      - name: Set up node & dependencies
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 24
 | 
			
		||||
          cache: "pnpm"
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: pnpm install --filter website --frozen-lockfile
 | 
			
		||||
 | 
			
		||||
      - name: Build the website
 | 
			
		||||
        run: pnpm website:build
 | 
			
		||||
 | 
			
		||||
      - name: Deploy
 | 
			
		||||
        uses: ./.github/actions/deploy-to-cloudflare-pages
 | 
			
		||||
        with:
 | 
			
		||||
          project_name: "trilium-homepage"
 | 
			
		||||
          comment_body: "📚 Website preview is ready"
 | 
			
		||||
          production_url: "https://triliumnotes.org"
 | 
			
		||||
          deploy_dir: "apps/website/dist"
 | 
			
		||||
          cloudflare_api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
 | 
			
		||||
          cloudflare_account_id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
 | 
			
		||||
          github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
							
								
								
									
										67
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,55 +1,16 @@
 | 
			
		||||
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
 | 
			
		||||
/.cache
 | 
			
		||||
 | 
			
		||||
# compiled output
 | 
			
		||||
dist
 | 
			
		||||
tmp
 | 
			
		||||
out-tsc
 | 
			
		||||
 | 
			
		||||
# dependencies
 | 
			
		||||
node_modules
 | 
			
		||||
 | 
			
		||||
# IDEs and editors
 | 
			
		||||
/.idea
 | 
			
		||||
.idea
 | 
			
		||||
.project
 | 
			
		||||
.classpath
 | 
			
		||||
.c9/
 | 
			
		||||
*.launch
 | 
			
		||||
.settings/
 | 
			
		||||
*.sublime-workspace
 | 
			
		||||
 | 
			
		||||
# misc
 | 
			
		||||
/.sass-cache
 | 
			
		||||
/connect.lock
 | 
			
		||||
/coverage
 | 
			
		||||
/libpeerconnection.log
 | 
			
		||||
.DS_Store
 | 
			
		||||
node_modules/
 | 
			
		||||
dist/
 | 
			
		||||
src/public/app-dist/
 | 
			
		||||
npm-debug.log
 | 
			
		||||
yarn-error.log
 | 
			
		||||
testem.log
 | 
			
		||||
/typings
 | 
			
		||||
 | 
			
		||||
# System Files
 | 
			
		||||
.DS_Store
 | 
			
		||||
Thumbs.db
 | 
			
		||||
 | 
			
		||||
vite.config.*.timestamp*
 | 
			
		||||
vitest.config.*.timestamp*
 | 
			
		||||
test-output
 | 
			
		||||
 | 
			
		||||
apps/*/data*
 | 
			
		||||
apps/*/out
 | 
			
		||||
upload
 | 
			
		||||
 | 
			
		||||
.rollup.cache
 | 
			
		||||
*.tsbuildinfo
 | 
			
		||||
 | 
			
		||||
/result
 | 
			
		||||
.svelte-kit
 | 
			
		||||
 | 
			
		||||
# docs
 | 
			
		||||
site/
 | 
			
		||||
 | 
			
		||||
# TypeScript and JavaScript maps
 | 
			
		||||
*.js.map
 | 
			
		||||
*.d.ts.map
 | 
			
		||||
*.db
 | 
			
		||||
config.ini
 | 
			
		||||
cert.key
 | 
			
		||||
cert.crt
 | 
			
		||||
server-package.json
 | 
			
		||||
.idea/httpRequests/
 | 
			
		||||
data/
 | 
			
		||||
data-test/
 | 
			
		||||
tmp/
 | 
			
		||||
.eslintcache
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								.gitpod.dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.gitpod.dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
FROM gitpod/workspace-full
 | 
			
		||||
 | 
			
		||||
RUN sudo apt-get update \
 | 
			
		||||
    && sudo apt-get install -yq --no-install-recommends \
 | 
			
		||||
        libpng16-16 \
 | 
			
		||||
        libpng-dev \
 | 
			
		||||
        pkg-config \
 | 
			
		||||
        autoconf \
 | 
			
		||||
        libtool \
 | 
			
		||||
        build-essential \
 | 
			
		||||
        nasm \
 | 
			
		||||
        libx11-dev \
 | 
			
		||||
        libxkbfile-dev \
 | 
			
		||||
    && sudo rm -rf /var/lib/apt/lists/*
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
image:
 | 
			
		||||
  file: .gitpod.dockerfile
 | 
			
		||||
 | 
			
		||||
tasks:
 | 
			
		||||
    - before: nvm install 18.18.2 && nvm use 18.18.2
 | 
			
		||||
      init: npm install
 | 
			
		||||
      command: npm run start-server
 | 
			
		||||
 | 
			
		||||
ports:
 | 
			
		||||
    - port: 8080
 | 
			
		||||
      onOpen: open-preview
 | 
			
		||||
							
								
								
									
										1
									
								
								.husky/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.husky/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
_
 | 
			
		||||
							
								
								
									
										4
									
								
								.husky/pre-commit
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.husky/pre-commit
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
. "$(dirname "$0")/_/husky.sh"
 | 
			
		||||
 | 
			
		||||
#npx lint-staged
 | 
			
		||||
							
								
								
									
										6
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# Default ignored files
 | 
			
		||||
/workspace.xml
 | 
			
		||||
 | 
			
		||||
# Datasource local storage ignored files
 | 
			
		||||
/dataSources.local.xml
 | 
			
		||||
/dataSources/
 | 
			
		||||
							
								
								
									
										13
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
<component name="ProjectCodeStyleConfiguration">
 | 
			
		||||
  <code_scheme name="Project" version="173">
 | 
			
		||||
    <option name="OTHER_INDENT_OPTIONS">
 | 
			
		||||
      <value>
 | 
			
		||||
        <option name="INDENT_SIZE" value="2" />
 | 
			
		||||
        <option name="TAB_SIZE" value="2" />
 | 
			
		||||
      </value>
 | 
			
		||||
    </option>
 | 
			
		||||
    <JSCodeStyleSettings version="0">
 | 
			
		||||
      <option name="USE_EXPLICIT_JS_EXTENSION" value="TRUE" />
 | 
			
		||||
    </JSCodeStyleSettings>
 | 
			
		||||
  </code_scheme>
 | 
			
		||||
</component>
 | 
			
		||||
							
								
								
									
										5
									
								
								.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<component name="ProjectCodeStyleConfiguration">
 | 
			
		||||
  <state>
 | 
			
		||||
    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
 | 
			
		||||
  </state>
 | 
			
		||||
</component>
 | 
			
		||||
							
								
								
									
										12
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
 | 
			
		||||
    <data-source source="LOCAL" name="document.db" uuid="2a4ac1e6-b828-4a2a-8e4a-3f59f10aff26">
 | 
			
		||||
      <driver-ref>sqlite.xerial</driver-ref>
 | 
			
		||||
      <synchronize>true</synchronize>
 | 
			
		||||
      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
			
		||||
      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/data/document.db</jdbc-url>
 | 
			
		||||
      <working-dir>$ProjectFileDir$</working-dir>
 | 
			
		||||
    </data-source>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										4
									
								
								.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="Encoding" addBOMForNewFiles="with NO BOM" />
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										15
									
								
								.idea/git_toolbox_prj.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.idea/git_toolbox_prj.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="GitToolBoxProjectSettings">
 | 
			
		||||
    <option name="commitMessageIssueKeyValidationOverride">
 | 
			
		||||
      <BoolValueOverride>
 | 
			
		||||
        <option name="enabled" value="true" />
 | 
			
		||||
      </BoolValueOverride>
 | 
			
		||||
    </option>
 | 
			
		||||
    <option name="commitMessageValidationEnabledOverride">
 | 
			
		||||
      <BoolValueOverride>
 | 
			
		||||
        <option name="enabled" value="true" />
 | 
			
		||||
      </BoolValueOverride>
 | 
			
		||||
    </option>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										11
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
<component name="InspectionProjectProfileManager">
 | 
			
		||||
  <profile version="1.0">
 | 
			
		||||
    <option name="myName" value="Project Default" />
 | 
			
		||||
    <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
 | 
			
		||||
    <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
 | 
			
		||||
      <option name="processCode" value="true" />
 | 
			
		||||
      <option name="processLiterals" value="true" />
 | 
			
		||||
      <option name="processComments" value="true" />
 | 
			
		||||
    </inspection_tool>
 | 
			
		||||
  </profile>
 | 
			
		||||
</component>
 | 
			
		||||
							
								
								
									
										6
									
								
								.idea/jsLibraryMappings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/jsLibraryMappings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="JavaScriptLibraryMappings">
 | 
			
		||||
    <includedPredefinedLibrary name="Node.js Core" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										6
									
								
								.idea/jsLinters/eslint.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/jsLinters/eslint.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="EslintConfiguration">
 | 
			
		||||
    <option name="fix-on-save" value="true" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										9
									
								
								.idea/jsLinters/jslint.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.idea/jsLinters/jslint.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="JSLintConfiguration">
 | 
			
		||||
    <option devel="true" />
 | 
			
		||||
    <option es6="true" />
 | 
			
		||||
    <option maxerr="50" />
 | 
			
		||||
    <option node="true" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										8
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="JavaScriptSettings">
 | 
			
		||||
    <option name="languageLevel" value="ES6" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="ProjectRootManager" version="2" languageLevel="JDK_16" default="true" project-jdk-name="openjdk-16" project-jdk-type="JavaSDK">
 | 
			
		||||
    <output url="file://$PROJECT_DIR$/out" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="ProjectModuleManager">
 | 
			
		||||
    <modules>
 | 
			
		||||
      <module fileurl="file://$PROJECT_DIR$/trilium.iml" filepath="$PROJECT_DIR$/trilium.iml" />
 | 
			
		||||
    </modules>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										7
									
								
								.idea/sqldialects.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.idea/sqldialects.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="SqlDialectMappings">
 | 
			
		||||
    <file url="file://$PROJECT_DIR$" dialect="SQLite" />
 | 
			
		||||
    <file url="PROJECT" dialect="SQLite" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="VcsDirectoryMappings">
 | 
			
		||||
    <mapping directory="" vcs="Git" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										13
									
								
								.prettierrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.prettierrc.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
//https://prettier.io/docs/en/options.html
 | 
			
		||||
module.exports = {
 | 
			
		||||
	semi: true,
 | 
			
		||||
	trailingComma: 'none',
 | 
			
		||||
	singleQuote: true,
 | 
			
		||||
	printWidth: 100,
 | 
			
		||||
	tabWidth: 4,
 | 
			
		||||
	useTabs: false,
 | 
			
		||||
	quoteProps: "as-needed",
 | 
			
		||||
	bracketSpacing: true,
 | 
			
		||||
	arrowParens: "avoid"
 | 
			
		||||
	// htmlWhitespaceSensitivity: 'ignore',
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										11
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							@@ -1,15 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "recommendations": [
 | 
			
		||||
    "dbaeumer.vscode-eslint",
 | 
			
		||||
    "editorconfig.editorconfig",
 | 
			
		||||
    "lokalise.i18n-ally",
 | 
			
		||||
    "ms-azuretools.vscode-docker",
 | 
			
		||||
    "ms-playwright.playwright",
 | 
			
		||||
    "redhat.vscode-yaml",
 | 
			
		||||
    "tobermory.es6-string-html",
 | 
			
		||||
    "vitest.explorer",
 | 
			
		||||
    "yzhang.markdown-all-in-one",
 | 
			
		||||
    "svelte.svelte-vscode",
 | 
			
		||||
    "bradlc.vscode-tailwindcss"
 | 
			
		||||
    "esbenp.prettier-vscode",
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								.vscode/i18n-ally-custom-framework.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								.vscode/i18n-ally-custom-framework.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,36 +0,0 @@
 | 
			
		||||
# An array of strings which contain Language Ids defined by VS Code
 | 
			
		||||
# You can check available language ids here: https://code.visualstudio.com/docs/languages/identifiers
 | 
			
		||||
languageIds:
 | 
			
		||||
  - javascript
 | 
			
		||||
  - typescript
 | 
			
		||||
  - typescriptreact
 | 
			
		||||
  - html
 | 
			
		||||
 | 
			
		||||
# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
 | 
			
		||||
# You should unescape RegEx strings in order to fit in the YAML file
 | 
			
		||||
# To help with this, you can use https://www.freeformatter.com/json-escape.html
 | 
			
		||||
usageMatchRegex:
 | 
			
		||||
  # The following example shows how to detect `t("your.i18n.keys")`
 | 
			
		||||
  # the `{key}` will be placed by a proper keypath matching regex,
 | 
			
		||||
  # you can ignore it and use your own matching rules as well
 | 
			
		||||
  - "[^\\w\\d]t\\(['\"`]({key})['\"`]"
 | 
			
		||||
  - <Trans\s*i18nKey="({key})"[^>]*>
 | 
			
		||||
 | 
			
		||||
# A RegEx to set a custom scope range. This scope will be used as a prefix when detecting keys
 | 
			
		||||
# and works like how the i18next framework identifies the namespace scope from the
 | 
			
		||||
# useTranslation() hook.
 | 
			
		||||
# You should unescape RegEx strings in order to fit in the YAML file
 | 
			
		||||
# To help with this, you can use https://www.freeformatter.com/json-escape.html
 | 
			
		||||
scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
 | 
			
		||||
 | 
			
		||||
# An array of strings containing refactor templates.
 | 
			
		||||
# The "$1" will be replaced by the keypath specified.
 | 
			
		||||
refactorTemplates:
 | 
			
		||||
  - t("$1")
 | 
			
		||||
  - {t("$1")}
 | 
			
		||||
  - ${t("$1")}
 | 
			
		||||
  - <%= t("$1") %>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# If set to true, only enables this custom framework (will disable all built-in frameworks)
 | 
			
		||||
monopoly: true
 | 
			
		||||
							
								
								
									
										24
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
{
 | 
			
		||||
    "version": "0.2.0",
 | 
			
		||||
    "configurations": [
 | 
			
		||||
        // nodemon should be installed globally, use npm i -g nodemon
 | 
			
		||||
        {
 | 
			
		||||
            "console": "integratedTerminal",
 | 
			
		||||
            "internalConsoleOptions": "neverOpen",
 | 
			
		||||
            "name": "nodemon start-server",
 | 
			
		||||
            "program": "${workspaceFolder}/src/www",
 | 
			
		||||
            "request": "launch",
 | 
			
		||||
            "restart": true,
 | 
			
		||||
            "runtimeExecutable": "nodemon",
 | 
			
		||||
            "env": {
 | 
			
		||||
                "TRILIUM_ENV": "dev",
 | 
			
		||||
                "TRILIUM_DATA_DIR": "./data"
 | 
			
		||||
            },
 | 
			
		||||
            "skipFiles": [
 | 
			
		||||
                "<node_internals>/**"
 | 
			
		||||
            ],
 | 
			
		||||
            "type": "node",
 | 
			
		||||
            "outputCapture": "std",
 | 
			
		||||
        },
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@@ -1,40 +1,33 @@
 | 
			
		||||
{
 | 
			
		||||
    "editor.formatOnSave": false,
 | 
			
		||||
    "files.eol": "\n",
 | 
			
		||||
    "i18n-ally.sourceLanguage": "en",
 | 
			
		||||
    "i18n-ally.keystyle": "nested",
 | 
			
		||||
    "i18n-ally.localesPaths": [
 | 
			
		||||
        "apps/server/src/assets/translations",
 | 
			
		||||
        "apps/client/src/translations",
 | 
			
		||||
        "apps/website/public/translations"
 | 
			
		||||
    ],
 | 
			
		||||
    "npm.exclude": [
 | 
			
		||||
        "**/dist",
 | 
			
		||||
    ],
 | 
			
		||||
    "[jsonc]": {
 | 
			
		||||
        "editor.defaultFormatter": "vscode.json-language-features"
 | 
			
		||||
    },
 | 
			
		||||
    "[javascript]": {
 | 
			
		||||
        "editor.defaultFormatter": "vscode.typescript-language-features"
 | 
			
		||||
    },
 | 
			
		||||
    "[typescript]": {
 | 
			
		||||
        "editor.defaultFormatter": "vscode.typescript-language-features"
 | 
			
		||||
    },
 | 
			
		||||
    "[css]": {
 | 
			
		||||
        "editor.defaultFormatter": "vscode.css-language-features"
 | 
			
		||||
    },
 | 
			
		||||
    "github-actions.workflows.pinned.workflows": [
 | 
			
		||||
        ".github/workflows/nightly.yml"
 | 
			
		||||
    ],
 | 
			
		||||
    "typescript.validate.enable": true,
 | 
			
		||||
    "typescript.tsserver.experimental.enableProjectDiagnostics": true,
 | 
			
		||||
    "typescript.tsdk": "node_modules/typescript/lib",
 | 
			
		||||
    "typescript.enablePromptUseWorkspaceTsdk": true,
 | 
			
		||||
    "search.exclude": {
 | 
			
		||||
        "**/node_modules": true,
 | 
			
		||||
        "docs/**/*.html": true,
 | 
			
		||||
        "docs/**/*.png": true,
 | 
			
		||||
        "apps/server/src/assets/doc_notes/**": true,
 | 
			
		||||
        "apps/edit-docs/demo/**": true
 | 
			
		||||
    }
 | 
			
		||||
  "[javascript]": {
 | 
			
		||||
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
 | 
			
		||||
  },
 | 
			
		||||
  "[json]": {
 | 
			
		||||
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
 | 
			
		||||
  },
 | 
			
		||||
  "editor.formatOnSave": true,
 | 
			
		||||
  "eslint.format.enable": true,
 | 
			
		||||
  "eslint.probe": [
 | 
			
		||||
    "javascript",
 | 
			
		||||
    "javascriptreact",
 | 
			
		||||
    "typescript",
 | 
			
		||||
    "typescriptreact",
 | 
			
		||||
    "html",
 | 
			
		||||
    "vue",
 | 
			
		||||
    "markdown",
 | 
			
		||||
    "json",
 | 
			
		||||
    "jsonc"
 | 
			
		||||
  ],
 | 
			
		||||
  "eslint.validate": [
 | 
			
		||||
    "javascript",
 | 
			
		||||
    "javascriptreact",
 | 
			
		||||
    "typescript",
 | 
			
		||||
    "typescriptreact",
 | 
			
		||||
    "html",
 | 
			
		||||
    "vue",
 | 
			
		||||
    "markdown",
 | 
			
		||||
    "json",
 | 
			
		||||
    "jsonc"
 | 
			
		||||
  ],
 | 
			
		||||
  "files.eol": "\n",
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								.vscode/snippets.code-snippets
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								.vscode/snippets.code-snippets
									
									
									
									
										vendored
									
									
								
							@@ -1,29 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    // Place your Notes workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
 | 
			
		||||
    // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
 | 
			
		||||
    // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
 | 
			
		||||
    // used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
 | 
			
		||||
    // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
 | 
			
		||||
    // Placeholders with the same ids are connected.
 | 
			
		||||
    // Example:
 | 
			
		||||
    // "Print to console": {
 | 
			
		||||
    // 	"scope": "javascript,typescript",
 | 
			
		||||
    // 	"prefix": "log",
 | 
			
		||||
    // 	"body": [
 | 
			
		||||
    // 		"console.log('$1');",
 | 
			
		||||
    // 		"$2"
 | 
			
		||||
    // 	],
 | 
			
		||||
    // 	"description": "Log output to console"
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    "JQuery HTMLElement field": {
 | 
			
		||||
        "scope": "typescript",
 | 
			
		||||
        "prefix": "jqf",
 | 
			
		||||
        "body": ["private $${1:name}!: JQuery<HTMLElement>;"]
 | 
			
		||||
    },
 | 
			
		||||
    "region": {
 | 
			
		||||
        "scope": "css",
 | 
			
		||||
        "prefix": "region",
 | 
			
		||||
        "body": ["/* #region ${1:name} */\n$0\n/* #endregion */"]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								CLAUDE.md
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								CLAUDE.md
									
									
									
									
									
								
							@@ -1,152 +0,0 @@
 | 
			
		||||
# CLAUDE.md
 | 
			
		||||
 | 
			
		||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
 | 
			
		||||
 | 
			
		||||
## Overview
 | 
			
		||||
 | 
			
		||||
Trilium Notes is a hierarchical note-taking application with advanced features like synchronization, scripting, and rich text editing. It's built as a TypeScript monorepo using pnpm, with multiple applications and shared packages.
 | 
			
		||||
 | 
			
		||||
## Development Commands
 | 
			
		||||
 | 
			
		||||
### Setup
 | 
			
		||||
- `pnpm install` - Install all dependencies
 | 
			
		||||
- `corepack enable` - Enable pnpm if not available
 | 
			
		||||
 | 
			
		||||
### Running Applications
 | 
			
		||||
- `pnpm run server:start` - Start development server (http://localhost:8080)
 | 
			
		||||
- `pnpm run server:start-prod` - Run server in production mode
 | 
			
		||||
 | 
			
		||||
### Building
 | 
			
		||||
- `pnpm run client:build` - Build client application
 | 
			
		||||
- `pnpm run server:build` - Build server application
 | 
			
		||||
- `pnpm run electron:build` - Build desktop application
 | 
			
		||||
 | 
			
		||||
### Testing
 | 
			
		||||
- `pnpm test:all` - Run all tests (parallel + sequential)
 | 
			
		||||
- `pnpm test:parallel` - Run tests that can run in parallel
 | 
			
		||||
- `pnpm test:sequential` - Run tests that must run sequentially (server, ckeditor5-mermaid, ckeditor5-math)
 | 
			
		||||
- `pnpm coverage` - Generate coverage reports
 | 
			
		||||
 | 
			
		||||
## Architecture Overview
 | 
			
		||||
 | 
			
		||||
### Monorepo Structure
 | 
			
		||||
- **apps/**: Runnable applications
 | 
			
		||||
  - `client/` - Frontend application (shared by server and desktop)
 | 
			
		||||
  - `server/` - Node.js server with web interface
 | 
			
		||||
  - `desktop/` - Electron desktop application
 | 
			
		||||
  - `web-clipper/` - Browser extension for saving web content
 | 
			
		||||
  - Additional tools: `db-compare`, `dump-db`, `edit-docs`
 | 
			
		||||
 | 
			
		||||
- **packages/**: Shared libraries
 | 
			
		||||
  - `commons/` - Shared interfaces and utilities
 | 
			
		||||
  - `ckeditor5/` - Custom rich text editor with Trilium-specific plugins
 | 
			
		||||
  - `codemirror/` - Code editor customizations
 | 
			
		||||
  - `highlightjs/` - Syntax highlighting
 | 
			
		||||
  - Custom CKEditor plugins: `ckeditor5-admonition`, `ckeditor5-footnotes`, `ckeditor5-math`, `ckeditor5-mermaid`
 | 
			
		||||
 | 
			
		||||
### Core Architecture Patterns
 | 
			
		||||
 | 
			
		||||
#### Three-Layer Cache System
 | 
			
		||||
- **Becca** (Backend Cache): Server-side entity cache (`apps/server/src/becca/`)
 | 
			
		||||
- **Froca** (Frontend Cache): Client-side mirror of backend data (`apps/client/src/services/froca.ts`)
 | 
			
		||||
- **Shaca** (Share Cache): Optimized cache for shared/published notes (`apps/server/src/share/`)
 | 
			
		||||
 | 
			
		||||
#### Entity System
 | 
			
		||||
Core entities are defined in `apps/server/src/becca/entities/`:
 | 
			
		||||
- `BNote` - Notes with content and metadata
 | 
			
		||||
- `BBranch` - Hierarchical relationships between notes (allows multiple parents)
 | 
			
		||||
- `BAttribute` - Key-value metadata attached to notes
 | 
			
		||||
- `BRevision` - Note version history
 | 
			
		||||
- `BOption` - Application configuration
 | 
			
		||||
 | 
			
		||||
#### Widget-Based UI
 | 
			
		||||
Frontend uses a widget system (`apps/client/src/widgets/`):
 | 
			
		||||
- `BasicWidget` - Base class for all UI components
 | 
			
		||||
- `NoteContextAwareWidget` - Widgets that respond to note changes
 | 
			
		||||
- `RightPanelWidget` - Widgets displayed in the right panel
 | 
			
		||||
- Type-specific widgets in `type_widgets/` directory
 | 
			
		||||
 | 
			
		||||
#### API Architecture
 | 
			
		||||
- **Internal API**: REST endpoints in `apps/server/src/routes/api/`
 | 
			
		||||
- **ETAPI**: External API for third-party integrations (`apps/server/src/etapi/`)
 | 
			
		||||
- **WebSocket**: Real-time synchronization (`apps/server/src/services/ws.ts`)
 | 
			
		||||
 | 
			
		||||
### Key Files for Understanding Architecture
 | 
			
		||||
 | 
			
		||||
1. **Application Entry Points**:
 | 
			
		||||
   - `apps/server/src/main.ts` - Server startup
 | 
			
		||||
   - `apps/client/src/desktop.ts` - Client initialization
 | 
			
		||||
 | 
			
		||||
2. **Core Services**:
 | 
			
		||||
   - `apps/server/src/becca/becca.ts` - Backend data management
 | 
			
		||||
   - `apps/client/src/services/froca.ts` - Frontend data synchronization
 | 
			
		||||
   - `apps/server/src/services/backend_script_api.ts` - Scripting API
 | 
			
		||||
 | 
			
		||||
3. **Database Schema**:
 | 
			
		||||
   - `apps/server/src/assets/db/schema.sql` - Core database structure
 | 
			
		||||
 | 
			
		||||
4. **Configuration**:
 | 
			
		||||
   - `package.json` - Project dependencies and scripts
 | 
			
		||||
 | 
			
		||||
## Note Types and Features
 | 
			
		||||
 | 
			
		||||
Trilium supports multiple note types, each with specialized widgets:
 | 
			
		||||
- **Text**: Rich text with CKEditor5 (markdown import/export)
 | 
			
		||||
- **Code**: Syntax-highlighted code editing with CodeMirror
 | 
			
		||||
- **File**: Binary file attachments
 | 
			
		||||
- **Image**: Image display with editing capabilities
 | 
			
		||||
- **Canvas**: Drawing/diagramming with Excalidraw
 | 
			
		||||
- **Mermaid**: Diagram generation
 | 
			
		||||
- **Relation Map**: Visual note relationship mapping
 | 
			
		||||
- **Web View**: Embedded web pages
 | 
			
		||||
- **Doc/Book**: Hierarchical documentation structure
 | 
			
		||||
 | 
			
		||||
## Development Guidelines
 | 
			
		||||
 | 
			
		||||
### Testing Strategy
 | 
			
		||||
- Server tests run sequentially due to shared database
 | 
			
		||||
- Client tests can run in parallel
 | 
			
		||||
- E2E tests use Playwright for both server and desktop apps
 | 
			
		||||
- Build validation tests check artifact integrity
 | 
			
		||||
 | 
			
		||||
### Scripting System
 | 
			
		||||
Trilium provides powerful user scripting capabilities:
 | 
			
		||||
- Frontend scripts run in browser context
 | 
			
		||||
- Backend scripts run in Node.js context with full API access
 | 
			
		||||
- Script API documentation available in `docs/Script API/`
 | 
			
		||||
 | 
			
		||||
### Internationalization
 | 
			
		||||
- Translation files in `apps/client/src/translations/`
 | 
			
		||||
- Supported languages: English, German, Spanish, French, Romanian, Chinese
 | 
			
		||||
 | 
			
		||||
### Security Considerations
 | 
			
		||||
- Per-note encryption with granular protected sessions
 | 
			
		||||
- CSRF protection for API endpoints
 | 
			
		||||
- OpenID and TOTP authentication support
 | 
			
		||||
- Sanitization of user-generated content
 | 
			
		||||
 | 
			
		||||
## Common Development Tasks
 | 
			
		||||
 | 
			
		||||
### Adding New Note Types
 | 
			
		||||
1. Create widget in `apps/client/src/widgets/type_widgets/`
 | 
			
		||||
2. Register in `apps/client/src/services/note_types.ts`
 | 
			
		||||
3. Add backend handling in `apps/server/src/services/notes.ts`
 | 
			
		||||
 | 
			
		||||
### Extending Search
 | 
			
		||||
- Search expressions handled in `apps/server/src/services/search/`
 | 
			
		||||
- Add new search operators in search context files
 | 
			
		||||
 | 
			
		||||
### Custom CKEditor Plugins
 | 
			
		||||
- Create new package in `packages/` following existing plugin structure
 | 
			
		||||
- Register in `packages/ckeditor5/src/plugins.ts`
 | 
			
		||||
 | 
			
		||||
### Database Migrations
 | 
			
		||||
- Add migration scripts in `apps/server/src/migrations/`
 | 
			
		||||
- Update schema in `apps/server/src/assets/db/schema.sql`
 | 
			
		||||
 | 
			
		||||
## Build System Notes
 | 
			
		||||
- Uses pnpm for monorepo management
 | 
			
		||||
- Vite for fast development builds
 | 
			
		||||
- ESBuild for production optimization
 | 
			
		||||
- pnpm workspaces for dependency management
 | 
			
		||||
- Docker support with multi-stage builds
 | 
			
		||||
							
								
								
									
										42
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
# !!! Don't try to build this Dockerfile directly, run it through bin/build-docker.sh script !!!
 | 
			
		||||
FROM node:18.18.2-alpine
 | 
			
		||||
 | 
			
		||||
# Create app directory
 | 
			
		||||
WORKDIR /usr/src/app
 | 
			
		||||
 | 
			
		||||
# Bundle app source
 | 
			
		||||
COPY . .
 | 
			
		||||
 | 
			
		||||
COPY server-package.json package.json
 | 
			
		||||
 | 
			
		||||
# Install app dependencies
 | 
			
		||||
RUN set -x \
 | 
			
		||||
    && apk add --no-cache --virtual .build-dependencies \
 | 
			
		||||
        autoconf \
 | 
			
		||||
        automake \
 | 
			
		||||
        g++ \
 | 
			
		||||
        gcc \
 | 
			
		||||
        libtool \
 | 
			
		||||
        make \
 | 
			
		||||
        nasm \
 | 
			
		||||
        libpng-dev \
 | 
			
		||||
        python3 \
 | 
			
		||||
    && npm install \
 | 
			
		||||
    && apk del .build-dependencies \
 | 
			
		||||
    && npm run webpack \
 | 
			
		||||
    && npm prune --omit=dev \
 | 
			
		||||
    && cp src/public/app/share.js src/public/app-dist/. \
 | 
			
		||||
    && cp -r src/public/app/doc_notes src/public/app-dist/. \
 | 
			
		||||
    && rm -rf src/public/app
 | 
			
		||||
 | 
			
		||||
# Some setup tools need to be kept
 | 
			
		||||
RUN apk add --no-cache su-exec shadow
 | 
			
		||||
 | 
			
		||||
# Add application user and setup proper volume permissions
 | 
			
		||||
RUN adduser -s /bin/false node; exit 0
 | 
			
		||||
 | 
			
		||||
# Start the application
 | 
			
		||||
EXPOSE 8080
 | 
			
		||||
CMD [ "./start-docker.sh" ]
 | 
			
		||||
 | 
			
		||||
HEALTHCHECK --start-period=10s CMD exec su-exec node node docker_healthcheck.js
 | 
			
		||||
							
								
								
									
										82
									
								
								README-ZH_CN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								README-ZH_CN.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
# Trilium Notes
 | 
			
		||||
 | 
			
		||||
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
 | 
			
		||||
 | 
			
		||||
[](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
			
		||||
Trilium Notes 是一个层次化的笔记应用程序,专注于建立大型个人知识库。请参阅[屏幕截图](https://github.com/zadam/trilium/wiki/Screenshot-tour)以快速了解:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Ukraine is currently suffering from Russian aggression, please consider donating to [one of these charities](https://old.reddit.com/r/ukraine/comments/s6g5un/want_to_support_ukraine_heres_a_list_of_charities/).
 | 
			
		||||
 | 
			
		||||
<img src="https://upload.wikimedia.org/wikipedia/commons/4/49/Flag_of_Ukraine.svg" alt="drawing" width="600"/>
 | 
			
		||||
<img src="https://signmyrocket.com//uploads/2b2a523cd0c0e76cdbba95a89a9636b2_1676971281.jpg" alt="Trilium Notes supports Ukraine!" width="600"/>
 | 
			
		||||
 | 
			
		||||
## 特性
 | 
			
		||||
 | 
			
		||||
* 笔记可以排列成任意深的树。单个笔记可以放在树中的多个位置(请参阅[克隆](https://github.com/zadam/trilium/wiki/Cloning-notes))
 | 
			
		||||
* 丰富的所见即所得笔记编辑功能,包括带有 Markdown [自动格式化功能的](https://github.com/zadam/trilium/wiki/Text-notes#autoformat)表格,图像和[数学](https://github.com/zadam/trilium/wiki/Text-notes#math-support)
 | 
			
		||||
* 支持编辑[使用源代码的笔记](https://github.com/zadam/trilium/wiki/Code-notes),包括语法高亮显示
 | 
			
		||||
* 笔记之间快速[导航](https://github.com/zadam/trilium/wiki/Note-navigation),全文搜索和[笔记聚焦](https://github.com/zadam/trilium/wiki/Note-hoisting)
 | 
			
		||||
* 无缝[笔记版本控制](https://github.com/zadam/trilium/wiki/Note-revisions)
 | 
			
		||||
* 笔记[属性](https://github.com/zadam/trilium/wiki/Attributes)可用于笔记组织,查询和高级[脚本编写](https://github.com/zadam/trilium/wiki/Scripts)
 | 
			
		||||
* [同步](https://github.com/zadam/trilium/wiki/Synchronization)与自托管同步服务器
 | 
			
		||||
  * 有一个[第三方提供的同步服务器托管服务](https://trilium.cc/paid-hosting)
 | 
			
		||||
* 公开地[分享](https://github.com/zadam/trilium/wiki/Sharing)(发布)笔记到互联网
 | 
			
		||||
* 具有按笔记粒度的强大的[笔记加密](https://github.com/zadam/trilium/wiki/Protected-notes)
 | 
			
		||||
* 使用自带的 Excalidraw 来绘制图表(笔记类型“画布”)
 | 
			
		||||
* [关系图](https://github.com/zadam/trilium/wiki/Relation-map)和[链接图](https://github.com/zadam/trilium/wiki/Link-map),用于可视化笔记及其关系
 | 
			
		||||
* [脚本](https://github.com/zadam/trilium/wiki/Scripts) - 请参阅[高级功能展示](https://github.com/zadam/trilium/wiki/Advanced-showcases)
 | 
			
		||||
* 在拥有超过 10 万条笔记时仍能保持良好的可用性和性能
 | 
			
		||||
* 针对智能手机和平板电脑进行优化的[用于移动设备的前端](https://github.com/zadam/trilium/wiki/Mobile-frontend)
 | 
			
		||||
* [夜间主题](https://github.com/zadam/trilium/wiki/Themes)
 | 
			
		||||
* [Evernote](https://github.com/zadam/trilium/wiki/Evernote-import) 和 [Markdown 导入导出](https://github.com/zadam/trilium/wiki/Markdown)功能
 | 
			
		||||
* 使用[网页剪藏](https://github.com/zadam/trilium/wiki/Web-clipper)轻松保存互联网上的内容
 | 
			
		||||
 | 
			
		||||
## 构建
 | 
			
		||||
 | 
			
		||||
Trilium 可以用作桌面应用程序(Linux 和 Windows)或服务器(Linux)上托管的 Web 应用程序。虽然有 macOS 版本的桌面应用程序,但它[不受支持](https://github.com/zadam/trilium/wiki/FAQ#mac-os-support)。
 | 
			
		||||
 | 
			
		||||
* 如果要在桌面上使用 Trilium,请从[最新版本](https://github.com/zadam/trilium/releases/latest)下载适用于您平台的二进制版本,解压缩该软件包并运行`trilium`可执行文件。
 | 
			
		||||
* 如果要在服务器上安装 Trilium,请参考[此页面](https://github.com/zadam/trilium/wiki/Server-installation)。
 | 
			
		||||
  * 当前仅支持(测试过)最近发布的 Chrome 和 Firefox 浏览器。
 | 
			
		||||
 | 
			
		||||
Trilium 也提供 Flatpak:
 | 
			
		||||
 | 
			
		||||
[<img width="240" src="https://flathub.org/assets/badges/flathub-badge-en.png">](https://flathub.org/apps/details/com.github.zadam.trilium)
 | 
			
		||||
 | 
			
		||||
## 文档
 | 
			
		||||
 | 
			
		||||
[有关文档页面的完整列表,请参见 Wiki。](https://github.com/zadam/trilium/wiki/)
 | 
			
		||||
 | 
			
		||||
* [Wiki 的中文翻译版本](https://github.com/baddate/trilium/wiki/)
 | 
			
		||||
 | 
			
		||||
您还可以阅读[个人知识库模式](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base),以获取有关如何使用 Trilium 的灵感。
 | 
			
		||||
 | 
			
		||||
## 贡献
 | 
			
		||||
 | 
			
		||||
使用基于浏览器的开发环境
 | 
			
		||||
 | 
			
		||||
[](https://gitpod.io/#https://github.com/zadam/trilium)
 | 
			
		||||
 | 
			
		||||
或者克隆本仓库到本地,并运行
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
npm install
 | 
			
		||||
npm run start-server
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 致谢
 | 
			
		||||
 | 
			
		||||
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - 市面上最好的所见即所得编辑器,拥有互动性强且聆听能力强的团队
 | 
			
		||||
* [FancyTree](https://github.com/mar10/fancytree) - 一个非常丰富的关于树的库,强大到没有对手。没有它,Trilium Notes 将不会如此。
 | 
			
		||||
* [CodeMirror](https://github.com/codemirror/CodeMirror) - 支持大量语言的代码编辑器
 | 
			
		||||
* [jsPlumb](https://github.com/jsplumb/jsplumb) - 强大的可视化连接库。用于[关系图](https://github.com/zadam/trilium/wiki/Relation-map)和[链接图](https://github.com/zadam/trilium/wiki/Link-map)
 | 
			
		||||
 | 
			
		||||
## 捐赠
 | 
			
		||||
 | 
			
		||||
你可以通过 GitHub Sponsors,[PayPal](https://paypal.me/za4am) 或者比特币 (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2) 来捐赠。
 | 
			
		||||
 | 
			
		||||
## 许可证
 | 
			
		||||
 | 
			
		||||
本程序是自由软件:你可以再发布本软件和/或修改本软件,只要你遵循 Free Software Foundation 发布的 GNU Affero General Public License 的第三版或者任何(由你选择)更晚的版本。
 | 
			
		||||
							
								
								
									
										83
									
								
								README.ja.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								README.ja.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
# Trilium Notes
 | 
			
		||||
 | 
			
		||||
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
 | 
			
		||||
 | 
			
		||||
Trilium Notes は、大規模な個人知識ベースの構築に焦点を当てた、階層型ノートアプリケーションです。概要は[スクリーンショット](https://github.com/zadam/trilium/wiki/Screenshot-tour)をご覧ください:
 | 
			
		||||
 | 
			
		||||
<a href="https://github.com/zadam/trilium/wiki/Screenshot-tour"><img src="https://raw.githubusercontent.com/wiki/zadam/trilium/images/screenshot.png" alt="Trilium Screenshot" width="1000"></a>
 | 
			
		||||
 | 
			
		||||
ウクライナは現在、ロシアの侵略から自国を守っています。[ウクライナ軍や人道的な慈善団体への寄付](https://standforukraine.com/)をご検討ください。
 | 
			
		||||
 | 
			
		||||
<p float="left">
 | 
			
		||||
  <img src="https://upload.wikimedia.org/wikipedia/commons/4/49/Flag_of_Ukraine.svg" alt="drawing" width="400"/>
 | 
			
		||||
  <img src="https://signmyrocket.com//uploads/2b2a523cd0c0e76cdbba95a89a9636b2_1676971281.jpg" alt="Trilium Notes supports Ukraine!" width="570"/>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
## 🎁 特徴
 | 
			
		||||
 | 
			
		||||
* ノートは、任意の深さのツリーに配置できます。単一のノートをツリー内の複数の場所に配置できます ([cloning](https://github.com/zadam/trilium/wiki/Cloning-notes) を参照)
 | 
			
		||||
* マークダウン[オートフォーマット](https://github.com/zadam/trilium/wiki/Text-notes#autoformat)による、表、画像、[数学](https://github.com/zadam/trilium/wiki/Text-notes#math-support)などの豊富な WYSIWYG ノート編集機能
 | 
			
		||||
* シンタックスハイライトを含む[ソースコード付きノート](https://github.com/zadam/trilium/wiki/Code-notes)の編集をサポート
 | 
			
		||||
* [ノート間のナビゲーション](https://github.com/zadam/trilium/wiki/Note-navigation)、全文検索、[ノートホイスト](https://github.com/zadam/trilium/wiki/Note-hoisting)が高速かつ簡単に行えます
 | 
			
		||||
* シームレスな[ノートのバージョン管理](https://github.com/zadam/trilium/wiki/Note-revisions)
 | 
			
		||||
* ノート[属性](https://github.com/zadam/trilium/wiki/Attributes)は、ノート整理、クエリ、高度な[スクリプト](https://github.com/zadam/trilium/wiki/Scripts)に使用できます
 | 
			
		||||
* 自己ホスト型同期サーバーとの[同期](https://github.com/zadam/trilium/wiki/Synchronization)
 | 
			
		||||
  * [同期サーバーをホストするサードパーティ・サービス](https://trilium.cc/paid-hosting)があります
 | 
			
		||||
* 公開インターネットへのノートの[共有](https://github.com/zadam/trilium/wiki/Sharing)(公開)
 | 
			
		||||
* ノートごとの粒度を持つ強力な[ノート暗号化](https://github.com/zadam/trilium/wiki/Protected-notes)
 | 
			
		||||
* 組み込みの Excalidraw を使用した図のスケッチ (ノート タイプ"キャンバス")
 | 
			
		||||
* ノートとその関係を可視化するための[関係図](https://github.com/zadam/trilium/wiki/Relation-map)と[リンクマップ](https://github.com/zadam/trilium/wiki/Link-map)
 | 
			
		||||
* [スクリプティング](https://github.com/zadam/trilium/wiki/Scripts) - [高度なショーケース](https://github.com/zadam/trilium/wiki/Advanced-showcases)を参照
 | 
			
		||||
* 自動化のための [REST API](https://github.com/zadam/trilium/wiki/ETAPI)
 | 
			
		||||
* ユーザビリティとパフォーマンスの両方で 100 000 ノート以上に拡張可能
 | 
			
		||||
* スマートフォンとタブレット向けのタッチ最適化[モバイルフロントエンド](https://github.com/zadam/trilium/wiki/Mobile-frontend)
 | 
			
		||||
* [ナイトテーマ](https://github.com/zadam/trilium/wiki/Themes)
 | 
			
		||||
* [Evernote](https://github.com/zadam/trilium/wiki/Evernote-import) と [Markdown のインポートとエクスポート](https://github.com/zadam/trilium/wiki/Markdown)
 | 
			
		||||
* Web コンテンツを簡単に保存するための [Web クリッパー](https://github.com/zadam/trilium/wiki/Web-clipper)
 | 
			
		||||
 | 
			
		||||
サードパーティのテーマ、スクリプト、プラグインなどは、 [awesome-trilium](https://github.com/Nriver/awesome-trilium) をチェックしてください。
 | 
			
		||||
 | 
			
		||||
## 🏗 ビルド
 | 
			
		||||
 | 
			
		||||
Trilium は、デスクトップアプリケーション(Linux、Windows)またはサーバー上でホストされるウェブアプリケーション(Linux)として提供されます。 Mac OS のデスクトップビルドも利用可能ですが、 [unsupported](https://github.com/zadam/trilium/wiki/FAQ#mac-os-support) となっています。
 | 
			
		||||
 | 
			
		||||
* デスクトップで Trilium を使用したい場合は、 [latest release](https://github.com/zadam/trilium/releases/latest) からお使いのプラットフォームのバイナリリリースをダウンロードし、パッケージを解凍して ``trilium`` の実行ファイルを実行してください。
 | 
			
		||||
* サーバーに Trilium をインストールする場合は、[このページ](https://github.com/zadam/trilium/wiki/Server-installation)に従ってください。
 | 
			
		||||
  * 現在、対応(動作確認)しているブラウザは、最近の Chrome と Firefox のみです。
 | 
			
		||||
 | 
			
		||||
Trilium は Flatpak としても提供されます:
 | 
			
		||||
 | 
			
		||||
[<img width="240" src="https://flathub.org/assets/badges/flathub-badge-en.png">](https://flathub.org/apps/details/com.github.zadam.trilium)
 | 
			
		||||
 | 
			
		||||
## 📝 ドキュメント
 | 
			
		||||
 | 
			
		||||
[ドキュメントページの全リストはwikiをご覧ください。](https://github.com/zadam/trilium/wiki/)
 | 
			
		||||
 | 
			
		||||
また、[個人的な知識基盤のパターン](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base)を読むと、 Trilium の使い方のヒントを得ることができます。
 | 
			
		||||
 | 
			
		||||
## 💻 コントリビュート
 | 
			
		||||
 | 
			
		||||
ブラウザベースの開発環境を使用
 | 
			
		||||
 | 
			
		||||
[](https://gitpod.io/#https://github.com/zadam/trilium)
 | 
			
		||||
 | 
			
		||||
または、ローカルにクローンして実行
 | 
			
		||||
```
 | 
			
		||||
npm install
 | 
			
		||||
npm run start-server
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 📢 シャウトアウト
 | 
			
		||||
 | 
			
		||||
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - 市場で最高の WYSIWYG エディター、非常にインタラクティブで聞き上手なチーム
 | 
			
		||||
* [FancyTree](https://github.com/mar10/fancytree) - 真の競争相手がいない、非常に機能豊富なツリーライブラリです。 Trilium Notes は、これなしでは成り立たないでしょう。
 | 
			
		||||
* [CodeMirror](https://github.com/codemirror/CodeMirror) - 膨大な数の言語をサポートするコードエディタ
 | 
			
		||||
* [jsPlumb](https://github.com/jsplumb/jsplumb) - 競合のないビジュアルコネクティビティライブラリです。[関係図](https://github.com/zadam/trilium/wiki/Relation-map)、[リンク図](https://github.com/zadam/trilium/wiki/Link-map)で使用。
 | 
			
		||||
 | 
			
		||||
## 🤝 サポート
 | 
			
		||||
 | 
			
		||||
GitHub スポンサー、[PayPal](https://paypal.me/za4am)もしくは Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2) にて Trilium をサポートすることができます。
 | 
			
		||||
 | 
			
		||||
## 🔑 ライセンス
 | 
			
		||||
 | 
			
		||||
このプログラムはフリーソフトウェアです:フリーソフトウェア財団が発行した GNU Affero General Public License のバージョン3、またはそれ以降のバージョンのいずれかに従って、再配布および/または改変することができます。
 | 
			
		||||
							
								
								
									
										231
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										231
									
								
								README.md
									
									
									
									
									
								
							@@ -1,218 +1,83 @@
 | 
			
		||||
<div align="center">
 | 
			
		||||
	<sup>Special thanks to:</sup><br />
 | 
			
		||||
	<a href="https://go.warp.dev/Trilium" target="_blank">		
 | 
			
		||||
		<img alt="Warp sponsorship" width="400" src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-03.png"><br />
 | 
			
		||||
		Warp, built for coding with multiple AI agents<br />
 | 
			
		||||
	</a>
 | 
			
		||||
  <sup>Available for macOS, Linux and Windows</sup>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<hr />
 | 
			
		||||
 | 
			
		||||
# Trilium Notes
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||

 | 
			
		||||
  
 | 
			
		||||
[](https://app.relative-ci.com/projects/Di5q7dz9daNDZ9UXi0Bp) [](https://hosted.weblate.org/engage/trilium/)
 | 
			
		||||
[](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
 | 
			
		||||
 | 
			
		||||
[English](./README.md) | [Chinese (Simplified)](./docs/README-ZH_CN.md) | [Chinese (Traditional)](./docs/README-ZH_TW.md) | [Russian](./docs/README-ru.md) | [Japanese](./docs/README-ja.md) | [Italian](./docs/README-it.md) | [Spanish](./docs/README-es.md)
 | 
			
		||||
Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases. See [screenshots](https://github.com/zadam/trilium/wiki/Screenshot-tour) for quick overview:
 | 
			
		||||
 | 
			
		||||
Trilium Notes is a free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
 | 
			
		||||
<a href="https://github.com/zadam/trilium/wiki/Screenshot-tour"><img src="https://raw.githubusercontent.com/wiki/zadam/trilium/images/screenshot.png" alt="Trilium Screenshot" width="1000"></a>
 | 
			
		||||
 | 
			
		||||
See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for quick overview:
 | 
			
		||||
Ukraine is currently defending itself from Russian aggression, please consider [donating to Ukrainian Army or humanitarian charities](https://standforukraine.com/).
 | 
			
		||||
 | 
			
		||||
<a href="https://triliumnext.github.io/Docs/Wiki/screenshot-tour"><img src="./docs/app.png" alt="Trilium Screenshot" width="1000"></a>
 | 
			
		||||
 | 
			
		||||
## ⏬ Download
 | 
			
		||||
- [Latest release](https://github.com/TriliumNext/Trilium/releases/latest) – stable version, recommended for most users.
 | 
			
		||||
- [Nightly build](https://github.com/TriliumNext/Trilium/releases/tag/nightly) – unstable development version, updated daily with the latest features and fixes.
 | 
			
		||||
 | 
			
		||||
## 📚 Documentation
 | 
			
		||||
 | 
			
		||||
**Visit our comprehensive documentation at [docs.triliumnotes.org](https://docs.triliumnotes.org/)**
 | 
			
		||||
 | 
			
		||||
Our documentation is available in multiple formats:
 | 
			
		||||
- **Online Documentation**: Browse the full documentation at [docs.triliumnotes.org](https://docs.triliumnotes.org/)
 | 
			
		||||
- **In-App Help**: Press `F1` within Trilium to access the same documentation directly in the application
 | 
			
		||||
- **GitHub**: Navigate through the [User Guide](./docs/User%20Guide/User%20Guide/) in this repository
 | 
			
		||||
 | 
			
		||||
### Quick Links
 | 
			
		||||
- [Getting Started Guide](https://docs.triliumnotes.org/)
 | 
			
		||||
- [Installation Instructions](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md)
 | 
			
		||||
- [Docker Setup](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation/1.%20Installing%20the%20server/Using%20Docker.md)
 | 
			
		||||
- [Upgrading TriliumNext](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Upgrading%20TriliumNext.md)
 | 
			
		||||
- [Basic Concepts and Features](./docs/User%20Guide/User%20Guide/Basic%20Concepts%20and%20Features/Notes.md)
 | 
			
		||||
- [Patterns of Personal Knowledge Base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge)
 | 
			
		||||
<p float="left">
 | 
			
		||||
  <img src="https://upload.wikimedia.org/wikipedia/commons/4/49/Flag_of_Ukraine.svg" alt="drawing" width="400"/>
 | 
			
		||||
  <img src="https://signmyrocket.com//uploads/2b2a523cd0c0e76cdbba95a89a9636b2_1676971281.jpg" alt="Trilium Notes supports Ukraine!" width="570"/>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
## 🎁 Features
 | 
			
		||||
 | 
			
		||||
* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://triliumnext.github.io/Docs/Wiki/cloning-notes))
 | 
			
		||||
* Rich WYSIWYG note editor including e.g. tables, images and [math](https://triliumnext.github.io/Docs/Wiki/text-notes) with markdown [autoformat](https://triliumnext.github.io/Docs/Wiki/text-notes#autoformat)
 | 
			
		||||
* Support for editing [notes with source code](https://triliumnext.github.io/Docs/Wiki/code-notes), including syntax highlighting
 | 
			
		||||
* Fast and easy [navigation between notes](https://triliumnext.github.io/Docs/Wiki/note-navigation), full text search and [note hoisting](https://triliumnext.github.io/Docs/Wiki/note-hoisting)
 | 
			
		||||
* Seamless [note versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions)
 | 
			
		||||
* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be used for note organization, querying and advanced [scripting](https://triliumnext.github.io/Docs/Wiki/scripts)
 | 
			
		||||
* UI available in English, German, Spanish, French, Romanian, and Chinese (simplified and traditional)
 | 
			
		||||
* Direct [OpenID and TOTP integration](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md) for more secure login
 | 
			
		||||
* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) with self-hosted sync server
 | 
			
		||||
* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://github.com/zadam/trilium/wiki/Cloning-notes))
 | 
			
		||||
* Rich WYSIWYG note editing including e.g. tables, images and [math](https://github.com/zadam/trilium/wiki/Text-notes#math-support) with markdown [autoformat](https://github.com/zadam/trilium/wiki/Text-notes#autoformat)
 | 
			
		||||
* Support for editing [notes with source code](https://github.com/zadam/trilium/wiki/Code-notes), including syntax highlighting
 | 
			
		||||
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation), full text search and [note hoisting](https://github.com/zadam/trilium/wiki/Note-hoisting)
 | 
			
		||||
* Seamless [note versioning](https://github.com/zadam/trilium/wiki/Note-revisions)
 | 
			
		||||
* Note [attributes](https://github.com/zadam/trilium/wiki/Attributes) can be used for note organization, querying and advanced [scripting](https://github.com/zadam/trilium/wiki/Scripts)
 | 
			
		||||
* [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization) with self-hosted sync server
 | 
			
		||||
  * there's a [3rd party service for hosting synchronisation server](https://trilium.cc/paid-hosting)
 | 
			
		||||
* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes to public internet
 | 
			
		||||
* Strong [note encryption](https://triliumnext.github.io/Docs/Wiki/protected-notes) with per-note granularity
 | 
			
		||||
* Sketching diagrams, based on [Excalidraw](https://excalidraw.com/) (note type "canvas")
 | 
			
		||||
* [Relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map) and [link maps](https://triliumnext.github.io/Docs/Wiki/link-map) for visualizing notes and their relations
 | 
			
		||||
* Mind maps, based on [Mind Elixir](https://docs.mind-elixir.com/)
 | 
			
		||||
* [Geo maps](./docs/User%20Guide/User%20Guide/Note%20Types/Geo%20Map.md) with location pins and GPX tracks
 | 
			
		||||
* [Scripting](https://triliumnext.github.io/Docs/Wiki/scripts) - see [Advanced showcases](https://triliumnext.github.io/Docs/Wiki/advanced-showcases)
 | 
			
		||||
* [REST API](https://triliumnext.github.io/Docs/Wiki/etapi) for automation
 | 
			
		||||
* [Sharing](https://github.com/zadam/trilium/wiki/Sharing) (publishing) notes to public internet
 | 
			
		||||
* Strong [note encryption](https://github.com/zadam/trilium/wiki/Protected-notes) with per-note granularity
 | 
			
		||||
* Sketching diagrams with built-in Excalidraw (note type "canvas")
 | 
			
		||||
* [Relation maps](https://github.com/zadam/trilium/wiki/Relation-map) and [link maps](https://github.com/zadam/trilium/wiki/Link-map) for visualizing notes and their relations
 | 
			
		||||
* [Scripting](https://github.com/zadam/trilium/wiki/Scripts) - see [Advanced showcases](https://github.com/zadam/trilium/wiki/Advanced-showcases)
 | 
			
		||||
* [REST API](https://github.com/zadam/trilium/wiki/ETAPI) for automation
 | 
			
		||||
* Scales well in both usability and performance upwards of 100 000 notes
 | 
			
		||||
* Touch optimized [mobile frontend](https://triliumnext.github.io/Docs/Wiki/mobile-frontend) for smartphones and tablets
 | 
			
		||||
* Built-in [dark theme](https://triliumnext.github.io/Docs/Wiki/themes), support for user themes
 | 
			
		||||
* [Evernote](https://triliumnext.github.io/Docs/Wiki/evernote-import) and [Markdown import & export](https://triliumnext.github.io/Docs/Wiki/markdown)
 | 
			
		||||
* [Web Clipper](https://triliumnext.github.io/Docs/Wiki/web-clipper) for easy saving of web content
 | 
			
		||||
* Customizable UI (sidebar buttons, user-defined widgets, ...)
 | 
			
		||||
* [Metrics](./docs/User%20Guide/User%20Guide/Advanced%20Usage/Metrics.md), along with a [Grafana Dashboard](./docs/User%20Guide/User%20Guide/Advanced%20Usage/Metrics/grafana-dashboard.json)
 | 
			
		||||
* Touch optimized [mobile frontend](https://github.com/zadam/trilium/wiki/Mobile-frontend) for smartphones and tablets
 | 
			
		||||
* [Night theme](https://github.com/zadam/trilium/wiki/Themes)
 | 
			
		||||
* [Evernote](https://github.com/zadam/trilium/wiki/Evernote-import) and [Markdown import & export](https://github.com/zadam/trilium/wiki/Markdown)
 | 
			
		||||
* [Web Clipper](https://github.com/zadam/trilium/wiki/Web-clipper) for easy saving of web content
 | 
			
		||||
 | 
			
		||||
✨ Check out the following third-party resources/communities for more TriliumNext related goodies:
 | 
			
		||||
Check out [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
 | 
			
		||||
 | 
			
		||||
- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
 | 
			
		||||
- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more.
 | 
			
		||||
## 🏗 Builds
 | 
			
		||||
 | 
			
		||||
## ❓Why TriliumNext?
 | 
			
		||||
Trilium is provided as either desktop application (Linux and Windows) or web application hosted on your server (Linux). Mac OS desktop build is available, but it is [unsupported](https://github.com/zadam/trilium/wiki/FAQ#mac-os-support).
 | 
			
		||||
 | 
			
		||||
The original Trilium developer ([Zadam](https://github.com/zadam)) has graciously given the Trilium repository to the community project which resides at https://github.com/TriliumNext
 | 
			
		||||
* If you want to use Trilium on the desktop, download binary release for your platform from [latest release](https://github.com/zadam/trilium/releases/latest), unzip the package and run ```trilium``` executable.
 | 
			
		||||
* If you want to install Trilium on server, follow [this page](https://github.com/zadam/trilium/wiki/Server-installation).
 | 
			
		||||
  * Currently only recent Chrome and Firefox are supported (tested) browsers.
 | 
			
		||||
 | 
			
		||||
### ⬆️Migrating from Zadam/Trilium?
 | 
			
		||||
Trilium is also provided as a Flatpak:
 | 
			
		||||
 | 
			
		||||
There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Trilium instance. Simply [install TriliumNext/Trilium](#-installation) as usual and it will use your existing database.
 | 
			
		||||
[<img width="240" src="https://flathub.org/assets/badges/flathub-badge-en.png">](https://flathub.org/apps/details/com.github.zadam.trilium)
 | 
			
		||||
 | 
			
		||||
Versions up to and including [v0.90.4](https://github.com/TriliumNext/Trilium/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext/Trilium have their sync versions incremented which prevents direct migration.
 | 
			
		||||
## 📝 Documentation
 | 
			
		||||
 | 
			
		||||
## 💬 Discuss with us
 | 
			
		||||
 | 
			
		||||
Feel free to join our official conversations. We would love to hear what features, suggestions, or issues you may have!
 | 
			
		||||
 | 
			
		||||
- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous discussions.)
 | 
			
		||||
  - The `General` Matrix room is also bridged to [XMPP](xmpp:discuss@trilium.thisgreat.party?join)
 | 
			
		||||
- [Github Discussions](https://github.com/TriliumNext/Trilium/discussions) (For asynchronous discussions.)
 | 
			
		||||
- [Github Issues](https://github.com/TriliumNext/Trilium/issues) (For bug reports and feature requests.)
 | 
			
		||||
 | 
			
		||||
## 🏗 Installation
 | 
			
		||||
 | 
			
		||||
### Windows / MacOS
 | 
			
		||||
 | 
			
		||||
Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package and run the `trilium` executable.
 | 
			
		||||
 | 
			
		||||
### Linux
 | 
			
		||||
 | 
			
		||||
If your distribution is listed in the table below, use your distribution's package.
 | 
			
		||||
 | 
			
		||||
[](https://repology.org/project/triliumnext/versions)
 | 
			
		||||
 | 
			
		||||
You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package and run the `trilium` executable.
 | 
			
		||||
 | 
			
		||||
TriliumNext is also provided as a Flatpak, but not yet published on FlatHub.
 | 
			
		||||
 | 
			
		||||
### Browser (any OS)
 | 
			
		||||
 | 
			
		||||
If you use a server installation (see below), you can directly access the web interface (which is almost identical to the desktop app).
 | 
			
		||||
 | 
			
		||||
Currently only the latest versions of Chrome & Firefox are supported (and tested).
 | 
			
		||||
 | 
			
		||||
### Mobile
 | 
			
		||||
 | 
			
		||||
To use TriliumNext on a mobile device, you can use a mobile web browser to access the mobile interface of a server installation (see below).
 | 
			
		||||
 | 
			
		||||
See issue https://github.com/TriliumNext/Trilium/issues/4962 for more information on mobile app support.
 | 
			
		||||
 | 
			
		||||
If you prefer a native Android app, you can use [TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid).
 | 
			
		||||
Report bugs and missing features at [their repository](https://github.com/FliegendeWurst/TriliumDroid).
 | 
			
		||||
Note: It is best to disable automatic updates on your server installation (see below) when using TriliumDroid since the sync version must match between Trilium and TriliumDroid.
 | 
			
		||||
 | 
			
		||||
### Server
 | 
			
		||||
 | 
			
		||||
To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/trilium)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
 | 
			
		||||
[See wiki for complete list of documentation pages.](https://github.com/zadam/trilium/wiki/)
 | 
			
		||||
 | 
			
		||||
You can also read [Patterns of personal knowledge base](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base) to get some inspiration on how you might use Trilium.
 | 
			
		||||
 | 
			
		||||
## 💻 Contribute
 | 
			
		||||
 | 
			
		||||
### Translations
 | 
			
		||||
Use a browser based dev environment
 | 
			
		||||
 | 
			
		||||
If you are a native speaker, help us translate Trilium by heading over to our [Weblate page](https://hosted.weblate.org/engage/trilium/).
 | 
			
		||||
[](https://gitpod.io/#https://github.com/zadam/trilium)
 | 
			
		||||
 | 
			
		||||
Here's the language coverage we have so far:
 | 
			
		||||
 | 
			
		||||
[](https://hosted.weblate.org/engage/trilium/)
 | 
			
		||||
 | 
			
		||||
### Code
 | 
			
		||||
 | 
			
		||||
Download the repository, install dependencies using `pnpm` and then run the server (available at http://localhost:8080):
 | 
			
		||||
```shell
 | 
			
		||||
git clone https://github.com/TriliumNext/Trilium.git
 | 
			
		||||
cd Trilium
 | 
			
		||||
pnpm install
 | 
			
		||||
pnpm run server:start
 | 
			
		||||
Or clone locally and run
 | 
			
		||||
```
 | 
			
		||||
npm install
 | 
			
		||||
npm run start-server
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Documentation
 | 
			
		||||
## 📢 Shoutouts
 | 
			
		||||
 | 
			
		||||
Download the repository, install dependencies using `pnpm` and then run the environment required to edit the documentation:
 | 
			
		||||
```shell
 | 
			
		||||
git clone https://github.com/TriliumNext/Trilium.git
 | 
			
		||||
cd Trilium
 | 
			
		||||
pnpm install
 | 
			
		||||
pnpm edit-docs:edit-docs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Building the Executable
 | 
			
		||||
Download the repository, install dependencies using `pnpm` and then build the desktop app for Windows:
 | 
			
		||||
```shell
 | 
			
		||||
git clone https://github.com/TriliumNext/Trilium.git
 | 
			
		||||
cd Trilium
 | 
			
		||||
pnpm install
 | 
			
		||||
pnpm run --filter desktop electron-forge:make --arch=x64 --platform=win32
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For more details, see the [development docs](https://github.com/TriliumNext/Trilium/tree/main/docs/Developer%20Guide/Developer%20Guide).
 | 
			
		||||
 | 
			
		||||
### Developer Documentation
 | 
			
		||||
 | 
			
		||||
Please view the [documentation guide](https://github.com/TriliumNext/Trilium/blob/main/docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) for details. If you have more questions, feel free to reach out via the links described in the "Discuss with us" section above.
 | 
			
		||||
 | 
			
		||||
## 👏 Shoutouts
 | 
			
		||||
 | 
			
		||||
* [zadam](https://github.com/zadam) for the original concept and implementation of the application.
 | 
			
		||||
* [Sarah Hussein](https://github.com/Sarah-Hussein) for designing the application icon.
 | 
			
		||||
* [nriver](https://github.com/nriver) for his work on internationalization.
 | 
			
		||||
* [Thomas Frei](https://github.com/thfrei) for his original work on the Canvas.
 | 
			
		||||
* [antoniotejada](https://github.com/nriver) for the original syntax highlight widget.
 | 
			
		||||
* [Dosu](https://dosu.dev/) for providing us with the automated responses to GitHub issues and discussions.
 | 
			
		||||
* [Tabler Icons](https://tabler.io/icons) for the system tray icons.
 | 
			
		||||
 | 
			
		||||
Trilium would not be possible without the technologies behind it:
 | 
			
		||||
 | 
			
		||||
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - the visual editor behind text notes. We are grateful for being offered a set of the premium features.
 | 
			
		||||
* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with support for huge amount of languages.
 | 
			
		||||
* [Excalidraw](https://github.com/excalidraw/excalidraw) - the infinite whiteboard used in Canvas notes.
 | 
			
		||||
* [Mind Elixir](https://github.com/SSShooter/mind-elixir-core) - providing the mind map functionality.
 | 
			
		||||
* [Leaflet](https://github.com/Leaflet/Leaflet) - for rendering geographical maps.
 | 
			
		||||
* [Tabulator](https://github.com/olifolkerd/tabulator) - for the interactive table used in collections.
 | 
			
		||||
* [FancyTree](https://github.com/mar10/fancytree) - feature-rich tree library without real competition. 
 | 
			
		||||
* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library. Used in [relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map.html) and [link maps](https://triliumnext.github.io/Docs/Wiki/note-map.html#link-map)
 | 
			
		||||
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - best WYSIWYG editor on the market, very interactive and listening team
 | 
			
		||||
* [FancyTree](https://github.com/mar10/fancytree) - very feature rich tree library without real competition. Trilium Notes would not be the same without it.
 | 
			
		||||
* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with support for huge amount of languages
 | 
			
		||||
* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library without competition. Used in [relation maps](https://github.com/zadam/trilium/wiki/Relation-map) and [link maps](https://github.com/zadam/trilium/wiki/Link-map)
 | 
			
		||||
 | 
			
		||||
## 🤝 Support
 | 
			
		||||
 | 
			
		||||
Trilium is built and maintained with [hundreds of hours of work](https://github.com/TriliumNext/Trilium/graphs/commit-activity). Your support keeps it open-source, improves features, and covers costs such as hosting.
 | 
			
		||||
 | 
			
		||||
Consider supporting the main developer ([eliandoran](https://github.com/eliandoran)) of the application via:
 | 
			
		||||
 | 
			
		||||
- [GitHub Sponsors](https://github.com/sponsors/eliandoran)
 | 
			
		||||
- [PayPal](https://paypal.me/eliandoran)
 | 
			
		||||
- [Buy Me a Coffee](https://buymeacoffee.com/eliandoran)
 | 
			
		||||
 | 
			
		||||
You can support Trilium using GitHub Sponsors, [PayPal](https://paypal.me/za4am) or Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2).
 | 
			
		||||
 | 
			
		||||
## 🔑 License
 | 
			
		||||
 | 
			
		||||
Copyright 2017-2025 zadam, Elian Doran, and other contributors
 | 
			
		||||
 | 
			
		||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								README.ru.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								README.ru.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
# Trilium Notes
 | 
			
		||||
 | 
			
		||||
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
 | 
			
		||||
 | 
			
		||||
[](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 | 
			
		||||
Trilium Notes – это приложение для заметок с иерархической структурой, ориентированное на создание больших персональных баз знаний. Для быстрого ознакомления посмотрите [скриншот-тур](https://github.com/zadam/trilium/wiki/Screenshot-tour):
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Ukraine is currently suffering from Russian aggression, please consider donating to [one of these charities](https://old.reddit.com/r/ukraine/comments/s6g5un/want_to_support_ukraine_heres_a_list_of_charities/).
 | 
			
		||||
 | 
			
		||||
<img src="https://upload.wikimedia.org/wikipedia/commons/4/49/Flag_of_Ukraine.svg" alt="drawing" width="600"/>
 | 
			
		||||
<img src="https://signmyrocket.com//uploads/2b2a523cd0c0e76cdbba95a89a9636b2_1676971281.jpg" alt="Trilium Notes supports Ukraine!" width="600"/>
 | 
			
		||||
 | 
			
		||||
## Возможности
 | 
			
		||||
 | 
			
		||||
* Заметки можно расположить в виде дерева произвольной глубины. Отдельную заметку можно разместить в нескольких местах дерева (см. [клонирование](https://github.com/zadam/trilium/wiki/Cloning-notes))
 | 
			
		||||
* Продвинутый визуальный редактор (WYSIWYG) позволяет работать с таблицами, изображениями, [формулами](https://github.com/zadam/trilium/wiki/Text-notes#math-support) и разметкой markdown, имеет [автоформатирование](https://github.com/zadam/trilium/wiki/Text-notes#autoformat)
 | 
			
		||||
* Редактирование [заметок с исходным кодом](https://github.com/zadam/trilium/wiki/Code-notes), включая подсветку синтаксиса
 | 
			
		||||
* Быстрая и простая [навигация между заметками](https://github.com/zadam/trilium/wiki/Note-navigation), полнотекстовый поиск и [выделение заметок](https://github.com/zadam/trilium/wiki/Note-hoisting) в отдельный блок
 | 
			
		||||
* Бесшовное [версионирование заметки](https://github.com/zadam/trilium/wiki/Note-revisions)
 | 
			
		||||
* Специальные [атрибуты](https://github.com/zadam/trilium/wiki/Attributes) позволяют гибко организовать структуру, используются для поиска и продвинутого [скриптинга](https://github.com/zadam/trilium/wiki/Scripts)
 | 
			
		||||
* [Синхронизация](https://github.com/zadam/trilium/wiki/Synchronization) заметок со своим сервером
 | 
			
		||||
* Надёжное [шифрование](https://github.com/zadam/trilium/wiki/Protected-notes) с детализацией по каждой заметке
 | 
			
		||||
* [Карты связей](https://github.com/zadam/trilium/wiki/Relation-map) и [карты ссылок](https://github.com/zadam/trilium/wiki/Link-map) для визуализации их взяимосвязей
 | 
			
		||||
* [Скрипты](https://github.com/zadam/trilium/wiki/Scripts) - см. [продвинутые примеры](https://github.com/zadam/trilium/wiki/Advanced-showcases)
 | 
			
		||||
* Хорошо масштабируется, как по удобству использования, так и по производительности до 100000 заметок
 | 
			
		||||
* Оптимизированный [мобильный фронтенд](https://github.com/zadam/trilium/wiki/Mobile-frontend) смартфонов и планшетов
 | 
			
		||||
* [Темная тема](https://github.com/zadam/trilium/wiki/Themes)
 | 
			
		||||
* Импорт и экпорт [Evernote](https://github.com/zadam/trilium/wiki/Evernote-import) и данных в [markdown](https://github.com/zadam/trilium/wiki/Markdown) формате
 | 
			
		||||
* [Web Clipper](https://github.com/zadam/trilium/wiki/Web-clipper) для удобного сохранения веб-контента
 | 
			
		||||
 | 
			
		||||
## Сборки
 | 
			
		||||
 | 
			
		||||
Trilium предоставляется в виде десктопного приложения (Linux и Windows) или веб-приложения, размещенного на вашем сервере (Linux). Доступна сборка Mac OS, но она [не поддерживается](https://github.com/zadam/trilium/wiki/FAQ#mac-os-support).
 | 
			
		||||
 | 
			
		||||
* Если вы хотите использовать Trilium на десктопе, скачайте архив для своей платформы со страницы [релизов](https://github.com/zadam/trilium/releases/latest), распакуйте и запустите исполняемый файл ```trilium```.
 | 
			
		||||
* Если вы хотите установить Trilium на сервере, следуйте этой [инструкции](https://github.com/zadam/trilium/wiki/Server-installation).
 | 
			
		||||
  * В данный момент поддерживаются (протестированы) последние версии браузеров Chrome и Firefox.
 | 
			
		||||
 | 
			
		||||
## Документация
 | 
			
		||||
 | 
			
		||||
[Полный список страниц документации доступен в Wiki.](https://github.com/zadam/trilium/wiki/)
 | 
			
		||||
 | 
			
		||||
Вы также можете ознакомиться с [шаблонами персональных баз знаний](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base), чтобы получить представление о том, как можно использовать Trilium.
 | 
			
		||||
 | 
			
		||||
## Участвуйте в разработке
 | 
			
		||||
 | 
			
		||||
Используйте онлайн среду разработки в браузере
 | 
			
		||||
 | 
			
		||||
[](https://gitpod.io/#https://github.com/zadam/trilium)
 | 
			
		||||
 | 
			
		||||
Или склонируйте на своё устройство и запустите
 | 
			
		||||
```
 | 
			
		||||
npm install
 | 
			
		||||
npm run start-server
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Благодарности
 | 
			
		||||
 | 
			
		||||
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - лучший WYSIWYG редактор, очень активная и внимательная команда.
 | 
			
		||||
* [FancyTree](https://github.com/mar10/fancytree) - многофункциональная библиотека для создания древовидных структур. Вне конкуренции. Без него Trilium Notes не были бы таким.
 | 
			
		||||
* [CodeMirror](https://github.com/codemirror/CodeMirror) - редактор кода с поддержкой огромного количество языков.
 | 
			
		||||
* [jsPlumb](https://github.com/jsplumb/jsplumb) - библиотека для визуализации связей. Вне конкуренции. Используется в [картах связей](https://github.com/zadam/trilium/wiki/Relation-map) и [картах ссылок](https://github.com/zadam/trilium/wiki/Link-map).
 | 
			
		||||
 | 
			
		||||
## Лицензия
 | 
			
		||||
 | 
			
		||||
Эта программа является бесплатным программным обеспечением: вы можете распространять и/или изменять ее в соответствии с условиями GNU Affero General Public License, опубликованной Free Software Foundation, либо версии 3 Лицензии, либо (по вашему выбору) любой более поздней версии.
 | 
			
		||||
@@ -10,4 +10,4 @@ Description above is a general rule and may be altered on case by case basis.
 | 
			
		||||
 | 
			
		||||
## Reporting a Vulnerability
 | 
			
		||||
 | 
			
		||||
You can report low severity vulnerabilities as GitHub issues, more severe vulnerabilities should be reported to the email [contact@eliandoran.me](mailto:contact@eliandoran.me)
 | 
			
		||||
You can report low severity vulnerabilities as GitHub issues, more severe vulnerabilities should be reported to the email zadam.apps@gmail.com
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
 | 
			
		||||
import anonymizationService from "../src/services/anonymization.js";
 | 
			
		||||
import fs from "fs";
 | 
			
		||||
import path from "path";
 | 
			
		||||
 | 
			
		||||
fs.writeFileSync(path.resolve(__dirname, "tpl", "anonymize-database.sql"), anonymizationService.getFullAnonymizationScript());
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
if ! command -v magick &> /dev/null; then
 | 
			
		||||
  echo "This tool requires ImageMagick to be installed in order to create the icons."
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if ! command -v inkscape &> /dev/null; then
 | 
			
		||||
  echo "This tool requires Inkscape to be render sharper SVGs than ImageMagick."
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if ! command -v icnsutil &> /dev/null; then
 | 
			
		||||
  echo "This tool requires icnsutil to be installed in order to generate macOS icons."
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
script_dir=$(realpath $(dirname $0))
 | 
			
		||||
cd "${script_dir}/../images/app-icons"
 | 
			
		||||
inkscape -w 180 -h 180 "../icon-color.svg" -o "./ios/apple-touch-icon.png"
 | 
			
		||||
 | 
			
		||||
# Build PNGs
 | 
			
		||||
inkscape -w 128 -h 128 "../icon-color.svg" -o "./png/128x128.png"
 | 
			
		||||
inkscape -w 256 -h 256 "../icon-color.svg" -o "./png/256x256.png"
 | 
			
		||||
 | 
			
		||||
# Build dev icons (including tray)
 | 
			
		||||
inkscape -w 16 -h 16 "../icon-purple.svg" -o "./png/16x16-dev.png"
 | 
			
		||||
inkscape -w 32 -h 32 "../icon-purple.svg" -o "./png/32x32-dev.png"
 | 
			
		||||
inkscape -w 256 -h 256 "../icon-purple.svg" -o "./png/256x256-dev.png"
 | 
			
		||||
 | 
			
		||||
# Build Mac .icns
 | 
			
		||||
declare -a sizes=("16" "32" "512" "1024")
 | 
			
		||||
for size in "${sizes[@]}"; do
 | 
			
		||||
  inkscape -w $size -h $size "../icon-color.svg" -o "./png/${size}x${size}.png"
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
mkdir -p fakeapp.app
 | 
			
		||||
npx iconsur set fakeapp.app -l -i "png/1024x1024.png" -o "mac/1024x1024.png" -s 0.8
 | 
			
		||||
declare -a sizes=("16x16" "32x32" "128x128" "512x512")
 | 
			
		||||
for size in "${sizes[@]}"; do
 | 
			
		||||
  magick "mac/1024x1024.png" -resize "${size}" "mac/${size}.png"
 | 
			
		||||
done
 | 
			
		||||
icnsutil compose -f "mac/icon.icns" ./mac/*.png
 | 
			
		||||
 | 
			
		||||
# Build Windows icon
 | 
			
		||||
magick -background none "../icon-color.svg" -define icon:auto-resize=16,32,48,64,128,256 "./icon.ico"
 | 
			
		||||
 | 
			
		||||
# Build Windows setup icon
 | 
			
		||||
magick -background none "../icon-installer.svg" -define icon:auto-resize=16,32,48,64,128,256 "./win/setup.ico"
 | 
			
		||||
 | 
			
		||||
# Build Squirrel splash image
 | 
			
		||||
magick "./png/256x256.png" -background "#ffffff" -gravity center -extent 640x480 "./win/setup-banner.gif"
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Usage: tsx ./generate_document.ts 1000
 | 
			
		||||
 * will create 1000 new notes and some clones into the current document.db
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import sqlInit from "../src/services/sql_init.js";
 | 
			
		||||
import noteService from "../src/services/notes.js";
 | 
			
		||||
import attributeService from "../src/services/attributes.js";
 | 
			
		||||
import cls from "../src/services/cls.js";
 | 
			
		||||
import cloningService from "../src/services/cloning.js";
 | 
			
		||||
import loremIpsum from "lorem-ipsum";
 | 
			
		||||
import "../src/becca/entity_constructor.js";
 | 
			
		||||
 | 
			
		||||
const noteCount = parseInt(process.argv[2]);
 | 
			
		||||
 | 
			
		||||
if (!noteCount) {
 | 
			
		||||
    console.error(`Please enter number of notes as program parameter.`);
 | 
			
		||||
    process.exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const notes = ["root"];
 | 
			
		||||
 | 
			
		||||
function getRandomNoteId() {
 | 
			
		||||
    const index = Math.floor(Math.random() * notes.length);
 | 
			
		||||
 | 
			
		||||
    return notes[index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function start() {
 | 
			
		||||
    for (let i = 0; i < noteCount; i++) {
 | 
			
		||||
        const title = loremIpsum.loremIpsum({
 | 
			
		||||
            count: 1,
 | 
			
		||||
            units: "sentences",
 | 
			
		||||
            sentenceLowerBound: 1,
 | 
			
		||||
            sentenceUpperBound: 10
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const paragraphCount = Math.floor(Math.random() * Math.random() * 100);
 | 
			
		||||
        const content = loremIpsum.loremIpsum({
 | 
			
		||||
            count: paragraphCount,
 | 
			
		||||
            units: "paragraphs",
 | 
			
		||||
            sentenceLowerBound: 1,
 | 
			
		||||
            sentenceUpperBound: 15,
 | 
			
		||||
            paragraphLowerBound: 3,
 | 
			
		||||
            paragraphUpperBound: 10,
 | 
			
		||||
            format: "html"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const { note } = noteService.createNewNote({
 | 
			
		||||
            parentNoteId: getRandomNoteId(),
 | 
			
		||||
            title,
 | 
			
		||||
            content,
 | 
			
		||||
            type: "text"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        console.log(`Created note ${i}: ${title}`);
 | 
			
		||||
 | 
			
		||||
        if (Math.random() < 0.04) {
 | 
			
		||||
            const noteIdToClone = note.noteId;
 | 
			
		||||
            const parentNoteId = getRandomNoteId();
 | 
			
		||||
            const prefix = Math.random() > 0.8 ? "prefix" : "";
 | 
			
		||||
 | 
			
		||||
            const result = await cloningService.cloneNoteToBranch(noteIdToClone, parentNoteId, prefix);
 | 
			
		||||
 | 
			
		||||
            console.log(`Cloning ${i}:`, result.success ? "succeeded" : "FAILED");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // does not have to be for the current note
 | 
			
		||||
        await attributeService.createAttribute({
 | 
			
		||||
            noteId: getRandomNoteId(),
 | 
			
		||||
            type: "label",
 | 
			
		||||
            name: "label",
 | 
			
		||||
            value: "value",
 | 
			
		||||
            isInheritable: Math.random() > 0.1 // 10% are inheritable
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await attributeService.createAttribute({
 | 
			
		||||
            noteId: getRandomNoteId(),
 | 
			
		||||
            type: "relation",
 | 
			
		||||
            name: "relation",
 | 
			
		||||
            value: getRandomNoteId(),
 | 
			
		||||
            isInheritable: Math.random() > 0.1 // 10% are inheritable
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        note.saveRevision();
 | 
			
		||||
 | 
			
		||||
        notes.push(note.noteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    process.exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// @TriliumNextTODO sqlInit.dbReady never seems to resolve so program hangs
 | 
			
		||||
// see https://github.com/TriliumNext/Trilium/issues/1020
 | 
			
		||||
sqlInit.dbReady.then(cls.wrap(start)).catch((err) => console.error(err));
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
if [[ $# -eq 0 ]] ; then
 | 
			
		||||
    echo "Missing argument of new version"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if ! command -v jq &> /dev/null; then
 | 
			
		||||
  echo "Missing command: jq"
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
VERSION=$1
 | 
			
		||||
 | 
			
		||||
if ! [[ ${VERSION} =~ ^[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}(-.+)?$ ]] ;
 | 
			
		||||
then
 | 
			
		||||
    echo "Version ${VERSION} isn't in format X.Y.Z"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if ! git diff-index --quiet HEAD --; then
 | 
			
		||||
    echo "There are uncommitted changes"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "Releasing Trilium $VERSION"
 | 
			
		||||
 | 
			
		||||
jq '.version = "'$VERSION'"' package.json > package.json.tmp
 | 
			
		||||
mv package.json.tmp package.json
 | 
			
		||||
 | 
			
		||||
git add package.json
 | 
			
		||||
 | 
			
		||||
npm run chore:update-build-info
 | 
			
		||||
 | 
			
		||||
git add src/services/build.ts
 | 
			
		||||
 | 
			
		||||
TAG=v$VERSION
 | 
			
		||||
 | 
			
		||||
echo "Committing package.json version change"
 | 
			
		||||
 | 
			
		||||
git commit -m "chore(release): $VERSION"
 | 
			
		||||
git push
 | 
			
		||||
 | 
			
		||||
echo "Tagging commit with $TAG"
 | 
			
		||||
 | 
			
		||||
git tag $TAG
 | 
			
		||||
git push origin $TAG
 | 
			
		||||
@@ -1,114 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
# --------------------------------------------------------------------------------------------------
 | 
			
		||||
#
 | 
			
		||||
# Create PO files to make easier the labor of translation.
 | 
			
		||||
#
 | 
			
		||||
# Info:
 | 
			
		||||
# 	https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
 | 
			
		||||
# 	https://docs.translatehouse.org/projects/translate-toolkit/en/latest/commands/json2po.html
 | 
			
		||||
#
 | 
			
		||||
# Dependencies:
 | 
			
		||||
# 	jq
 | 
			
		||||
# 	translate-toolkit
 | 
			
		||||
# 		python-wcwidth
 | 
			
		||||
#
 | 
			
		||||
# Created by @hasecilu
 | 
			
		||||
#
 | 
			
		||||
# --------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
number_of_keys() {
 | 
			
		||||
	[ -f "$1" ] && jq 'path(..) | select(length == 2) | .[1]' "$1" | wc -l || echo "0"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
stats() {
 | 
			
		||||
	# Print the number of existing strings on the JSON files for each locale
 | 
			
		||||
	s=$(number_of_keys "${paths[0]}/en/server.json")
 | 
			
		||||
	c=$(number_of_keys "${paths[1]}/en/translation.json")
 | 
			
		||||
	echo "| locale | server strings | client strings |"
 | 
			
		||||
	echo "|--------|----------------|----------------|"
 | 
			
		||||
	echo "|   en   |       ${s}      |      ${c}      |"
 | 
			
		||||
	echo "|--------|----------------|----------------|"
 | 
			
		||||
	for locale in "${locales[@]}"; do
 | 
			
		||||
		s=$(number_of_keys "${paths[0]}/${locale}/server.json")
 | 
			
		||||
		c=$(number_of_keys "${paths[1]}/${locale}/translation.json")
 | 
			
		||||
		n1=$(((8 - ${#locale}) / 2))
 | 
			
		||||
		n2=$((n1 == 1 ? n1 + 1 : n1))
 | 
			
		||||
		echo "|$(printf "%${n1}s")${locale}$(printf "%${n2}s")|       ${s}      |      ${c}      |"
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
update_1() {
 | 
			
		||||
	# Update PO files from English and localized JSON files as source
 | 
			
		||||
	# NOTE: if you want a new language you need to first create the JSON files
 | 
			
		||||
	# on their corresponding place with `{}` as content to avoid error on `json2po`
 | 
			
		||||
	local locales=("$@")
 | 
			
		||||
	for path in "${paths[@]}"; do
 | 
			
		||||
		for locale in "${locales[@]}"; do
 | 
			
		||||
			json2po -t "${path}/en" "${path}/${locale}" "${path}/po-${locale}"
 | 
			
		||||
		done
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
update_2() {
 | 
			
		||||
	# Recover translation from PO files to localized JSON files
 | 
			
		||||
	local locales=("$@")
 | 
			
		||||
	for path in "${paths[@]}"; do
 | 
			
		||||
		for locale in "${locales[@]}"; do
 | 
			
		||||
			po2json -t "${path}/en" "${path}/po-${locale}" "${path}/${locale}"
 | 
			
		||||
		done
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
help() {
 | 
			
		||||
	echo -e "\nDescription:"
 | 
			
		||||
	echo -e "\tCreate PO files to make easier the labor of translation"
 | 
			
		||||
	echo -e "\nUsage:"
 | 
			
		||||
	echo -e "\t./translation.sh [--stats] [--update1 <OPT_LOCALE>] [--update2 <OPT_LOCALE>]"
 | 
			
		||||
	echo -e "\nFlags:"
 | 
			
		||||
	echo -e "  --clear\n\tClear all po-* directories"
 | 
			
		||||
	echo -e "  --stats\n\tPrint the number of existing strings on the JSON files for each locale"
 | 
			
		||||
	echo -e "  --update1 <LOCALE>\n\tUpdate PO files from English and localized JSON files as source"
 | 
			
		||||
	echo -e "  --update2 <LOCALE>\n\tRecover translation from PO files to localized JSON files"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Main function ------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Get script directory to set file path relative to it
 | 
			
		||||
file_path="$(
 | 
			
		||||
	cd -- "$(dirname "${0}")" >/dev/null 2>&1 || exit
 | 
			
		||||
	pwd -P
 | 
			
		||||
)"
 | 
			
		||||
paths=(
 | 
			
		||||
	"${file_path}/../../apps/server/src/assets/translations/"
 | 
			
		||||
	"${file_path}/../../apps/client/src/translations/"
 | 
			
		||||
)
 | 
			
		||||
locales=(cn de es fr pt_br ro tw)
 | 
			
		||||
 | 
			
		||||
if [ $# -eq 1 ]; then
 | 
			
		||||
	if [ "$1" == "--clear" ]; then
 | 
			
		||||
		for path in "${paths[@]}"; do
 | 
			
		||||
			for locale in "${locales[@]}"; do
 | 
			
		||||
				[ -d "${path}/po-${locale}" ] && rm -r "${path}/po-${locale}"
 | 
			
		||||
			done
 | 
			
		||||
		done
 | 
			
		||||
	elif [ "$1" == "--stats" ]; then
 | 
			
		||||
		stats
 | 
			
		||||
	elif [ "$1" == "--update1" ]; then
 | 
			
		||||
		update_1 "${locales[@]}"
 | 
			
		||||
	elif [ "$1" == "--update2" ]; then
 | 
			
		||||
		update_2 "${locales[@]}"
 | 
			
		||||
	else
 | 
			
		||||
		help
 | 
			
		||||
	fi
 | 
			
		||||
elif [ $# -eq 2 ]; then
 | 
			
		||||
	if [ "$1" == "--update1" ]; then
 | 
			
		||||
		update_1 "$2"
 | 
			
		||||
	elif [ "$1" == "--update2" ]; then
 | 
			
		||||
		update_2 "$2"
 | 
			
		||||
	else
 | 
			
		||||
		help
 | 
			
		||||
	fi
 | 
			
		||||
else
 | 
			
		||||
	help
 | 
			
		||||
fi
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="1"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-bookmark"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M18 7v14l-6 -4l-6 4v-14a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4z" /></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 383 B  | 
@@ -1,39 +0,0 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
if ! command -v magick &> /dev/null; then
 | 
			
		||||
  echo "This tool requires ImageMagick to be installed in order to create the icons."
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if ! command -v inkscape &> /dev/null; then
 | 
			
		||||
  echo "This tool requires Inkscape to be render sharper SVGs than ImageMagick."
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
script_dir=$(realpath $(dirname $0))
 | 
			
		||||
images_dir="$script_dir/../../images"
 | 
			
		||||
output_dir="$images_dir/app-icons/tray"
 | 
			
		||||
 | 
			
		||||
function generateDpiScaledIcons {
 | 
			
		||||
  file=$1
 | 
			
		||||
  suffix=$2
 | 
			
		||||
  name="$(basename $file .svg)$suffix"
 | 
			
		||||
  inkscape -w 16 -h 16 "$file" -o "$output_dir/$name.png"
 | 
			
		||||
  inkscape -w 20 -h 20 "$file" -o "$output_dir/$name@1.25x.png"
 | 
			
		||||
  inkscape -w 24 -h 24 "$file" -o "$output_dir/$name@1.5x.png"
 | 
			
		||||
  inkscape -w 32 -h 32 "$file" -o "$output_dir/$name@2x.png"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
generateDpiScaledIcons "$images_dir/icon-black.svg" "Template"
 | 
			
		||||
generateDpiScaledIcons "$images_dir/icon-color.svg"
 | 
			
		||||
generateDpiScaledIcons "$images_dir/icon-purple.svg"
 | 
			
		||||
 | 
			
		||||
for file in *.svg; do
 | 
			
		||||
    name="$(basename $file .svg)Template"
 | 
			
		||||
    generateDpiScaledIcons "$file" "Template"
 | 
			
		||||
    magick "$output_dir/$name.png" -channel RGB -negate "$output_dir/$name-inverted.png"
 | 
			
		||||
    magick "$output_dir/$name@1.25x.png" -channel RGB -negate "$output_dir/$name-inverted@1.25x.png"
 | 
			
		||||
    magick "$output_dir/$name@1.5x.png" -channel RGB -negate "$output_dir/$name-inverted@1.5x.png"
 | 
			
		||||
    magick "$output_dir/$name@2x.png" -channel RGB -negate "$output_dir/$name-inverted@2x.png"
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="1"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-x"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M18 6l-12 12" /><path d="M6 6l12 12" /></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 356 B  | 
@@ -1 +0,0 @@
 | 
			
		||||
<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="1"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-plus"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 357 B  | 
@@ -1 +0,0 @@
 | 
			
		||||
<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="1"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-history"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 8l0 4l2 2" /><path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" /></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 387 B  | 
@@ -1 +0,0 @@
 | 
			
		||||
<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="1"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-calendar-star"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11 21h-5a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v3.5" /><path d="M16 3v4" /><path d="M8 3v4" /><path d="M4 11h11" /><path d="M17.8 20.817l-2.172 1.138a.392 .392 0 0 1 -.568 -.41l.415 -2.411l-1.757 -1.707a.389 .389 0 0 1 .217 -.665l2.428 -.352l1.086 -2.193a.392 .392 0 0 1 .702 0l1.086 2.193l2.428 .352a.39 .39 0 0 1 .217 .665l-1.757 1.707l.414 2.41a.39 .39 0 0 1 -.567 .411l-2.172 -1.138z" /></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 734 B  | 
@@ -1,10 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
  <dict>
 | 
			
		||||
    <key>com.apple.security.cs.allow-jit</key>
 | 
			
		||||
    <true/>
 | 
			
		||||
    <key>com.apple.security.files.user-selected.read-write</key>
 | 
			
		||||
    <true/>
 | 
			
		||||
  </dict>
 | 
			
		||||
</plist>
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
import eslint from "@eslint/js";
 | 
			
		||||
import tseslint from "typescript-eslint";
 | 
			
		||||
import simpleImportSort from "eslint-plugin-simple-import-sort";
 | 
			
		||||
 | 
			
		||||
export default tseslint.config(
 | 
			
		||||
    eslint.configs.recommended,
 | 
			
		||||
    tseslint.configs.recommended,
 | 
			
		||||
    // consider using rules below, once we have a full TS codebase and can be more strict
 | 
			
		||||
    // tseslint.configs.strictTypeChecked,
 | 
			
		||||
    // tseslint.configs.stylisticTypeChecked,
 | 
			
		||||
    // tseslint.configs.recommendedTypeChecked,
 | 
			
		||||
    {
 | 
			
		||||
        languageOptions: {
 | 
			
		||||
            parserOptions: {
 | 
			
		||||
                projectService: true,
 | 
			
		||||
                tsconfigRootDir: import.meta.dirname
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        plugins: {
 | 
			
		||||
            "simple-import-sort": simpleImportSort
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        rules: {
 | 
			
		||||
            // add rule overrides here
 | 
			
		||||
            "no-undef": "off",
 | 
			
		||||
            "no-unused-vars": "off",
 | 
			
		||||
            "@typescript-eslint/no-unused-vars": [
 | 
			
		||||
                "error",
 | 
			
		||||
                {
 | 
			
		||||
                    argsIgnorePattern: "^_",
 | 
			
		||||
                    varsIgnorePattern: "^_"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "simple-import-sort/imports": "error",
 | 
			
		||||
            "simple-import-sort/exports": "error"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        ignores: [
 | 
			
		||||
            "build/*",
 | 
			
		||||
            "dist/*",
 | 
			
		||||
            "docs/*",
 | 
			
		||||
            "demo/*",
 | 
			
		||||
            "src/public/app-dist/*",
 | 
			
		||||
            "src/public/app/doc_notes/*"
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
import stylistic from "@stylistic/eslint-plugin";
 | 
			
		||||
import tsParser from "@typescript-eslint/parser";
 | 
			
		||||
 | 
			
		||||
// eslint config just for formatting rules
 | 
			
		||||
// potentially to be merged with the linting rules into one single config,
 | 
			
		||||
// once we have fixed the majority of lint errors
 | 
			
		||||
 | 
			
		||||
// Go to https://eslint.style/rules/default/${rule_without_prefix} to check the rule details
 | 
			
		||||
export const stylisticRules = {
 | 
			
		||||
    "@stylistic/indent": [ "error", 4 ],
 | 
			
		||||
    "@stylistic/quotes": [ "error", "double", { avoidEscape: true, allowTemplateLiterals: "always" } ],
 | 
			
		||||
    "@stylistic/semi": [ "error", "always" ],
 | 
			
		||||
    "@stylistic/quote-props": [ "error", "consistent-as-needed" ],
 | 
			
		||||
    "@stylistic/max-len": [ "error", { code: 100 } ],
 | 
			
		||||
    "@stylistic/comma-dangle": [ "error", "never" ],
 | 
			
		||||
    "@stylistic/linebreak-style": [ "error", "unix" ],
 | 
			
		||||
    "@stylistic/array-bracket-spacing": [ "error", "always" ],
 | 
			
		||||
    "@stylistic/object-curly-spacing": [ "error", "always" ],
 | 
			
		||||
    "@stylistic/padded-blocks": [ "error", { classes: "always" } ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default [
 | 
			
		||||
    {
 | 
			
		||||
        files: [ "**/*.{js,ts,mjs,cjs}" ],
 | 
			
		||||
        languageOptions: {
 | 
			
		||||
            parser: tsParser
 | 
			
		||||
        },
 | 
			
		||||
        plugins: {
 | 
			
		||||
            "@stylistic": stylistic
 | 
			
		||||
        },
 | 
			
		||||
        rules: {
 | 
			
		||||
            ...stylisticRules
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        ignores: [
 | 
			
		||||
            "build/*",
 | 
			
		||||
            "dist/*",
 | 
			
		||||
            "docs/*",
 | 
			
		||||
            "demo/*",
 | 
			
		||||
            // TriliumNextTODO: check if we want to format packages here as well - for now skipping it
 | 
			
		||||
            "packages/*",
 | 
			
		||||
            "src/public/app-dist/*",
 | 
			
		||||
            "src/public/app/doc_notes/*"
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
import { test as setup, expect } from "@playwright/test";
 | 
			
		||||
 | 
			
		||||
const authFile = "playwright/.auth/user.json";
 | 
			
		||||
 | 
			
		||||
const ROOT_URL = "http://localhost:8082";
 | 
			
		||||
const LOGIN_PASSWORD = "demo1234";
 | 
			
		||||
 | 
			
		||||
// Reference: https://playwright.dev/docs/auth#basic-shared-account-in-all-tests
 | 
			
		||||
 | 
			
		||||
setup("authenticate", async ({ page }) => {
 | 
			
		||||
    await page.goto(ROOT_URL);
 | 
			
		||||
    await expect(page).toHaveURL(`${ROOT_URL}/login`);
 | 
			
		||||
 | 
			
		||||
    await page.getByRole("textbox", { name: "Password" }).fill(LOGIN_PASSWORD);
 | 
			
		||||
    await page.getByRole("button", { name: "Login" }).click();
 | 
			
		||||
    await page.context().storageState({ path: authFile });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
import { test, expect } from "@playwright/test";
 | 
			
		||||
 | 
			
		||||
test("Can duplicate note with broken links", async ({ page }) => {
 | 
			
		||||
    await page.goto(`http://localhost:8082/#2VammGGdG6Ie`);
 | 
			
		||||
    await page.locator(".tree-wrapper .fancytree-active").getByText("Note map").click({ button: "right" });
 | 
			
		||||
    await page.getByText("Duplicate subtree").click();
 | 
			
		||||
    await expect(page.locator(".toast-body")).toBeHidden();
 | 
			
		||||
    await expect(page.locator(".tree-wrapper").getByText("Note map (dup)")).toBeVisible();
 | 
			
		||||
});
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
import { test, expect } from "@playwright/test";
 | 
			
		||||
 | 
			
		||||
test("has title", async ({ page }) => {
 | 
			
		||||
    await page.goto("https://playwright.dev/");
 | 
			
		||||
 | 
			
		||||
    // Expect a title "to contain" a substring.
 | 
			
		||||
    await expect(page).toHaveTitle(/Playwright/);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test("get started link", async ({ page }) => {
 | 
			
		||||
    await page.goto("https://playwright.dev/");
 | 
			
		||||
 | 
			
		||||
    // Click the get started link.
 | 
			
		||||
    await page.getByRole("link", { name: "Get started" }).click();
 | 
			
		||||
 | 
			
		||||
    // Expects page to have a heading with the name of Installation.
 | 
			
		||||
    await expect(page.getByRole("heading", { name: "Installation" })).toBeVisible();
 | 
			
		||||
});
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
import test, { expect } from "@playwright/test";
 | 
			
		||||
 | 
			
		||||
test("Native Title Bar not displayed on web", async ({ page }) => {
 | 
			
		||||
    await page.goto("http://localhost:8082/#root/_hidden/_options/_optionsAppearance");
 | 
			
		||||
    await expect(page.getByRole("heading", { name: "Theme" })).toBeVisible();
 | 
			
		||||
    await expect(page.getByRole("heading", { name: "Native Title Bar (requires" })).toBeHidden();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test("Tray settings not displayed on web", async ({ page }) => {
 | 
			
		||||
    await page.goto("http://localhost:8082/#root/_hidden/_options/_optionsOther");
 | 
			
		||||
    await expect(page.getByRole("heading", { name: "Note Erasure Timeout" })).toBeVisible();
 | 
			
		||||
    await expect(page.getByRole("heading", { name: "Tray" })).toBeHidden();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test("Spellcheck settings not displayed on web", async ({ page }) => {
 | 
			
		||||
    await page.goto("http://localhost:8082/#root/_hidden/_options/_optionsSpellcheck");
 | 
			
		||||
    await expect(page.getByRole("heading", { name: "Spell Check" })).toBeVisible();
 | 
			
		||||
    await expect(page.getByRole("heading", { name: "Tray" })).toBeHidden();
 | 
			
		||||
    await expect(page.getByText("These options apply only for desktop builds")).toBeVisible();
 | 
			
		||||
    await expect(page.getByText("Enable spellcheck")).toBeHidden();
 | 
			
		||||
});
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
import test, { expect } from "@playwright/test";
 | 
			
		||||
 | 
			
		||||
test("Renders on desktop", async ({ page, context }) => {
 | 
			
		||||
    await page.goto("http://localhost:8082");
 | 
			
		||||
    await expect(page.locator(".tree")).toContainText("Trilium Integration Test");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test("Renders on mobile", async ({ page, context }) => {
 | 
			
		||||
    await context.addCookies([
 | 
			
		||||
        {
 | 
			
		||||
            url: "http://localhost:8082",
 | 
			
		||||
            name: "trilium-device",
 | 
			
		||||
            value: "mobile"
 | 
			
		||||
        }
 | 
			
		||||
    ]);
 | 
			
		||||
    await page.goto("http://localhost:8082");
 | 
			
		||||
    await expect(page.locator(".tree")).toContainText("Trilium Integration Test");
 | 
			
		||||
});
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
import { test, expect } from "@playwright/test";
 | 
			
		||||
 | 
			
		||||
const expectedVersion = "0.90.3";
 | 
			
		||||
 | 
			
		||||
test("Displays update badge when there is a version available", async ({ page }) => {
 | 
			
		||||
    await page.goto("http://localhost:8080");
 | 
			
		||||
    await page.getByRole("button", { name: "" }).click();
 | 
			
		||||
    await page.getByText(`Version ${expectedVersion} is available,`).click();
 | 
			
		||||
 | 
			
		||||
    const page1 = await page.waitForEvent("popup");
 | 
			
		||||
    expect(page1.url()).toBe(`https://github.com/TriliumNext/Trilium/releases/tag/v${expectedVersion}`);
 | 
			
		||||
});
 | 
			
		||||
@@ -1,56 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "main": "./electron-main.js",
 | 
			
		||||
  "bin": {
 | 
			
		||||
    "trilium": "src/main.js"
 | 
			
		||||
  },
 | 
			
		||||
  "type": "module",
 | 
			
		||||
  "scripts": {    
 | 
			
		||||
    "server:start-safe": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev nodemon src/main.ts",
 | 
			
		||||
    "server:start-no-dir": "cross-env TRILIUM_ENV=dev nodemon src/main.ts",
 | 
			
		||||
    "server:start-test": "npm run server:switch && rimraf ./data-test && cross-env TRILIUM_DATA_DIR=./data-test TRILIUM_ENV=dev TRILIUM_PORT=9999 nodemon src/main.ts",
 | 
			
		||||
    "server:qstart": "npm run server:switch && npm run server:start",
 | 
			
		||||
    "server:switch": "rimraf ./node_modules/better-sqlite3 && npm install",
 | 
			
		||||
    "electron:start-no-dir": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_ENV=dev TRILIUM_PORT=37742 electron --inspect=5858 .",
 | 
			
		||||
    "electron:start-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-main.ts --inspect=5858 .\"",
 | 
			
		||||
    "electron:start-nix-no-dir": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_ENV=dev TRILIUM_PORT=37742 nix-shell -p electron_33 --run \"electron ./electron-main.ts --inspect=5858 .\"",    
 | 
			
		||||
    "electron:start-prod-no-dir": "npm run build:prepare-dist && cross-env TRILIUM_ENV=prod electron --inspect=5858 .",
 | 
			
		||||
    "electron:start-prod-nix": "electron-rebuild --version 33.3.1 && npm run build:prepare-dist && cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./dist/electron-main.js --inspect=5858 .\"",
 | 
			
		||||
    "electron:start-prod-nix-no-dir": "electron-rebuild --version 33.3.1 && npm run build:prepare-dist && cross-env TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./dist/electron-main.js --inspect=5858 .\"",
 | 
			
		||||
    "electron:qstart": "npm run electron:switch && npm run electron:start",
 | 
			
		||||
    "electron:switch": "electron-rebuild",    
 | 
			
		||||
    "docs:build": "typedoc",        
 | 
			
		||||
    "test": "npm run client:test && npm run server:test",    
 | 
			
		||||
    "client:test": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app",
 | 
			
		||||
    "client:coverage": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app --coverage",
 | 
			
		||||
    "test:playwright": "playwright test --workers 1",
 | 
			
		||||
    "test:integration-edit-db": "cross-env TRILIUM_INTEGRATION_TEST=edit TRILIUM_PORT=8081 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts",
 | 
			
		||||
    "test:integration-mem-db": "cross-env    nodemon src/main.ts",
 | 
			
		||||
    "test:integration-mem-db-dev": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts",
 | 
			
		||||
    "dev:watch-dist": "tsx ./bin/watch-dist.ts",
 | 
			
		||||
    "dev:format-check": "eslint -c eslint.format.config.js .",
 | 
			
		||||
    "dev:format-fix": "eslint -c eslint.format.config.js . --fix",
 | 
			
		||||
    "dev:linter-check": "eslint .",
 | 
			
		||||
    "dev:linter-fix": "eslint . --fix",    
 | 
			
		||||
    "chore:generate-document": "cross-env nodemon ./bin/generate_document.ts 1000",
 | 
			
		||||
    "chore:generate-openapi": "tsx bin/generate-openapi.js"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {    
 | 
			
		||||
    "@playwright/test": "1.56.1",
 | 
			
		||||
    "@stylistic/eslint-plugin": "5.5.0",        
 | 
			
		||||
    "@types/express": "5.0.5",    
 | 
			
		||||
    "@types/node": "24.9.2",    
 | 
			
		||||
    "@types/yargs": "17.0.34",
 | 
			
		||||
    "@vitest/coverage-v8": "3.2.4",
 | 
			
		||||
    "eslint": "9.39.0",
 | 
			
		||||
    "eslint-plugin-simple-import-sort": "12.1.1",
 | 
			
		||||
    "esm": "3.2.25",
 | 
			
		||||
    "jsdoc": "4.0.5",
 | 
			
		||||
    "lorem-ipsum": "2.0.8",    
 | 
			
		||||
    "rcedit": "4.0.1",
 | 
			
		||||
    "rimraf": "6.1.0",    
 | 
			
		||||
    "tslib": "2.8.1" 
 | 
			
		||||
  },
 | 
			
		||||
  "optionalDependencies": {
 | 
			
		||||
    "appdmg": "0.6.6"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
import etapi from "../support/etapi.js";
 | 
			
		||||
/* TriliumNextTODO: port to Vitest 
 | 
			
		||||
etapi.describeEtapi("app_info", () => {
 | 
			
		||||
    it("get", async () => {
 | 
			
		||||
        const appInfo = await etapi.getEtapi("app-info");
 | 
			
		||||
        expect(appInfo.clipperProtocolVersion).toEqual("1.0");
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
import etapi from "../support/etapi.js";
 | 
			
		||||
 | 
			
		||||
/* TriliumNextTODO: port to Vitest
 | 
			
		||||
etapi.describeEtapi("backup", () => {
 | 
			
		||||
    it("create", async () => {
 | 
			
		||||
        const response = await etapi.putEtapiContent("backup/etapi_test");
 | 
			
		||||
        expect(response.status).toEqual(204);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
import etapi from "../support/etapi.js";
 | 
			
		||||
import fs from "fs";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { fileURLToPath } from "url";
 | 
			
		||||
 | 
			
		||||
/* TriliumNextTODO: port to Vitest 
 | 
			
		||||
etapi.describeEtapi("import", () => {
 | 
			
		||||
    // temporarily skip this test since test-export.zip is missing
 | 
			
		||||
    xit("import", async () => {
 | 
			
		||||
        const scriptDir = path.dirname(fileURLToPath(import.meta.url));
 | 
			
		||||
 | 
			
		||||
        const zipFileBuffer = fs.readFileSync(path.resolve(scriptDir, "test-export.zip"));
 | 
			
		||||
 | 
			
		||||
        const response = await etapi.postEtapiContent("notes/root/import", zipFileBuffer);
 | 
			
		||||
        expect(response.status).toEqual(201);
 | 
			
		||||
 | 
			
		||||
        const { note, branch } = await response.json();
 | 
			
		||||
 | 
			
		||||
        expect(note.title).toEqual("test-export");
 | 
			
		||||
        expect(branch.parentNoteId).toEqual("root");
 | 
			
		||||
 | 
			
		||||
        const content = await (await etapi.getEtapiContent(`notes/${note.noteId}/content`)).text();
 | 
			
		||||
        expect(content).toContain("test export content");
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
import crypto from "crypto";
 | 
			
		||||
import etapi from "../support/etapi.js";
 | 
			
		||||
 | 
			
		||||
/* TriliumNextTODO: port to Vitest
 | 
			
		||||
etapi.describeEtapi("notes", () => {
 | 
			
		||||
    it("create", async () => {
 | 
			
		||||
        const { note, branch } = await etapi.postEtapi("create-note", {
 | 
			
		||||
            parentNoteId: "root",
 | 
			
		||||
            type: "text",
 | 
			
		||||
            title: "Hello World!",
 | 
			
		||||
            content: "Content",
 | 
			
		||||
            prefix: "Custom prefix"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        expect(note.title).toEqual("Hello World!");
 | 
			
		||||
        expect(branch.parentNoteId).toEqual("root");
 | 
			
		||||
        expect(branch.prefix).toEqual("Custom prefix");
 | 
			
		||||
 | 
			
		||||
        const rNote = await etapi.getEtapi(`notes/${note.noteId}`);
 | 
			
		||||
        expect(rNote.title).toEqual("Hello World!");
 | 
			
		||||
 | 
			
		||||
        const rContent = await (await etapi.getEtapiContent(`notes/${note.noteId}/content`)).text();
 | 
			
		||||
        expect(rContent).toEqual("Content");
 | 
			
		||||
 | 
			
		||||
        const rBranch = await etapi.getEtapi(`branches/${branch.branchId}`);
 | 
			
		||||
        expect(rBranch.parentNoteId).toEqual("root");
 | 
			
		||||
        expect(rBranch.prefix).toEqual("Custom prefix");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("patch", async () => {
 | 
			
		||||
        const { note } = await etapi.postEtapi("create-note", {
 | 
			
		||||
            parentNoteId: "root",
 | 
			
		||||
            type: "text",
 | 
			
		||||
            title: "Hello World!",
 | 
			
		||||
            content: "Content"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await etapi.patchEtapi(`notes/${note.noteId}`, {
 | 
			
		||||
            title: "new title",
 | 
			
		||||
            type: "code",
 | 
			
		||||
            mime: "text/apl",
 | 
			
		||||
            dateCreated: "2000-01-01 12:34:56.999+0200",
 | 
			
		||||
            utcDateCreated: "2000-01-01 10:34:56.999Z"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const rNote = await etapi.getEtapi(`notes/${note.noteId}`);
 | 
			
		||||
        expect(rNote.title).toEqual("new title");
 | 
			
		||||
        expect(rNote.type).toEqual("code");
 | 
			
		||||
        expect(rNote.mime).toEqual("text/apl");
 | 
			
		||||
        expect(rNote.dateCreated).toEqual("2000-01-01 12:34:56.999+0200");
 | 
			
		||||
        expect(rNote.utcDateCreated).toEqual("2000-01-01 10:34:56.999Z");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("update content", async () => {
 | 
			
		||||
        const { note } = await etapi.postEtapi("create-note", {
 | 
			
		||||
            parentNoteId: "root",
 | 
			
		||||
            type: "text",
 | 
			
		||||
            title: "Hello World!",
 | 
			
		||||
            content: "Content"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await etapi.putEtapiContent(`notes/${note.noteId}/content`, "new content");
 | 
			
		||||
 | 
			
		||||
        const rContent = await (await etapi.getEtapiContent(`notes/${note.noteId}/content`)).text();
 | 
			
		||||
        expect(rContent).toEqual("new content");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("create / update binary content", async () => {
 | 
			
		||||
        const { note } = await etapi.postEtapi("create-note", {
 | 
			
		||||
            parentNoteId: "root",
 | 
			
		||||
            type: "file",
 | 
			
		||||
            title: "Hello World!",
 | 
			
		||||
            content: "ZZZ"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const updatedContent = crypto.randomBytes(16);
 | 
			
		||||
 | 
			
		||||
        await etapi.putEtapiContent(`notes/${note.noteId}/content`, updatedContent);
 | 
			
		||||
 | 
			
		||||
        const rContent = await (await etapi.getEtapiContent(`notes/${note.noteId}/content`)).arrayBuffer();
 | 
			
		||||
        expect(Buffer.from(new Uint8Array(rContent))).toEqual(updatedContent);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("delete note", async () => {
 | 
			
		||||
        const { note } = await etapi.postEtapi("create-note", {
 | 
			
		||||
            parentNoteId: "root",
 | 
			
		||||
            type: "text",
 | 
			
		||||
            title: "Hello World!",
 | 
			
		||||
            content: "Content"
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await etapi.deleteEtapi(`notes/${note.noteId}`);
 | 
			
		||||
 | 
			
		||||
        const resp = await etapi.getEtapiResponse(`notes/${note.noteId}`);
 | 
			
		||||
        expect(resp.status).toEqual(404);
 | 
			
		||||
 | 
			
		||||
        const error = await resp.json();
 | 
			
		||||
        expect(error.status).toEqual(404);
 | 
			
		||||
        expect(error.code).toEqual("NOTE_NOT_FOUND");
 | 
			
		||||
        expect(error.message).toEqual(`Note '${note.noteId}' not found.`);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
*/
 | 
			
		||||
@@ -1,152 +0,0 @@
 | 
			
		||||
import { describe, beforeAll, afterAll } from "vitest";
 | 
			
		||||
 | 
			
		||||
let etapiAuthToken: string | undefined;
 | 
			
		||||
 | 
			
		||||
const getEtapiAuthorizationHeader = (): string => "Basic " + Buffer.from(`etapi:${etapiAuthToken}`).toString("base64");
 | 
			
		||||
 | 
			
		||||
const PORT: string = "9999";
 | 
			
		||||
const HOST: string = "http://localhost:" + PORT;
 | 
			
		||||
 | 
			
		||||
type SpecDefinitionsFunc = () => void;
 | 
			
		||||
 | 
			
		||||
function describeEtapi(description: string, specDefinitions: SpecDefinitionsFunc): void {
 | 
			
		||||
    describe(description, () => {
 | 
			
		||||
        beforeAll(async () => {});
 | 
			
		||||
 | 
			
		||||
        afterAll(() => {});
 | 
			
		||||
 | 
			
		||||
        specDefinitions();
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getEtapiResponse(url: string): Promise<Response> {
 | 
			
		||||
    return await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "GET",
 | 
			
		||||
        headers: {
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getEtapi(url: string): Promise<any> {
 | 
			
		||||
    const response = await getEtapiResponse(url);
 | 
			
		||||
    return await processEtapiResponse(response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getEtapiContent(url: string): Promise<Response> {
 | 
			
		||||
    const response = await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "GET",
 | 
			
		||||
        headers: {
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    checkStatus(response);
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function postEtapi(url: string, data: Record<string, unknown> = {}): Promise<any> {
 | 
			
		||||
    const response = await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify(data)
 | 
			
		||||
    });
 | 
			
		||||
    return await processEtapiResponse(response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function postEtapiContent(url: string, data: BodyInit): Promise<Response> {
 | 
			
		||||
    const response = await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/octet-stream",
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        },
 | 
			
		||||
        body: data
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    checkStatus(response);
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function putEtapi(url: string, data: Record<string, unknown> = {}): Promise<any> {
 | 
			
		||||
    const response = await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "PUT",
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify(data)
 | 
			
		||||
    });
 | 
			
		||||
    return await processEtapiResponse(response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function putEtapiContent(url: string, data?: BodyInit): Promise<Response> {
 | 
			
		||||
    const response = await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "PUT",
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/octet-stream",
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        },
 | 
			
		||||
        body: data
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    checkStatus(response);
 | 
			
		||||
 | 
			
		||||
    return response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function patchEtapi(url: string, data: Record<string, unknown> = {}): Promise<any> {
 | 
			
		||||
    const response = await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "PATCH",
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify(data)
 | 
			
		||||
    });
 | 
			
		||||
    return await processEtapiResponse(response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function deleteEtapi(url: string): Promise<any> {
 | 
			
		||||
    const response = await fetch(`${HOST}/etapi/${url}`, {
 | 
			
		||||
        method: "DELETE",
 | 
			
		||||
        headers: {
 | 
			
		||||
            Authorization: getEtapiAuthorizationHeader()
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    return await processEtapiResponse(response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function processEtapiResponse(response: Response): Promise<any> {
 | 
			
		||||
    const text = await response.text();
 | 
			
		||||
 | 
			
		||||
    if (response.status < 200 || response.status >= 300) {
 | 
			
		||||
        throw new Error(`ETAPI error ${response.status}: ${text}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return text?.trim() ? JSON.parse(text) : null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function checkStatus(response: Response): void {
 | 
			
		||||
    if (response.status < 200 || response.status >= 300) {
 | 
			
		||||
        throw new Error(`ETAPI error ${response.status}`);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    describeEtapi,
 | 
			
		||||
    getEtapi,
 | 
			
		||||
    getEtapiResponse,
 | 
			
		||||
    getEtapiContent,
 | 
			
		||||
    postEtapi,
 | 
			
		||||
    postEtapiContent,
 | 
			
		||||
    putEtapi,
 | 
			
		||||
    putEtapiContent,
 | 
			
		||||
    patchEtapi,
 | 
			
		||||
    deleteEtapi
 | 
			
		||||
};
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "module": "NodeNext",
 | 
			
		||||
    "declaration": false,
 | 
			
		||||
    "sourceMap": true,
 | 
			
		||||
    "outDir": "./build",
 | 
			
		||||
    "strict": true,
 | 
			
		||||
    "noImplicitAny": true,
 | 
			
		||||
    "resolveJsonModule": true,
 | 
			
		||||
    "lib": ["ES2023"],
 | 
			
		||||
    "downlevelIteration": true,
 | 
			
		||||
    "skipLibCheck": true,
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "allowJs": true
 | 
			
		||||
  },
 | 
			
		||||
  "include": ["./src/public/app/**/*"],
 | 
			
		||||
  "files": [
 | 
			
		||||
    "./src/public/app/types.d.ts",
 | 
			
		||||
    "./src/public/app/types-lib.d.ts",
 | 
			
		||||
    "./src/types.d.ts"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "build-docs",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "main": "src/main.ts",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "tsx ."
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [],
 | 
			
		||||
  "author": "Elian Doran <contact@eliandoran.me>",
 | 
			
		||||
  "license": "AGPL-3.0-only",
 | 
			
		||||
  "packageManager": "pnpm@10.19.0",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@redocly/cli": "2.10.0",
 | 
			
		||||
    "archiver": "7.0.1",
 | 
			
		||||
    "fs-extra": "11.3.2",
 | 
			
		||||
    "react": "19.2.0",
 | 
			
		||||
    "react-dom": "19.2.0",
 | 
			
		||||
    "typedoc": "0.28.14",
 | 
			
		||||
    "typedoc-plugin-missing-exports": "4.1.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * The backend script API is accessible to code notes with the "JS (backend)" language.
 | 
			
		||||
 *
 | 
			
		||||
 * The entire API is exposed as a single global: {@link api}
 | 
			
		||||
 *
 | 
			
		||||
 * @module Backend Script API
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This file creates the entrypoint for TypeDoc that simulates the context from within a
 | 
			
		||||
 * script note on the server side.
 | 
			
		||||
 *
 | 
			
		||||
 * Make sure to keep in line with backend's `script_context.ts`.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export type { default as AbstractBeccaEntity } from "../../server/src/becca/entities/abstract_becca_entity.js";
 | 
			
		||||
export type { default as BAttachment } from "../../server/src/becca/entities/battachment.js";
 | 
			
		||||
export type { default as BAttribute } from "../../server/src/becca/entities/battribute.js";
 | 
			
		||||
export type { default as BBranch } from "../../server/src/becca/entities/bbranch.js";
 | 
			
		||||
export type { default as BEtapiToken } from "../../server/src/becca/entities/betapi_token.js";
 | 
			
		||||
export type { BNote };
 | 
			
		||||
export type { default as BOption } from "../../server/src/becca/entities/boption.js";
 | 
			
		||||
export type { default as BRecentNote } from "../../server/src/becca/entities/brecent_note.js";
 | 
			
		||||
export type { default as BRevision } from "../../server/src/becca/entities/brevision.js";
 | 
			
		||||
 | 
			
		||||
import BNote from "../../server/src/becca/entities/bnote.js";
 | 
			
		||||
import BackendScriptApi, { type Api } from "../../server/src/services/backend_script_api.js";
 | 
			
		||||
 | 
			
		||||
export type { Api };
 | 
			
		||||
 | 
			
		||||
const fakeNote = new BNote();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The `api` global variable allows access to the backend script API, which is documented in {@link Api}.
 | 
			
		||||
 */
 | 
			
		||||
export const api: Api = new BackendScriptApi(fakeNote, {});
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
process.env.TRILIUM_INTEGRATION_TEST = "memory-no-store";
 | 
			
		||||
process.env.TRILIUM_RESOURCE_DIR = "../server/src";
 | 
			
		||||
process.env.NODE_ENV = "development";
 | 
			
		||||
 | 
			
		||||
import cls from "@triliumnext/server/src/services/cls.js";
 | 
			
		||||
import { dirname, join, resolve } from "path";
 | 
			
		||||
import * as fs from "fs/promises";
 | 
			
		||||
import * as fsExtra from "fs-extra";
 | 
			
		||||
import archiver from "archiver";
 | 
			
		||||
import { WriteStream } from "fs";
 | 
			
		||||
import { execSync } from "child_process";
 | 
			
		||||
import BuildContext from "./context.js";
 | 
			
		||||
 | 
			
		||||
const DOCS_ROOT = "../../../docs";
 | 
			
		||||
const OUTPUT_DIR = "../../site";
 | 
			
		||||
 | 
			
		||||
async function buildDocsInner() {
 | 
			
		||||
    const i18n = await import("@triliumnext/server/src/services/i18n.js");
 | 
			
		||||
    await i18n.initializeTranslations();
 | 
			
		||||
 | 
			
		||||
    const sqlInit = (await import("../../server/src/services/sql_init.js")).default;
 | 
			
		||||
    await sqlInit.createInitialDatabase(true);
 | 
			
		||||
 | 
			
		||||
    const note = await importData(join(__dirname, DOCS_ROOT, "User Guide"));
 | 
			
		||||
 | 
			
		||||
    // Export
 | 
			
		||||
    const zipFilePath = "output.zip";
 | 
			
		||||
    try {
 | 
			
		||||
        const { exportToZip } = (await import("@triliumnext/server/src/services/export/zip.js")).default;
 | 
			
		||||
        const branch = note.getParentBranches()[0];
 | 
			
		||||
        const taskContext = new (await import("@triliumnext/server/src/services/task_context.js")).default(
 | 
			
		||||
            "no-progress-reporting",
 | 
			
		||||
            "export",
 | 
			
		||||
            null
 | 
			
		||||
        );
 | 
			
		||||
        const fileOutputStream = fsExtra.createWriteStream(zipFilePath);
 | 
			
		||||
        await exportToZip(taskContext, branch, "share", fileOutputStream);
 | 
			
		||||
        await waitForStreamToFinish(fileOutputStream);
 | 
			
		||||
        await extractZip(zipFilePath, OUTPUT_DIR);
 | 
			
		||||
    } finally {
 | 
			
		||||
        if (await fsExtra.exists(zipFilePath)) {
 | 
			
		||||
            await fsExtra.rm(zipFilePath);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Copy favicon.
 | 
			
		||||
    await fs.copyFile("../../apps/website/src/assets/favicon.ico", join(OUTPUT_DIR, "favicon.ico"));
 | 
			
		||||
 | 
			
		||||
    console.log("Documentation built successfully!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function importData(path: string) {
 | 
			
		||||
    const buffer = await createImportZip(path);
 | 
			
		||||
    const importService = (await import("@triliumnext/server/src/services/import/zip.js")).default;
 | 
			
		||||
    const TaskContext = (await import("@triliumnext/server/src/services/task_context.js")).default;
 | 
			
		||||
    const context = new TaskContext("no-progress-reporting", "importNotes", null);
 | 
			
		||||
    const becca = (await import("@triliumnext/server/src/becca/becca.js")).default;
 | 
			
		||||
 | 
			
		||||
    const rootNote = becca.getRoot();
 | 
			
		||||
    if (!rootNote) {
 | 
			
		||||
        throw new Error("Missing root note for import.");
 | 
			
		||||
    }
 | 
			
		||||
    return await importService.importZip(context, buffer, rootNote, {
 | 
			
		||||
        preserveIds: true
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function createImportZip(path: string) {
 | 
			
		||||
    const inputFile = "input.zip";
 | 
			
		||||
    const archive = archiver("zip", {
 | 
			
		||||
        zlib: { level: 0 }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    console.log("Archive path is ", resolve(path))
 | 
			
		||||
    archive.directory(path, "/");
 | 
			
		||||
 | 
			
		||||
    const outputStream = fsExtra.createWriteStream(inputFile);
 | 
			
		||||
    archive.pipe(outputStream);
 | 
			
		||||
    archive.finalize();
 | 
			
		||||
    await waitForStreamToFinish(outputStream);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await fsExtra.readFile(inputFile);
 | 
			
		||||
    } finally {
 | 
			
		||||
        await fsExtra.rm(inputFile);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function waitForStreamToFinish(stream: WriteStream) {
 | 
			
		||||
    return new Promise<void>((res, rej) => {
 | 
			
		||||
        stream.on("finish", () => res());
 | 
			
		||||
        stream.on("error", (err) => rej(err));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function extractZip(zipFilePath: string, outputPath: string, ignoredFiles?: Set<string>) {
 | 
			
		||||
    const { readZipFile, readContent } = (await import("@triliumnext/server/src/services/import/zip.js"));
 | 
			
		||||
    await readZipFile(await fs.readFile(zipFilePath), async (zip, entry) => {
 | 
			
		||||
        // We ignore directories since they can appear out of order anyway.
 | 
			
		||||
        if (!entry.fileName.endsWith("/") && !ignoredFiles?.has(entry.fileName)) {
 | 
			
		||||
            const destPath = join(outputPath, entry.fileName);
 | 
			
		||||
            const fileContent = await readContent(zip, entry);
 | 
			
		||||
 | 
			
		||||
            await fsExtra.mkdirs(dirname(destPath));
 | 
			
		||||
            await fs.writeFile(destPath, fileContent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        zip.readEntry();
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default async function buildDocs({ gitRootDir }: BuildContext) {
 | 
			
		||||
    // Build the share theme.
 | 
			
		||||
    execSync(`pnpm run --filter share-theme build`, {
 | 
			
		||||
        stdio: "inherit",
 | 
			
		||||
        cwd: gitRootDir
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Trigger the actual build.
 | 
			
		||||
    await new Promise((res, rej) => {
 | 
			
		||||
        cls.init(() => {
 | 
			
		||||
            buildDocsInner()
 | 
			
		||||
                .catch(rej)
 | 
			
		||||
                .then(res);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
export default interface BuildContext {
 | 
			
		||||
    gitRootDir: string;
 | 
			
		||||
    baseDir: string;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * The front script API is accessible to code notes with the "JS (frontend)" language.
 | 
			
		||||
 *
 | 
			
		||||
 * The entire API is exposed as a single global: {@link api}
 | 
			
		||||
 *
 | 
			
		||||
 * @module Frontend Script API
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This file creates the entrypoint for TypeDoc that simulates the context from within a
 | 
			
		||||
 * script note.
 | 
			
		||||
 *
 | 
			
		||||
 * Make sure to keep in line with frontend's `script_context.ts`.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export type { default as BasicWidget } from "../../client/src/widgets/basic_widget.js";
 | 
			
		||||
export type { default as FAttachment } from "../../client/src/entities/fattachment.js";
 | 
			
		||||
export type { default as FAttribute } from "../../client/src/entities/fattribute.js";
 | 
			
		||||
export type { default as FBranch } from "../../client/src/entities/fbranch.js";
 | 
			
		||||
export type { default as FNote } from "../../client/src/entities/fnote.js";
 | 
			
		||||
export type { Api } from "../../client/src/services/frontend_script_api.js";
 | 
			
		||||
export type { default as NoteContextAwareWidget } from "../../client/src/widgets/note_context_aware_widget.js";
 | 
			
		||||
export type { default as RightPanelWidget } from "../../client/src/widgets/right_panel_widget.js";
 | 
			
		||||
 | 
			
		||||
import FrontendScriptApi, { type Api } from "../../client/src/services/frontend_script_api.js";
 | 
			
		||||
 | 
			
		||||
//@ts-expect-error
 | 
			
		||||
export const api: Api = new FrontendScriptApi();
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
import { join } from "path";
 | 
			
		||||
import BuildContext from "./context";
 | 
			
		||||
import buildSwagger from "./swagger";
 | 
			
		||||
import { existsSync, mkdirSync, rmSync } from "fs";
 | 
			
		||||
import buildDocs from "./build-docs";
 | 
			
		||||
import buildScriptApi from "./script-api";
 | 
			
		||||
 | 
			
		||||
const context: BuildContext = {
 | 
			
		||||
    gitRootDir: join(__dirname, "../../../"),
 | 
			
		||||
    baseDir: join(__dirname, "../../../site")
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
    // Clean input dir.
 | 
			
		||||
    if (existsSync(context.baseDir)) {
 | 
			
		||||
        rmSync(context.baseDir, { recursive: true });
 | 
			
		||||
    }
 | 
			
		||||
    mkdirSync(context.baseDir);
 | 
			
		||||
 | 
			
		||||
    // Start building.
 | 
			
		||||
    await buildDocs(context);
 | 
			
		||||
    buildSwagger(context);
 | 
			
		||||
    buildScriptApi(context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main();
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
import { execSync } from "child_process";
 | 
			
		||||
import BuildContext from "./context";
 | 
			
		||||
import { join } from "path";
 | 
			
		||||
 | 
			
		||||
export default function buildScriptApi({ baseDir, gitRootDir }: BuildContext) {
 | 
			
		||||
    // Generate types
 | 
			
		||||
    execSync(`pnpm typecheck`, { stdio: "inherit", cwd: gitRootDir });
 | 
			
		||||
 | 
			
		||||
    for (const config of [ "backend", "frontend" ]) {
 | 
			
		||||
        const outDir = join(baseDir, "script-api", config);
 | 
			
		||||
        execSync(`pnpm typedoc --options typedoc.${config}.json --html "${outDir}"`, {
 | 
			
		||||
            stdio: "inherit"
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
import BuildContext from "./context";
 | 
			
		||||
import { join } from "path";
 | 
			
		||||
import { execSync } from "child_process";
 | 
			
		||||
import { mkdirSync } from "fs";
 | 
			
		||||
 | 
			
		||||
interface BuildInfo {
 | 
			
		||||
    specPath: string;
 | 
			
		||||
    outDir: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DIR_PREFIX = "rest-api";
 | 
			
		||||
 | 
			
		||||
const buildInfos: BuildInfo[] = [
 | 
			
		||||
    {
 | 
			
		||||
        // Paths are relative to Git root.
 | 
			
		||||
        specPath: "apps/server/internal.openapi.yaml",
 | 
			
		||||
        outDir: `${DIR_PREFIX}/internal`
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        specPath: "apps/server/etapi.openapi.yaml",
 | 
			
		||||
        outDir: `${DIR_PREFIX}/etapi`
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export default function buildSwagger({ baseDir, gitRootDir }: BuildContext) {
 | 
			
		||||
    for (const { specPath, outDir } of buildInfos) {
 | 
			
		||||
        const absSpecPath = join(gitRootDir, specPath);
 | 
			
		||||
        const targetDir = join(baseDir, outDir);
 | 
			
		||||
        mkdirSync(targetDir, { recursive: true });
 | 
			
		||||
        execSync(`pnpm redocly build-docs ${absSpecPath} -o ${targetDir}/index.html`, { stdio: "inherit" });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "extends": "../../tsconfig.base.json",
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "module": "ESNext",
 | 
			
		||||
    "moduleResolution": "bundler",
 | 
			
		||||
    "target": "ES2020",
 | 
			
		||||
    "outDir": "dist",
 | 
			
		||||
    "strict": false,
 | 
			
		||||
    "types": [
 | 
			
		||||
      "node",
 | 
			
		||||
      "express"
 | 
			
		||||
    ],
 | 
			
		||||
    "rootDir": "src",
 | 
			
		||||
    "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
 | 
			
		||||
  },
 | 
			
		||||
  "include": [
 | 
			
		||||
    "src/**/*.ts",
 | 
			
		||||
    "../server/src/*.d.ts"
 | 
			
		||||
  ],
 | 
			
		||||
  "exclude": [
 | 
			
		||||
    "eslint.config.js",
 | 
			
		||||
    "eslint.config.cjs",
 | 
			
		||||
    "eslint.config.mjs"
 | 
			
		||||
  ],
 | 
			
		||||
  "references": [
 | 
			
		||||
    {
 | 
			
		||||
      "path": "../server/tsconfig.app.json"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "path": "../desktop/tsconfig.app.json"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "path": "../client/tsconfig.app.json"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "extends": "../../tsconfig.base.json",
 | 
			
		||||
  "include": [],
 | 
			
		||||
  "references": [
 | 
			
		||||
    {
 | 
			
		||||
      "path": "../server"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "path": "../client"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "path": "./tsconfig.app.json"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "https://typedoc.org/schema.json",
 | 
			
		||||
  "name": "Trilium Backend API",
 | 
			
		||||
  "entryPoints": [
 | 
			
		||||
    "src/backend_script_entrypoint.ts"
 | 
			
		||||
  ],
 | 
			
		||||
  "plugin": [
 | 
			
		||||
    "typedoc-plugin-missing-exports"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user