mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
Merge branch 'main' into fix/ime-shortcut-input-fix
This commit is contained in:
commit
6c0e4b6a48
2
.github/actions/build-electron/action.yml
vendored
2
.github/actions/build-electron/action.yml
vendored
@ -86,7 +86,7 @@ runs:
|
||||
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 }}
|
||||
run: pnpm nx --project=desktop electron-forge:make -- --arch=${{ inputs.arch }} --platform=${{ inputs.forge_platform }}
|
||||
run: pnpm run --filter desktop electron-forge:make --arch=${{ inputs.arch }} --platform=${{ inputs.forge_platform }}
|
||||
|
||||
# Add DMG signing step
|
||||
- name: Sign DMG
|
||||
|
4
.github/actions/build-server/action.yml
vendored
4
.github/actions/build-server/action.yml
vendored
@ -10,7 +10,7 @@ runs:
|
||||
steps:
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "pnpm"
|
||||
@ -23,7 +23,7 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm run chore:update-build-info
|
||||
pnpm nx --project=server package
|
||||
pnpm run --filter server package
|
||||
- name: Prepare artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
|
40
.github/instructions/nx.instructions.md
vendored
40
.github/instructions/nx.instructions.md
vendored
@ -1,40 +0,0 @@
|
||||
---
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
// This file is automatically generated by Nx Console
|
||||
|
||||
You are in an nx workspace using Nx 21.3.9 and pnpm as the package manager.
|
||||
|
||||
You have access to the Nx MCP server and the tools it provides. Use them. Follow these guidelines in order to best help the user:
|
||||
|
||||
# General Guidelines
|
||||
- When answering questions, use the nx_workspace tool first to gain an understanding of the workspace architecture
|
||||
- For questions around nx configuration, best practices or if you're unsure, use the nx_docs tool to get relevant, up-to-date docs!! Always use this instead of assuming things about nx configuration
|
||||
- If the user needs help with an Nx configuration or project graph error, use the 'nx_workspace' tool to get any errors
|
||||
- To help answer questions about the workspace structure or simply help with demonstrating how tasks depend on each other, use the 'nx_visualize_graph' tool
|
||||
|
||||
# Generation Guidelines
|
||||
If the user wants to generate something, use the following flow:
|
||||
|
||||
- learn about the nx workspace and any specifics the user needs by using the 'nx_workspace' tool and the 'nx_project_details' tool if applicable
|
||||
- get the available generators using the 'nx_generators' tool
|
||||
- decide which generator to use. If no generators seem relevant, check the 'nx_available_plugins' tool to see if the user could install a plugin to help them
|
||||
- get generator details using the 'nx_generator_schema' tool
|
||||
- you may use the 'nx_docs' tool to learn more about a specific generator or technology if you're unsure
|
||||
- decide which options to provide in order to best complete the user's request. Don't make any assumptions and keep the options minimalistic
|
||||
- open the generator UI using the 'nx_open_generate_ui' tool
|
||||
- wait for the user to finish the generator
|
||||
- read the generator log file using the 'nx_read_generator_log' tool
|
||||
- use the information provided in the log file to answer the user's question or continue with what they were doing
|
||||
|
||||
# Running Tasks Guidelines
|
||||
If the user wants help with tasks or commands (which include keywords like "test", "build", "lint", or other similar actions), use the following flow:
|
||||
- Use the 'nx_current_running_tasks_details' tool to get the list of tasks (this can include tasks that were completed, stopped or failed).
|
||||
- If there are any tasks, ask the user if they would like help with a specific task then use the 'nx_current_running_task_output' tool to get the terminal output for that task/command
|
||||
- Use the terminal output from 'nx_current_running_task_output' to see what's wrong and help the user fix their problem. Use the appropriate tools if necessary
|
||||
- If the user would like to rerun the task or command, always use `nx run <taskId>` to rerun in the terminal. This will ensure that the task will run in the nx context and will be run the same way it originally executed
|
||||
- If the task was marked as "continuous" do not offer to rerun the task. This task is already running and the user can see the output in the terminal. You can use 'nx_current_running_task_output' to get the output of the task to verify the output.
|
||||
|
||||
|
||||
|
183
.github/workflows/deploy-docs.yml
vendored
Normal file
183
.github/workflows/deploy-docs.yml
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
# GitHub Actions workflow for deploying MkDocs documentation to Cloudflare Pages
|
||||
# This workflow builds and deploys your MkDocs site when changes are pushed to main
|
||||
name: Deploy MkDocs Documentation
|
||||
|
||||
on:
|
||||
# Trigger on push to main branch
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master # Also support master branch
|
||||
# Only run when docs files change
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'README.md' # README is synced to docs/index.md
|
||||
- 'mkdocs.yml'
|
||||
- 'requirements-docs.txt'
|
||||
- '.github/workflows/deploy-docs.yml'
|
||||
- 'scripts/fix-mkdocs-structure.ts'
|
||||
|
||||
# Allow manual triggering from Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Run on pull requests for preview deployments
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'README.md' # README is synced to docs/index.md
|
||||
- 'mkdocs.yml'
|
||||
- 'requirements-docs.txt'
|
||||
- '.github/workflows/deploy-docs.yml'
|
||||
- 'scripts/fix-mkdocs-structure.ts'
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
name: Build and Deploy MkDocs
|
||||
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
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for git info and mkdocs-git-revision-date plugin
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.13'
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements-docs.txt'
|
||||
|
||||
- name: Install MkDocs and Dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements-docs.txt
|
||||
env:
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
|
||||
# Setup pnpm before fixing docs structure
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
# Setup Node.js with pnpm
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'pnpm'
|
||||
|
||||
# Install Node.js dependencies for the TypeScript script
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
- name: Fix Documentation Structure
|
||||
run: |
|
||||
# Fix duplicate navigation entries by moving overview pages to index.md
|
||||
pnpm run chore:fix-mkdocs-structure
|
||||
|
||||
- name: Build MkDocs Site
|
||||
run: |
|
||||
# Build with strict mode but allow expected warnings
|
||||
mkdocs build --verbose || {
|
||||
EXIT_CODE=$?
|
||||
# Check if the only issue is expected warnings
|
||||
if mkdocs build 2>&1 | grep -E "WARNING.*(README|not found)" && \
|
||||
[ $(mkdocs build 2>&1 | grep -c "ERROR") -eq 0 ]; then
|
||||
echo "✅ Build succeeded with expected warnings"
|
||||
mkdocs build --verbose
|
||||
else
|
||||
echo "❌ Build failed with unexpected errors"
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
}
|
||||
|
||||
- name: Validate Built Site
|
||||
run: |
|
||||
# Basic validation that important files exist
|
||||
test -f site/index.html || (echo "ERROR: site/index.html not found" && exit 1)
|
||||
test -f site/sitemap.xml || (echo "ERROR: site/sitemap.xml not found" && exit 1)
|
||||
test -d site/assets || (echo "ERROR: site/assets directory not found" && exit 1)
|
||||
echo "✅ Site validation passed"
|
||||
|
||||
# Install wrangler globally to avoid workspace issues
|
||||
- name: Install Wrangler
|
||||
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: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy site --project-name=trilium-docs --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: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy site --project-name=trilium-docs --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
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.issue.number;
|
||||
// Construct preview URL based on Cloudflare Pages pattern
|
||||
const previewUrl = `https://pr-${prNumber}.trilium-docs.pages.dev`;
|
||||
const mainUrl = 'https://docs.triliumnotes.org';
|
||||
|
||||
// Check if we already commented
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber
|
||||
});
|
||||
|
||||
const botComment = comments.data.find(comment =>
|
||||
comment.user.type === 'Bot' &&
|
||||
comment.body.includes('Documentation preview is ready')
|
||||
);
|
||||
|
||||
const commentBody = `📚 Documentation preview is ready!\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
|
||||
});
|
||||
}
|
37
.github/workflows/dev.yml
vendored
37
.github/workflows/dev.yml
vendored
@ -19,45 +19,24 @@ permissions:
|
||||
pull-requests: write # for PR comments
|
||||
|
||||
jobs:
|
||||
check-affected:
|
||||
name: Check affected jobs (NX)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0 # needed for https://github.com/marketplace/actions/nx-set-shas
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- uses: nrwl/nx-set-shas@v4
|
||||
- name: Check affected
|
||||
run: pnpm nx affected --verbose -t typecheck build rebuild-deps test-build
|
||||
|
||||
test_dev:
|
||||
name: Test development
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- check-affected
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "pnpm"
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm typecheck
|
||||
|
||||
- name: Run the unit tests
|
||||
run: pnpm run test:all
|
||||
|
||||
@ -66,7 +45,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- test_dev
|
||||
- check-affected
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: pnpm/action-setup@v4
|
||||
@ -75,7 +53,7 @@ jobs:
|
||||
- name: Update build info
|
||||
run: pnpm run chore:update-build-info
|
||||
- name: Trigger client build
|
||||
run: pnpm nx run client:build
|
||||
run: pnpm client:build
|
||||
- name: Send client bundle stats to RelativeCI
|
||||
if: false
|
||||
uses: relative-ci/agent-action@v3
|
||||
@ -83,7 +61,7 @@ jobs:
|
||||
webpackStatsFile: ./apps/client/dist/webpack-stats.json
|
||||
key: ${{ secrets.RELATIVE_CI_CLIENT_KEY }}
|
||||
- name: Trigger server build
|
||||
run: pnpm nx run server:build
|
||||
run: pnpm run server:build
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
@ -95,7 +73,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build_docker
|
||||
- check-affected
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@ -112,7 +89,7 @@ jobs:
|
||||
- name: Update build info
|
||||
run: pnpm run chore:update-build-info
|
||||
- name: Trigger build
|
||||
run: pnpm nx run server:build
|
||||
run: pnpm server:build
|
||||
|
||||
- name: Set IMAGE_NAME to lowercase
|
||||
run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
|
||||
|
6
.github/workflows/main-docker.yml
vendored
6
.github/workflows/main-docker.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "pnpm"
|
||||
@ -82,7 +82,7 @@ jobs:
|
||||
require-healthy: true
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm exec nx run server-e2e:e2e
|
||||
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm --filter=server-e2e e2e
|
||||
|
||||
- name: Upload Playwright trace
|
||||
if: failure()
|
||||
@ -144,7 +144,7 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
|
7
.github/workflows/nightly.yml
vendored
7
.github/workflows/nightly.yml
vendored
@ -51,13 +51,12 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- uses: nrwl/nx-set-shas@v4
|
||||
- name: Update nightly version
|
||||
run: npm run chore:ci-update-nightly-version
|
||||
- name: Run the build
|
||||
@ -79,7 +78,7 @@ jobs:
|
||||
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
|
||||
|
||||
- name: Publish release
|
||||
uses: softprops/action-gh-release@v2.3.2
|
||||
uses: softprops/action-gh-release@v2.3.3
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
make_latest: false
|
||||
@ -120,7 +119,7 @@ jobs:
|
||||
arch: ${{ matrix.arch }}
|
||||
|
||||
- name: Publish release
|
||||
uses: softprops/action-gh-release@v2.3.2
|
||||
uses: softprops/action-gh-release@v2.3.3
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
make_latest: false
|
||||
|
22
.github/workflows/playwright.yml
vendored
22
.github/workflows/playwright.yml
vendored
@ -19,14 +19,8 @@ jobs:
|
||||
filter: tree:0
|
||||
fetch-depth: 0
|
||||
|
||||
# This enables task distribution via Nx Cloud
|
||||
# Run this command as early as possible, before dependencies are installed
|
||||
# Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
|
||||
# Connect your workspace by running "nx connect" and uncomment this line to enable task distribution
|
||||
# - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="e2e-ci"
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
@ -34,10 +28,12 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- run: pnpm exec playwright install --with-deps
|
||||
- uses: nrwl/nx-set-shas@v4
|
||||
|
||||
# Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud
|
||||
# - run: npx nx-cloud record -- echo Hello World
|
||||
# Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected
|
||||
# When you enable task distribution, run the e2e-ci task instead of e2e
|
||||
- run: pnpm exec nx affected -t e2e --exclude desktop-e2e
|
||||
- run: pnpm --filter server-e2e e2e
|
||||
|
||||
- name: Upload test report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: e2e report
|
||||
path: apps/server-e2e/test-output
|
||||
|
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@ -35,13 +35,12 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- uses: nrwl/nx-set-shas@v4
|
||||
- name: Run the build
|
||||
uses: ./.github/actions/build-electron
|
||||
with:
|
||||
@ -115,7 +114,7 @@ jobs:
|
||||
path: upload
|
||||
|
||||
- name: Publish stable release
|
||||
uses: softprops/action-gh-release@v2.3.2
|
||||
uses: softprops/action-gh-release@v2.3.3
|
||||
with:
|
||||
draft: false
|
||||
body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
/.cache
|
||||
|
||||
# compiled output
|
||||
dist
|
||||
@ -32,14 +33,11 @@ testem.log
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
|
||||
vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*
|
||||
test-output
|
||||
|
||||
apps/*/data
|
||||
apps/*/data*
|
||||
apps/*/out
|
||||
upload
|
||||
|
||||
@ -48,3 +46,6 @@ upload
|
||||
|
||||
/result
|
||||
.svelte-kit
|
||||
|
||||
# docs
|
||||
site/
|
||||
|
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@ -5,7 +5,6 @@
|
||||
"lokalise.i18n-ally",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"ms-playwright.playwright",
|
||||
"nrwl.angular-console",
|
||||
"redhat.vscode-yaml",
|
||||
"tobermory.es6-string-html",
|
||||
"vitest.explorer",
|
||||
|
8
.vscode/mcp.json
vendored
8
.vscode/mcp.json
vendored
@ -1,8 +0,0 @@
|
||||
{
|
||||
"servers": {
|
||||
"nx-mcp": {
|
||||
"type": "http",
|
||||
"url": "http://localhost:9461/mcp"
|
||||
}
|
||||
}
|
||||
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -35,6 +35,5 @@
|
||||
"docs/**/*.png": true,
|
||||
"apps/server/src/assets/doc_notes/**": true,
|
||||
"apps/edit-docs/demo/**": true
|
||||
},
|
||||
"nxConsole.generateAiAgentRules": true
|
||||
}
|
||||
}
|
13
CLAUDE.md
13
CLAUDE.md
@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## 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 NX, with multiple applications and shared packages.
|
||||
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
|
||||
|
||||
@ -14,12 +14,9 @@ Trilium Notes is a hierarchical note-taking application with advanced features l
|
||||
|
||||
### Running Applications
|
||||
- `pnpm run server:start` - Start development server (http://localhost:8080)
|
||||
- `pnpm nx run server:serve` - Alternative server start command
|
||||
- `pnpm nx run desktop:serve` - Run desktop Electron app
|
||||
- `pnpm run server:start-prod` - Run server in production mode
|
||||
|
||||
### Building
|
||||
- `pnpm nx build <project>` - Build specific project (server, client, desktop, etc.)
|
||||
- `pnpm run client:build` - Build client application
|
||||
- `pnpm run server:build` - Build server application
|
||||
- `pnpm run electron:build` - Build desktop application
|
||||
@ -28,13 +25,8 @@ Trilium Notes is a hierarchical note-taking application with advanced features l
|
||||
- `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 nx test <project>` - Run tests for specific project
|
||||
- `pnpm coverage` - Generate coverage reports
|
||||
|
||||
### Linting & Type Checking
|
||||
- `pnpm nx run <project>:lint` - Lint specific project
|
||||
- `pnpm nx run <project>:typecheck` - Type check specific project
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Monorepo Structure
|
||||
@ -94,7 +86,6 @@ Frontend uses a widget system (`apps/client/src/widgets/`):
|
||||
- `apps/server/src/assets/db/schema.sql` - Core database structure
|
||||
|
||||
4. **Configuration**:
|
||||
- `nx.json` - NX workspace configuration
|
||||
- `package.json` - Project dependencies and scripts
|
||||
|
||||
## Note Types and Features
|
||||
@ -154,7 +145,7 @@ Trilium provides powerful user scripting capabilities:
|
||||
- Update schema in `apps/server/src/assets/db/schema.sql`
|
||||
|
||||
## Build System Notes
|
||||
- Uses NX for monorepo management with build caching
|
||||
- Uses pnpm for monorepo management
|
||||
- Vite for fast development builds
|
||||
- ESBuild for production optimization
|
||||
- pnpm workspaces for dependency management
|
||||
|
@ -142,7 +142,7 @@ Download the repository, install dependencies using `pnpm` and then run the envi
|
||||
git clone https://github.com/TriliumNext/Trilium.git
|
||||
cd Trilium
|
||||
pnpm install
|
||||
pnpm nx run edit-docs:edit-docs
|
||||
pnpm edit-docs:edit-docs
|
||||
```
|
||||
|
||||
### Building the Executable
|
||||
@ -151,7 +151,7 @@ Download the repository, install dependencies using `pnpm` and then build the de
|
||||
git clone https://github.com/TriliumNext/Trilium.git
|
||||
cd Trilium
|
||||
pnpm install
|
||||
pnpm nx --project=desktop electron-forge:make -- --arch=x64 --platform=win32
|
||||
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).
|
||||
|
@ -36,12 +36,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.55.0",
|
||||
"@stylistic/eslint-plugin": "5.2.3",
|
||||
"@stylistic/eslint-plugin": "5.3.1",
|
||||
"@types/express": "5.0.3",
|
||||
"@types/node": "22.18.0",
|
||||
"@types/node": "22.18.1",
|
||||
"@types/yargs": "17.0.33",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"eslint": "9.34.0",
|
||||
"eslint": "9.35.0",
|
||||
"eslint-plugin-simple-import-sort": "12.1.1",
|
||||
"esm": "3.2.25",
|
||||
"jsdoc": "4.0.4",
|
||||
@ -49,7 +49,7 @@
|
||||
"rcedit": "4.0.1",
|
||||
"rimraf": "6.0.1",
|
||||
"tslib": "2.8.1",
|
||||
"typedoc": "0.28.11",
|
||||
"typedoc": "0.28.12",
|
||||
"typedoc-plugin-missing-exports": "4.1.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
@ -9,8 +9,13 @@
|
||||
"email": "contact@eliandoran.me",
|
||||
"url": "https://github.com/TriliumNext/Notes"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build",
|
||||
"test": "vitest",
|
||||
"circular-deps": "dpdm -T src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "9.34.0",
|
||||
"@eslint/js": "9.35.0",
|
||||
"@excalidraw/excalidraw": "0.18.0",
|
||||
"@fullcalendar/core": "6.1.19",
|
||||
"@fullcalendar/daygrid": "6.1.19",
|
||||
@ -19,7 +24,7 @@
|
||||
"@fullcalendar/multimonth": "6.1.19",
|
||||
"@fullcalendar/timegrid": "6.1.19",
|
||||
"@maplibre/maplibre-gl-leaflet": "0.1.3",
|
||||
"@mermaid-js/layout-elk": "0.1.9",
|
||||
"@mermaid-js/layout-elk": "0.2.0",
|
||||
"@mind-elixir/node-menu": "5.0.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@triliumnext/ckeditor5": "workspace:*",
|
||||
@ -30,13 +35,13 @@
|
||||
"autocomplete.js": "0.38.1",
|
||||
"bootstrap": "5.3.8",
|
||||
"boxicons": "2.1.4",
|
||||
"dayjs": "1.11.14",
|
||||
"dayjs": "1.11.18",
|
||||
"dayjs-plugin-utc": "0.1.2",
|
||||
"debounce": "2.2.0",
|
||||
"draggabilly": "3.0.0",
|
||||
"force-graph": "1.50.1",
|
||||
"force-graph": "1.51.0",
|
||||
"globals": "16.3.0",
|
||||
"i18next": "25.4.2",
|
||||
"i18next": "25.5.2",
|
||||
"i18next-http-backend": "3.0.2",
|
||||
"jquery": "3.7.1",
|
||||
"jquery.fancytree": "2.38.5",
|
||||
@ -47,12 +52,12 @@
|
||||
"leaflet-gpx": "2.2.0",
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "16.2.1",
|
||||
"mermaid": "11.10.1",
|
||||
"mind-elixir": "5.0.6",
|
||||
"mermaid": "11.11.0",
|
||||
"mind-elixir": "5.1.1",
|
||||
"normalize.css": "8.0.1",
|
||||
"panzoom": "9.4.3",
|
||||
"preact": "10.27.1",
|
||||
"react-i18next": "15.7.2",
|
||||
"react-i18next": "15.7.3",
|
||||
"split.js": "1.6.5",
|
||||
"svg-pan-zoom": "3.6.2",
|
||||
"tabulator-tables": "6.3.1",
|
||||
@ -64,25 +69,12 @@
|
||||
"@types/bootstrap": "5.2.10",
|
||||
"@types/jquery": "3.5.33",
|
||||
"@types/leaflet": "1.9.20",
|
||||
"@types/leaflet-gpx": "1.3.7",
|
||||
"@types/leaflet-gpx": "1.3.8",
|
||||
"@types/mark.js": "8.11.12",
|
||||
"@types/tabulator-tables": "6.2.10",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"happy-dom": "18.0.1",
|
||||
"script-loader": "0.7.2",
|
||||
"vite-plugin-static-copy": "3.1.2"
|
||||
},
|
||||
"nx": {
|
||||
"name": "client",
|
||||
"targets": {
|
||||
"serve": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"circular-deps": {
|
||||
"command": "pnpx dpdm -T {projectRoot}/src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import { t } from "./services/i18n.js";
|
||||
import options from "./services/options.js";
|
||||
import type ElectronRemote from "@electron/remote";
|
||||
import type Electron from "electron";
|
||||
import "./stylesheets/bootstrap.scss";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "boxicons/css/boxicons.min.css";
|
||||
import "autocomplete.js/index_jquery.js";
|
||||
|
||||
|
@ -22,6 +22,7 @@ import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
||||
import { MOBILE_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
||||
import ToggleSidebarButton from "../widgets/mobile_widgets/toggle_sidebar_button.jsx";
|
||||
import CloseZenModeButton from "../widgets/close_zen_button.js";
|
||||
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
||||
import MobileDetailMenu from "../widgets/mobile_widgets/mobile_detail_menu.js";
|
||||
|
||||
const MOBILE_CSS = `
|
||||
@ -131,30 +132,33 @@ export default class MobileLayout {
|
||||
.child(new FlexContainer("column").filling().id("mobile-sidebar-wrapper").child(new QuickSearchWidget()).child(new NoteTreeWidget().cssBlock(FANCYTREE_CSS)))
|
||||
)
|
||||
.child(
|
||||
new ScreenContainer("detail", "column")
|
||||
new ScreenContainer("detail", "row")
|
||||
.id("detail-container")
|
||||
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-9")
|
||||
.child(
|
||||
new FlexContainer("row")
|
||||
.contentSized()
|
||||
.css("font-size", "larger")
|
||||
.css("align-items", "center")
|
||||
.child(<ToggleSidebarButton />)
|
||||
.child(<NoteTitleWidget />)
|
||||
.child(<MobileDetailMenu />)
|
||||
new NoteWrapperWidget()
|
||||
.child(
|
||||
new FlexContainer("row")
|
||||
.contentSized()
|
||||
.css("font-size", "larger")
|
||||
.css("align-items", "center")
|
||||
.child(<ToggleSidebarButton />)
|
||||
.child(<NoteTitleWidget />)
|
||||
.child(<MobileDetailMenu />)
|
||||
)
|
||||
.child(<SharedInfoWidget />)
|
||||
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
|
||||
.child(new PromotedAttributesWidget())
|
||||
.child(
|
||||
new ScrollingContainer()
|
||||
.filling()
|
||||
.contentSized()
|
||||
.child(new NoteDetailWidget())
|
||||
.child(new NoteListWidget(false))
|
||||
.child(<FilePropertiesWrapper />)
|
||||
)
|
||||
.child(<MobileEditorToolbar />)
|
||||
)
|
||||
.child(<SharedInfoWidget />)
|
||||
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
|
||||
.child(new PromotedAttributesWidget())
|
||||
.child(
|
||||
new ScrollingContainer()
|
||||
.filling()
|
||||
.contentSized()
|
||||
.child(new NoteDetailWidget())
|
||||
.child(new NoteListWidget(false))
|
||||
.child(<FilePropertiesWrapper />)
|
||||
)
|
||||
.child(<MobileEditorToolbar />)
|
||||
)
|
||||
)
|
||||
.child(
|
||||
|
@ -1,4 +1,4 @@
|
||||
import "./stylesheets/bootstrap.scss";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
|
||||
// @ts-ignore - module = undefined
|
||||
// Required for correct loading of scripts in Electron
|
||||
|
@ -1,7 +1,7 @@
|
||||
import appContext from "./components/app_context.js";
|
||||
import noteAutocompleteService from "./services/note_autocomplete.js";
|
||||
import glob from "./services/glob.js";
|
||||
import "./stylesheets/bootstrap.scss";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "boxicons/css/boxicons.min.css";
|
||||
import "autocomplete.js/index_jquery.js";
|
||||
|
||||
|
@ -48,6 +48,6 @@ function getUrl(docNameValue: string, language: string) {
|
||||
// Cannot have spaces in the URL due to how JQuery.load works.
|
||||
docNameValue = docNameValue.replaceAll(" ", "%20");
|
||||
|
||||
const basePath = window.glob.isDev ? new URL(window.glob.assetPath).pathname : window.glob.assetPath;
|
||||
const basePath = window.glob.isDev ? window.glob.assetPath + "/.." : window.glob.assetPath;
|
||||
return `${basePath}/doc_notes/${language}/${docNameValue}.html`;
|
||||
}
|
||||
|
@ -10,6 +10,10 @@ let leftInstance: ReturnType<typeof Split> | null;
|
||||
let rightPaneWidth: number;
|
||||
let rightInstance: ReturnType<typeof Split> | null;
|
||||
|
||||
const noteSplitMap = new Map<string[], ReturnType<typeof Split> | undefined>(); // key: a group of ntxIds, value: the corresponding Split instance
|
||||
const noteSplitRafMap = new Map<string[], number>();
|
||||
let splitNoteContainer: HTMLElement | undefined;
|
||||
|
||||
function setupLeftPaneResizer(leftPaneVisible: boolean) {
|
||||
if (leftInstance) {
|
||||
leftInstance.destroy();
|
||||
@ -83,7 +87,86 @@ function setupRightPaneResizer() {
|
||||
}
|
||||
}
|
||||
|
||||
function findKeyByNtxId(ntxId: string): string[] | undefined {
|
||||
// Find the corresponding key in noteSplitMap based on ntxId
|
||||
for (const key of noteSplitMap.keys()) {
|
||||
if (key.includes(ntxId)) return key;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function setupNoteSplitResizer(ntxIds: string[]) {
|
||||
let targetNtxIds: string[] | undefined;
|
||||
for (const ntxId of ntxIds) {
|
||||
targetNtxIds = findKeyByNtxId(ntxId);
|
||||
if (targetNtxIds) break;
|
||||
}
|
||||
|
||||
if (targetNtxIds) {
|
||||
noteSplitMap.get(targetNtxIds)?.destroy();
|
||||
for (const id of ntxIds) {
|
||||
if (!targetNtxIds.includes(id)) {
|
||||
targetNtxIds.push(id)
|
||||
};
|
||||
}
|
||||
} else {
|
||||
targetNtxIds = [...ntxIds];
|
||||
}
|
||||
noteSplitMap.set(targetNtxIds, undefined);
|
||||
createSplitInstance(targetNtxIds);
|
||||
}
|
||||
|
||||
|
||||
function delNoteSplitResizer(ntxIds: string[]) {
|
||||
let targetNtxIds = findKeyByNtxId(ntxIds[0]);
|
||||
if (!targetNtxIds) {
|
||||
return;
|
||||
}
|
||||
|
||||
noteSplitMap.get(targetNtxIds)?.destroy();
|
||||
noteSplitMap.delete(targetNtxIds);
|
||||
targetNtxIds = targetNtxIds.filter(id => !ntxIds.includes(id));
|
||||
|
||||
if (targetNtxIds.length >= 2) {
|
||||
noteSplitMap.set(targetNtxIds, undefined);
|
||||
createSplitInstance(targetNtxIds);
|
||||
}
|
||||
}
|
||||
|
||||
function moveNoteSplitResizer(ntxId: string) {
|
||||
const targetNtxIds = findKeyByNtxId(ntxId);
|
||||
if (!targetNtxIds) {
|
||||
return;
|
||||
}
|
||||
noteSplitMap.get(targetNtxIds)?.destroy();
|
||||
noteSplitMap.set(targetNtxIds, undefined);
|
||||
createSplitInstance(targetNtxIds);
|
||||
}
|
||||
|
||||
function createSplitInstance(targetNtxIds: string[]) {
|
||||
const prevRafId = noteSplitRafMap.get(targetNtxIds);
|
||||
if (prevRafId) {
|
||||
cancelAnimationFrame(prevRafId);
|
||||
}
|
||||
|
||||
const rafId = requestAnimationFrame(() => {
|
||||
splitNoteContainer = splitNoteContainer ?? $("#center-pane").find(".split-note-container-widget")[0];
|
||||
const splitPanels = [...splitNoteContainer.querySelectorAll<HTMLElement>(':scope > .note-split')]
|
||||
.filter(el => targetNtxIds.includes(el.getAttribute('data-ntx-id') ?? ""));
|
||||
const splitInstance = Split(splitPanels, {
|
||||
gutterSize: DEFAULT_GUTTER_SIZE,
|
||||
minSize: 150,
|
||||
});
|
||||
noteSplitMap.set(targetNtxIds, splitInstance);
|
||||
noteSplitRafMap.delete(targetNtxIds);
|
||||
});
|
||||
noteSplitRafMap.set(targetNtxIds, rafId);
|
||||
}
|
||||
|
||||
export default {
|
||||
setupLeftPaneResizer,
|
||||
setupRightPaneResizer
|
||||
setupRightPaneResizer,
|
||||
setupNoteSplitResizer,
|
||||
delNoteSplitResizer,
|
||||
moveNoteSplitResizer
|
||||
};
|
||||
|
@ -297,6 +297,54 @@ function isHtmlEmpty(html: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function formatHtml(html: string) {
|
||||
let indent = "\n";
|
||||
const tab = "\t";
|
||||
let i = 0;
|
||||
let pre: { indent: string; tag: string }[] = [];
|
||||
|
||||
html = html
|
||||
.replace(new RegExp("<pre>([\\s\\S]+?)?</pre>"), function (x) {
|
||||
pre.push({ indent: "", tag: x });
|
||||
return "<--TEMPPRE" + i++ + "/-->";
|
||||
})
|
||||
.replace(new RegExp("<[^<>]+>[^<]?", "g"), function (x) {
|
||||
let ret;
|
||||
const tagRegEx = /<\/?([^\s/>]+)/.exec(x);
|
||||
let tag = tagRegEx ? tagRegEx[1] : "";
|
||||
let p = new RegExp("<--TEMPPRE(\\d+)/-->").exec(x);
|
||||
|
||||
if (p) {
|
||||
const pInd = parseInt(p[1]);
|
||||
pre[pInd].indent = indent;
|
||||
}
|
||||
|
||||
if (["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"].indexOf(tag) >= 0) {
|
||||
// self closing tag
|
||||
ret = indent + x;
|
||||
} else {
|
||||
if (x.indexOf("</") < 0) {
|
||||
//open tag
|
||||
if (x.charAt(x.length - 1) !== ">") ret = indent + x.substr(0, x.length - 1) + indent + tab + x.substr(x.length - 1, x.length);
|
||||
else ret = indent + x;
|
||||
!p && (indent += tab);
|
||||
} else {
|
||||
//close tag
|
||||
indent = indent.substr(0, indent.length - 1);
|
||||
if (x.charAt(x.length - 1) !== ">") ret = indent + x.substr(0, x.length - 1) + indent + x.substr(x.length - 1, x.length);
|
||||
else ret = indent + x;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
for (i = pre.length; i--;) {
|
||||
html = html.replace("<--TEMPPRE" + i + "/-->", pre[i].tag.replace("<pre>", "<pre>\n").replace("</pre>", pre[i].indent + "</pre>"));
|
||||
}
|
||||
|
||||
return html.charAt(0) === "\n" ? html.substr(1, html.length - 1) : html;
|
||||
}
|
||||
|
||||
export async function clearBrowserCache() {
|
||||
if (isElectron()) {
|
||||
const win = dynamicRequire("@electron/remote").getCurrentWindow();
|
||||
@ -855,6 +903,7 @@ export default {
|
||||
getNoteTypeClass,
|
||||
getMimeTypeClass,
|
||||
isHtmlEmpty,
|
||||
formatHtml,
|
||||
clearBrowserCache,
|
||||
copySelectionToClipboard,
|
||||
dynamicRequire,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import "./stylesheets/bootstrap.scss";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import "./stylesheets/auth.css";
|
||||
|
||||
// @TriliumNextTODO: is this even needed anymore?
|
||||
|
@ -1,7 +1,7 @@
|
||||
import "jquery";
|
||||
import utils from "./services/utils.js";
|
||||
import ko from "knockout";
|
||||
import "./stylesheets/bootstrap.scss";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
|
||||
// TriliumNextTODO: properly make use of below types
|
||||
// type SetupModelSetupType = "new-document" | "sync-from-desktop" | "sync-from-server" | "";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "normalize.css";
|
||||
import "boxicons/css/boxicons.min.css";
|
||||
import "@triliumnext/ckeditor5/content.css";
|
||||
import "@triliumnext/ckeditor5/src/theme/ck-content.css";
|
||||
import "@triliumnext/share-theme/styles/index.css";
|
||||
import "@triliumnext/share-theme/scripts/index.js";
|
||||
|
||||
|
2
apps/client/src/stylesheets/bootstrap.scss
vendored
2
apps/client/src/stylesheets/bootstrap.scss
vendored
@ -1,2 +0,0 @@
|
||||
/* Import all of Bootstrap's CSS */
|
||||
@use "bootstrap/scss/bootstrap";
|
@ -1134,6 +1134,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
||||
|
||||
.toast-body {
|
||||
white-space: preserve-breaks;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ck-mentions .ck-button {
|
||||
@ -1242,6 +1243,10 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
.hidden-ext.note-split + .gutter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#context-menu-cover.show {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -1463,7 +1468,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
color: var(--launcher-pane-text-color);
|
||||
background-color: var(--launcher-pane-background-color);
|
||||
background: transparent;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@ -1771,7 +1776,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
||||
}
|
||||
|
||||
.note-split {
|
||||
flex-basis: 0; /* so that each split has same width */
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
@ -2366,3 +2370,21 @@ footer.webview-footer button {
|
||||
content: "\ec24";
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* CK Edito */
|
||||
|
||||
/* Insert text snippet: limit the width of the listed items to avoid overly long names */
|
||||
:root body.desktop div.ck-template-form li.ck-list__item .ck-template-form__text-part > span {
|
||||
max-width: 25vw;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.revision-diff-added {
|
||||
background: rgba(100, 200, 100, 0.5);
|
||||
}
|
||||
|
||||
.revision-diff-removed {
|
||||
background: rgba(255, 100, 100, 0.5);
|
||||
text-decoration: line-through;
|
||||
}
|
@ -13,12 +13,13 @@
|
||||
|
||||
--theme-style: dark;
|
||||
--native-titlebar-background: #00000000;
|
||||
--window-background-color-bgfx: transparent; /* When background effects enabled */
|
||||
|
||||
--main-background-color: #272727;
|
||||
--main-text-color: #ccc;
|
||||
--main-border-color: #454545;
|
||||
--subtle-border-color: #313131;
|
||||
--dropdown-border-color: #292929;
|
||||
--dropdown-border-color: #404040;
|
||||
--dropdown-shadow-opacity: 0.6;
|
||||
--dropdown-item-icon-destructive-color: #de6e5b;
|
||||
--disabled-tooltip-icon-color: #7fd2ef;
|
||||
@ -147,6 +148,7 @@
|
||||
--launcher-pane-vert-button-hover-background: #ffffff1c;
|
||||
--launcher-pane-vert-button-hover-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
|
||||
--launcher-pane-vert-button-focus-outline-color: var(--input-focus-outline-color);
|
||||
--launcher-pane-vert-background-color-bgfx: #00000026; /* When background effects enabled */
|
||||
|
||||
--launcher-pane-horiz-border-color: rgb(22, 22, 22);
|
||||
--launcher-pane-horiz-background-color: #282828;
|
||||
@ -155,6 +157,8 @@
|
||||
--launcher-pane-horiz-button-hover-background: #ffffff1c;
|
||||
--launcher-pane-horiz-button-hover-shadow: unset;
|
||||
--launcher-pane-horiz-button-focus-outline-color: var(--input-focus-outline-color);
|
||||
--launcher-pane-horiz-background-color-bgfx: #ffffff17; /* When background effects enabled */
|
||||
--launcher-pane-horiz-border-color-bgfx: #00000080; /* When background effects enabled */
|
||||
|
||||
--protected-session-active-icon-color: #8edd8e;
|
||||
--sync-status-error-pulse-color: #f47871;
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
--theme-style: light;
|
||||
--native-titlebar-background: #ffffff00;
|
||||
--window-background-color-bgfx: transparent; /* When background effects enabled */
|
||||
|
||||
--main-background-color: white;
|
||||
--main-text-color: black;
|
||||
@ -115,17 +116,17 @@
|
||||
--quick-search-focus-border: #00000029;
|
||||
--quick-search-focus-background: #ffffff80;
|
||||
--quick-search-focus-color: #000;
|
||||
--quick-search-result-content-background: #00000017;
|
||||
--quick-search-result-content-background: #0000000f;
|
||||
--quick-search-result-highlight-color: #c65050;
|
||||
|
||||
--left-pane-collapsed-border-color: #0000000d;
|
||||
--left-pane-background-color: #f2f2f2;
|
||||
--left-pane-text-color: #383838;
|
||||
--left-pane-item-hover-background: #eaeaea;
|
||||
--left-pane-item-hover-background: rgba(0, 0, 0, 0.032);
|
||||
--left-pane-item-selected-background: white;
|
||||
--left-pane-item-selected-color: black;
|
||||
--left-pane-item-selected-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
|
||||
--left-pane-item-action-button-background: #d7d7d7;
|
||||
--left-pane-item-action-button-background: rgba(0, 0, 0, 0.11);
|
||||
--left-pane-item-action-button-color: inherit;
|
||||
--left-pane-item-action-button-hover-background: white;
|
||||
--left-pane-item-action-button-hover-shadow: 2px 2px 3px rgba(0, 0, 0, 0.15);
|
||||
@ -141,6 +142,7 @@
|
||||
--launcher-pane-vert-button-hover-background: white;
|
||||
--launcher-pane-vert-button-hover-shadow: 4px 4px 4px rgba(0, 0, 0, 0.075);
|
||||
--launcher-pane-vert-button-focus-outline-color: var(--input-focus-outline-color);
|
||||
--launcher-pane-vert-background-color-bgfx: #00000009; /* When background effects enabled */
|
||||
|
||||
--launcher-pane-horiz-border-color: rgba(0, 0, 0, 0.1);
|
||||
--launcher-pane-horiz-background-color: #fafafa;
|
||||
@ -148,6 +150,8 @@
|
||||
--launcher-pane-horiz-button-hover-background: var(--icon-button-hover-background);
|
||||
--launcher-pane-horiz-button-hover-shadow: unset;
|
||||
--launcher-pane-horiz-button-focus-outline-color: var(--input-focus-outline-color);
|
||||
--launcher-pane-horiz-background-color-bgfx: #ffffffb3; /* When background effects enabled */
|
||||
--launcher-pane-horiz-border-color-bgfx: #00000026; /* When background effects enabled */
|
||||
|
||||
--protected-session-active-icon-color: #16b516;
|
||||
--sync-status-error-pulse-color: #ff5528;
|
||||
|
@ -329,6 +329,8 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
|
||||
|
||||
#toast-container .toast .toast-body {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5,7 +5,8 @@
|
||||
button.btn.btn-primary,
|
||||
button.btn.btn-secondary,
|
||||
button.btn.btn-sm:not(.select-button),
|
||||
button.btn.btn-success {
|
||||
button.btn.btn-success,
|
||||
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -21,7 +22,8 @@ button.btn.btn-success {
|
||||
button.btn.btn-primary:hover,
|
||||
button.btn.btn-secondary:hover,
|
||||
button.btn.btn-sm:not(.select-button):hover,
|
||||
button.btn.btn-success:hover {
|
||||
button.btn.btn-success:hover,
|
||||
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text:not(.ck-disabled):hover {
|
||||
background: var(--cmd-button-hover-background-color);
|
||||
color: var(--cmd-button-hover-text-color);
|
||||
}
|
||||
@ -29,7 +31,8 @@ button.btn.btn-success:hover {
|
||||
button.btn.btn-primary:active,
|
||||
button.btn.btn-secondary:active,
|
||||
button.btn.btn-sm:not(.select-button):active,
|
||||
button.btn.btn-success:active {
|
||||
button.btn.btn-success:active,
|
||||
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text:not(.ck-disabled):active {
|
||||
opacity: 0.85;
|
||||
box-shadow: unset;
|
||||
background: var(--cmd-button-background-color) !important;
|
||||
@ -40,14 +43,16 @@ button.btn.btn-success:active {
|
||||
button.btn.btn-primary:disabled,
|
||||
button.btn.btn-secondary:disabled,
|
||||
button.btn.btn-sm:not(.select-button):disabled,
|
||||
button.btn.btn-success:disabled {
|
||||
button.btn.btn-success:disabled,
|
||||
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text.ck-disabled {
|
||||
opacity: var(--cmd-button-disabled-opacity);
|
||||
}
|
||||
|
||||
button.btn.btn-primary:focus-visible,
|
||||
button.btn.btn-secondary:focus-visible,
|
||||
button.btn.btn-sm:not(.select-button):focus-visible,
|
||||
button.btn.btn-success:focus-visible {
|
||||
button.btn.btn-success:focus-visible,
|
||||
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text:not(.ck-disabled):focus-visible {
|
||||
outline: 2px solid var(--input-focus-outline-color);
|
||||
}
|
||||
|
||||
@ -149,8 +154,11 @@ input[type="password"],
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
:root input.ck.ck-input-text,
|
||||
:root input.ck.ck-input-number,
|
||||
textarea.form-control,
|
||||
textarea,
|
||||
:root textarea.ck.ck-textarea,
|
||||
.tn-input-field {
|
||||
outline: 3px solid transparent;
|
||||
outline-offset: 6px;
|
||||
@ -167,8 +175,11 @@ input[type="password"]:hover,
|
||||
input[type="date"]:hover,
|
||||
input[type="time"]:hover,
|
||||
input[type="datetime-local"]:hover,
|
||||
:root input.ck.ck-input-text:not([readonly="true"]):hover,
|
||||
:root input.ck.ck-input-number:not([readonly="true"]):hover,
|
||||
textarea.form-control:hover,
|
||||
textarea:hover,
|
||||
:root textarea.ck.ck-textarea:hover,
|
||||
.tn-input-field:hover {
|
||||
background: var(--input-hover-background);
|
||||
color: var(--input-hover-color);
|
||||
@ -181,8 +192,11 @@ input[type="password"]:focus,
|
||||
input[type="date"]:focus,
|
||||
input[type="time"]:focus,
|
||||
input[type="datetime-local"]:focus,
|
||||
:root input.ck.ck-input-text:focus,
|
||||
:root input.ck.ck-input-number:focus,
|
||||
textarea.form-control:focus,
|
||||
textarea:focus,
|
||||
:root textarea.ck.ck-textarea:focus,
|
||||
.tn-input-field:focus,
|
||||
.tn-input-field:focus-within {
|
||||
box-shadow: unset;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
:root {
|
||||
--ck-font-face: var(--main-font-family);
|
||||
--ck-input-label-height: 1.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -307,6 +308,11 @@
|
||||
fill: black !important;
|
||||
}
|
||||
|
||||
/* Hex color input box prefix */
|
||||
:root .ck.ck-color-selector .ck-color-picker__hash-view {
|
||||
margin-top: var(--ck-input-label-height);
|
||||
}
|
||||
|
||||
/* Numbered list */
|
||||
|
||||
:root .ck.ck-list-properties_with-numbered-properties .ck.ck-list-styles-list {
|
||||
@ -363,19 +369,86 @@
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Action buttons */
|
||||
/* Text snippet dropdown */
|
||||
|
||||
:root .ck-link-actions button.ck-button,
|
||||
:root .ck-link-form button.ck-button {
|
||||
--ck-border-radius: 6px;
|
||||
|
||||
background: transparent;
|
||||
box-shadow: unset;
|
||||
div.ck-template-form {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
:root .ck-link-actions button.ck-button:hover,
|
||||
:root .ck-link-form button.ck-button:hover {
|
||||
background: var(--hover-item-background-color);
|
||||
div.ck-template-form .ck-labeled-field-view {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Template item */
|
||||
|
||||
:root div.ck-template-form li.ck-list__item button.ck-template-button {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
/* Template icon */
|
||||
:root .ck-template-form .ck-button__icon {
|
||||
--ck-spacing-medium: 2px;
|
||||
}
|
||||
|
||||
:root div.ck-template-form .note-icon {
|
||||
color: var(--menu-item-icon-color);
|
||||
}
|
||||
|
||||
/* Template name */
|
||||
div.ck-template-form .ck-template-form__text-part {
|
||||
color: var(--hover-item-text-color);
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
div.ck-template-form .ck-template-form__text-part mark {
|
||||
background: unset;
|
||||
color: var(--quick-search-result-highlight-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Template description */
|
||||
:root div.ck-template-form .ck-template-form__description {
|
||||
opacity: .5;
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
div.ck-template-form .ck-search__info > span {
|
||||
line-height: initial;
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
div.ck-template-form .ck-search__info span:nth-child(2) {
|
||||
display: block;
|
||||
opacity: .5;
|
||||
margin-top: 8px;
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
/* Link dropdown */
|
||||
|
||||
:root .ck.ck-form.ck-link-form ul.ck-link-form__providers-list {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* Math popup */
|
||||
|
||||
.ck-math-form .ck-labeled-field-view {
|
||||
--ck-input-label-height: 0;
|
||||
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
|
||||
/* Emoji dropdown */
|
||||
|
||||
.ck-emoji-picker-form .ck-emoji__search .ck-button_with-text:not(.ck-list-item-button) {
|
||||
margin-top: var(--ck-input-label-height);
|
||||
}
|
||||
|
||||
/* Find and replace dialog */
|
||||
|
||||
.ck-find-and-replace-form .ck-find-and-replace-form__inputs button {
|
||||
margin-top: var(--ck-input-label-height);
|
||||
}
|
||||
|
||||
/* Mention list (the autocompletion list for emojis, labels and relations) */
|
||||
@ -392,6 +465,58 @@
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/*
|
||||
* FORMS
|
||||
*/
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
*/
|
||||
|
||||
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck-button_with-text {
|
||||
--ck-color-text: var(--cmd-button-text-color);
|
||||
|
||||
min-width: 60px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/*
|
||||
* Text boxes
|
||||
*/
|
||||
|
||||
.ck.ck-labeled-field-view {
|
||||
padding-top: var(--ck-input-label-height) !important; /* Create space for the label */
|
||||
}
|
||||
|
||||
.ck.ck-labeled-field-view > .ck.ck-labeled-field-view__input-wrapper > label.ck.ck-label {
|
||||
/* Move the label above the text box regardless of the text box state */
|
||||
transform: translate(0, calc(-.2em - var(--ck-input-label-height))) !important;
|
||||
|
||||
padding-left: 0 !important;
|
||||
background: transparent;
|
||||
font-size: .85em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:root input.ck.ck-input-text[readonly="true"] {
|
||||
cursor: not-allowed;
|
||||
background: var(--input-background-color);
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
|
||||
:root .ck.ck-form__row.ck-form__row_with-submit > :not(:first-child) {
|
||||
margin-inline-start: 16px;
|
||||
}
|
||||
|
||||
.ck.ck-form__row_with-submit button {
|
||||
margin-top: var(--ck-input-label-height);
|
||||
}
|
||||
|
||||
.ck.ck-form__header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* EDITOR'S CONTENT
|
||||
*/
|
||||
|
@ -36,32 +36,23 @@ body.mobile {
|
||||
|
||||
/* #region Mica */
|
||||
body.background-effects.platform-win32 {
|
||||
--launcher-pane-horiz-border-color: rgba(0, 0, 0, 0.15);
|
||||
--launcher-pane-horiz-background-color: rgba(255, 255, 255, 0.7);
|
||||
--launcher-pane-vert-background-color: rgba(255, 255, 255, 0.055);
|
||||
--tab-background-color: transparent;
|
||||
--new-tab-button-background: transparent;
|
||||
--active-tab-background-color: var(--launcher-pane-horiz-background-color);
|
||||
--background-material: tabbed;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body.background-effects.platform-win32 {
|
||||
--launcher-pane-horiz-border-color: rgba(0, 0, 0, 0.5);
|
||||
--launcher-pane-horiz-background-color: rgba(255, 255, 255, 0.09);
|
||||
}
|
||||
--launcher-pane-horiz-border-color: var(--launcher-pane-horiz-border-color-bgfx);
|
||||
--launcher-pane-horiz-background-color: var(--launcher-pane-horiz-background-color-bgfx);
|
||||
--launcher-pane-vert-background-color: var(--launcher-pane-vert-background-color-bgfx);
|
||||
--tab-background-color: var(--window-background-color-bgfx);
|
||||
--new-tab-button-background: var(--window-background-color-bgfx);
|
||||
--active-tab-background-color: var(--launcher-pane-horiz-background-color);
|
||||
}
|
||||
|
||||
body.background-effects.platform-win32.layout-vertical {
|
||||
--left-pane-background-color: transparent;
|
||||
--left-pane-item-hover-background: rgba(127, 127, 127, 0.05);
|
||||
--left-pane-background-color: var(--window-background-color-bgfx);
|
||||
--background-material: mica;
|
||||
}
|
||||
|
||||
body.background-effects.platform-win32,
|
||||
body.background-effects.platform-win32 #root-widget,
|
||||
body.background-effects.platform-win32 #launcher-pane .launcher-button {
|
||||
background: transparent !important;
|
||||
body.background-effects.platform-win32 #root-widget {
|
||||
background: var(--window-background-color-bgfx) !important;
|
||||
}
|
||||
|
||||
body.background-effects.platform-win32.layout-horizontal #horizontal-main-container,
|
||||
@ -90,7 +81,7 @@ body.background-effects.zen #root-widget {
|
||||
* Gutter
|
||||
*/
|
||||
|
||||
.gutter {
|
||||
.gutter {
|
||||
background: var(--gutter-color) !important;
|
||||
transition: background 150ms ease-out;
|
||||
}
|
||||
@ -575,31 +566,20 @@ div.quick-search .search-button.show {
|
||||
* Quick search results
|
||||
*/
|
||||
|
||||
div.quick-search .dropdown-menu {
|
||||
--quick-search-item-delimiter-color: transparent;
|
||||
--menu-item-icon-vert-offset: -.065em;
|
||||
}
|
||||
|
||||
/* Item */
|
||||
.quick-search .dropdown-menu *.dropdown-item {
|
||||
padding: 8px 12px !important;
|
||||
}
|
||||
|
||||
/* Note icon */
|
||||
.quick-search .dropdown-menu .dropdown-item > .bx {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.quick-search .quick-search-item-icon {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
/* Note title */
|
||||
.quick-search .dropdown-menu .dropdown-item > a {
|
||||
color: var(--menu-text-color);
|
||||
}
|
||||
|
||||
.quick-search .dropdown-menu .dropdown-item > a:hover {
|
||||
--hover-item-background-color: transparent;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Note path */
|
||||
.quick-search .dropdown-menu small {
|
||||
display: block;
|
||||
@ -622,9 +602,8 @@ div.quick-search .search-button.show {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Divider line */
|
||||
.quick-search .dropdown-item::after {
|
||||
display: none;
|
||||
.quick-search div.dropdown-divider {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -899,6 +878,80 @@ body.layout-horizontal .tab-row-container {
|
||||
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container {
|
||||
border-bottom: unset !important;
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .note-tab-wrapper {
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .toggle-button {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .toggle-button:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: -10px;
|
||||
right: -10px;
|
||||
top: 29px;
|
||||
height: 1px;
|
||||
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-left,
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-right {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-left:after,
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-right:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
height: 1px;
|
||||
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .note-tab[active]:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: -32768px;
|
||||
top: var(--tab-height);
|
||||
right: calc(100% - 1px);
|
||||
height: 1px;
|
||||
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .note-tab[active]:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 100%;
|
||||
top: var(--tab-height);
|
||||
right: 0;
|
||||
width: 100vw;
|
||||
height: 1px;
|
||||
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
|
||||
}
|
||||
|
||||
body.electron.background-effects.layout-horizontal .tab-row-container .note-new-tab:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: -4px;
|
||||
top: calc(var(--tab-height), -1);
|
||||
right: 0;
|
||||
width: 100vw;
|
||||
height: 1px;
|
||||
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
|
||||
}
|
||||
|
||||
body.layout-vertical.electron.platform-darwin .tab-row-container {
|
||||
border-bottom: 1px solid var(--subtle-border-color);
|
||||
}
|
||||
@ -1114,6 +1167,11 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
|
||||
/* will-change: opacity; -- causes some weird artifacts to the note menu in split view */
|
||||
}
|
||||
|
||||
.split-note-container-widget > .gutter {
|
||||
background: var(--root-background) !important;
|
||||
transition: background 150ms ease-out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ribbon & note header
|
||||
*/
|
||||
@ -1122,10 +1180,6 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.note-split:not(.hidden-ext) + .note-split:not(.hidden-ext) {
|
||||
border-left: 4px solid var(--root-background);
|
||||
}
|
||||
|
||||
@keyframes note-entrance {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
@ -263,6 +263,11 @@
|
||||
"confirm_delete_all": "Do you want to delete all revisions of this note?",
|
||||
"no_revisions": "No revisions for this note yet...",
|
||||
"restore_button": "Restore",
|
||||
"diff_on": "Show diff",
|
||||
"diff_off": "Show content",
|
||||
"diff_on_hint": "Click to show note source diff",
|
||||
"diff_off_hint": "Click to show note content",
|
||||
"diff_not_available": "Diff isn't available.",
|
||||
"confirm_restore": "Do you want to restore this revision? This will overwrite the current title and content of the note with this revision.",
|
||||
"delete_button": "Delete",
|
||||
"confirm_delete": "Do you want to delete this revision?",
|
||||
@ -1118,7 +1123,9 @@
|
||||
"title": "Performance",
|
||||
"enable-motion": "Enable transitions and animations",
|
||||
"enable-shadows": "Enable shadows",
|
||||
"enable-backdrop-effects": "Enable background effects for menus, popups and panels"
|
||||
"enable-backdrop-effects": "Enable background effects for menus, popups and panels",
|
||||
"enable-smooth-scroll": "Enable smooth scrolling",
|
||||
"app-restart-required": "(a restart of the application is required for the change to take effect)"
|
||||
},
|
||||
"ai_llm": {
|
||||
"not_started": "Not started",
|
||||
|
@ -30,13 +30,16 @@
|
||||
"search_note": "Wyszukaj notatkę po nazwie",
|
||||
"link_title_arbitrary": "Tytuł linku można dowolnie zmieniać",
|
||||
"link_title": "Tytuł linku",
|
||||
"button_add_link": "Dodaj link"
|
||||
"button_add_link": "Dodaj link",
|
||||
"help_on_links": "Pomoc dotycząca linków",
|
||||
"link_title_mirrors": "tytuł linku odzwierciedla tytuł obecnej notatki"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"save": "Zapisz",
|
||||
"edit_branch_prefix": "Edytuj prefiks gałęzi",
|
||||
"prefix": "Prefiks: ",
|
||||
"branch_prefix_saved": "Zapisano prefiks gałęzi."
|
||||
"branch_prefix_saved": "Zapisano prefiks gałęzi.",
|
||||
"help_on_tree_prefix": "Pomoc dotycząca prefiksu drzewa"
|
||||
},
|
||||
"bulk_actions": {
|
||||
"labels": "Etykiety",
|
||||
@ -98,7 +101,8 @@
|
||||
"prefix_optional": "Prefiks (opcjonalne)",
|
||||
"clone_to_selected_note": "Sklonuj do wybranej notatki",
|
||||
"no_path_to_clone_to": "Brak ścieżki do sklonowania.",
|
||||
"note_cloned": "Notatka \"{{clonedTitle}}\" została sklonowana do \"{{targetTitle}}\""
|
||||
"note_cloned": "Notatka \"{{clonedTitle}}\" została sklonowana do \"{{targetTitle}}\"",
|
||||
"help_on_links": "Pomoc dotycząca linków"
|
||||
},
|
||||
"help": {
|
||||
"title": "Ściągawka",
|
||||
|
@ -2013,7 +2013,9 @@
|
||||
"title": "Setări de performanță",
|
||||
"enable-motion": "Activează tranzițiile și animațiile",
|
||||
"enable-shadows": "Activează umbrirea elementelor",
|
||||
"enable-backdrop-effects": "Activează efectele de fundal pentru meniuri, popup-uri și panouri"
|
||||
"enable-backdrop-effects": "Activează efectele de fundal pentru meniuri, popup-uri și panouri",
|
||||
"enable-smooth-scroll": "Activează derularea lină",
|
||||
"app-restart-required": "(este necesară repornirea aplicației pentru ca modificarea să aibă efect)"
|
||||
},
|
||||
"settings": {
|
||||
"related_settings": "Setări similare"
|
||||
|
@ -844,7 +844,8 @@
|
||||
"note_type": "Тип нотатки",
|
||||
"editable": "Редагув.",
|
||||
"basic_properties": "Основні Властивості",
|
||||
"language": "Мова"
|
||||
"language": "Мова",
|
||||
"configure_code_notes": "Конфігурація нотатки з кодом..."
|
||||
},
|
||||
"book_properties": {
|
||||
"view_type": "Тип перегляду",
|
||||
@ -1586,7 +1587,8 @@
|
||||
"hoist-this-note-workspace": "Закріпити цю нотатку (робочий простір)",
|
||||
"refresh-saved-search-results": "Оновити збережені результати пошуку",
|
||||
"create-child-note": "Створити дочірню нотатку",
|
||||
"unhoist": "Відкріпити"
|
||||
"unhoist": "Відкріпити",
|
||||
"toggle-sidebar": "Перемикання бічної панелі"
|
||||
},
|
||||
"title_bar_buttons": {
|
||||
"window-on-top": "Тримати вікно зверху"
|
||||
@ -1909,8 +1911,8 @@
|
||||
"open-in-popup": "Швидке редагування"
|
||||
},
|
||||
"shared_info": {
|
||||
"shared_publicly": "Ця нотатка опублікована на {{- link}}",
|
||||
"shared_locally": "Цю нотатку опубліковано локально на {{- link}}",
|
||||
"shared_publicly": "Ця нотатка опублікована на {{- link}}.",
|
||||
"shared_locally": "Цю нотатку опубліковано локально на {{- link}}.",
|
||||
"help_link": "Щоб отримати допомогу, відвідайте <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">вікі</a>."
|
||||
},
|
||||
"note_types": {
|
||||
@ -2018,5 +2020,11 @@
|
||||
},
|
||||
"units": {
|
||||
"percentage": "%"
|
||||
},
|
||||
"ui-performance": {
|
||||
"title": "Продуктивність",
|
||||
"enable-motion": "Увімкнути переходи та анімацію",
|
||||
"enable-shadows": "Увімкнути тіні",
|
||||
"enable-backdrop-effects": "Увімкнути фонові ефекти для меню, спливаючих вікон та панелей"
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import appContext, { type CommandData, type CommandListenerData, type EventData,
|
||||
import type BasicWidget from "../basic_widget.js";
|
||||
import type NoteContext from "../../components/note_context.js";
|
||||
import Component from "../../components/component.js";
|
||||
|
||||
import splitService from "../../services/resizer.js";
|
||||
interface NoteContextEvent {
|
||||
noteContext: NoteContext;
|
||||
}
|
||||
@ -52,6 +52,10 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
await widget.handleEvent("setNoteContext", { noteContext });
|
||||
|
||||
this.child(widget);
|
||||
|
||||
if (noteContext.mainNtxId && noteContext.ntxId) {
|
||||
splitService.setupNoteSplitResizer([noteContext.mainNtxId,noteContext.ntxId]);
|
||||
}
|
||||
}
|
||||
|
||||
async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: EventData<"openNewNoteSplit">) {
|
||||
@ -95,9 +99,9 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) {
|
||||
async closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) {
|
||||
if (ntxId) {
|
||||
appContext.tabManager.removeNoteContext(ntxId);
|
||||
await appContext.tabManager.removeNoteContext(ntxId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,6 +141,8 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
|
||||
// activate context that now contains the original note
|
||||
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
|
||||
|
||||
splitService.moveNoteSplitResizer(ntxIds[leftIndex]);
|
||||
}
|
||||
|
||||
activeContextChangedEvent() {
|
||||
@ -157,6 +163,8 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
recursiveCleanup(widget);
|
||||
delete this.widgets[ntxId];
|
||||
}
|
||||
|
||||
splitService.delNoteSplitResizer(ntxIds);
|
||||
}
|
||||
|
||||
contextsReopenedEvent({ ntxId, afterNtxId }: EventData<"contextsReopened">) {
|
||||
|
@ -7,6 +7,7 @@ import { t } from "../../services/i18n";
|
||||
import server from "../../services/server";
|
||||
import toast from "../../services/toast";
|
||||
import Button from "../react/Button";
|
||||
import FormToggle from "../react/FormToggle";
|
||||
import Modal from "../react/Modal";
|
||||
import FormList, { FormListItem } from "../react/FormList";
|
||||
import utils from "../../services/utils";
|
||||
@ -18,12 +19,15 @@ import open from "../../services/open";
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import options from "../../services/options";
|
||||
import { useTriliumEvent } from "../react/hooks";
|
||||
import { diffWords } from "diff";
|
||||
|
||||
export default function RevisionsDialog() {
|
||||
const [ note, setNote ] = useState<FNote>();
|
||||
const [ noteContent, setNoteContent ] = useState<string>();
|
||||
const [ revisions, setRevisions ] = useState<RevisionItem[]>();
|
||||
const [ currentRevision, setCurrentRevision ] = useState<RevisionItem>();
|
||||
const [ shown, setShown ] = useState(false);
|
||||
const [ showDiff, setShowDiff ] = useState(false);
|
||||
const [ refreshCounter, setRefreshCounter ] = useState(0);
|
||||
|
||||
useTriliumEvent("showRevisions", async ({ noteId }) => {
|
||||
@ -37,8 +41,10 @@ export default function RevisionsDialog() {
|
||||
useEffect(() => {
|
||||
if (note?.noteId) {
|
||||
server.get<RevisionItem[]>(`notes/${note.noteId}/revisions`).then(setRevisions);
|
||||
note.getContent().then(setNoteContent);
|
||||
} else {
|
||||
setRevisions(undefined);
|
||||
setNoteContent(undefined);
|
||||
}
|
||||
}, [ note?.noteId, refreshCounter ]);
|
||||
|
||||
@ -54,22 +60,42 @@ export default function RevisionsDialog() {
|
||||
helpPageId="vZWERwf8U3nx"
|
||||
bodyStyle={{ display: "flex", height: "80vh" }}
|
||||
header={
|
||||
(!!revisions?.length && <Button text={t("revisions.delete_all_revisions")} size="small" style={{ padding: "0 10px" }}
|
||||
onClick={async () => {
|
||||
const text = t("revisions.confirm_delete_all");
|
||||
!!revisions?.length && (
|
||||
<>
|
||||
{["text", "code", "mermaid"].includes(currentRevision?.type ?? "") && (
|
||||
<FormToggle
|
||||
currentValue={showDiff}
|
||||
onChange={(newValue) => setShowDiff(newValue)}
|
||||
switchOnName={t("revisions.diff_on")}
|
||||
switchOffName={t("revisions.diff_off")}
|
||||
switchOnTooltip={t("revisions.diff_on_hint")}
|
||||
switchOffTooltip={t("revisions.diff_off_hint")}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
text={t("revisions.delete_all_revisions")}
|
||||
size="small"
|
||||
style={{ padding: "0 10px" }}
|
||||
onClick={async () => {
|
||||
const text = t("revisions.confirm_delete_all");
|
||||
|
||||
if (note && await dialog.confirm(text)) {
|
||||
await server.remove(`notes/${note.noteId}/revisions`);
|
||||
setRevisions([]);
|
||||
setCurrentRevision(undefined);
|
||||
toast.showMessage(t("revisions.revisions_deleted"));
|
||||
}
|
||||
}}/>)
|
||||
if (note && await dialog.confirm(text)) {
|
||||
await server.remove(`notes/${note.noteId}/revisions`);
|
||||
setRevisions([]);
|
||||
setCurrentRevision(undefined);
|
||||
toast.showMessage(t("revisions.revisions_deleted"));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
footer={<RevisionFooter note={note} />}
|
||||
footerStyle={{ paddingTop: 0, paddingBottom: 0 }}
|
||||
onHidden={() => {
|
||||
setShown(false);
|
||||
setShowDiff(false);
|
||||
setNote(undefined);
|
||||
setCurrentRevision(undefined);
|
||||
setRevisions(undefined);
|
||||
@ -92,10 +118,13 @@ export default function RevisionsDialog() {
|
||||
marginLeft: "20px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
maxWidth: "calc(100% - 150px)",
|
||||
minWidth: 0
|
||||
}}>
|
||||
<RevisionPreview
|
||||
noteContent={noteContent}
|
||||
revisionItem={currentRevision}
|
||||
showDiff={showDiff}
|
||||
setShown={setShown}
|
||||
onRevisionDeleted={() => {
|
||||
setRefreshCounter(c => c + 1);
|
||||
@ -121,8 +150,10 @@ function RevisionsList({ revisions, onSelect, currentRevision }: { revisions: Re
|
||||
</FormList>);
|
||||
}
|
||||
|
||||
function RevisionPreview({ revisionItem, setShown, onRevisionDeleted }: {
|
||||
function RevisionPreview({noteContent, revisionItem, showDiff, setShown, onRevisionDeleted }: {
|
||||
noteContent?: string,
|
||||
revisionItem?: RevisionItem,
|
||||
showDiff: boolean,
|
||||
setShown: Dispatch<StateUpdater<boolean>>,
|
||||
onRevisionDeleted?: () => void
|
||||
}) {
|
||||
@ -179,7 +210,7 @@ function RevisionPreview({ revisionItem, setShown, onRevisionDeleted }: {
|
||||
</div>)}
|
||||
</div>
|
||||
<div className="revision-content use-tn-links" style={{ overflow: "auto", wordBreak: "break-word" }}>
|
||||
<RevisionContent revisionItem={revisionItem} fullRevision={fullRevision} />
|
||||
<RevisionContent noteContent={noteContent} revisionItem={revisionItem} fullRevision={fullRevision} showDiff={showDiff}/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@ -197,12 +228,15 @@ const CODE_STYLE: CSSProperties = {
|
||||
whiteSpace: "pre-wrap"
|
||||
};
|
||||
|
||||
function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: RevisionItem, fullRevision?: RevisionPojo }) {
|
||||
function RevisionContent({ noteContent, revisionItem, fullRevision, showDiff }: { noteContent?:string, revisionItem?: RevisionItem, fullRevision?: RevisionPojo, showDiff: boolean}) {
|
||||
const content = fullRevision?.content;
|
||||
if (!revisionItem || !content) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (showDiff) {
|
||||
return <RevisionContentDiff noteContent={noteContent} itemContent={content} itemType={revisionItem.type}/>
|
||||
}
|
||||
switch (revisionItem.type) {
|
||||
case "text":
|
||||
return <RevisionContentText content={content} />
|
||||
@ -267,6 +301,48 @@ function RevisionContentText({ content }: { content: string | Buffer<ArrayBuffer
|
||||
return <div ref={contentRef} className="ck-content" dangerouslySetInnerHTML={{ __html: content as string }}></div>
|
||||
}
|
||||
|
||||
function RevisionContentDiff({ noteContent, itemContent, itemType }: {
|
||||
noteContent?: string,
|
||||
itemContent: string | Buffer<ArrayBufferLike> | undefined,
|
||||
itemType: string
|
||||
}) {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!noteContent || typeof itemContent !== "string") {
|
||||
if (contentRef.current) {
|
||||
contentRef.current.textContent = t("revisions.diff_not_available");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let processedNoteContent = noteContent;
|
||||
let processedItemContent = itemContent;
|
||||
|
||||
if (itemType === "text") {
|
||||
processedNoteContent = utils.formatHtml(noteContent);
|
||||
processedItemContent = utils.formatHtml(itemContent);
|
||||
}
|
||||
|
||||
const diff = diffWords(processedNoteContent, processedItemContent);
|
||||
const diffHtml = diff.map(part => {
|
||||
if (part.added) {
|
||||
return `<span class="revision-diff-added">${utils.escapeHtml(part.value)}</span>`;
|
||||
} else if (part.removed) {
|
||||
return `<span class="revision-diff-removed">${utils.escapeHtml(part.value)}</span>`;
|
||||
} else {
|
||||
return utils.escapeHtml(part.value);
|
||||
}
|
||||
}).join("");
|
||||
|
||||
if (contentRef.current) {
|
||||
contentRef.current.innerHTML = diffHtml;
|
||||
}
|
||||
}, [noteContent, itemContent, itemType]);
|
||||
|
||||
return <div ref={contentRef} className="ck-content" style={{ whiteSpace: "pre-wrap" }}></div>;
|
||||
}
|
||||
|
||||
function RevisionFooter({ note }: { note?: FNote }) {
|
||||
if (!note) {
|
||||
return <></>;
|
||||
|
@ -23,15 +23,21 @@ export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
noteSwitchedAndActivatedEvent() {
|
||||
noteSwitchedAndActivatedEvent({ noteContext }: EventData<"setNoteContext">) {
|
||||
this.noteContext = noteContext;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
noteSwitchedEvent() {
|
||||
noteSwitchedEvent({ noteContext }: EventData<"setNoteContext">) {
|
||||
this.noteContext = noteContext;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
activeContextChangedEvent() {
|
||||
activeContextChangedEvent({ noteContext }: EventData<"setNoteContext">) {
|
||||
this.noteContext = noteContext;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,8 @@ const TPL = /*html*/`
|
||||
}
|
||||
|
||||
.quick-search .dropdown-menu {
|
||||
--quick-search-item-delimiter-color: var(--dropdown-border-color);
|
||||
|
||||
max-height: 80vh;
|
||||
min-width: 400px;
|
||||
max-width: 720px;
|
||||
@ -38,14 +40,14 @@ const TPL = /*html*/`
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-search .dropdown-item:not(:last-child)::after {
|
||||
.quick-search .dropdown-item + .dropdown-item::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: var(--dropdown-border-color);
|
||||
border-bottom: 1px solid var(--quick-search-item-delimiter-color);
|
||||
}
|
||||
|
||||
.quick-search .dropdown-item:last-child::after {
|
||||
@ -92,6 +94,8 @@ const TPL = /*html*/`
|
||||
background-color: var(--accented-background-color);
|
||||
color: var(--main-text-color);
|
||||
font-size: .85em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Search result highlighting */
|
||||
@ -106,6 +110,10 @@ const TPL = /*html*/`
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.quick-search .bx-loader {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="input-group-prepend">
|
||||
@ -228,7 +236,11 @@ export default class QuickSearchWidget extends BasicWidget {
|
||||
this.isLoadingMore = false;
|
||||
|
||||
this.$dropdownMenu.empty();
|
||||
this.$dropdownMenu.append(`<span class="dropdown-item disabled"><span class="bx bx-loader bx-spin"></span>${t("quick-search.searching")}</span>`);
|
||||
this.$dropdownMenu.append(`
|
||||
<span class="dropdown-item disabled">
|
||||
<span class="bx bx-loader bx-spin"></span>
|
||||
${t("quick-search.searching")}
|
||||
</span>`);
|
||||
|
||||
const { searchResultNoteIds, searchResults, error } = await server.get<QuickSearchResponse>(`quick-search/${encodeURIComponent(searchString)}`);
|
||||
|
||||
|
@ -388,7 +388,7 @@ async function setupFonts() {
|
||||
if (!glob.isDev) {
|
||||
path = `${window.location.pathname}/node_modules/@excalidraw/excalidraw/dist/prod`;
|
||||
} else {
|
||||
path = (await import("../../../node_modules/@excalidraw/excalidraw/dist/prod/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2?url")).default;
|
||||
path = (await import("../../../../../node_modules/@excalidraw/excalidraw/dist/prod/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2?url")).default;
|
||||
let pathComponents = path.split("/");
|
||||
path = pathComponents.slice(0, pathComponents.length - 2).join("/");
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { buildExtraCommands, type EditorConfig, PREMIUM_PLUGINS } from "@trilium
|
||||
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
|
||||
import options from "../../../services/options.js";
|
||||
import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
|
||||
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
|
||||
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/src/emoji_definitions/en.json?url";
|
||||
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
|
||||
import getTemplates from "./snippets.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
|
@ -1,5 +1,6 @@
|
||||
import utils from "../../../services/utils.js";
|
||||
import options from "../../../services/options.js";
|
||||
import IconAlignCenter from "@ckeditor/ckeditor5-icons/theme/icons/align-center.svg?raw";
|
||||
|
||||
const TEXT_FORMATTING_GROUP = {
|
||||
label: "Text formatting",
|
||||
@ -77,7 +78,7 @@ export function buildClassicToolbar(multilineToolbar: boolean) {
|
||||
items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
|
||||
},
|
||||
"|",
|
||||
"alignment",
|
||||
buildAlignmentToolbar(),
|
||||
"outdent",
|
||||
"indent",
|
||||
"|",
|
||||
@ -134,7 +135,7 @@ export function buildFloatingToolbar() {
|
||||
items: ["link", "bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
|
||||
},
|
||||
"|",
|
||||
"alignment",
|
||||
buildAlignmentToolbar(),
|
||||
"outdent",
|
||||
"indent",
|
||||
"|",
|
||||
@ -147,3 +148,11 @@ export function buildFloatingToolbar() {
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function buildAlignmentToolbar() {
|
||||
return {
|
||||
label: "Alignment",
|
||||
icon: IconAlignCenter,
|
||||
items: ["alignment:left", "alignment:center", "alignment:right", "|", "alignment:justify"]
|
||||
};
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
|
||||
import { buildConfig, BuildEditorOptions, OPEN_SOURCE_LICENSE_KEY } from "./ckeditor/config.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5";
|
||||
import "@triliumnext/ckeditor5/index.css";
|
||||
import { updateTemplateCache } from "./ckeditor/snippets.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
|
@ -266,9 +266,20 @@ function Performance() {
|
||||
label={t("ui-performance.enable-backdrop-effects")}
|
||||
currentValue={backdropEffectsEnabled} onChange={setBackdropEffectsEnabled}
|
||||
/>
|
||||
|
||||
{isElectron() && <SmoothScrollEnabledOption />}
|
||||
|
||||
</OptionsSection>
|
||||
}
|
||||
|
||||
function SmoothScrollEnabledOption() {
|
||||
const [ smoothScrollEnabled, setSmoothScrollEnabled ] = useTriliumOptionBool("smoothScrollEnabled");
|
||||
|
||||
return <FormCheckbox
|
||||
label={`${t("ui-performance.enable-smooth-scroll")} ${t("ui-performance.app-restart-required")}`}
|
||||
currentValue={smoothScrollEnabled} onChange={setSmoothScrollEnabled}
|
||||
/>
|
||||
}
|
||||
|
||||
function MaxContentWidth() {
|
||||
const [ maxContentWidth, setMaxContentWidth ] = useTriliumOption("maxContentWidth");
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { EventData } from "../../components/app_context.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
import AbstractCodeTypeWidget from "./abstract_code_type_widget.js";
|
||||
import utils from "../../services/utils.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="note-detail-readonly-code note-detail-printable">
|
||||
@ -33,7 +34,7 @@ export default class ReadOnlyCodeTypeWidget extends AbstractCodeTypeWidget {
|
||||
if (!blob) return;
|
||||
|
||||
const isFormattable = note.type === "text" && this.noteContext?.viewScope?.viewMode === "source";
|
||||
const content = isFormattable ? this.format(blob.content) : blob.content;
|
||||
const content = isFormattable ? utils.formatHtml(blob.content) : blob.content;
|
||||
|
||||
this._update(note, content);
|
||||
this.show();
|
||||
@ -54,52 +55,4 @@ export default class ReadOnlyCodeTypeWidget extends AbstractCodeTypeWidget {
|
||||
|
||||
resolve(this.$editor);
|
||||
}
|
||||
|
||||
format(html: string) {
|
||||
let indent = "\n";
|
||||
const tab = "\t";
|
||||
let i = 0;
|
||||
let pre: { indent: string; tag: string }[] = [];
|
||||
|
||||
html = html
|
||||
.replace(new RegExp("<pre>((.|\\t|\\n|\\r)+)?</pre>"), function (x) {
|
||||
pre.push({ indent: "", tag: x });
|
||||
return "<--TEMPPRE" + i++ + "/-->";
|
||||
})
|
||||
.replace(new RegExp("<[^<>]+>[^<]?", "g"), function (x) {
|
||||
let ret;
|
||||
const tagRegEx = /<\/?([^\s/>]+)/.exec(x);
|
||||
let tag = tagRegEx ? tagRegEx[1] : "";
|
||||
let p = new RegExp("<--TEMPPRE(\\d+)/-->").exec(x);
|
||||
|
||||
if (p) {
|
||||
const pInd = parseInt(p[1]);
|
||||
pre[pInd].indent = indent;
|
||||
}
|
||||
|
||||
if (["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"].indexOf(tag) >= 0) {
|
||||
// self closing tag
|
||||
ret = indent + x;
|
||||
} else {
|
||||
if (x.indexOf("</") < 0) {
|
||||
//open tag
|
||||
if (x.charAt(x.length - 1) !== ">") ret = indent + x.substr(0, x.length - 1) + indent + tab + x.substr(x.length - 1, x.length);
|
||||
else ret = indent + x;
|
||||
!p && (indent += tab);
|
||||
} else {
|
||||
//close tag
|
||||
indent = indent.substr(0, indent.length - 1);
|
||||
if (x.charAt(x.length - 1) !== ">") ret = indent + x.substr(0, x.length - 1) + indent + x.substr(x.length - 1, x.length);
|
||||
else ret = indent + x;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
for (i = pre.length; i--;) {
|
||||
html = html.replace("<--TEMPPRE" + i + "/-->", pre[i].tag.replace("<pre>", "<pre>\n").replace("</pre>", pre[i].indent + "</pre>"));
|
||||
}
|
||||
|
||||
return html.charAt(0) === "\n" ? html.substr(1, html.length - 1) : html;
|
||||
}
|
||||
}
|
||||
|
@ -2,26 +2,23 @@
|
||||
import { join, resolve } from 'path';
|
||||
import { defineConfig, type Plugin } from 'vite';
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
import asset_path from './src/asset_path';
|
||||
import webpackStatsPlugin from 'rollup-plugin-webpack-stats';
|
||||
import preact from "@preact/preset-vite";
|
||||
|
||||
const assets = [ "assets", "stylesheets", "fonts", "translations" ];
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/client',
|
||||
base: process.env.NODE_ENV === "production" ? "" : asset_path,
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
plugins: [
|
||||
preact(),
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
let plugins: any = [
|
||||
preact({
|
||||
babel: {
|
||||
compact: !isDev
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
if (!isDev) {
|
||||
plugins = [
|
||||
...plugins,
|
||||
viteStaticCopy({
|
||||
targets: assets.map((asset) => ({
|
||||
src: `src/${asset}/*`,
|
||||
@ -32,20 +29,22 @@ export default defineConfig(() => ({
|
||||
structured: true,
|
||||
targets: [
|
||||
{
|
||||
src: "node_modules/@excalidraw/excalidraw/dist/prod/fonts/*",
|
||||
src: "../../node_modules/@excalidraw/excalidraw/dist/prod/fonts/*",
|
||||
dest: "",
|
||||
}
|
||||
]
|
||||
}),
|
||||
webpackStatsPlugin()
|
||||
] as Plugin[],
|
||||
]
|
||||
}
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/client',
|
||||
base: "",
|
||||
plugins,
|
||||
resolve: {
|
||||
alias: [
|
||||
// Force the use of dist in development mode because upstream ESM is broken (some hybrid between CJS and ESM, will be improved in upcoming versions).
|
||||
{
|
||||
find: "@triliumnext/highlightjs",
|
||||
replacement: resolve(__dirname, "node_modules/@triliumnext/highlightjs/dist")
|
||||
},
|
||||
{
|
||||
find: "react",
|
||||
replacement: "preact/compat"
|
||||
@ -63,10 +62,6 @@ export default defineConfig(() => ({
|
||||
"preact/hooks"
|
||||
]
|
||||
},
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
build: {
|
||||
target: "esnext",
|
||||
outDir: './dist',
|
||||
@ -105,18 +100,6 @@ export default defineConfig(() => ({
|
||||
"./src/test/setup.ts"
|
||||
]
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: [
|
||||
"@triliumnext/highlightjs"
|
||||
]
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
quietDeps: true
|
||||
}
|
||||
}
|
||||
},
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
|
@ -6,12 +6,12 @@
|
||||
To build and run manually:
|
||||
|
||||
```sh
|
||||
nx build db-compare
|
||||
pnpm build db-compare
|
||||
node ./apps/db-compare/dist/compare.js
|
||||
```
|
||||
|
||||
To serve development build with arguments:
|
||||
|
||||
```sh
|
||||
nx serve db-compare --args "apps/server/spec/db/document_v214.db" --args "apps/server/spec/db/document_v214_migrated.db"
|
||||
pnpm dev --args "apps/server/spec/db/document_v214.db" --args "apps/server/spec/db/document_v214_migrated.db"
|
||||
```
|
@ -9,63 +9,8 @@
|
||||
"sqlite": "5.1.1",
|
||||
"sqlite3": "5.1.7"
|
||||
},
|
||||
"nx": {
|
||||
"name": "db-compare",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"platform": "node",
|
||||
"outputPath": "apps/db-compare/dist",
|
||||
"format": [
|
||||
"cjs"
|
||||
],
|
||||
"bundle": false,
|
||||
"main": "apps/db-compare/src/compare.ts",
|
||||
"tsConfig": "apps/db-compare/tsconfig.app.json",
|
||||
"assets": [],
|
||||
"esbuildOptions": {
|
||||
"sourcemap": true,
|
||||
"outExtension": {
|
||||
".js": ".js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"development": {},
|
||||
"production": {
|
||||
"esbuildOptions": {
|
||||
"sourcemap": false,
|
||||
"outExtension": {
|
||||
".js": ".js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"defaultConfiguration": "development",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"options": {
|
||||
"buildTarget": "db-compare:build",
|
||||
"runBuildTargetDependencies": false
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "db-compare:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "db-compare:build:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"scripts": {
|
||||
"dev": "tsx src/compare.ts",
|
||||
"build": "esbuild --platform=node --format=cjs --outdir=dist src/compare.ts"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
TRILIUM_INTEGRATION_TEST=memory-no-store
|
||||
TRILIUM_PORT=8082
|
||||
TRILIUM_DATA_DIR=data
|
@ -1,15 +0,0 @@
|
||||
import playwright from "eslint-plugin-playwright";
|
||||
import baseConfig from "../../eslint.config.mjs";
|
||||
|
||||
export default [
|
||||
playwright.configs["flat/recommended"],
|
||||
...baseConfig,
|
||||
{
|
||||
files: [
|
||||
"**/*.ts",
|
||||
"**/*.js"
|
||||
],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
}
|
||||
];
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"name": "@triliumnext/desktop-e2e",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"nx": {
|
||||
"name": "desktop-e2e",
|
||||
"implicitDependencies": [
|
||||
"client",
|
||||
"desktop"
|
||||
],
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"dependsOn": [
|
||||
"desktop:build",
|
||||
"desktop:rebuild-deps"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "17.2.1",
|
||||
"electron": "37.4.0"
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"outDir": "out-tsc/playwright",
|
||||
"sourceMap": false
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
"playwright.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"test-output",
|
||||
"eslint.config.js",
|
||||
"eslint.config.mjs",
|
||||
"eslint.config.cjs"
|
||||
]
|
||||
}
|
@ -1 +0,0 @@
|
||||
node-linker = hoisted
|
@ -1 +0,0 @@
|
||||
TRILIUM_PORT=37743
|
@ -1,3 +0,0 @@
|
||||
TRILIUM_PORT=37741
|
||||
TRILIUM_DATA_DIR=../data
|
||||
NODE_OPTIONS=--enable-source-maps
|
@ -60,7 +60,7 @@ test('First setup', async () => {
|
||||
// Verify the shared link is valid
|
||||
const requestContext = await request.newContext();
|
||||
const response = await requestContext.get(linkUrl!);
|
||||
expect(response).toBeOK();
|
||||
await expect(response).toBeOK();
|
||||
|
||||
await mainWindow.waitForTimeout(5000);
|
||||
});
|
@ -3,7 +3,23 @@
|
||||
"version": "0.98.1",
|
||||
"description": "Build your personal knowledge base with Trilium Notes",
|
||||
"private": true,
|
||||
"main": "main.cjs",
|
||||
"main": "src/main.ts",
|
||||
"license": "AGPL-3.0-only",
|
||||
"author": {
|
||||
"name": "Trilium Notes Team",
|
||||
"email": "contact@eliandoran.me",
|
||||
"url": "https://github.com/TriliumNext/Notes"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "cross-env TRILIUM_PORT=37741 TRILIUM_DATA_DIR=data tsx ../../scripts/electron-start.mts src/main.ts",
|
||||
"start-no-dir": "cross-env TRILIUM_PORT=37743 tsx ../../scripts/electron-start.mts src/main.ts",
|
||||
"build": "tsx scripts/build.ts",
|
||||
"start-prod": "pnpm build && cross-env TRILIUM_DATA_DIR=data TRILIUM_PORT=37841 ELECTRON_IS_DEV=0 electron dist",
|
||||
"electron-forge:make": "pnpm build && cross-env electron-forge make dist",
|
||||
"electron-forge:package": "pnpm build && electron-forge package dist",
|
||||
"electron-forge:start": "pnpm build && electron-forge start dist",
|
||||
"e2e": "pnpm build && cross-env TRILIUM_INTEGRATION_TEST=memory-no-store TRILIUM_PORT=8082 TRILIUM_DATA_DIR=data-e2e ELECTRON_IS_DEV=0 playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "2.1.3",
|
||||
"better-sqlite3": "^12.0.0",
|
||||
@ -15,6 +31,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/electron-squirrel-startup": "1.0.2",
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/server": "workspace:*",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"electron": "37.4.0",
|
||||
@ -27,174 +44,5 @@
|
||||
"@electron-forge/maker-zip": "7.8.3",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "7.8.3",
|
||||
"prebuild-install": "^7.1.1"
|
||||
},
|
||||
"config": {
|
||||
"forge": "./electron-forge/forge.config.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"start-prod": "nx build desktop && cross-env TRILIUM_DATA_DIR=data TRILIUM_RESOURCE_DIR=dist TRILIUM_PORT=37841 electron dist/main.js"
|
||||
},
|
||||
"license": "AGPL-3.0-only",
|
||||
"author": {
|
||||
"name": "Trilium Notes Team",
|
||||
"email": "contact@eliandoran.me",
|
||||
"url": "https://github.com/TriliumNext/Notes"
|
||||
},
|
||||
"nx": {
|
||||
"name": "desktop",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"defaultConfiguration": "production",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"minify": true,
|
||||
"sourcemap": false
|
||||
},
|
||||
"development": {
|
||||
"minify": false,
|
||||
"sourcemap": true
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"main": "apps/desktop/src/electron-main.ts",
|
||||
"outputPath": "apps/desktop/dist",
|
||||
"outputFileName": "main.js",
|
||||
"tsConfig": "apps/desktop/tsconfig.app.json",
|
||||
"platform": "node",
|
||||
"external": [
|
||||
"electron",
|
||||
"@electron/remote",
|
||||
"better-sqlite3",
|
||||
"./xhr-sync-worker.js"
|
||||
],
|
||||
"format": [
|
||||
"cjs"
|
||||
],
|
||||
"thirdParty": true,
|
||||
"declaration": false,
|
||||
"esbuildOptions": {
|
||||
"splitting": false,
|
||||
"loader": {
|
||||
".css": "text"
|
||||
}
|
||||
},
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/dist/node_modules",
|
||||
"output": "node_modules"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/desktop/node_modules/@electron/remote",
|
||||
"output": "node_modules/@electron/remote"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/dist/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "packages/share-theme/src/templates",
|
||||
"output": "share-theme/templates"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/desktop/src/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/dist/public",
|
||||
"output": "public"
|
||||
},
|
||||
{
|
||||
"glob": "xhr-sync-worker.js",
|
||||
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
|
||||
"output": ""
|
||||
}
|
||||
],
|
||||
"declarationRootDir": "apps/desktop/src"
|
||||
}
|
||||
},
|
||||
"rebuild-deps": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"defaultConfiguration": "default",
|
||||
"cache": false,
|
||||
"configurations": {
|
||||
"default": {
|
||||
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist"
|
||||
},
|
||||
"nixos": {
|
||||
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist $(nix-shell -p electron_35 --run \"electron --version\")"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"rebuild-deps"
|
||||
],
|
||||
"defaultConfiguration": "default",
|
||||
"configurations": {
|
||||
"default": {
|
||||
"command": "electron main.cjs",
|
||||
"cwd": "{projectRoot}/dist"
|
||||
},
|
||||
"nixos": {
|
||||
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
|
||||
"cwd": ".",
|
||||
"forwardAllArgs": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve-nodir": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"rebuild-deps"
|
||||
],
|
||||
"defaultConfiguration": "default",
|
||||
"configurations": {
|
||||
"default": {
|
||||
"command": "electron main.cjs",
|
||||
"cwd": "{projectRoot}/dist"
|
||||
},
|
||||
"nixos": {
|
||||
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
|
||||
"cwd": ".",
|
||||
"forwardAllArgs": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"electron-forge:make": {
|
||||
"dependsOn": [
|
||||
"build",
|
||||
"rebuild-deps"
|
||||
],
|
||||
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge make dist"
|
||||
},
|
||||
"electron-forge:package": {
|
||||
"dependsOn": [
|
||||
"build",
|
||||
"rebuild-deps"
|
||||
],
|
||||
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge package dist"
|
||||
},
|
||||
"electron-forge:start": {
|
||||
"dependsOn": [
|
||||
"build",
|
||||
"rebuild-deps"
|
||||
],
|
||||
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm TRILIUM_DATA_DIR=./data electron-forge start dist"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,5 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
require('dotenv').config({
|
||||
path: __dirname + "/" + ".env"
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
@ -14,6 +9,8 @@ export default defineConfig({
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
testDir: "e2e",
|
||||
outputDir: "test-output",
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
46
apps/desktop/scripts/build.ts
Normal file
46
apps/desktop/scripts/build.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { join } from "path";
|
||||
import BuildHelper from "../../../scripts/build-utils";
|
||||
import originalPackageJson from "../package.json" with { type: "json" };
|
||||
import { writeFileSync } from "fs";
|
||||
|
||||
const build = new BuildHelper("apps/desktop");
|
||||
|
||||
async function main() {
|
||||
await build.buildBackend([ "src/main.ts"]);
|
||||
|
||||
// Copy assets.
|
||||
build.copy("src/assets", "assets/");
|
||||
build.copy("/apps/server/src/assets", "assets/");
|
||||
build.copy("/packages/share-theme/src/templates", "share-theme/templates/");
|
||||
|
||||
// Copy node modules dependencies
|
||||
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "@electron/remote" ]);
|
||||
build.copy("/node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js", "xhr-sync-worker.js");
|
||||
|
||||
// Integrate the client.
|
||||
build.triggerBuildAndCopyTo("apps/client", "public/");
|
||||
build.deleteFromOutput("public/webpack-stats.json");
|
||||
|
||||
generatePackageJson();
|
||||
}
|
||||
|
||||
function generatePackageJson() {
|
||||
const { version, author, license, description, dependencies, devDependencies } = originalPackageJson;
|
||||
const packageJson = {
|
||||
name: "trilium",
|
||||
main: "main.cjs",
|
||||
version, author, license, description,
|
||||
dependencies: {
|
||||
"better-sqlite3": dependencies["better-sqlite3"],
|
||||
},
|
||||
devDependencies: {
|
||||
electron: devDependencies.electron
|
||||
},
|
||||
config: {
|
||||
forge: "../electron-forge/forge.config.ts"
|
||||
}
|
||||
};
|
||||
writeFileSync(join(build.outDir, "package.json"), JSON.stringify(packageJson, null, "\t"), "utf-8");
|
||||
}
|
||||
|
||||
main();
|
@ -26,6 +26,12 @@ async function main() {
|
||||
electron.app.commandLine.appendSwitch("enable-experimental-web-platform-features");
|
||||
electron.app.commandLine.appendSwitch("lang", options.getOptionOrNull("formattingLocale") ?? "en");
|
||||
|
||||
// Disable smooth scroll if the option is set
|
||||
const smoothScrollEnabled = options.getOptionOrNull("smoothScrollEnabled");
|
||||
if (smoothScrollEnabled === "false") {
|
||||
electron.app.commandLine.appendSwitch("disable-smooth-scrolling");
|
||||
}
|
||||
|
||||
// Electron 36 crashes with "Using GTK 2/3 and GTK 4 in the same process is not supported" on some distributions.
|
||||
// See https://github.com/electron/electron/issues/46538 for more info.
|
||||
if (process.platform === "linux") {
|
@ -15,65 +15,8 @@
|
||||
"@types/mime-types": "^3.0.0",
|
||||
"@types/yargs": "^17.0.33"
|
||||
},
|
||||
"nx": {
|
||||
"name": "dump-db",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"platform": "node",
|
||||
"outputPath": "apps/dump-db/dist",
|
||||
"format": [
|
||||
"cjs"
|
||||
],
|
||||
"bundle": false,
|
||||
"main": "apps/dump-db/src/main.ts",
|
||||
"tsConfig": "apps/dump-db/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/dump-db/src/assets"
|
||||
],
|
||||
"esbuildOptions": {
|
||||
"sourcemap": true,
|
||||
"outExtension": {
|
||||
".js": ".js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"development": {},
|
||||
"production": {
|
||||
"esbuildOptions": {
|
||||
"sourcemap": false,
|
||||
"outExtension": {
|
||||
".js": ".js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"defaultConfiguration": "development",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"options": {
|
||||
"buildTarget": "dump-db:build",
|
||||
"runBuildTargetDependencies": false
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "dump-db:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "dump-db:build:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"scripts": {
|
||||
"dev": "tsx src/main.ts",
|
||||
"build": "esbuild --platform=node --format=cjs --outdir=dist src/main.ts"
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
TRILIUM_DATA_DIR=../data
|
||||
TRILIUM_INTEGRATION_TEST=memory-no-store
|
||||
TRILIUM_PORT=37741
|
||||
|
||||
# Paths are relative to dist root
|
||||
DOCS_ROOT=../../../docs
|
||||
USER_GUIDE_ROOT=../../../apps/server/src/assets/doc_notes/en/User Guide
|
@ -15,120 +15,8 @@
|
||||
"electron": "37.4.0",
|
||||
"fs-extra": "11.3.1"
|
||||
},
|
||||
"nx": {
|
||||
"name": "edit-docs",
|
||||
"implicitDependencies": [
|
||||
"server"
|
||||
],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"main": "apps/edit-docs/src/edit-docs.ts",
|
||||
"outputPath": "apps/edit-docs/dist",
|
||||
"tsConfig": "apps/edit-docs/tsconfig.app.json",
|
||||
"platform": "node",
|
||||
"additionalEntryPoints": [
|
||||
"apps/edit-docs/src/edit-demo.ts"
|
||||
],
|
||||
"external": [
|
||||
"electron",
|
||||
"@electron/remote",
|
||||
"better-sqlite3",
|
||||
"./xhr-sync-worker.js"
|
||||
],
|
||||
"format": [
|
||||
"cjs"
|
||||
],
|
||||
"minify": false,
|
||||
"thirdParty": true,
|
||||
"declaration": false,
|
||||
"esbuildOptions": {
|
||||
"splitting": false,
|
||||
"loader": {
|
||||
".css": "text"
|
||||
}
|
||||
},
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/dist/node_modules",
|
||||
"output": "node_modules"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/dist/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/dist/public",
|
||||
"output": "public"
|
||||
},
|
||||
{
|
||||
"glob": "xhr-sync-worker.js",
|
||||
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
|
||||
"output": ""
|
||||
}
|
||||
],
|
||||
"declarationRootDir": "apps/edit-docs/src"
|
||||
}
|
||||
},
|
||||
"rebuild-deps": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"defaultConfiguration": "default",
|
||||
"cache": true,
|
||||
"configurations": {
|
||||
"default": {
|
||||
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist"
|
||||
},
|
||||
"nixos": {
|
||||
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist $(nix-shell -p electron_35 --run \"electron --version\")"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edit-docs": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"rebuild-deps"
|
||||
],
|
||||
"defaultConfiguration": "default",
|
||||
"configurations": {
|
||||
"default": {
|
||||
"command": "electron edit-docs.cjs",
|
||||
"cwd": "{projectRoot}/dist"
|
||||
},
|
||||
"nixos": {
|
||||
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/edit-docs.cjs\"",
|
||||
"cwd": ".",
|
||||
"forwardAllArgs": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"edit-demo": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"rebuild-deps"
|
||||
],
|
||||
"defaultConfiguration": "default",
|
||||
"configurations": {
|
||||
"default": {
|
||||
"command": "electron edit-demo.cjs",
|
||||
"cwd": "{projectRoot}/dist"
|
||||
},
|
||||
"nixos": {
|
||||
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/edit-demo.cjs\"",
|
||||
"cwd": ".",
|
||||
"forwardAllArgs": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"scripts": {
|
||||
"edit-docs": "cross-env TRILIUM_PORT=37741 TRILIUM_DATA_DIR=data TRILIUM_INTEGRATION_TEST=memory-no-store DOCS_ROOT=../../../docs USER_GUIDE_ROOT=\"../../server/src/assets/doc_notes/en/User Guide\" tsx ../../scripts/electron-start.mts src/edit-docs.ts",
|
||||
"edit-demo": "cross-env TRILIUM_PORT=37741 TRILIUM_DATA_DIR=data TRILIUM_INTEGRATION_TEST=memory-no-store DOCS_ROOT=../../../docs USER_GUIDE_ROOT=\"../../server/src/assets/doc_notes/en/User Guide\" tsx ../../scripts/electron-start.mts src/edit-demo.ts"
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
TRILIUM_INTEGRATION_TEST=memory
|
||||
TRILIUM_PORT=8082
|
||||
TRILIUM_DATA_DIR=apps/server/spec/db
|
@ -2,21 +2,10 @@
|
||||
"name": "@triliumnext/server-e2e",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"nx": {
|
||||
"name": "server-e2e",
|
||||
"implicitDependencies": [
|
||||
"client",
|
||||
"server"
|
||||
],
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"dependsOn": [
|
||||
"server:build"
|
||||
]
|
||||
}
|
||||
}
|
||||
"scripts": {
|
||||
"e2e": "playwright test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "17.2.1"
|
||||
"dotenv": "17.2.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,68 +1,44 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
import { nxE2EPreset } from '@nx/playwright/preset';
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
|
||||
require('dotenv').config({
|
||||
path: __dirname + "/" + ".env"
|
||||
});
|
||||
import { join } from 'path';
|
||||
|
||||
// For CI, you may want to set BASE_URL to the deployed application.
|
||||
const port = process.env['TRILIUM_PORT'];
|
||||
const port = process.env['TRILIUM_PORT'] ?? "8082";
|
||||
const baseURL = process.env['BASE_URL'] || `http://127.0.0.1:${port}`;
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
...nxE2EPreset(__filename, { testDir: './src' }),
|
||||
testDir: "src",
|
||||
reporter: [["list"], ["html", { outputFolder: "test-output" }]],
|
||||
outputDir: "test-output",
|
||||
retries: 3,
|
||||
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
baseURL,
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: !process.env.TRILIUM_DOCKER ? {
|
||||
command: 'pnpm server:start-prod',
|
||||
command: 'pnpm start-prod-no-dir',
|
||||
url: baseURL,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
cwd: workspaceRoot,
|
||||
cwd: join(__dirname, "../server"),
|
||||
env: {
|
||||
TRILIUM_DATA_DIR: "spec/db",
|
||||
TRILIUM_PORT: port,
|
||||
TRILIUM_INTEGRATION_TEST: "memory"
|
||||
},
|
||||
timeout: 5 * 60 * 1000
|
||||
} : undefined,
|
||||
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
// {
|
||||
// name: "firefox",
|
||||
// use: { ...devices["Desktop Firefox"] },
|
||||
// },
|
||||
|
||||
// {
|
||||
// name: "webkit",
|
||||
// use: { ...devices["Desktop Safari"] },
|
||||
// },
|
||||
|
||||
// Uncomment for mobile browsers support
|
||||
/* {
|
||||
name: 'Mobile Chrome',
|
||||
use: { ...devices['Pixel 5'] },
|
||||
},
|
||||
{
|
||||
name: 'Mobile Safari',
|
||||
use: { ...devices['iPhone 12'] },
|
||||
}, */
|
||||
|
||||
// Uncomment for branded browsers
|
||||
/* {
|
||||
name: 'Microsoft Edge',
|
||||
use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
},
|
||||
{
|
||||
name: 'Google Chrome',
|
||||
use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
} */
|
||||
],
|
||||
}
|
||||
]
|
||||
});
|
||||
|
@ -79,7 +79,7 @@ test("Tabs are restored in right order", async ({ page, context }) => {
|
||||
|
||||
// Refresh the page and check the order.
|
||||
await app.goto( { preserveTabs: true });
|
||||
await expect(app.getTab(0)).toContainText("Code notes");
|
||||
await expect(app.getTab(0)).toContainText("Code notes", { timeout: 15_000 });
|
||||
await expect(app.getTab(1)).toContainText("Text notes");
|
||||
await expect(app.getTab(2)).toContainText("Mermaid");
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
TRILIUM_ENV=dev
|
||||
TRILIUM_DATA_DIR=./apps/server/spec/db
|
||||
TRILIUM_RESOURCE_DIR=./apps/server/dist
|
||||
TRILIUM_PUBLIC_SERVER=http://localhost:4200
|
||||
TRILIUM_PORT=8086
|
||||
TRILIUM_INTEGRATION_TEST=edit
|
@ -1,3 +0,0 @@
|
||||
TRILIUM_ENV=dev
|
||||
TRILIUM_RESOURCE_DIR=./apps/server/dist
|
||||
TRILIUM_PUBLIC_SERVER=http://localhost:4200
|
@ -1,4 +0,0 @@
|
||||
TRILIUM_ENV=dev
|
||||
TRILIUM_DATA_DIR=./apps/server/data
|
||||
TRILIUM_RESOURCE_DIR=./apps/server/dist
|
||||
TRILIUM_PUBLIC_SERVER=http://localhost:4200
|
@ -1,3 +0,0 @@
|
||||
TRILIUM_ENV=production
|
||||
TRILIUM_DATA_DIR=./apps/server/data
|
||||
TRILIUM_PORT=8082
|
@ -1,4 +0,0 @@
|
||||
TRILIUM_ENV=dev
|
||||
TRILIUM_DATA_DIR=./spec/db
|
||||
TRILIUM_PUBLIC_SERVER=http://localhost:4200
|
||||
TRILIUM_INTEGRATION_TEST=memory
|
@ -1,4 +1,4 @@
|
||||
FROM node:22.18.0-bullseye-slim AS builder
|
||||
FROM node:22.19.0-bullseye-slim AS builder
|
||||
RUN corepack enable
|
||||
|
||||
# Install native dependencies since we might be building cross-platform.
|
||||
@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
|
||||
# We have to use --no-frozen-lockfile due to CKEditor patches
|
||||
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
|
||||
|
||||
FROM node:22.18.0-bullseye-slim
|
||||
FROM node:22.19.0-bullseye-slim
|
||||
# Install only runtime dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:22.18.0-alpine AS builder
|
||||
FROM node:22.19.0-alpine AS builder
|
||||
RUN corepack enable
|
||||
|
||||
# Install native dependencies since we might be building cross-platform.
|
||||
@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
|
||||
# We have to use --no-frozen-lockfile due to CKEditor patches
|
||||
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
|
||||
|
||||
FROM node:22.18.0-alpine
|
||||
FROM node:22.19.0-alpine
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache su-exec shadow
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:22.18.0-alpine AS builder
|
||||
FROM node:22.19.0-alpine AS builder
|
||||
RUN corepack enable
|
||||
|
||||
# Install native dependencies since we might be building cross-platform.
|
||||
@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
|
||||
# We have to use --no-frozen-lockfile due to CKEditor patches
|
||||
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
|
||||
|
||||
FROM node:22.18.0-alpine
|
||||
FROM node:22.19.0-alpine
|
||||
# Create a non-root user with configurable UID/GID
|
||||
ARG USER=trilium
|
||||
ARG UID=1001
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:22.18.0-bullseye-slim AS builder
|
||||
FROM node:22.19.0-bullseye-slim AS builder
|
||||
RUN corepack enable
|
||||
|
||||
# Install native dependencies since we might be building cross-platform.
|
||||
@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
|
||||
# We have to use --no-frozen-lockfile due to CKEditor patches
|
||||
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
|
||||
|
||||
FROM node:22.18.0-bullseye-slim
|
||||
FROM node:22.19.0-bullseye-slim
|
||||
# Create a non-root user with configurable UID/GID
|
||||
ARG USER=trilium
|
||||
ARG UID=1001
|
||||
|
@ -3,11 +3,38 @@
|
||||
"version": "0.98.1",
|
||||
"description": "The server-side component of TriliumNext, which exposes the client via the web, allows for sync and provides a REST API for both internal and external use.",
|
||||
"private": true,
|
||||
"main": "./src/main.ts",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development TRILIUM_ENV=dev TRILIUM_DATA_DIR=data TRILIUM_RESOURCE_DIR=src tsx watch --ignore '../client/node_modules/.vite-temp' ./src/main.ts",
|
||||
"start-no-dir": "cross-env NODE_ENV=development TRILIUM_ENV=dev TRILIUM_RESOURCE_DIR=src tsx watch --ignore '../client/node_modules/.vite-temp' ./src/main.ts",
|
||||
"edit-integration-db": "cross-env NODE_ENV=development TRILIUM_PORT=8086 TRILIUM_ENV=dev TRILIUM_DATA_DIR=spec/db TRILIUM_INTEGRATION_TEST=edit TRILIUM_RESOURCE_DIR=src tsx watch --ignore '../client/node_modules/.vite-temp' ./src/main.ts",
|
||||
"build": "tsx scripts/build.ts",
|
||||
"package": "pnpm build && bash scripts/build-server.sh",
|
||||
"test": "vitest",
|
||||
"test-build": "vitest --config vitest.build.config.mts",
|
||||
"start-prod": "cross-env TRILIUM_DATA_DIR=data pnpm start-prod-no-dir",
|
||||
"start-prod-no-dir": "pnpm build && cross-env TRILIUM_ENV=production TRILIUM_PORT=8082 node dist/main.cjs",
|
||||
"circular-deps": "dpdm -T src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular",
|
||||
"docker-build-debian": "pnpm build && docker build . -t triliumnext-debian -f Dockerfile",
|
||||
"docker-build-alpine": "pnpm build && docker build . -t triliumnext-alpine -f Dockerfile.alpine",
|
||||
"docker-build-rootless-debian": "pnpm build && docker build . -t triliumnext-rootless-debian -f Dockerfile.rootless",
|
||||
"docker-build-rootless-alpine": "pnpm build && docker build . -t triliumnext-rootless-alpine -f Dockerfile.alpine.rootless",
|
||||
"docker-start-debian": "pnpm docker-build-debian && docker run -p 8081:8080 triliumnext-debian",
|
||||
"docker-start-alpine": "pnpm docker-build-alpine && docker run -p 8081:8080 triliumnext-alpine",
|
||||
"docker-start-rootless-debian": "pnpm docker-build-rootless-debian && docker run -p 8081:8080 triliumnext-rootless-debian",
|
||||
"docker-start-rootless-alpine": "pnpm docker-build-rootless-alpine && docker run -p 8081:8080 triliumnext-rootless-alpine"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "12.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.61.0",
|
||||
"@braintree/sanitize-url": "7.1.1",
|
||||
"@electron/remote": "2.1.3",
|
||||
"@preact/preset-vite": "2.10.2",
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/express-partial-content": "workspace:*",
|
||||
"@triliumnext/turndown-plugin-gfm": "workspace:*",
|
||||
"@types/archiver": "6.0.3",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/cls-hooked": "4.3.9",
|
||||
@ -38,16 +65,11 @@
|
||||
"@types/turndown": "5.0.5",
|
||||
"@types/ws": "8.18.1",
|
||||
"@types/xml2js": "0.4.14",
|
||||
"express-http-proxy": "2.1.1",
|
||||
"@anthropic-ai/sdk": "0.60.0",
|
||||
"@braintree/sanitize-url": "7.1.1",
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/express-partial-content": "workspace:*",
|
||||
"@triliumnext/turndown-plugin-gfm": "workspace:*",
|
||||
"archiver": "7.0.1",
|
||||
"async-mutex": "0.5.0",
|
||||
"axios": "1.11.0",
|
||||
"bindings": "1.5.0",
|
||||
"bootstrap": "5.3.8",
|
||||
"chardet": "2.1.0",
|
||||
"cheerio": "1.1.2",
|
||||
"chokidar": "4.0.3",
|
||||
@ -55,7 +77,7 @@
|
||||
"compression": "1.8.1",
|
||||
"cookie-parser": "1.4.7",
|
||||
"csrf-csrf": "3.2.2",
|
||||
"dayjs": "1.11.14",
|
||||
"dayjs": "1.11.18",
|
||||
"debounce": "2.2.0",
|
||||
"debug": "4.4.1",
|
||||
"ejs": "3.1.10",
|
||||
@ -64,8 +86,9 @@
|
||||
"electron-window-state": "5.0.3",
|
||||
"escape-html": "1.0.3",
|
||||
"express": "5.1.0",
|
||||
"express-http-proxy": "2.1.1",
|
||||
"express-openid-connect": "^2.17.1",
|
||||
"express-rate-limit": "8.0.1",
|
||||
"express-rate-limit": "8.1.0",
|
||||
"express-session": "1.18.2",
|
||||
"file-uri-to-path": "2.0.0",
|
||||
"fs-extra": "11.3.1",
|
||||
@ -74,7 +97,7 @@
|
||||
"html2plaintext": "2.1.4",
|
||||
"http-proxy-agent": "7.0.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"i18next": "25.4.2",
|
||||
"i18next": "25.5.2",
|
||||
"i18next-fs-backend": "2.6.0",
|
||||
"image-type": "6.0.0",
|
||||
"ini": "5.0.0",
|
||||
@ -105,272 +128,9 @@
|
||||
"tmp": "0.2.5",
|
||||
"turndown": "7.2.1",
|
||||
"unescape": "1.0.1",
|
||||
"vite": "^7.1.3",
|
||||
"ws": "8.18.3",
|
||||
"xml2js": "0.6.2",
|
||||
"yauzl": "3.2.0"
|
||||
},
|
||||
"nx": {
|
||||
"name": "server",
|
||||
"implicitDependencies": [
|
||||
"share-theme"
|
||||
],
|
||||
"targets": {
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": [
|
||||
"client"
|
||||
],
|
||||
"target": "serve"
|
||||
},
|
||||
"build-without-client"
|
||||
],
|
||||
"continuous": true,
|
||||
"options": {
|
||||
"buildTarget": "server:build-without-client:development",
|
||||
"runBuildTargetDependencies": false
|
||||
}
|
||||
},
|
||||
"serve-nodir": {
|
||||
"executor": "@nx/js:node",
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": [
|
||||
"client"
|
||||
],
|
||||
"target": "serve"
|
||||
},
|
||||
"build-without-client"
|
||||
],
|
||||
"continuous": true,
|
||||
"options": {
|
||||
"buildTarget": "server:build-without-client:development",
|
||||
"runBuildTargetDependencies": false
|
||||
}
|
||||
},
|
||||
"edit-integration-db": {
|
||||
"executor": "@nx/js:node",
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": [
|
||||
"client"
|
||||
],
|
||||
"target": "serve"
|
||||
},
|
||||
"build-without-client"
|
||||
],
|
||||
"continuous": true,
|
||||
"options": {
|
||||
"buildTarget": "server:build-without-client:development",
|
||||
"runBuildTargetDependencies": false
|
||||
}
|
||||
},
|
||||
"package": {
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"command": "bash apps/server/scripts/build-server.sh"
|
||||
},
|
||||
"start-prod": {
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"command": "node apps/server/dist/main.cjs"
|
||||
},
|
||||
"docker-build": {
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}"
|
||||
},
|
||||
"executor": "nx:run-commands",
|
||||
"defaultConfiguration": "alpine",
|
||||
"configurations": {
|
||||
"debian": {
|
||||
"command": "docker build . -t triliumnext-debian -f Dockerfile"
|
||||
},
|
||||
"alpine": {
|
||||
"command": "docker build . -t triliumnext-alpine -f Dockerfile.alpine"
|
||||
},
|
||||
"rootless-debian": {
|
||||
"command": "docker build . -t triliumnext-rootless-debian -f Dockerfile.rootless"
|
||||
},
|
||||
"rootless-alpine": {
|
||||
"command": "docker build . -t triliumnext-rootless-alpine -f Dockerfile.alpine.rootless"
|
||||
}
|
||||
}
|
||||
},
|
||||
"docker-start": {
|
||||
"dependsOn": [
|
||||
"docker-build"
|
||||
],
|
||||
"executor": "nx:run-commands",
|
||||
"defaultConfiguration": "alpine",
|
||||
"configurations": {
|
||||
"debian": {
|
||||
"command": "docker run -p 8081:8080 triliumnext-debian"
|
||||
},
|
||||
"alpine": {
|
||||
"command": "docker run -p 8081:8080 triliumnext-alpine"
|
||||
},
|
||||
"rootless-debian": {
|
||||
"command": "docker run -p 8081:8080 triliumnext-rootless-debian"
|
||||
},
|
||||
"rootless-alpine": {
|
||||
"command": "docker run -p 8081:8080 triliumnext-rootless-alpine"
|
||||
}
|
||||
}
|
||||
},
|
||||
"build-without-client": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"main": "apps/server/src/main.ts",
|
||||
"outputPath": "apps/server/dist",
|
||||
"outputFileName": "main.js",
|
||||
"tsConfig": "apps/server/tsconfig.app.json",
|
||||
"platform": "node",
|
||||
"format": [
|
||||
"cjs"
|
||||
],
|
||||
"esbuildOptions": {
|
||||
"loader": {
|
||||
".css": "text",
|
||||
".ejs": "text"
|
||||
}
|
||||
},
|
||||
"declarationRootDir": "apps/server/src",
|
||||
"minify": false,
|
||||
"sourcemap": true,
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/src/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "packages/share-theme/src/templates",
|
||||
"output": "share-theme/templates"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"dependsOn": [
|
||||
"^build",
|
||||
"client:build"
|
||||
],
|
||||
"defaultConfiguration": "production",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"minify": true,
|
||||
"sourcemap": false
|
||||
},
|
||||
"development": {
|
||||
"minify": false,
|
||||
"sourcemap": true
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"main": "apps/server/src/main.ts",
|
||||
"outputPath": "apps/server/dist",
|
||||
"tsConfig": "apps/server/tsconfig.app.json",
|
||||
"platform": "node",
|
||||
"external": [
|
||||
"electron",
|
||||
"@electron/remote",
|
||||
"better-sqlite3",
|
||||
"./xhr-sync-worker.js"
|
||||
],
|
||||
"format": [
|
||||
"cjs"
|
||||
],
|
||||
"declarationRootDir": "apps/server/src",
|
||||
"thirdParty": true,
|
||||
"declaration": false,
|
||||
"esbuildOptions": {
|
||||
"splitting": false,
|
||||
"loader": {
|
||||
".css": "text",
|
||||
".ejs": "text"
|
||||
}
|
||||
},
|
||||
"additionalEntryPoints": [
|
||||
"apps/server/src/docker_healthcheck.ts"
|
||||
],
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/src/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "packages/share-theme/src/templates",
|
||||
"output": "share-theme/templates"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/client/dist",
|
||||
"output": "public",
|
||||
"ignore": [
|
||||
"webpack-stats.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/node_modules/better-sqlite3",
|
||||
"output": "node_modules/better-sqlite3"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/node_modules/bindings",
|
||||
"output": "node_modules/bindings"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/server/node_modules/file-uri-to-path",
|
||||
"output": "node_modules/file-uri-to-path"
|
||||
},
|
||||
{
|
||||
"glob": "xhr-sync-worker.js",
|
||||
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
|
||||
"output": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"test-build": {
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"command": "vitest --config {projectRoot}/vitest.build.config.mts"
|
||||
},
|
||||
"circular-deps": {
|
||||
"command": "pnpx dpdm -T {projectRoot}/src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./src/*": "./src/*",
|
||||
".": {
|
||||
"development": "./src/main.ts",
|
||||
"types": "./dist/main.d.ts",
|
||||
"import": "./dist/main.js",
|
||||
"default": "./dist/main.js"
|
||||
}
|
||||
},
|
||||
"types": "./dist/main.d.ts",
|
||||
"module": "./dist/main.js",
|
||||
"main": "./dist/main.js"
|
||||
}
|
||||
}
|
22
apps/server/scripts/build.ts
Normal file
22
apps/server/scripts/build.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import BuildHelper from "../../../scripts/build-utils";
|
||||
|
||||
const build = new BuildHelper("apps/server");
|
||||
|
||||
async function main() {
|
||||
await build.buildBackend([ "src/main.ts", "src/docker_healthcheck.ts" ])
|
||||
|
||||
// Copy assets
|
||||
build.copy("src/assets", "assets/");
|
||||
build.copy("/packages/share-theme/src/templates", "share-theme/templates/");
|
||||
|
||||
// Copy node modules dependencies
|
||||
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path" ]);
|
||||
build.copy("/node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js", "xhr-sync-worker.js");
|
||||
build.copy("/node_modules/ckeditor5/dist/ckeditor5-content.css", "ckeditor5-content.css");
|
||||
|
||||
// Integrate the client.
|
||||
build.triggerBuildAndCopyTo("apps/client", "public/");
|
||||
build.deleteFromOutput("public/webpack-stats.json");
|
||||
}
|
||||
|
||||
main();
|
@ -30,5 +30,5 @@ describe("etapi/import", () => {
|
||||
.expect(201);
|
||||
expect(response.body.note.title).toStrictEqual("Journal");
|
||||
expect(response.body.branch.parentNoteId).toStrictEqual("root");
|
||||
});
|
||||
}, 10_000);
|
||||
});
|
||||
|
@ -74,7 +74,10 @@
|
||||
"zoom-out": "Pomniejsz",
|
||||
"zoom-in": "Powiększ",
|
||||
"print-active-note": "Drukuj aktywną notatkę",
|
||||
"toggle-full-screen": "Przełącz pełny ekran"
|
||||
"toggle-full-screen": "Przełącz pełny ekran",
|
||||
"cut-into-note": "Wycina zaznaczony tekst i tworzy podrzędną notatkę z tym tekstem",
|
||||
"edit-readonly-note": "Edytuj notatkę tylko do odczytu",
|
||||
"other": "Inne"
|
||||
},
|
||||
"keyboard_action_names": {
|
||||
"zoom-in": "Powiększ",
|
||||
|
@ -65,6 +65,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
|
||||
"monthlyBackupEnabled",
|
||||
"motionEnabled",
|
||||
"shadowsEnabled",
|
||||
"smoothScrollEnabled",
|
||||
"backdropEffectsEnabled",
|
||||
"maxContentWidth",
|
||||
"compressImages",
|
||||
|
@ -3,7 +3,6 @@ import path from "path";
|
||||
import express from "express";
|
||||
import { getResourceDir, isDev } from "../services/utils.js";
|
||||
import type serveStatic from "serve-static";
|
||||
import proxy from "express-http-proxy";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOptions<express.Response<unknown, Record<string, unknown>>>) => {
|
||||
@ -17,17 +16,22 @@ const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOp
|
||||
};
|
||||
|
||||
async function register(app: express.Application) {
|
||||
const srcRoot = path.join(__dirname, "..");
|
||||
const srcRoot = path.join(__dirname, "..", "..");
|
||||
const resourceDir = getResourceDir();
|
||||
|
||||
if (isDev) {
|
||||
const publicUrl = process.env.TRILIUM_PUBLIC_SERVER;
|
||||
if (!publicUrl) {
|
||||
throw new Error("Missing TRILIUM_PUBLIC_SERVER");
|
||||
}
|
||||
app.use("/" + assetUrlFragment + `/@fs`, proxy(publicUrl, {
|
||||
proxyReqPathResolver: (req) => "/" + assetUrlFragment + `/@fs` + req.url
|
||||
}));
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
const { createServer: createViteServer } = await import("vite");
|
||||
const vite = await createViteServer({
|
||||
server: { middlewareMode: true },
|
||||
appType: "custom",
|
||||
cacheDir: path.join(srcRoot, "../../.cache/vite"),
|
||||
base: `/${assetUrlFragment}/`,
|
||||
root: path.join(srcRoot, "../client")
|
||||
});
|
||||
app.use(`/${assetUrlFragment}/`, (req, res, next) => {
|
||||
req.url = `/${assetUrlFragment}` + req.url;
|
||||
vite.middlewares(req, res, next);
|
||||
});
|
||||
} else {
|
||||
const publicDir = path.join(resourceDir, "public");
|
||||
if (!existsSync(publicDir)) {
|
||||
|
@ -5,7 +5,7 @@ import attributeService from "../services/attributes.js";
|
||||
import config from "../services/config.js";
|
||||
import optionService from "../services/options.js";
|
||||
import log from "../services/log.js";
|
||||
import { isDev, isElectron } from "../services/utils.js";
|
||||
import { isDev, isElectron, isWindows11 } from "../services/utils.js";
|
||||
import protectedSessionService from "../services/protected_session.js";
|
||||
import packageJson from "../../package.json" with { type: "json" };
|
||||
import assetPath from "../services/asset_path.js";
|
||||
@ -42,7 +42,7 @@ function index(req: Request, res: Response) {
|
||||
platform: process.platform,
|
||||
isElectron,
|
||||
hasNativeTitleBar: isElectron && options.nativeTitleBarVisible === "true",
|
||||
hasBackgroundEffects: isElectron && options.backgroundEffects === "true",
|
||||
hasBackgroundEffects: isElectron && isWindows11 && options.backgroundEffects === "true",
|
||||
mainFontSize: parseInt(options.mainFontSize),
|
||||
treeFontSize: parseInt(options.treeFontSize),
|
||||
detailFontSize: parseInt(options.detailFontSize),
|
||||
|
@ -1,3 +1,4 @@
|
||||
import assetPath from "./asset_path.js";
|
||||
import { isDev } from "./utils.js";
|
||||
|
||||
export default assetPath + "/src";
|
||||
export default isDev ? assetPath : assetPath + "/src";
|
||||
|
@ -1,7 +1,14 @@
|
||||
import packageJson from "../../package.json" with { type: "json" };
|
||||
import { isDev } from "./utils";
|
||||
|
||||
/**
|
||||
* The URL prefix for assets (e.g. `assets/v1.2.3`).
|
||||
*/
|
||||
export const assetUrlFragment = `assets/v${packageJson.version}`;
|
||||
const assetPath = isDev ? `http://localhost:4200/${assetUrlFragment}` : assetUrlFragment;
|
||||
|
||||
/**
|
||||
* Similar to the {@link assetUrlFragment}, but on dev mode it also contains the `/src` suffix.
|
||||
*/
|
||||
const assetPath = isDev ? assetUrlFragment + "/src" : assetUrlFragment;
|
||||
|
||||
export default assetPath;
|
||||
|
@ -6,7 +6,7 @@ import path from "path";
|
||||
import mimeTypes from "mime-types";
|
||||
import mdService from "./markdown.js";
|
||||
import packageInfo from "../../../package.json" with { type: "json" };
|
||||
import { getContentDisposition, escapeHtml, getResourceDir } from "../utils.js";
|
||||
import { getContentDisposition, escapeHtml, getResourceDir, isDev } from "../utils.js";
|
||||
import protectedSessionService from "../protected_session.js";
|
||||
import sanitize from "sanitize-filename";
|
||||
import fs from "fs";
|
||||
@ -21,7 +21,6 @@ import type AttributeMeta from "../meta/attribute_meta.js";
|
||||
import type BBranch from "../../becca/entities/bbranch.js";
|
||||
import type { Response } from "express";
|
||||
import type { NoteMetaFile } from "../meta/note_meta.js";
|
||||
import cssContent from "@triliumnext/ckeditor5/content.css";
|
||||
|
||||
type RewriteLinksFn = (content: string, noteMeta: NoteMeta) => string;
|
||||
|
||||
@ -515,7 +514,11 @@ ${markdownContent}`;
|
||||
return;
|
||||
}
|
||||
|
||||
archive.append(cssContent, { name: cssMeta.dataFileName });
|
||||
const cssFile = isDev
|
||||
? path.join(__dirname, "../../../../../node_modules/ckeditor5/dist/ckeditor5-content.css")
|
||||
: path.join(getResourceDir(), "ckeditor5-content.css");
|
||||
|
||||
archive.append(fs.readFileSync(cssFile, "utf-8"), { name: cssMeta.dataFileName });
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -155,7 +155,7 @@ const defaultOptions: DefaultOption[] = [
|
||||
{ name: "motionEnabled", value: "true", isSynced: false },
|
||||
{ name: "shadowsEnabled", value: "true", isSynced: false },
|
||||
{ name: "backdropEffectsEnabled", value: "true", isSynced: false },
|
||||
|
||||
{ name: "smoothScrollEnabled", value: "true", isSynced: false },
|
||||
|
||||
// Internationalization
|
||||
{ name: "locale", value: "en", isSynced: true },
|
||||
|
@ -1,4 +1,3 @@
|
||||
import log from "./log.js";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
@ -10,7 +9,7 @@ const ELECTRON_APP_ROOT_DIR = path.resolve(RESOURCE_DIR, "../..");
|
||||
const DB_INIT_DIR = path.resolve(RESOURCE_DIR, "db");
|
||||
|
||||
if (!fs.existsSync(DB_INIT_DIR)) {
|
||||
log.error(`Could not find DB initialization directory: ${DB_INIT_DIR}`);
|
||||
console.error(`Could not find DB initialization directory: ${DB_INIT_DIR}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,10 @@ import becca_loader from "../becca/becca_loader.js";
|
||||
import entity_changes from "./entity_changes.js";
|
||||
import config from "./config.js";
|
||||
|
||||
const dbOpts: Database.Options = {
|
||||
nativeBinding: process.env.BETTERSQLITE3_NATIVE_PATH || undefined
|
||||
};
|
||||
|
||||
let dbConnection: DatabaseType = buildDatabase();
|
||||
let statementCache: Record<string, Statement> = {};
|
||||
|
||||
@ -23,15 +27,18 @@ function buildDatabase() {
|
||||
if (process.env.TRILIUM_INTEGRATION_TEST === "memory") {
|
||||
return buildIntegrationTestDatabase();
|
||||
} else if (process.env.TRILIUM_INTEGRATION_TEST === "memory-no-store") {
|
||||
return new Database(":memory:");
|
||||
return new Database(":memory:", dbOpts);
|
||||
}
|
||||
|
||||
return new Database(dataDir.DOCUMENT_PATH, { readonly: config.General.readOnly });
|
||||
return new Database(dataDir.DOCUMENT_PATH, {
|
||||
...dbOpts,
|
||||
readonly: config.General.readOnly
|
||||
});
|
||||
}
|
||||
|
||||
function buildIntegrationTestDatabase(dbPath?: string) {
|
||||
const dbBuffer = fs.readFileSync(dbPath ?? dataDir.DOCUMENT_PATH);
|
||||
return new Database(dbBuffer);
|
||||
return new Database(dbBuffer, dbOpts);
|
||||
}
|
||||
|
||||
function rebuildIntegrationTestDatabase(dbPath?: string) {
|
||||
|
@ -29,12 +29,21 @@ function getTrayIconPath() {
|
||||
name = "icon-color";
|
||||
}
|
||||
|
||||
return path.resolve(path.join(getResourceDir(), "assets", "images", "tray", `${name}.png`));
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return path.join(__dirname, "../../../desktop/src/assets/images/tray", `${name}.png`);
|
||||
} else {
|
||||
return path.resolve(path.join(getResourceDir(), "assets", "images", "tray", `${name}.png`));
|
||||
}
|
||||
}
|
||||
|
||||
function getIconPath(name: string) {
|
||||
const suffix = !isMac && electron.nativeTheme.shouldUseDarkColors ? "-inverted" : "";
|
||||
return path.resolve(path.join(getResourceDir(), "assets", "images", "tray", `${name}Template${suffix}.png`));
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return path.join(__dirname, "../../../desktop/src/assets/images/tray", `${name}Template${suffix}.png`);
|
||||
} else {
|
||||
return path.resolve(path.join(getResourceDir(), "assets", "images", "tray", `${name}Template${suffix}.png`));
|
||||
}
|
||||
}
|
||||
|
||||
function registerVisibilityListener(window: BrowserWindow) {
|
||||
|
@ -12,6 +12,9 @@ import path from "path";
|
||||
import type NoteMeta from "./meta/note_meta.js";
|
||||
import log from "./log.js";
|
||||
import { t } from "i18next";
|
||||
import { release as osRelease } from "os";
|
||||
|
||||
const osVersion = osRelease().split('.').map(Number);
|
||||
|
||||
const randtoken = generator({ source: "crypto" });
|
||||
|
||||
@ -19,6 +22,8 @@ export const isMac = process.platform === "darwin";
|
||||
|
||||
export const isWindows = process.platform === "win32";
|
||||
|
||||
export const isWindows11 = isWindows && osVersion[0] === 10 && osVersion[2] >= 22000;
|
||||
|
||||
export const isElectron = !!process.versions["electron"];
|
||||
|
||||
export const isDev = !!(process.env.TRILIUM_ENV && process.env.TRILIUM_ENV === "dev");
|
||||
|
@ -224,7 +224,7 @@ function getWindowExtraOpts() {
|
||||
}
|
||||
|
||||
async function configureWebContents(webContents: WebContents, spellcheckEnabled: boolean) {
|
||||
const remoteMain = (await import("@electron/remote/main/index.js")).default;
|
||||
const remoteMain = (await import("@electron/remote/main/index.js"));
|
||||
remoteMain.enable(webContents);
|
||||
|
||||
webContents.setWindowOpenHandler((details) => {
|
||||
@ -257,7 +257,11 @@ async function configureWebContents(webContents: WebContents, spellcheckEnabled:
|
||||
}
|
||||
|
||||
function getIcon() {
|
||||
return path.join(RESOURCE_DIR, "../public/assets/icon.png");
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return path.join(__dirname, "../../../desktop/electron-forge/app-icon/png/256x256-dev.png");
|
||||
} else {
|
||||
return path.join(RESOURCE_DIR, "../public/assets/icon.png");
|
||||
}
|
||||
}
|
||||
|
||||
async function createSetupWindow() {
|
||||
|
@ -18,6 +18,7 @@ import utils, { isDev, safeExtractMessageAndStackFromError } from "../services/u
|
||||
import options from "../services/options.js";
|
||||
import { t } from "i18next";
|
||||
import ejs from "ejs";
|
||||
import { join } from "path";
|
||||
|
||||
function getSharedSubTreeRoot(note: SNote): { note?: SNote; branch?: SBranch } {
|
||||
if (note.noteId === shareRoot.SHARE_ROOT_NOTE_ID) {
|
||||
@ -401,7 +402,9 @@ function register(router: Router) {
|
||||
|
||||
function renderDefault(res: Response<any, Record<string, any>>, template: "page" | "404", opts: any = {}) {
|
||||
// Path is relative to apps/server/dist/assets/views
|
||||
const shareThemePath = `../../share-theme/templates/${template}.ejs`;
|
||||
const shareThemePath = process.env.NODE_ENV === "development"
|
||||
? join(__dirname, `../../../../packages/share-theme/src/templates/${template}.ejs`)
|
||||
: `../../share-theme/templates/${template}.ejs`;
|
||||
res.render(shareThemePath, opts);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user