diff --git a/.devcontainer/buildAppImage.sh b/.devcontainer/buildAppImage.sh new file mode 100644 index 000000000..efb963d5a --- /dev/null +++ b/.devcontainer/buildAppImage.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +make clean + +# To reproduce https://github.com/menloresearch/jan/pull/5463 +TAURI_TOOLKIT_PATH="${XDG_CACHE_HOME:-$HOME/.cache}/tauri" +mkdir -p "$TAURI_TOOLKIT_PATH" +wget https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20250213-2/linuxdeploy-x86_64.AppImage -O "$TAURI_TOOLKIT_PATH/linuxdeploy-x86_64.AppImage" +chmod +x "$TAURI_TOOLKIT_PATH/linuxdeploy-x86_64.AppImage" + +jq '.bundle.resources = ["resources/pre-install/**/*"] | .bundle.externalBin = ["binaries/cortex-server", "resources/bin/uv"]' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json +mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json + +make build-tauri + +cp ./src-tauri/resources/bin/bun ./src-tauri/target/release/bundle/appimage/Jan.AppDir/usr/bin/bun +mkdir -p ./src-tauri/target/release/bundle/appimage/Jan.AppDir/usr/lib/Jan/binaries/engines +cp -f ./src-tauri/binaries/deps/*.so* ./src-tauri/target/release/bundle/appimage/Jan.AppDir/usr/lib/Jan/binaries/ +cp -f ./src-tauri/binaries/*.so* ./src-tauri/target/release/bundle/appimage/Jan.AppDir/usr/lib/Jan/binaries/ +cp -rf ./src-tauri/binaries/engines ./src-tauri/target/release/bundle/appimage/Jan.AppDir/usr/lib/Jan/binaries/ +APP_IMAGE=./src-tauri/target/release/bundle/appimage/$(ls ./src-tauri/target/release/bundle/appimage/ | grep AppImage | head -1) +echo $APP_IMAGE +rm -f $APP_IMAGE +/opt/bin/appimagetool ./src-tauri/target/release/bundle/appimage/Jan.AppDir $APP_IMAGE \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index db1eed38d..f42828b31 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,4 +1,20 @@ { - "name": "jan", - "image": "node:20" + "name": "Jan", + "image": "mcr.microsoft.com/devcontainers/base:jammy", + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "20" + }, + "ghcr.io/devcontainers/features/rust:1": {}, + "ghcr.io/devcontainers-extra/features/corepack:1": {} + }, + + "postCreateCommand": "./.devcontainer/postCreateCommand.sh", + + // appimagekit requires fuse to package appimage, to use fuse in the container you need to enable it on the host + "runArgs": [ + "--device", "/dev/fuse", + "--cap-add=SYS_ADMIN", + "--security-opt", "apparmor:unconfined" + ] } diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh new file mode 100755 index 000000000..79fb4de1c --- /dev/null +++ b/.devcontainer/postCreateCommand.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# install tauri prerequisites + xdg-utils for xdg-open + libfuse2 for using appimagekit + +sudo apt update +sudo apt install -yqq libwebkit2gtk-4.1-dev \ + build-essential \ + curl \ + wget \ + file \ + libxdo-dev \ + libssl-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + xdg-utils \ + libfuse2 + +sudo mkdir -p /opt/bin +sudo wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /opt/bin/appimagetool +sudo chmod +x /opt/bin/appimagetool \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/1-bug-report.yml similarity index 94% rename from .github/ISSUE_TEMPLATE/bug_report.yml rename to .github/ISSUE_TEMPLATE/1-bug-report.yml index be0f34319..b429b670a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/1-bug-report.yml @@ -1,8 +1,8 @@ name: "\U0001F41B Bug Report" +title: 'bug: ' description: "If something isn't working as expected \U0001F914" -labels: [ "type: bug" ] -title: 'bug: [DESCRIPTION]' - +type: "Bug" +projects: ["menloresearch/30"] body: - type: input validations: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/2-feature-request.yml similarity index 87% rename from .github/ISSUE_TEMPLATE/feature_request.yml rename to .github/ISSUE_TEMPLATE/2-feature-request.yml index b1a10e856..4c1f1decd 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/2-feature-request.yml @@ -1,7 +1,7 @@ name: "\U0001F680 Feature Request" +title: 'idea: ' description: "Suggest an idea for this project \U0001F63B!" -title: 'idea: [DESCRIPTION]' -labels: 'type: feature request' +projects: ["menloresearch/30"] body: - type: textarea validations: @@ -17,4 +17,4 @@ body: required: true attributes: label: "Feature Idea" - description: "Describe what you want instead. Examples are welcome!" \ No newline at end of file + description: "Describe what you want instead. Examples are welcome!" diff --git a/.github/ISSUE_TEMPLATE/3-epic.yml b/.github/ISSUE_TEMPLATE/3-epic.yml new file mode 100644 index 000000000..9993db4f8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3-epic.yml @@ -0,0 +1,22 @@ +name: "\U0001F31F Epic" +description: "Major building block that advances Jan's goals" +title: 'epic: ' +type: "Epic" +projects: ["menloresearch/30"] +body: + - type: textarea + validations: + required: true + attributes: + label: "Goal" + description: "Describe the epic's objective and expected impact" + - type: textarea + validations: + required: true + attributes: + label: "Tasklist" + description: "List the key tasks required to complete this epic" + - type: textarea + attributes: + label: "Out of scope" + description: "List items that are explicitly excluded from this epic" diff --git a/.github/ISSUE_TEMPLATE/4-goal.yml b/.github/ISSUE_TEMPLATE/4-goal.yml new file mode 100644 index 000000000..3ed2acf53 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4-goal.yml @@ -0,0 +1,23 @@ +name: "\U0001F3AF Goal" +description: "External communication of Jan's roadmap and objectives" +title: 'goal: ' +type: "Goal" +projects: ["menloresearch/30"] +assignees: freelerobot +body: + - type: textarea + validations: + required: true + attributes: + label: "Goal" + description: "Describe the objective and desired outcome" + - type: textarea + validations: + required: true + attributes: + label: "Tasklist" + description: "List the specific tasks needed to achieve this goal" + - type: textarea + attributes: + label: "Out of scope" + description: "List items that are explicitly excluded from this goal" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2d49f0d6e..550b73916 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,5 @@ -## To encourage contributors to use issue templates, we don't allow blank issues -blank_issues_enabled: true - +blank_issues_enabled: false contact_links: - - name: "\1F4AC Jan Discussions" - url: "https://github.com/orgs/menloresearch/discussions/categories/q-a" - about: "Get help, discuss features & roadmap, and share your projects" \ No newline at end of file + - name: Jan Discussions + url: https://github.com/orgs/menloresearch/discussions/categories/q-a + about: Get help, discuss features & roadmap, and share your projects diff --git a/.github/ISSUE_TEMPLATE/model_request.yml b/.github/ISSUE_TEMPLATE/model_request.yml deleted file mode 100644 index 7f7c4f63c..000000000 --- a/.github/ISSUE_TEMPLATE/model_request.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: "\U0001F929 Model Request" -description: "Request a new model to be compiled" -title: 'feat: [DESCRIPTION]' -labels: 'type: model request' -body: - - type: markdown - attributes: - value: "**Tip:** Download any HuggingFace model in app ([see guides](https://jan.ai/docs/models/manage-models#add-models)). Use this form for unsupported models only." - - type: textarea - validations: - required: true - attributes: - label: "Model Requests" - description: "If applicable, include the source URL, licenses, and any other relevant information" - - type: checkboxes - attributes: - label: "Which formats?" - options: - - label: GGUF (llama.cpp) - - label: TensorRT (TensorRT-LLM) - - label: ONNX (Onnx Runtime) diff --git a/.github/ISSUE_TEMPLATE/roadmap.md b/.github/ISSUE_TEMPLATE/roadmap.md deleted file mode 100644 index 7947f31bf..000000000 --- a/.github/ISSUE_TEMPLATE/roadmap.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Roadmap -about: Plan Roadmap items with subtasks -title: 'roadmap: ' -labels: 'type: planning' -assignees: '' - ---- - -## Goal - -## Tasklist - -### Frontend -- [ ] link to janhq/jan epics - -**Bugs** -- [ ] link to bugs - -### Backend -- [ ] link to janhq/cortex.cpp epics - -**Bugs** -- [ ] link to bug issues - -### Infra -- [ ] link to infra issues - -### Administrative / Management -- [ ] link to infra issues - -### Marketing - -------- -## Resources diff --git a/.github/workflows/issues.yaml b/.github/workflows/issues.yaml new file mode 100644 index 000000000..732f66862 --- /dev/null +++ b/.github/workflows/issues.yaml @@ -0,0 +1,16 @@ +name: Adds all issues to project board + +on: + issues: + types: + - opened + +jobs: + add-to-project: + name: Add issue to project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@v1.0.2 + with: + project-url: https://github.com/orgs/${{ vars.ORG_NAME }}/projects/${{ vars.JAN_PROJECT_NUMBER }} + github-token: ${{ secrets.AUTO_ADD_TICKET_PAT }} diff --git a/.github/workflows/jan-electron-linter-and-test.yml b/.github/workflows/jan-electron-linter-and-test.yml index 6c2842c30..e895baf06 100644 --- a/.github/workflows/jan-electron-linter-and-test.yml +++ b/.github/workflows/jan-electron-linter-and-test.yml @@ -154,6 +154,10 @@ jobs: with: node-version: 20 + - name: Install tauri-driver dependencies + run: | + cargo install tauri-driver --locked + # Clean cache, continue on error - name: 'Cleanup cache' shell: powershell @@ -192,11 +196,25 @@ jobs: with: fetch-depth: 0 + - uses: actions/cache@v4 # v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('**/yarn.lock') }} + - name: Installing node uses: actions/setup-node@v3 with: node-version: 20 + - name: Install tauri-driver dependencies + run: | + cargo install tauri-driver --locked + # Clean cache, continue on error - name: 'Cleanup cache' shell: powershell @@ -221,6 +239,20 @@ jobs: # run: | # make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}" + - name: Install Prerequisites + shell: 'powershell' + # https://github.com/actions/runner-images/issues/9538 + # https://github.com/microsoft/playwright/pull/30009/files + # https://github.com/tauri-apps/wry/issues/1268 + # Evergreen Bootstrapper + # The Bootstrapper is a tiny installer that downloads + # the Evergreen Runtime matching device architecture + # and installs it locally. + # https://developer.microsoft.com/en-us/microsoft-edge/webview2/consumer/?form=MA13LH + run: | + Invoke-WebRequest -Uri 'https://go.microsoft.com/fwlink/p/?LinkId=2124703' -OutFile 'setup.exe' + Start-Process -FilePath setup.exe -Verb RunAs -Wait + - name: Linter and test shell: powershell run: | @@ -240,6 +272,10 @@ jobs: with: node-version: 20 + - name: Install tauri-driver dependencies + run: | + cargo install tauri-driver --locked + # Clean cache, continue on error - name: 'Cleanup cache' shell: powershell @@ -272,6 +308,15 @@ jobs: with: node-version: 20 + - name: Install Tauri dependencies + run: | + sudo apt update + sudo apt install -y libglib2.0-dev libatk1.0-dev libpango1.0-dev libgtk-3-dev libsoup-3.0-dev libwebkit2gtk-4.1-dev librsvg2-dev libfuse2 webkit2gtk-driver + + - name: Install tauri-driver dependencies + run: | + cargo install tauri-driver --locked + - name: 'Cleanup cache' continue-on-error: true run: | @@ -368,6 +413,15 @@ jobs: with: node-version: 20 + - name: Install Tauri dependencies + run: | + sudo apt update + sudo apt install -y libglib2.0-dev libatk1.0-dev libpango1.0-dev libgtk-3-dev libsoup-3.0-dev libwebkit2gtk-4.1-dev librsvg2-dev libfuse2 webkit2gtk-driver + + - name: Install tauri-driver dependencies + run: | + cargo install tauri-driver --locked + - name: 'Cleanup cache' continue-on-error: true run: | diff --git a/.github/workflows/publish-npm-core.yml b/.github/workflows/publish-npm-core.yml index 403deb100..f719ab3d4 100644 --- a/.github/workflows/publish-npm-core.yml +++ b/.github/workflows/publish-npm-core.yml @@ -6,7 +6,6 @@ on: workflow_dispatch: jobs: build-and-publish-plugins: - environment: production runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/template-electron-build-linux-x64.yml b/.github/workflows/template-electron-build-linux-x64.yml index 4dfab5c09..cef11cb0f 100644 --- a/.github/workflows/template-electron-build-linux-x64.yml +++ b/.github/workflows/template-electron-build-linux-x64.yml @@ -43,7 +43,6 @@ jobs: build-linux-x64: if: inputs.public_provider == 'github' || inputs.public_provider == 'none' runs-on: ubuntu-latest - environment: production permissions: contents: write steps: @@ -76,7 +75,6 @@ jobs: cp electron/icons_dev/jan-nightly-tray@2x.png electron/icons/icon-tray@2x.png cp electron/icons_dev/jan-nightly-tray.png electron/icons/icon-tray.png - - name: Installing node uses: actions/setup-node@v1 with: @@ -131,7 +129,7 @@ jobs: env: VERSION_TAG: ${{ inputs.new_version }} - - name: Build and publish app to aws s3 r2 or github artifactory + - name: Build and publish app to aws s3 r2 or github artifactory if: inputs.public_provider != 'github' run: | # check public_provider is true or not @@ -185,4 +183,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: jan-electron-linux-amd64-${{ inputs.new_version }}-AppImage - path: ./electron/dist/*.AppImage \ No newline at end of file + path: ./electron/dist/*.AppImage diff --git a/.github/workflows/template-electron-build-macos.yml b/.github/workflows/template-electron-build-macos.yml index ab9f002cb..8bcdcdf95 100644 --- a/.github/workflows/template-electron-build-macos.yml +++ b/.github/workflows/template-electron-build-macos.yml @@ -53,7 +53,6 @@ jobs: build-macos: if: inputs.public_provider == 'github' || inputs.public_provider == 'none' runs-on: macos-latest - environment: production permissions: contents: write steps: @@ -61,7 +60,7 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ inputs.ref }} - + - name: Replace Icons for Beta Build if: inputs.beta == true && inputs.nightly != true shell: bash @@ -161,7 +160,7 @@ jobs: p12-file-base64: ${{ secrets.CODE_SIGN_P12_BASE64 }} p12-password: ${{ secrets.CODE_SIGN_P12_PASSWORD }} - - name: Build and publish app to aws s3 r2 or github artifactory + - name: Build and publish app to aws s3 r2 or github artifactory if: inputs.public_provider != 'github' run: | # check public_provider is true or not @@ -231,4 +230,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: jan-electron-mac-universal-${{ inputs.new_version }} - path: ./electron/dist/*.dmg \ No newline at end of file + path: ./electron/dist/*.dmg diff --git a/.github/workflows/template-get-update-version.yml b/.github/workflows/template-get-update-version.yml index 70f5eace9..2d992fbec 100644 --- a/.github/workflows/template-get-update-version.yml +++ b/.github/workflows/template-get-update-version.yml @@ -9,7 +9,6 @@ on: jobs: get-update-version: runs-on: ubuntu-latest - environment: production outputs: new_version: ${{ steps.version_update.outputs.new_version }} steps: diff --git a/.github/workflows/template-noti-discord-and-update-url-readme.yml b/.github/workflows/template-noti-discord-and-update-url-readme.yml index eaaee7e50..ce288f541 100644 --- a/.github/workflows/template-noti-discord-and-update-url-readme.yml +++ b/.github/workflows/template-noti-discord-and-update-url-readme.yml @@ -26,7 +26,6 @@ on: jobs: noti-discord-and-update-url-readme: - environment: production runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/template-tauri-build-linux-x64.yml b/.github/workflows/template-tauri-build-linux-x64.yml index 9356c3f28..274aa3662 100644 --- a/.github/workflows/template-tauri-build-linux-x64.yml +++ b/.github/workflows/template-tauri-build-linux-x64.yml @@ -55,7 +55,6 @@ jobs: DEB_SIG: ${{ steps.packageinfo.outputs.DEB_SIG }} APPIMAGE_SIG: ${{ steps.packageinfo.outputs.APPIMAGE_SIG }} APPIMAGE_FILE_NAME: ${{ steps.packageinfo.outputs.APPIMAGE_FILE_NAME }} - environment: production permissions: contents: write steps: @@ -96,7 +95,7 @@ jobs: run: | cargo install ctoml - - name: Install Tauri dependecies + - name: Install Tauri dependencies run: | sudo apt update sudo apt install -y libglib2.0-dev libatk1.0-dev libpango1.0-dev libgtk-3-dev libsoup-3.0-dev libwebkit2gtk-4.1-dev librsvg2-dev libfuse2 diff --git a/.github/workflows/template-tauri-build-macos.yml b/.github/workflows/template-tauri-build-macos.yml index 086e14ad2..038a084fa 100644 --- a/.github/workflows/template-tauri-build-macos.yml +++ b/.github/workflows/template-tauri-build-macos.yml @@ -63,7 +63,6 @@ jobs: outputs: MAC_UNIVERSAL_SIG: ${{ steps.metadata.outputs.MAC_UNIVERSAL_SIG }} TAR_NAME: ${{ steps.metadata.outputs.TAR_NAME }} - environment: production permissions: contents: write steps: diff --git a/.vscode/settings.json b/.vscode/settings.json index 9bf4d12b5..78bf4f0ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,7 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true + "editor.formatOnSave": true, + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer" + } } diff --git a/Makefile b/Makefile index 56b50a9d2..6e69c602d 100644 --- a/Makefile +++ b/Makefile @@ -33,23 +33,14 @@ dev: install-and-build yarn copy:lib yarn dev -# Deprecated soon -dev-tauri: install-and-build - yarn install:cortex - yarn download:bin - yarn copy:lib - yarn dev:tauri - # Linting lint: install-and-build yarn lint # Testing test: lint - # yarn build:test - # yarn test:coverage - # Need e2e setup for tauri backend yarn test + yarn test:e2e # Builds and publishes the app build-and-publish: install-and-build diff --git a/core/package.json b/core/package.json index 1e6291375..1654bf74d 100644 --- a/core/package.json +++ b/core/package.json @@ -36,11 +36,11 @@ "pacote": "^21.0.0", "request": "^2.88.2", "request-progress": "^3.0.0", - "rimraf": "^3.0.2", + "rimraf": "^6.0.1", "rolldown": "1.0.0-beta.1", "ts-jest": "^29.2.5", "tslib": "^2.6.2", - "typescript": "^5.3.3" + "typescript": "^5.8.3" }, "dependencies": { "rxjs": "^7.8.1", diff --git a/docs/package.json b/docs/package.json index fd691a09a..08ff495c0 100644 --- a/docs/package.json +++ b/docs/package.json @@ -27,7 +27,7 @@ "embla-carousel-react": "^8.0.0", "fs": "^0.0.1-security", "gray-matter": "^4.0.3", - "lucide-react": "^0.372.0", + "lucide-react": "^0.522.0", "next": "^14.1.4", "next-seo": "^6.5.0", "next-sitemap": "^4.2.3", diff --git a/docs/yarn.lock b/docs/yarn.lock index dd88120aa..eb57429c8 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -5503,10 +5503,10 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -lucide-react@^0.372.0: - version "0.372.0" - resolved "https://registry.npmjs.org/lucide-react/-/lucide-react-0.372.0.tgz" - integrity sha512-0cKdqmilHXWUwWAWnf6CrrjHD8YaqPMtLrmEHXolZusNTr9epULCsiJwIOHk2q1yFxdEwd96D4zShlAj67UJdA== +lucide-react@^0.522.0: + version "0.522.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.522.0.tgz#c0951dd32936b6a7bcc474a829a251fede0bdfbd" + integrity sha512-jnJbw974yZ7rQHHEFKJOlWAefG3ATSCZHANZxIdx8Rk/16siuwjgA4fBULpXEAWx/RlTs3FzmKW/udWUuO0aRw== lz-string@^1.5.0: version "1.5.0" diff --git a/extensions/assistant-extension/package.json b/extensions/assistant-extension/package.json index 4761aa900..f17d42588 100644 --- a/extensions/assistant-extension/package.json +++ b/extensions/assistant-extension/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "cpx": "^1.5.0", - "rimraf": "^3.0.2", + "rimraf": "^6.0.1", "rolldown": "1.0.0-beta.1", "run-script-os": "^1.1.6", "typescript": "^5.3.3" diff --git a/extensions/conversational-extension/package.json b/extensions/conversational-extension/package.json index abb76e4d0..26ba21b9d 100644 --- a/extensions/conversational-extension/package.json +++ b/extensions/conversational-extension/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "cpx": "^1.5.0", - "rimraf": "^3.0.2", + "rimraf": "^6.0.1", "rolldown": "1.0.0-beta.1", "ts-loader": "^9.5.0", "typescript": "^5.7.2" diff --git a/extensions/download-extension/package.json b/extensions/download-extension/package.json index 1c3f2f174..f15f12bdb 100644 --- a/extensions/download-extension/package.json +++ b/extensions/download-extension/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "cpx": "^1.5.0", - "rimraf": "^3.0.2", + "rimraf": "^6.0.1", "rolldown": "1.0.0-beta.1", "run-script-os": "^1.1.6", "typescript": "5.8.3", diff --git a/mise.toml b/mise.toml index ea6d3e65b..9f6cee5c7 100644 --- a/mise.toml +++ b/mise.toml @@ -24,13 +24,7 @@ run = [ [tasks.install] description = "Install dependencies" depends = ["config-yarn"] -run = ''' -#!/usr/bin/env bash -# Skip install on Windows per Makefile logic -if [[ "$OSTYPE" != "msys" && "$OSTYPE" != "win32" ]]; then - yarn install -fi -''' +run = "yarn install" sources = ['package.json', 'yarn.lock'] outputs = ['node_modules'] diff --git a/package.json b/package.json index 4d9f8382e..7e525f07b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "workspaces": { "packages": [ "core", - "web-app" + "web-app", + "tests-e2e-js" ] }, "scripts": { @@ -13,6 +14,11 @@ "build": "yarn build:web && yarn build:tauri", "test": "yarn workspace @janhq/web-app test", "test:coverage": "yarn workspace @janhq/web-app test", + "test:prepare": "yarn build:icon && yarn copy:lib && yarn copy:assets:tauri && yarn build --no-bundle ", + "test:e2e:linux": "yarn test:prepare && xvfb-run yarn workspace tests-e2-js test", + "test:e2e:win32": "yarn test:prepare && yarn workspace tests-e2-js test", + "test:e2e:darwin": "echo 'E2E tests are not supported on macOS yet due to WebDriver limitations'", + "test:e2e": "run-script-os", "dev:web": "yarn workspace @janhq/web-app dev", "dev:tauri": "CLEAN=true yarn build:icon && yarn copy:assets:tauri && tauri dev", "install:cortex:linux:darwin": "cd src-tauri/binaries && ./download.sh", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c55c8ea4e..7068ffba6 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -19,8 +19,8 @@ tauri-build = { version = "2.0.2", features = [] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4" -tauri = { version = "2.4.0", features = [ "protocol-asset", "macos-private-api", - "test", +tauri = { version = "2.5.0", features = [ "protocol-asset", "macos-private-api", + "test" ] } tauri-plugin-log = "2.0.0-rc" tauri-plugin-shell = "2.2.0" diff --git a/src-tauri/src/core/setup.rs b/src-tauri/src/core/setup.rs index 8d8a3d557..42ee0faa5 100644 --- a/src-tauri/src/core/setup.rs +++ b/src-tauri/src/core/setup.rs @@ -277,7 +277,12 @@ pub fn setup_sidecar(app: &App) -> Result<(), String> { ]); #[cfg(target_os = "windows")] { - let resource_dir = app_handle_for_spawn.path().resource_dir().unwrap(); + let mut resource_dir = app_handle_for_spawn.path().resource_dir().unwrap(); + // If debug + #[cfg(debug_assertions)] + { + resource_dir = resource_dir.join("binaries"); + } let normalized_path = resource_dir.to_string_lossy().replace(r"\\?\", ""); let normalized_pathbuf = PathBuf::from(normalized_path); cmd = cmd.current_dir(normalized_pathbuf); @@ -286,12 +291,12 @@ pub fn setup_sidecar(app: &App) -> Result<(), String> { #[cfg(not(target_os = "windows"))] { cmd = cmd.env("LD_LIBRARY_PATH", { - let current_app_data_dir = app_handle_for_spawn - .path() - .resource_dir() - .unwrap() - .join("binaries"); - let dest = current_app_data_dir.to_str().unwrap(); + let mut resource_dir = app_handle_for_spawn.path().resource_dir().unwrap(); + #[cfg(not(debug_assertions))] + { + resource_dir = resource_dir.join("binaries"); + } + let dest = resource_dir.to_str().unwrap(); let ld_path_env = std::env::var("LD_LIBRARY_PATH").unwrap_or_default(); format!("{}{}{}", ld_path_env, ":", dest) }); diff --git a/src-tauri/tauri.bundle.windows.nsis.template b/src-tauri/tauri.bundle.windows.nsis.template index 2da569bac..e991d62f7 100644 --- a/src-tauri/tauri.bundle.windows.nsis.template +++ b/src-tauri/tauri.bundle.windows.nsis.template @@ -33,7 +33,7 @@ ${StrLoc} !define VERSION "jan_version" !define VERSIONWITHBUILD "jan_build" !define HOMEPAGE "" -!define INSTALLMODE "currentUser" +!define INSTALLMODE "both" !define LICENSE "" !define INSTALLERICON "D:\a\jan\jan\src-tauri\icons\icon.ico" !define SIDEBARIMAGE "" diff --git a/tests-e2e-js/.gitignore b/tests-e2e-js/.gitignore new file mode 100644 index 000000000..1521c8b76 --- /dev/null +++ b/tests-e2e-js/.gitignore @@ -0,0 +1 @@ +dist diff --git a/tests-e2e-js/package.json b/tests-e2e-js/package.json new file mode 100644 index 000000000..e7dc18587 --- /dev/null +++ b/tests-e2e-js/package.json @@ -0,0 +1,23 @@ +{ + "name": "tests-e2-js", + "version": "0.0.0", + "private": true, + "type": "module", + "main": "src/main.ts", + "scripts": { + "build": "tsc", + "test": "node --test --test-force-exit --loader ts-node/esm ./src/main.ts" + }, + "dependencies": { + "@tauri-e2e/selenium": "0.2.2", + "log4js": "^6.9.1", + "selenium-webdriver": "^4.22.0", + "ts-node": "^10.9.2" + }, + "devDependencies": { + "@types/node": "^20.14.9", + "@types/selenium-webdriver": "^4.1.28", + "tsimp": "^2.0.11", + "typescript": "^5.5.2" + } +} diff --git a/tests-e2e-js/src/main.ts b/tests-e2e-js/src/main.ts new file mode 100644 index 000000000..c6e0aeba6 --- /dev/null +++ b/tests-e2e-js/src/main.ts @@ -0,0 +1,51 @@ +import assert from 'node:assert' +import { ChildProcess } from 'node:child_process' +import { afterEach, beforeEach, describe, test } from 'node:test' +import { By, until, WebDriver } from 'selenium-webdriver' +import * as e2e from '@tauri-e2e/selenium' +import { default as log4js } from 'log4js' + +let logger = log4js.getLogger() +logger.level = 'debug' + +process.env.TAURI_WEBDRIVER_LOGLEVEL = 'debug' +process.env.TAURI_WEBDRIVER_BINARY = await e2e.install.PlatformDriver() +process.env.TAURI_SELENIUM_BINARY = '../src-tauri/target/release/Jan.exe' +process.env.SELENIUM_REMOTE_URL = 'http://127.0.0.1:6655' + +//@ts-ignore fuck you javascript +e2e.setLogger(logger) + +describe('Tauri E2E tests', async () => { + let driver: WebDriver + let webDriver: ChildProcess + + beforeEach(async () => { + // Spawn WebDriver process. + webDriver = await e2e.launch.spawnWebDriver() + // wait 1 second + await new Promise((r) => setTimeout(r, 1000)) + // Create driver session. + driver = new e2e.selenium.Builder().build() + // Wait for the body element to be present + // await driver.wait(until.elementLocated({ css: 'body' })) + }) + + afterEach(async () => { + await e2e.selenium.cleanupSession(driver) + e2e.launch.killWebDriver(webDriver) + }) + + test('Find hub', async () => { + const hub = until.elementLocated(By.css('[data-test-id="menu-common:hub"')) + // console.log('GG', hub) + // @ts-ignore + await driver.wait(hub.fn, 120000) + + const menuElement = await driver.findElement({ + css: '[data-test-id="menu-common:hub"]', + }) + assert(menuElement !== null, 'Hub menu element should be available') + await menuElement.isDisplayed() + }) +}) diff --git a/tests-e2e-js/tsconfig.json b/tests-e2e-js/tsconfig.json new file mode 100644 index 000000000..6189f7d38 --- /dev/null +++ b/tests-e2e-js/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "src/*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ], +} diff --git a/web-app/package.json b/web-app/package.json index 0272d1151..340104f00 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -46,7 +46,7 @@ "i18next": "^25.0.1", "katex": "^0.16.22", "lodash.debounce": "^4.0.8", - "lucide-react": "^0.503.0", + "lucide-react": "^0.522.0", "motion": "^12.10.5", "next-themes": "^0.4.6", "posthog-js": "^1.246.0", @@ -88,7 +88,7 @@ "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", "tailwind-merge": "^3.2.0", - "typescript": "~5.7.2", + "typescript": "~5.8.3", "typescript-eslint": "^8.26.1", "vite": "^6.3.0", "vite-plugin-node-polyfills": "^0.23.0", diff --git a/web-app/src/containers/ChatInput.tsx b/web-app/src/containers/ChatInput.tsx index 2d16b2eee..0cecb2bf3 100644 --- a/web-app/src/containers/ChatInput.tsx +++ b/web-app/src/containers/ChatInput.tsx @@ -365,6 +365,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { rows={1} maxRows={10} value={prompt} + data-test-id={'chat-input'} onChange={(e) => { setPrompt(e.target.value) // Count the number of newlines to estimate rows @@ -567,6 +568,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { variant={!prompt.trim() ? null : 'default'} size="icon" disabled={!prompt.trim()} + data-test-id="send-message-button" onClick={() => handleSendMesage(prompt)} > {streamingContent ? ( diff --git a/web-app/src/containers/LeftPanel.tsx b/web-app/src/containers/LeftPanel.tsx index 5d0b1542e..ea4fbafc2 100644 --- a/web-app/src/containers/LeftPanel.tsx +++ b/web-app/src/containers/LeftPanel.tsx @@ -324,6 +324,7 @@ const LeftPanel = () => { )} {isDownloaded ? ( - ) : (