diff --git a/.github/workflows/main-docker.yml b/.github/workflows/main-docker.yml
new file mode 100644
index 000000000..70a3592b1
--- /dev/null
+++ b/.github/workflows/main-docker.yml
@@ -0,0 +1,152 @@
+on:
+ push:
+ branches:
+ - "develop"
+ - "feature/update**"
+ - "feature/server_esm**"
+ paths-ignore:
+ - "docs/**"
+ - "bin/**"
+ tags:
+ - "v*"
+ workflow_dispatch:
+
+env:
+ GHCR_REGISTRY: ghcr.io
+ DOCKERHUB_REGISTRY: docker.io
+ IMAGE_NAME: ${{ github.repository }}
+ TEST_TAG: triliumnext/notes:test
+ PLATFORMS: linux/amd64,linux/arm64,linux/arm/v7
+
+jobs:
+ test_docker:
+ name: Check Docker build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout the repository
+ uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Set up node & dependencies
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: "npm"
+
+ - run: npm ci
+
+ - name: Run the TypeScript build
+ run: npx tsc
+
+ - name: Create server-package.json
+ run: cat package.json | grep -v electron > server-package.json
+
+ - name: Build and export to Docker
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ load: true
+ tags: ${{ env.TEST_TAG }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+
+ - name: Run the container in the background
+ run: docker run -d --rm --name trilium_local ${{ env.TEST_TAG }}
+
+ - name: Wait for the healthchecks to pass
+ uses: stringbean/docker-healthcheck-action@v1
+ with:
+ container: trilium_local
+ wait-time: 50
+ require-status: running
+ require-healthy: true
+
+ build_docker:
+ name: Build Docker images
+ runs-on: ubuntu-latest
+ needs:
+ - test_docker
+ permissions:
+ contents: read
+ packages: write
+ attestations: write
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+ - name: Extract metadata (tags, labels) for GHCR image
+ id: ghcr-meta
+ uses: docker/metadata-action@v4
+ with:
+ images: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=ref,event=branch
+ type=ref,event=tag
+ type=sha
+ - name: Extract metadata (tags, labels) for DockerHub image
+ id: dh-meta
+ uses: docker/metadata-action@v4
+ with:
+ images: ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=ref,event=branch
+ type=ref,event=tag
+ type=sha
+ - name: Set up node & dependencies
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: "npm"
+ - run: npm ci
+ - name: Run the TypeScript build
+ run: npx tsc
+ - name: Create server-package.json
+ run: cat package.json | grep -v electron > server-package.json
+ - name: Log in to the GHCR container registry
+ uses: docker/login-action@v2
+ with:
+ registry: ${{ env.GHCR_REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - uses: docker/setup-buildx-action@v3
+ - name: Build and push container image to GHCR
+ uses: docker/build-push-action@v6
+ id: ghcr-push
+ with:
+ context: .
+ platforms: ${{ env.PLATFORMS }}
+ push: true
+ tags: ${{ steps.ghcr-meta.outputs.tags }}
+ labels: ${{ steps.ghcr-meta.outputs.labels }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ - name: Generate and push artifact attestation to GHCR
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-name: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME}}
+ subject-digest: ${{ steps.ghcr-push.outputs.digest }}
+ push-to-registry: true
+ - name: Log in to the DockerHub container registry
+ uses: docker/login-action@v2
+ with:
+ registry: ${{ env.DOCKERHUB_REGISTRY }}
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Build and push image to DockerHub
+ uses: docker/build-push-action@v6
+ id: dh-push
+ with:
+ context: .
+ platforms: ${{ env.PLATFORMS }}
+ push: true
+ tags: ${{ steps.dh-meta.outputs.tags }}
+ labels: ${{ steps.dh-meta.outputs.labels }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ - name: Generate and push artifact attestation to DockerHub
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-name: ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME}}
+ subject-digest: ${{ steps.dh-push.outputs.digest }}
+ push-to-registry: true
\ No newline at end of file
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ae322f7a4..66eff0d1e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -8,17 +8,15 @@ on:
paths-ignore:
- "docs/**"
- "bin/**"
+ - ".github/workflows/main-docker.yml"
+ tags:
+ - "v*"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
-env:
- GHCR_REGISTRY: ghcr.io
- DOCKERHUB_REGISTRY: docker.io
- IMAGE_NAME: ${{ github.repository }}
-
jobs:
build_darwin:
name: Build macOS (x86_64, arm64)
@@ -141,79 +139,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: TriliumNext Notes for Windows (Setup)
- path: out/make/squirrel.windows/x64/*.exe
- build_docker:
- name: Build Docker images
- runs-on: ubuntu-latest
- permissions:
- contents: read
- packages: write
- attestations: write
- id-token: write
- steps:
- - uses: actions/checkout@v4
- - name: Extract metadata (tags, labels) for GHCR image
- id: ghcr-meta
- uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
- with:
- images: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}
- - name: Extract metadata (tags, labels) for DockerHub image
- id: dh-meta
- uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
- with:
- images: ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }}
- - name: Set up node & dependencies
- uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: "npm"
- - run: npm ci
- - name: Run the TypeScript build
- run: npx tsc
- - name: Create server-package.json
- run: cat package.json | grep -v electron > server-package.json
- - name: Log in to the GHCR container registry
- uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
- with:
- registry: ${{ env.GHCR_REGISTRY }}
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
- - uses: docker/setup-buildx-action@v3
- - name: Build and push container image to GHCR
- uses: docker/build-push-action@v6
- id: ghcr-push
- with:
- context: .
- push: true
- tags: ${{ steps.ghcr-meta.outputs.tags }}
- labels: ${{ steps.ghcr-meta.outputs.labels }}
- cache-from: type=gha
- cache-to: type=gha,mode=max
- - name: Generate and push artifact attestation to GHCR
- uses: actions/attest-build-provenance@v1
- with:
- subject-name: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME}}
- subject-digest: ${{ steps.ghcr-push.outputs.digest }}
- push-to-registry: true
- - name: Log in to the DockerHub container registry
- uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
- with:
- registry: ${{ env.DOCKERHUB_REGISTRY }}
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- - name: Build and push image to DockerHub
- uses: docker/build-push-action@v6
- id: dh-push
- with:
- context: .
- push: true
- tags: ${{ steps.dh-meta.outputs.tags }}
- labels: ${{ steps.dh-meta.outputs.labels }}
- cache-from: type=gha
- cache-to: type=gha,mode=max
- - name: Generate and push artifact attestation to DockerHub
- uses: actions/attest-build-provenance@v1
- with:
- subject-name: ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME}}
- subject-digest: ${{ steps.dh-push.outputs.digest }}
- push-to-registry: true
+ path: out/make/squirrel.windows/x64/*.exe
\ No newline at end of file
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
new file mode 100644
index 000000000..467190be6
--- /dev/null
+++ b/.github/workflows/playwright.yml
@@ -0,0 +1,27 @@
+name: Playwright Tests
+on:
+ push:
+ branches: [ main, master ]
+ pull_request:
+ branches: [ main, master ]
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+ - name: Install dependencies
+ run: npm ci
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run Playwright tests
+ run: npx playwright test
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
diff --git a/.gitignore b/.gitignore
index c87703b2b..42c96c6d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,14 @@ build/
src/public/app-dist/
npm-debug.log
yarn-error.log
+
*.db
+!integration-tests/db/document.db
+integration-tests/db/log
+integration-tests/db/sessions
+integration-tests/db/backup
+integration-tests/db/session_secret.txt
+
config.ini
cert.key
cert.crt
@@ -18,8 +25,11 @@ tmp/
out/
-images/app-icons/png/16x16.png
-images/app-icons/png/32x32.png
images/app-icons/png/512x512.png
images/app-icons/png/1024x1024.png
-images/app-icons/mac/*.png
\ No newline at end of file
+images/app-icons/mac/*.png
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
+/playwright/.auth/
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 8087af0af..2dc3ccef4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,8 +1,8 @@
# !!! Don't try to build this Dockerfile directly, run it through bin/build-docker.sh script !!!
-FROM node:20.15.1-alpine
+FROM node:20.15.1-bullseye-slim
# Configure system dependencies
-RUN apk add --no-cache --virtual .build-dependencies \
+RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf \
automake \
g++ \
@@ -11,7 +11,9 @@ RUN apk add --no-cache --virtual .build-dependencies \
make \
nasm \
libpng-dev \
- python3
+ python3 \
+ gosu \
+ && rm -rf /var/lib/apt/lists/*
# Create app directory
WORKDIR /usr/src/app
@@ -24,27 +26,41 @@ COPY server-package.json package.json
# Copy TypeScript build artifacts into the original directory structure.
RUN ls
RUN cp -R build/src/* src/.
+
+# Copy the healthcheck
+RUN cp build/docker_healthcheck.js .
+RUN rm docker_healthcheck.ts
+
RUN rm -r build
# Install app dependencies
-RUN set -x \
- && npm install \
- && apk del .build-dependencies \
- && npm run webpack \
- && npm prune --omit=dev \
- && cp src/public/app/share.js src/public/app-dist/. \
- && cp -r src/public/app/doc_notes src/public/app-dist/. \
- && rm -rf src/public/app \
- && rm src/services/asset_path.ts
+RUN set -x
+RUN npm install
+RUN apt-get purge -y --auto-remove \
+ autoconf \
+ automake \
+ g++ \
+ gcc \
+ libtool \
+ make \
+ nasm \
+ libpng-dev \
+ python3 \
+ && rm -rf /var/lib/apt/lists/*
+RUN npm run webpack
+RUN npm prune --omit=dev
+RUN cp src/public/app/share.js src/public/app-dist/.
+RUN cp -r src/public/app/doc_notes src/public/app-dist/.
+RUN rm -rf src/public/app
+RUN rm src/services/asset_path.ts
# Some setup tools need to be kept
-RUN apk add --no-cache su-exec shadow
-
-# Add application user and setup proper volume permissions
-RUN adduser -s /bin/false node; exit 0
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ gosu \
+ && rm -rf /var/lib/apt/lists/*
# Start the application
EXPOSE 8080
CMD [ "./start-docker.sh" ]
-HEALTHCHECK --start-period=10s CMD exec su-exec node node docker_healthcheck.js
+HEALTHCHECK --start-period=10s CMD exec gosu node node docker_healthcheck.js
\ No newline at end of file
diff --git a/README.md b/README.md
index 75e8d0b93..f5bb6f4e4 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[English](https://github.com/TriliumNext/Notes/blob/master/README.md) | [Chinese](https://github.com/TriliumNext/Notes/blob/master/README-ZH_CN.md) | [Russian](https://github.com/TriliumNext/Notes/blob/master/README.ru.md) | [Japanese](https://github.com/TriliumNext/Notes/blob/master/README.ja.md) | [Italian](https://github.com/TriliumNext/Notes/blob/master/README.it.md)
-TriliumNext Notes is a hierarchical note taking application with focus on building large personal knowledge bases.
+TriliumNext Notes is an open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for quick overview:
@@ -14,18 +14,13 @@ See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for q
## 💬 Discuss with us
-Feel free to join our official discussions and community. We are focused on the development on Trilium, and would love to hear what features, suggestions, or issues you may have!
+Feel free to join our official conversations. We would love to hear what features, suggestions, or issues you may have!
- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous discussions)
+ - The `General` Matrix room is also bridged to [XMPP](xmpp:discuss@trilium.thisgreat.party?join)
- [Github Discussions](https://github.com/TriliumNext/Notes/discussions) (For Asynchronous discussions)
- [Wiki](https://triliumnext.github.io/Docs/) (For common how-to questions and user guides)
-The two rooms linked above are mirrored, so you can use either XMPP or Matrix, from any client you prefer, on pretty much any platform under the sun!
-
-### Unofficial Communities
-
-[Trilium Rocks](https://discord.gg/aqdX9mXX4r)
-
## 🎁 Features
* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://triliumnext.github.io/Docs/Wiki/cloning-notes)
@@ -48,28 +43,38 @@ The two rooms linked above are mirrored, so you can use either XMPP or Matrix, f
* [Evernote](https://triliumnext.github.io/Docs/Wiki/evernote-import) and [Markdown import & export](https://triliumnext.github.io/Docs/Wiki/markdown)
* [Web Clipper](https://triliumnext.github.io/Docs/Wiki/web-clipper) for easy saving of web content
-✨ Check out the following third-party resources for more TriliumNext related goodies:
+✨ Check out the following third-party resources/communities for more TriliumNext related goodies:
- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more.
-## 🏗 Builds
+## 🏗 Installation
-Trilium is provided as either desktop application (Linux and Windows) or web application hosted on your server (Linux). Mac OS desktop build is available, but it is [unsupported](https://triliumnext.github.io/Docs/Wiki/faq#mac-os-support).
+### Desktop
-* If you want to use TriliumNext on the desktop, download binary release for your platform from [latest release](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run ```trilium``` executable.
-* If you want to install TriliumNext on your own server, follow [this page](https://triliumnext.github.io/Docs/Wiki/server-installation).
- * Currently only recent versions of Chrome and Firefox are supported (tested) browsers.
+To use TriliumNext on your desktop machine (Linux, MacOS, and Windows) you have a few options:
-TriliumNext will also provided as a Flatpak:
+* Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the ```trilium``` executable.
+* Access TriliumNext via the web interface of a server installation (see below)
+ * Currently only the latest versions of Chrome & Firefox are supported (and tested).
+* (Coming Soon) TriliumNext will also be provided as a Flatpak
-
+### Mobile
+
+To use TriliumNext on a mobile device:
+
+* Use a mobile web browser to access the mobile interface of a server installation (see below)
+* Use of a mobile app is not yet supported ([see here](https://github.com/TriliumNext/Notes/issues/72)) to track mobile improvements.
+
+### Server
+
+To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/notes)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
## 📝 Documentation
[See wiki for complete list of documentation pages.](https://triliumnext.github.io/Docs)
-You can also read [Patterns of personal knowledge base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge) to get some inspiration on how you might use Trilium.
+You can also read [Patterns of personal knowledge base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge) to get some inspiration on how you might use TriliumNext.
## 💻 Contribute
@@ -82,7 +87,7 @@ npm run start-server
## 👏 Shoutouts
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - best WYSIWYG editor on the market, very interactive and listening team
-* [FancyTree](https://github.com/mar10/fancytree) - very feature rich tree library without real competition. Trilium Notes would not be the same without it.
+* [FancyTree](https://github.com/mar10/fancytree) - very feature rich tree library without real competition. TriliumNext Notes would not be the same without it.
* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with support for huge amount of languages
* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library without competition. Used in [relation maps](https://triliumnext.github.io/Docs/Wiki/Relation-map) and [link maps](https://triliumnext.github.io/Docs/Wiki/Link-map)
diff --git a/bin/build-docker.sh b/bin/build-docker.sh
index 9d614eb2b..a765930db 100755
--- a/bin/build-docker.sh
+++ b/bin/build-docker.sh
@@ -10,8 +10,8 @@ cat package.json | grep -v electron > server-package.json
echo "Compiling typescript..."
npx tsc
-sudo docker build -t zadam/trilium:$VERSION --network host -t zadam/trilium:$SERIES .
+sudo docker build -t triliumnext/notes:$VERSION --network host -t triliumnext/notes:$SERIES .
if [[ $VERSION != *"beta"* ]]; then
- sudo docker tag zadam/trilium:$VERSION zadam/trilium:latest
+ sudo docker tag triliumnext/notes:$VERSION triliumnext/notes:latest
fi
diff --git a/bin/release.sh b/bin/release.sh
index 0d4ef905d..30dd0c462 100755
--- a/bin/release.sh
+++ b/bin/release.sh
@@ -69,7 +69,7 @@ if [ ! -z "$GITHUB_CLI_AUTH_TOKEN" ]; then
echo "$GITHUB_CLI_AUTH_TOKEN" | gh auth login --with-token
fi
-gh release create "$TAG" \
+gh release create -d "$TAG" \
--title "$TAG release" \
--notes "" \
$EXTRA \
diff --git a/docker-compose.yml b/docker-compose.yml
index 6798574ac..d6f5a3c65 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,16 +1,21 @@
# Running `docker-compose up` will create/use the "trilium-data" directory in the user home
# Run `TRILIUM_DATA_DIR=/path/of/your/choice docker-compose up` to set a different directory
-version: '2.1'
+# To run in the background, use `docker-compose up -d`
services:
trilium:
- image: zadam/trilium
- restart: always
+ # Optionally, replace `latest` with a version tag like `v0.90.3`
+ # Using `latest` may cause unintended updates to the container
+ image: triliumnext/notes:latest
+ # Restart the container unless it was stopped by the user
+ restart: unless-stopped
environment:
- TRILIUM_DATA_DIR=/home/node/trilium-data
ports:
- - "8080:8080"
+ # By default, Trilium will be available at http://localhost:8080
+ # It will also be accessible at http://:8080
+ # You might want to limit this with something like Docker Networks, reverse proxies, or firewall rules, such as UFW
+ - '8080:8080'
volumes:
+ # Unless TRILIUM_DATA_DIR is set, the data will be stored in the "trilium-data" directory in the home directory.
+ # This can also be changed with by replacing the line below with `- /path/of/your/choice:/home/node/trilium-data
- ${TRILIUM_DATA_DIR:-~/trilium-data}:/home/node/trilium-data
-
-volumes:
- trilium:
diff --git a/docker_healthcheck.js b/docker_healthcheck.ts
similarity index 72%
rename from docker_healthcheck.js
rename to docker_healthcheck.ts
index 9761aebe2..c11c853a4 100755
--- a/docker_healthcheck.js
+++ b/docker_healthcheck.ts
@@ -1,7 +1,7 @@
-const http = require("http");
-const ini = require("ini");
-const fs = require("fs");
-const dataDir = require('./src/services/data_dir');
+import http from "http";
+import ini from "ini";
+import fs from "fs";
+import dataDir from './src/services/data_dir.js';
const config = ini.parse(fs.readFileSync(dataDir.CONFIG_INI_PATH, 'utf-8'));
if (config.Network.https) {
@@ -10,12 +10,12 @@ if (config.Network.https) {
process.exit(0);
}
-const port = require('./src/services/port');
-const host = require('./src/services/host');
+import port from './src/services/port.js';
+import host from './src/services/host.js';
-const options = { timeout: 2000 };
+const options: http.RequestOptions = { timeout: 2000 };
-const callback = res => {
+const callback: (res: http.IncomingMessage) => void = res => {
console.log(`STATUS: ${res.statusCode}`);
if (res.statusCode === 200) {
process.exit(0);
diff --git a/images/app-icons/png/16x16.png b/images/app-icons/png/16x16.png
new file mode 100644
index 000000000..4645fe056
Binary files /dev/null and b/images/app-icons/png/16x16.png differ
diff --git a/images/app-icons/png/32x32.png b/images/app-icons/png/32x32.png
new file mode 100644
index 000000000..dbe57df0e
Binary files /dev/null and b/images/app-icons/png/32x32.png differ
diff --git a/integration-tests/auth.setup.ts b/integration-tests/auth.setup.ts
new file mode 100644
index 000000000..ed27ca648
--- /dev/null
+++ b/integration-tests/auth.setup.ts
@@ -0,0 +1,17 @@
+import { test as setup, expect } from '@playwright/test';
+
+const authFile = 'playwright/.auth/user.json';
+
+const ROOT_URL = "http://localhost:8082";
+const LOGIN_PASSWORD = "demo1234";
+
+// Reference: https://playwright.dev/docs/auth#basic-shared-account-in-all-tests
+
+setup("authenticate", async ({ page }) => {
+ await page.goto(ROOT_URL);
+ await expect(page).toHaveURL(`${ROOT_URL}/login`);
+
+ await page.getByRole("textbox", { name: "Password" }).fill(LOGIN_PASSWORD);
+ await page.getByRole("button", { name: "Login"}).click();
+ await page.context().storageState({ path: authFile });
+});
\ No newline at end of file
diff --git a/integration-tests/db/document.db b/integration-tests/db/document.db
new file mode 100644
index 000000000..4857edcd9
Binary files /dev/null and b/integration-tests/db/document.db differ
diff --git a/integration-tests/duplicate.spec.ts b/integration-tests/duplicate.spec.ts
new file mode 100644
index 000000000..7decbd7f0
--- /dev/null
+++ b/integration-tests/duplicate.spec.ts
@@ -0,0 +1,9 @@
+import { test, expect } from '@playwright/test';
+
+test("Can duplicate note with broken links", async ({ page }) => {
+ await page.goto(`http://localhost:8082/#2VammGGdG6Ie`);
+ await page.locator('.tree-wrapper .fancytree-active').getByText('Note map').click({ button: 'right' });
+ await page.getByText('Duplicate subtree').click();
+ await expect(page.locator(".toast-body")).toBeHidden();
+ await expect(page.locator('.tree-wrapper').getByText('Note map (dup)')).toBeVisible();
+});
\ No newline at end of file
diff --git a/integration-tests/example.disabled.ts b/integration-tests/example.disabled.ts
new file mode 100644
index 000000000..54a906a4e
--- /dev/null
+++ b/integration-tests/example.disabled.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects page to have a heading with the name of Installation.
+ await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
+});
diff --git a/integration-tests/katex.disabled.ts b/integration-tests/katex.disabled.ts
new file mode 100644
index 000000000..c1ce0d9d7
--- /dev/null
+++ b/integration-tests/katex.disabled.ts
@@ -0,0 +1,18 @@
+import { test, expect } from '@playwright/test';
+
+const ROOT_URL = "http://localhost:8080";
+const LOGIN_PASSWORD = "eliandoran";
+
+test("Can insert equations", async ({ page }) => {
+ await page.setDefaultTimeout(60_000);
+ await page.setDefaultNavigationTimeout(60_000);
+
+ // Create a new note
+ // await page.locator("button.button-widget.bx-file-blank")
+ // .click();
+
+ const activeNote = page.locator(".component.note-split:visible");
+ const noteContent = activeNote
+ .locator(".note-detail-editable-text-editor")
+ await noteContent.press("Ctrl+M");
+});
\ No newline at end of file
diff --git a/integration-tests/update_check.spec.ts b/integration-tests/update_check.spec.ts
new file mode 100644
index 000000000..207395296
--- /dev/null
+++ b/integration-tests/update_check.spec.ts
@@ -0,0 +1,12 @@
+import { test, expect } from '@playwright/test';
+
+const expectedVersion = "0.90.3";
+
+test("Displays update badge when there is a version available", async ({ page }) => {
+ await page.goto("http://localhost:8080");
+ await page.getByRole('button', { name: '' }).click();
+ await page.getByText(`Version ${expectedVersion} is available,`).click();
+
+ const page1 = await page.waitForEvent('popup');
+ expect(page1.url()).toBe(`https://github.com/TriliumNext/Notes/releases/tag/v${expectedVersion}`);
+});
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 125c22d49..b633603ad 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "trilium",
- "version": "0.90.2-beta",
+ "version": "0.90.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "trilium",
- "version": "0.90.2-beta",
+ "version": "0.90.3",
"license": "AGPL-3.0-only",
"dependencies": {
"@braintree/sanitize-url": "^7.1.0",
@@ -96,6 +96,7 @@
"@electron-forge/maker-squirrel": "^6.4.2",
"@electron-forge/maker-zip": "^7.4.0",
"@electron-forge/plugin-auto-unpack-natives": "^6.4.2",
+ "@playwright/test": "^1.46.0",
"@types/archiver": "^6.0.2",
"@types/better-sqlite3": "^7.6.9",
"@types/cls-hooked": "^4.3.8",
@@ -113,6 +114,7 @@
"@types/jsdom": "^21.1.6",
"@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.11",
+ "@types/node": "^22.1.0",
"@types/safe-compare": "^1.1.2",
"@types/sanitize-html": "^2.11.0",
"@types/sax": "^1.2.7",
@@ -3590,6 +3592,21 @@
"node": ">=14"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz",
+ "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==",
+ "dev": true,
+ "dependencies": {
+ "playwright": "1.46.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
@@ -4007,11 +4024,11 @@
}
},
"node_modules/@types/node": {
- "version": "20.14.11",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
- "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
+ "version": "22.1.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz",
+ "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==",
"dependencies": {
- "undici-types": "~5.26.4"
+ "undici-types": "~6.13.0"
}
},
"node_modules/@types/qs": {
@@ -7931,6 +7948,19 @@
"node": ">=6 <7 || >=8"
}
},
+ "node_modules/electron/node_modules/@types/node": {
+ "version": "20.14.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz",
+ "integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/electron/node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
"node_modules/elkjs": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.2.tgz",
@@ -9502,6 +9532,19 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"devOptional": true
},
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -13919,6 +13962,36 @@
"node": ">=8"
}
},
+ "node_modules/playwright": {
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
+ "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
+ "dev": true,
+ "dependencies": {
+ "playwright-core": "1.46.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
+ "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/plist": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
@@ -16310,6 +16383,20 @@
"fsevents": "~2.3.3"
}
},
+ "node_modules/tsx/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -16414,9 +16501,9 @@
"dev": true
},
"node_modules/undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz",
+ "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg=="
},
"node_modules/unescape": {
"version": "1.0.1",
diff --git a/package.json b/package.json
index 95d4599ca..d2a3f1d76 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "TriliumNext Notes",
"description": "Build your personal knowledge base with TriliumNext Notes",
- "version": "0.90.2-beta",
+ "version": "0.90.3",
"license": "AGPL-3.0-only",
"main": "./dist/electron.js",
"author": {
@@ -42,7 +42,9 @@
"package-electron": "electron-forge package",
"prepare-dist": "rimraf ./dist && tsc && tsx ./bin/copy-dist.ts",
"update-build-info": "tsx bin/update-build-info.ts",
- "errors": "tsc --watch --noEmit"
+ "errors": "tsc --watch --noEmit",
+ "integration-edit-db": "cross-env TRILIUM_INTEGRATION_TEST=edit TRILIUM_PORT=8081 TRILIUM_DATA_DIR=./integration-tests/db nodemon src/www.ts",
+ "integration-mem-db": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_DATA_DIR=./integration-tests/db nodemon src/www.ts"
},
"dependencies": {
"@braintree/sanitize-url": "^7.1.0",
@@ -129,6 +131,7 @@
"@electron-forge/maker-squirrel": "^6.4.2",
"@electron-forge/maker-zip": "^7.4.0",
"@electron-forge/plugin-auto-unpack-natives": "^6.4.2",
+ "@playwright/test": "^1.46.0",
"@types/archiver": "^6.0.2",
"@types/better-sqlite3": "^7.6.9",
"@types/cls-hooked": "^4.3.8",
@@ -146,6 +149,7 @@
"@types/jsdom": "^21.1.6",
"@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.11",
+ "@types/node": "^22.1.0",
"@types/safe-compare": "^1.1.2",
"@types/sanitize-html": "^2.11.0",
"@types/sax": "^1.2.7",
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 000000000..12420d3a5
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,77 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// import dotenv from 'dotenv';
+// dotenv.config({ path: path.resolve(__dirname, '.env') });
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './integration-tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: "setup",
+ testMatch: /.*\.setup\.ts/
+ },
+
+ {
+ name: "firefox",
+ use: {
+ ...devices[ "Desktop Firefox" ],
+ storageState: "playwright/.auth/user.json"
+ },
+ dependencies: [ "setup" ]
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/src/becca/entities/abstract_becca_entity.ts b/src/becca/entities/abstract_becca_entity.ts
index 0016b2a07..cd169c0b2 100644
--- a/src/becca/entities/abstract_becca_entity.ts
+++ b/src/becca/entities/abstract_becca_entity.ts
@@ -34,7 +34,7 @@ abstract class AbstractBeccaEntity> {
isSynced?: boolean;
blobId?: string;
- protected beforeSaving() {
+ protected beforeSaving(opts?: {}) {
const constructorData = (this.constructor as unknown as ConstructorData);
if (!(this as any)[constructorData.primaryKeyName]) {
(this as any)[constructorData.primaryKeyName] = utils.newEntityId();
@@ -101,7 +101,6 @@ abstract class AbstractBeccaEntity> {
/**
* Saves entity - executes SQL, but doesn't commit the transaction on its own
*/
- // TODO: opts not used but called a few times, maybe should be used by derived classes or passed to beforeSaving.
save(opts?: {}): this {
const constructorData = (this.constructor as unknown as ConstructorData);
const entityName = constructorData.entityName;
@@ -109,7 +108,7 @@ abstract class AbstractBeccaEntity> {
const isNewEntity = !(this as any)[primaryKeyName];
- this.beforeSaving();
+ this.beforeSaving(opts);
const pojo = this.getPojoToSave();
diff --git a/src/etapi/etapi.openapi.yaml b/src/etapi/etapi.openapi.yaml
index 61eb1a6cf..b53bfb01c 100644
--- a/src/etapi/etapi.openapi.yaml
+++ b/src/etapi/etapi.openapi.yaml
@@ -48,7 +48,7 @@ paths:
- name: search
in: query
required: true
- description: search query string as described in https://github.com/zadam/trilium/wiki/Search
+ description: search query string as described in https://github.com/TriliumNext/Docs/blob/main/Wiki/search.md
schema:
type: string
examples:
diff --git a/src/public/app/components/entrypoints.js b/src/public/app/components/entrypoints.js
index 6b281911f..6a375d5af 100644
--- a/src/public/app/components/entrypoints.js
+++ b/src/public/app/components/entrypoints.js
@@ -102,7 +102,7 @@ export default class Entrypoints extends Component {
if (utils.isElectron()) {
// standard JS version does not work completely correctly in electron
const webContents = utils.dynamicRequire('@electron/remote').getCurrentWebContents();
- const activeIndex = parseInt(webContents.getActiveIndex());
+ const activeIndex = parseInt(webContents.navigationHistory.getActiveIndex());
webContents.goToIndex(activeIndex - 1);
}
@@ -115,7 +115,7 @@ export default class Entrypoints extends Component {
if (utils.isElectron()) {
// standard JS version does not work completely correctly in electron
const webContents = utils.dynamicRequire('@electron/remote').getCurrentWebContents();
- const activeIndex = parseInt(webContents.getActiveIndex());
+ const activeIndex = parseInt(webContents.navigationHistory.getActiveIndex());
webContents.goToIndex(activeIndex + 1);
}
diff --git a/src/public/app/services/frontend_script_api.js b/src/public/app/services/frontend_script_api.js
index 989a49879..f0e8cc30b 100644
--- a/src/public/app/services/frontend_script_api.js
+++ b/src/public/app/services/frontend_script_api.js
@@ -249,7 +249,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
- * "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
+ * "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/TriliumNext/Docs/blob/main/Wiki/search.md
*
* @method
* @param {string} searchString
@@ -261,7 +261,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
- * "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
+ * "#dateModified =* MONTH AND #log". See full documentation for all options at: https://github.com/TriliumNext/Docs/blob/main/Wiki/search.md
*
* @method
* @param {string} searchString
@@ -558,7 +558,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.getYearNote = dateNotesService.getYearNote;
/**
- * Hoist note in the current tab. See https://github.com/zadam/trilium/wiki/Note-hoisting
+ * Hoist note in the current tab. See https://github.com/TriliumNext/Docs/blob/main/Wiki/note-hoisting.md
*
* @method
* @param {string} noteId - set hoisted note. 'root' will effectively unhoist
diff --git a/src/public/app/services/glob.js b/src/public/app/services/glob.js
index f2ef87128..925feaf82 100644
--- a/src/public/app/services/glob.js
+++ b/src/public/app/services/glob.js
@@ -27,7 +27,7 @@ function setupGlobs() {
window.glob.importMarkdownInline = async () => appContext.triggerCommand("importMarkdownInline");
window.glob.SEARCH_HELP_TEXT = `
- Search tips - also see
+ Search tips - also see
Just enter any text for full text search
diff --git a/src/public/app/services/utils.js b/src/public/app/services/utils.js
index 9e17f1c38..7802ff128 100644
--- a/src/public/app/services/utils.js
+++ b/src/public/app/services/utils.js
@@ -330,7 +330,7 @@ function initHelpDropdown($el) {
initHelpButtons($dropdownMenu);
}
-const wikiBaseUrl = "https://github.com/zadam/trilium/wiki/";
+const wikiBaseUrl = "https://github.com/TriliumNext/Docs/blob/main/Wiki/";
function openHelp($button) {
const helpPage = $button.attr("data-help-page");
diff --git a/src/public/app/widgets/attribute_widgets/attribute_detail.js b/src/public/app/widgets/attribute_widgets/attribute_detail.js
index 0ffac8fd7..fa4dc724d 100644
--- a/src/public/app/widgets/attribute_widgets/attribute_detail.js
+++ b/src/public/app/widgets/attribute_widgets/attribute_detail.js
@@ -211,8 +211,8 @@ const ATTR_HELP = {
"cssClass": "value of this label is then added as CSS class to the node representing given note in the tree. This can be useful for advanced theming. Can be used in template notes.",
"iconClass": "value of this label is added as a CSS class to the icon on the tree which can help visually distinguish the notes in the tree. Example might be bx bx-home - icons are taken from boxicons. Can be used in template notes.",
"pageSize": "number of items per page in note listing",
- "customRequestHandler": 'see Custom request handler',
- "customResourceProvider": 'see Custom request handler',
+ "customRequestHandler": 'see Custom request handler',
+ "customResourceProvider": 'see Custom request handler',
"widget": "marks this note as a custom widget which will be added to the Trilium component tree",
"workspace": "marks this note as a workspace which allows easy hoisting",
"workspaceIconClass": "defines box icon CSS class which will be used in tab when hoisted to this note",
@@ -245,7 +245,7 @@ const ATTR_HELP = {
Log for \${now.format('YYYY-MM-DD HH:mm:ss')}
- See wiki with details, API docs for parentNote and now for details.`,
+ See wiki with details, API docs for parentNote and now for details.`,
"template": "This note will appear in the selection of available template when creating new note",
"toc": "#toc or #toc=show will force the Table of Contents to be shown, #toc=hide will force hiding it. If the label doesn't exist, the global setting is observed",
"color": "defines color of the note in note tree, links etc. Use any valid CSS color value like 'red' or #a13d5f",
diff --git a/src/public/app/widgets/buttons/global_menu.js b/src/public/app/widgets/buttons/global_menu.js
index 7203cb690..ad7806f0e 100644
--- a/src/public/app/widgets/buttons/global_menu.js
+++ b/src/public/app/widgets/buttons/global_menu.js
@@ -337,7 +337,7 @@ export default class GlobalMenuWidget extends BasicWidget {
}
downloadLatestVersionCommand() {
- window.open("https://github.com/zadam/trilium/releases/latest");
+ window.open("https://github.com/TriliumNext/Notes/releases/latest");
}
activeContextChangedEvent() {
diff --git a/src/public/app/widgets/dialogs/add_link.js b/src/public/app/widgets/dialogs/add_link.js
index 117bc052f..0c7772fc8 100644
--- a/src/public/app/widgets/dialogs/add_link.js
+++ b/src/public/app/widgets/dialogs/add_link.js
@@ -10,7 +10,7 @@ const TPL = `