Compare commits
48 Commits
feat/jan-b
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
154301b3ad | ||
|
|
e7b7ac9e94 | ||
|
|
e531eaa4ad | ||
|
|
23b03da714 | ||
|
|
22be93807d | ||
|
|
653ecdb494 | ||
|
|
15c426aefc | ||
|
|
2fa153ac34 | ||
|
|
62bd91a1e1 | ||
|
|
f7e0e790b6 | ||
|
|
c854c54c0c | ||
|
|
a14872666a | ||
|
|
e9f469b623 | ||
|
|
5a016860aa | ||
|
|
c773abb688 | ||
|
|
2561fcd78a | ||
|
|
28ed5e2af2 | ||
|
|
4c5c8e6aed | ||
|
|
f07e43cfe0 | ||
|
|
e46200868e | ||
|
|
147cab94a8 | ||
|
|
2fb956ccaf | ||
|
|
4dee0a4ba1 | ||
|
|
418a48ab39 | ||
|
|
9bc56f6e30 | ||
|
|
f0ca9cce35 | ||
|
|
746dbc632b | ||
|
|
462b05e612 | ||
|
|
946b347f44 | ||
|
|
b23e88f078 | ||
|
|
476fdd6040 | ||
|
|
fa8b3664cb | ||
|
|
8b687619b2 | ||
|
|
176ad07f1d | ||
|
|
7b5060c9be | ||
|
|
584daa9682 | ||
|
|
31f9501d8e | ||
|
|
c096929d8b | ||
|
|
45d57dd34d | ||
|
|
f4066e6e5a | ||
|
|
a2fbce698f | ||
|
|
fc784620e0 | ||
|
|
340042682a | ||
|
|
6dd2d2d6c1 | ||
|
|
7762cea10a | ||
|
|
ff93dc3c5c | ||
|
|
510c4a5188 | ||
|
|
a72c74dbf9 |
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Jan Discussions
|
- name: Jan Discussions
|
||||||
url: https://github.com/orgs/menloresearch/discussions/categories/q-a
|
url: https://github.com/orgs/janhq/discussions/categories/q-a
|
||||||
about: Get help, discuss features & roadmap, and share your projects
|
about: Get help, discuss features & roadmap, and share your projects
|
||||||
|
|||||||
4
.github/workflows/jan-server-web-ci-dev.yml
vendored
4
.github/workflows/jan-server-web-ci-dev.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
build-and-preview:
|
build-and-preview:
|
||||||
runs-on: [ubuntu-24-04-docker]
|
runs-on: [ubuntu-24-04-docker]
|
||||||
env:
|
env:
|
||||||
JAN_API_BASE: "https://api-dev.menlo.ai/v1"
|
MENLO_PLATFORM_BASE_URL: "https://api-dev.jan.ai/v1"
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
contents: write
|
contents: write
|
||||||
@ -52,7 +52,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build docker image
|
- name: Build docker image
|
||||||
run: |
|
run: |
|
||||||
docker build --build-arg JAN_API_BASE=${{ env.JAN_API_BASE }} -t ${{ steps.vars.outputs.FULL_IMAGE }} .
|
docker build --build-arg MENLO_PLATFORM_BASE_URL=${{ env.MENLO_PLATFORM_BASE_URL }} -t ${{ steps.vars.outputs.FULL_IMAGE }} .
|
||||||
|
|
||||||
- name: Push docker image
|
- name: Push docker image
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|||||||
4
.github/workflows/jan-server-web-ci-prod.yml
vendored
4
.github/workflows/jan-server-web-ci-prod.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
deployments: write
|
deployments: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
env:
|
env:
|
||||||
JAN_API_BASE: "https://api.menlo.ai/v1"
|
MENLO_PLATFORM_BASE_URL: "https://api.jan.ai/v1"
|
||||||
GA_MEASUREMENT_ID: "G-YK53MX8M8M"
|
GA_MEASUREMENT_ID: "G-YK53MX8M8M"
|
||||||
CLOUDFLARE_PROJECT_NAME: "jan-server-web"
|
CLOUDFLARE_PROJECT_NAME: "jan-server-web"
|
||||||
steps:
|
steps:
|
||||||
@ -43,7 +43,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: make config-yarn && yarn install && yarn build:core && make build-web-app
|
run: make config-yarn && yarn install && yarn build:core && make build-web-app
|
||||||
env:
|
env:
|
||||||
JAN_API_BASE: ${{ env.JAN_API_BASE }}
|
MENLO_PLATFORM_BASE_URL: ${{ env.MENLO_PLATFORM_BASE_URL }}
|
||||||
GA_MEASUREMENT_ID: ${{ env.GA_MEASUREMENT_ID }}
|
GA_MEASUREMENT_ID: ${{ env.GA_MEASUREMENT_ID }}
|
||||||
|
|
||||||
- name: Publish to Cloudflare Pages Production
|
- name: Publish to Cloudflare Pages Production
|
||||||
|
|||||||
4
.github/workflows/jan-server-web-ci-stag.yml
vendored
4
.github/workflows/jan-server-web-ci-stag.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
build-and-preview:
|
build-and-preview:
|
||||||
runs-on: [ubuntu-24-04-docker]
|
runs-on: [ubuntu-24-04-docker]
|
||||||
env:
|
env:
|
||||||
JAN_API_BASE: "https://api-stag.menlo.ai/v1"
|
MENLO_PLATFORM_BASE_URL: "https://api-stag.jan.ai/v1"
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
contents: write
|
contents: write
|
||||||
@ -52,7 +52,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build docker image
|
- name: Build docker image
|
||||||
run: |
|
run: |
|
||||||
docker build --build-arg JAN_API_BASE=${{ env.JAN_API_BASE }} -t ${{ steps.vars.outputs.FULL_IMAGE }} .
|
docker build --build-arg MENLO_PLATFORM_BASE_URL=${{ env.MENLO_PLATFORM_BASE_URL }} -t ${{ steps.vars.outputs.FULL_IMAGE }} .
|
||||||
|
|
||||||
- name: Push docker image
|
- name: Push docker image
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|||||||
108
.github/workflows/jan-tauri-build-nightly.yaml
vendored
108
.github/workflows/jan-tauri-build-nightly.yaml
vendored
@ -168,62 +168,62 @@ jobs:
|
|||||||
AWS_DEFAULT_REGION: ${{ secrets.DELTA_AWS_REGION }}
|
AWS_DEFAULT_REGION: ${{ secrets.DELTA_AWS_REGION }}
|
||||||
AWS_EC2_METADATA_DISABLED: 'true'
|
AWS_EC2_METADATA_DISABLED: 'true'
|
||||||
|
|
||||||
noti-discord-nightly-and-update-url-readme:
|
# noti-discord-nightly-and-update-url-readme:
|
||||||
needs:
|
# needs:
|
||||||
[
|
# [
|
||||||
build-macos,
|
# build-macos,
|
||||||
build-windows-x64,
|
# build-windows-x64,
|
||||||
build-linux-x64,
|
# build-linux-x64,
|
||||||
get-update-version,
|
# get-update-version,
|
||||||
set-public-provider,
|
# set-public-provider,
|
||||||
sync-temp-to-latest,
|
# sync-temp-to-latest,
|
||||||
]
|
# ]
|
||||||
secrets: inherit
|
# secrets: inherit
|
||||||
if: github.event_name == 'schedule'
|
# if: github.event_name == 'schedule'
|
||||||
uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
|
# uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
|
||||||
with:
|
# with:
|
||||||
ref: refs/heads/dev
|
# ref: refs/heads/dev
|
||||||
build_reason: Nightly
|
# build_reason: Nightly
|
||||||
push_to_branch: dev
|
# push_to_branch: dev
|
||||||
new_version: ${{ needs.get-update-version.outputs.new_version }}
|
# new_version: ${{ needs.get-update-version.outputs.new_version }}
|
||||||
|
|
||||||
noti-discord-pre-release-and-update-url-readme:
|
# noti-discord-pre-release-and-update-url-readme:
|
||||||
needs:
|
# needs:
|
||||||
[
|
# [
|
||||||
build-macos,
|
# build-macos,
|
||||||
build-windows-x64,
|
# build-windows-x64,
|
||||||
build-linux-x64,
|
# build-linux-x64,
|
||||||
get-update-version,
|
# get-update-version,
|
||||||
set-public-provider,
|
# set-public-provider,
|
||||||
sync-temp-to-latest,
|
# sync-temp-to-latest,
|
||||||
]
|
# ]
|
||||||
secrets: inherit
|
# secrets: inherit
|
||||||
if: github.event_name == 'push'
|
# if: github.event_name == 'push'
|
||||||
uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
|
# uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
|
||||||
with:
|
# with:
|
||||||
ref: refs/heads/dev
|
# ref: refs/heads/dev
|
||||||
build_reason: Pre-release
|
# build_reason: Pre-release
|
||||||
push_to_branch: dev
|
# push_to_branch: dev
|
||||||
new_version: ${{ needs.get-update-version.outputs.new_version }}
|
# new_version: ${{ needs.get-update-version.outputs.new_version }}
|
||||||
|
|
||||||
noti-discord-manual-and-update-url-readme:
|
# noti-discord-manual-and-update-url-readme:
|
||||||
needs:
|
# needs:
|
||||||
[
|
# [
|
||||||
build-macos,
|
# build-macos,
|
||||||
build-windows-x64,
|
# build-windows-x64,
|
||||||
build-linux-x64,
|
# build-linux-x64,
|
||||||
get-update-version,
|
# get-update-version,
|
||||||
set-public-provider,
|
# set-public-provider,
|
||||||
sync-temp-to-latest,
|
# sync-temp-to-latest,
|
||||||
]
|
# ]
|
||||||
secrets: inherit
|
# secrets: inherit
|
||||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.public_provider == 'aws-s3'
|
# if: github.event_name == 'workflow_dispatch' && github.event.inputs.public_provider == 'aws-s3'
|
||||||
uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
|
# uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml
|
||||||
with:
|
# with:
|
||||||
ref: refs/heads/dev
|
# ref: refs/heads/dev
|
||||||
build_reason: Manual
|
# build_reason: Manual
|
||||||
push_to_branch: dev
|
# push_to_branch: dev
|
||||||
new_version: ${{ needs.get-update-version.outputs.new_version }}
|
# new_version: ${{ needs.get-update-version.outputs.new_version }}
|
||||||
|
|
||||||
comment-pr-build-url:
|
comment-pr-build-url:
|
||||||
needs:
|
needs:
|
||||||
|
|||||||
6
.github/workflows/jan-tauri-build.yaml
vendored
6
.github/workflows/jan-tauri-build.yaml
vendored
@ -82,11 +82,11 @@ jobs:
|
|||||||
VERSION=${{ needs.get-update-version.outputs.new_version }}
|
VERSION=${{ needs.get-update-version.outputs.new_version }}
|
||||||
PUB_DATE=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
|
PUB_DATE=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
|
||||||
LINUX_SIGNATURE="${{ needs.build-linux-x64.outputs.APPIMAGE_SIG }}"
|
LINUX_SIGNATURE="${{ needs.build-linux-x64.outputs.APPIMAGE_SIG }}"
|
||||||
LINUX_URL="https://github.com/menloresearch/jan/releases/download/v${{ needs.get-update-version.outputs.new_version }}/${{ needs.build-linux-x64.outputs.APPIMAGE_FILE_NAME }}"
|
LINUX_URL="https://github.com/janhq/jan/releases/download/v${{ needs.get-update-version.outputs.new_version }}/${{ needs.build-linux-x64.outputs.APPIMAGE_FILE_NAME }}"
|
||||||
WINDOWS_SIGNATURE="${{ needs.build-windows-x64.outputs.WIN_SIG }}"
|
WINDOWS_SIGNATURE="${{ needs.build-windows-x64.outputs.WIN_SIG }}"
|
||||||
WINDOWS_URL="https://github.com/menloresearch/jan/releases/download/v${{ needs.get-update-version.outputs.new_version }}/${{ needs.build-windows-x64.outputs.FILE_NAME }}"
|
WINDOWS_URL="https://github.com/janhq/jan/releases/download/v${{ needs.get-update-version.outputs.new_version }}/${{ needs.build-windows-x64.outputs.FILE_NAME }}"
|
||||||
DARWIN_SIGNATURE="${{ needs.build-macos.outputs.MAC_UNIVERSAL_SIG }}"
|
DARWIN_SIGNATURE="${{ needs.build-macos.outputs.MAC_UNIVERSAL_SIG }}"
|
||||||
DARWIN_URL="https://github.com/menloresearch/jan/releases/download/v${{ needs.get-update-version.outputs.new_version }}/${{ needs.build-macos.outputs.TAR_NAME }}"
|
DARWIN_URL="https://github.com/janhq/jan/releases/download/v${{ needs.get-update-version.outputs.new_version }}/${{ needs.build-macos.outputs.TAR_NAME }}"
|
||||||
|
|
||||||
jq --arg version "$VERSION" \
|
jq --arg version "$VERSION" \
|
||||||
--arg pub_date "$PUB_DATE" \
|
--arg pub_date "$PUB_DATE" \
|
||||||
|
|||||||
@ -29,7 +29,7 @@ jobs:
|
|||||||
local max_retries=3
|
local max_retries=3
|
||||||
local tag
|
local tag
|
||||||
while [ $retries -lt $max_retries ]; do
|
while [ $retries -lt $max_retries ]; do
|
||||||
tag=$(curl -s https://api.github.com/repos/menloresearch/jan/releases/latest | jq -r .tag_name)
|
tag=$(curl -s https://api.github.com/repos/janhq/jan/releases/latest | jq -r .tag_name)
|
||||||
if [ -n "$tag" ] && [ "$tag" != "null" ]; then
|
if [ -n "$tag" ] && [ "$tag" != "null" ]; then
|
||||||
echo $tag
|
echo $tag
|
||||||
return
|
return
|
||||||
|
|||||||
@ -50,6 +50,6 @@ jobs:
|
|||||||
- macOS Universal: https://delta.jan.ai/nightly/Jan-nightly_{{ VERSION }}_universal.dmg
|
- macOS Universal: https://delta.jan.ai/nightly/Jan-nightly_{{ VERSION }}_universal.dmg
|
||||||
- Linux Deb: https://delta.jan.ai/nightly/Jan-nightly_{{ VERSION }}_amd64.deb
|
- Linux Deb: https://delta.jan.ai/nightly/Jan-nightly_{{ VERSION }}_amd64.deb
|
||||||
- Linux AppImage: https://delta.jan.ai/nightly/Jan-nightly_{{ VERSION }}_amd64.AppImage
|
- Linux AppImage: https://delta.jan.ai/nightly/Jan-nightly_{{ VERSION }}_amd64.AppImage
|
||||||
- Github action run: https://github.com/menloresearch/jan/actions/runs/{{ GITHUB_RUN_ID }}
|
- Github action run: https://github.com/janhq/jan/actions/runs/{{ GITHUB_RUN_ID }}
|
||||||
env:
|
env:
|
||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
|
|||||||
@ -143,7 +143,7 @@ jan/
|
|||||||
|
|
||||||
**Option 1: The Easy Way (Make)**
|
**Option 1: The Easy Way (Make)**
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/menloresearch/jan
|
git clone https://github.com/janhq/jan
|
||||||
cd jan
|
cd jan
|
||||||
make dev
|
make dev
|
||||||
```
|
```
|
||||||
@ -152,8 +152,8 @@ make dev
|
|||||||
|
|
||||||
### Reporting Bugs
|
### Reporting Bugs
|
||||||
|
|
||||||
- **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/menloresearch/jan/issues)
|
- **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/janhq/jan/issues)
|
||||||
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/menloresearch/jan/issues/new)
|
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/janhq/jan/issues/new)
|
||||||
- Include your system specs and error logs - it helps a ton
|
- Include your system specs and error logs - it helps a ton
|
||||||
|
|
||||||
### Suggesting Enhancements
|
### Suggesting Enhancements
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
# Stage 1: Build stage with Node.js and Yarn v4
|
# Stage 1: Build stage with Node.js and Yarn v4
|
||||||
FROM node:20-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
ARG JAN_API_BASE=https://api-dev.jan.ai/v1
|
ARG MENLO_PLATFORM_BASE_URL=https://api-dev.menlo.ai/v1
|
||||||
ENV JAN_API_BASE=$JAN_API_BASE
|
ENV MENLO_PLATFORM_BASE_URL=$MENLO_PLATFORM_BASE_URL
|
||||||
|
|
||||||
# Install build dependencies
|
# Install build dependencies
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
|
|||||||
16
README.md
16
README.md
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/menloresearch/jan"/>
|
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/janhq/jan"/>
|
||||||
<img alt="Github Last Commit" src="https://img.shields.io/github/last-commit/menloresearch/jan"/>
|
<img alt="Github Last Commit" src="https://img.shields.io/github/last-commit/janhq/jan"/>
|
||||||
<img alt="Github Contributors" src="https://img.shields.io/github/contributors/menloresearch/jan"/>
|
<img alt="Github Contributors" src="https://img.shields.io/github/contributors/janhq/jan"/>
|
||||||
<img alt="GitHub closed issues" src="https://img.shields.io/github/issues-closed/menloresearch/jan"/>
|
<img alt="GitHub closed issues" src="https://img.shields.io/github/issues-closed/janhq/jan"/>
|
||||||
<img alt="Discord" src="https://img.shields.io/discord/1107178041848909847?label=discord"/>
|
<img alt="Discord" src="https://img.shields.io/discord/1107178041848909847?label=discord"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -15,7 +15,7 @@
|
|||||||
<a href="https://www.jan.ai/docs/desktop">Getting Started</a>
|
<a href="https://www.jan.ai/docs/desktop">Getting Started</a>
|
||||||
- <a href="https://discord.gg/Exe46xPMbK">Community</a>
|
- <a href="https://discord.gg/Exe46xPMbK">Community</a>
|
||||||
- <a href="https://jan.ai/changelog">Changelog</a>
|
- <a href="https://jan.ai/changelog">Changelog</a>
|
||||||
- <a href="https://github.com/menloresearch/jan/issues">Bug reports</a>
|
- <a href="https://github.com/janhq/jan/issues">Bug reports</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Jan is bringing the best of open-source AI in an easy-to-use product. Download and run LLMs with **full control** and **privacy**.
|
Jan is bringing the best of open-source AI in an easy-to-use product. Download and run LLMs with **full control** and **privacy**.
|
||||||
@ -48,7 +48,7 @@ The easiest way to get started is by downloading one of the following versions f
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
Download from [jan.ai](https://jan.ai/) or [GitHub Releases](https://github.com/menloresearch/jan/releases).
|
Download from [jan.ai](https://jan.ai/) or [GitHub Releases](https://github.com/janhq/jan/releases).
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ For those who enjoy the scenic route:
|
|||||||
### Run with Make
|
### Run with Make
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/menloresearch/jan
|
git clone https://github.com/janhq/jan
|
||||||
cd jan
|
cd jan
|
||||||
make dev
|
make dev
|
||||||
```
|
```
|
||||||
@ -128,7 +128,7 @@ Contributions welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full spiel
|
|||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
- **Bugs**: [GitHub Issues](https://github.com/menloresearch/jan/issues)
|
- **Bugs**: [GitHub Issues](https://github.com/janhq/jan/issues)
|
||||||
- **Business**: hello@jan.ai
|
- **Business**: hello@jan.ai
|
||||||
- **Jobs**: hr@jan.ai
|
- **Jobs**: hr@jan.ai
|
||||||
- **General Discussion**: [Discord](https://discord.gg/FTk2MvZwJH)
|
- **General Discussion**: [Discord](https://discord.gg/FTk2MvZwJH)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# Core dependencies
|
# Core dependencies
|
||||||
cua-computer[all]~=0.3.5
|
cua-computer[all]~=0.3.5
|
||||||
cua-agent[all]~=0.3.0
|
cua-agent[all]~=0.3.0
|
||||||
cua-agent @ git+https://github.com/menloresearch/cua.git@compute-agent-0.3.0-patch#subdirectory=libs/python/agent
|
cua-agent @ git+https://github.com/janhq/cua.git@compute-agent-0.3.0-patch#subdirectory=libs/python/agent
|
||||||
|
|
||||||
# ReportPortal integration
|
# ReportPortal integration
|
||||||
reportportal-client~=5.6.5
|
reportportal-client~=5.6.5
|
||||||
|
|||||||
@ -1,46 +0,0 @@
|
|||||||
[workspace]
|
|
||||||
resolver = "2"
|
|
||||||
|
|
||||||
members = [
|
|
||||||
"download-cef",
|
|
||||||
"get-latest",
|
|
||||||
"update-bindings",
|
|
||||||
"export-cef-dir",
|
|
||||||
"sys",
|
|
||||||
"cef",
|
|
||||||
"app",
|
|
||||||
]
|
|
||||||
|
|
||||||
[workspace.package]
|
|
||||||
version = "140.3.1+140.1.14"
|
|
||||||
edition = "2021"
|
|
||||||
license = "Apache-2.0 OR MIT"
|
|
||||||
authors = []
|
|
||||||
repository = "https://github.com/menloresearch/jan"
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
cef = { path = "cef" }
|
|
||||||
cef-dll-sys = { version = "140.3.1", path = "sys" }
|
|
||||||
download-cef = { version = "2.2", path = "download-cef" }
|
|
||||||
|
|
||||||
anyhow = "1"
|
|
||||||
bindgen = "0.72"
|
|
||||||
clap = { version = "4", features = ["derive"] }
|
|
||||||
cmake = "0.1.52"
|
|
||||||
convert_case = "0.8"
|
|
||||||
git-cliff = "2"
|
|
||||||
git-cliff-core = "2"
|
|
||||||
plist = "1"
|
|
||||||
proc-macro2 = "1"
|
|
||||||
quote = "1"
|
|
||||||
regex = "1"
|
|
||||||
semver = "1"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1"
|
|
||||||
syn = { version = "2", features = ["full"] }
|
|
||||||
thiserror = "2"
|
|
||||||
toml_edit = "0.23"
|
|
||||||
|
|
||||||
[workspace.dependencies.windows-sys]
|
|
||||||
version = "0.61"
|
|
||||||
features = ["Win32_System_Environment", "Win32_System_LibraryLoader"]
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
# cef-rs
|
|
||||||
|
|
||||||
Use CEF in Rust.
|
|
||||||
|
|
||||||
## Supported Targets
|
|
||||||
|
|
||||||
| Target | Linux | macOS | Windows |
|
|
||||||
| ------ | ----- | ----- | ------- |
|
|
||||||
| x86_64 | ✅ | ✅ | ✅ |
|
|
||||||
| ARM64 | ✅ | ✅ | ✅ |
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Install Shared CEF Binaries
|
|
||||||
|
|
||||||
This step is optional, but it will make all other builds of the `cef` crate much faster. If you don't do this, the `cef-dll-sys` crate `build.rs` script will download and extract the same files under its `OUT_DIR` directory. You should repeat this step each time you upgrade to a new version of the `cef` crate.
|
|
||||||
|
|
||||||
#### Linux or macOS:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo run -p export-cef-dir -- --force $HOME/.local/share/cef
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Windows (using PowerShell)
|
|
||||||
|
|
||||||
```pwsh
|
|
||||||
cargo run -p export-cef-dir -- --force $env:USERPROFILE/.local/share/cef
|
|
||||||
```
|
|
||||||
|
|
||||||
### Set Environment Variables
|
|
||||||
|
|
||||||
#### Linux
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export CEF_PATH="$HOME/.local/share/cef"
|
|
||||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$CEF_PATH"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### macOS
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export CEF_PATH="$HOME/.local/share/cef"
|
|
||||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$CEF_PATH:$CEF_PATH/Chromium Embedded Framework.framework/Libraries"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Windows (using PowerShell)
|
|
||||||
|
|
||||||
```pwsh
|
|
||||||
$env:CEF_PATH="$env:USERPROFILE/.local/share/cef"
|
|
||||||
$env:PATH="$env:PATH;$env:CEF_PATH"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run the `app`
|
|
||||||
|
|
||||||
#### Linux
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo run --bin app
|
|
||||||
```
|
|
||||||
|
|
||||||
#### macOS
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo run --bin bundle_app
|
|
||||||
open target/debug/app.app
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Windows (using PowerShell)
|
|
||||||
|
|
||||||
```pwsh
|
|
||||||
cp ./app/win/jan.exe.manifest ./target/debug/
|
|
||||||
cargo run --bin app
|
|
||||||
```
|
|
||||||
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "app"
|
|
||||||
edition = "2024"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "app"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "app_helper"
|
|
||||||
path = "src/mac/helper.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "bundle_app"
|
|
||||||
path = "src/mac/bundle_app.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["sandbox"]
|
|
||||||
sandbox = ["cef/sandbox"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
cef.workspace = true
|
|
||||||
cef-dll-sys.workspace = true
|
|
||||||
parking_lot = "0.12"
|
|
||||||
once_cell = "1.19"
|
|
||||||
tokio = { version = "1", features = ["time"] }
|
|
||||||
|
|
||||||
egui = "0.29"
|
|
||||||
egui-wgpu = "0.29"
|
|
||||||
egui-winit = "0.29"
|
|
||||||
wgpu = "22"
|
|
||||||
winit = { version = "0.30", features = [] }
|
|
||||||
pollster = "0.4"
|
|
||||||
env_logger = "0.11"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
|
||||||
plist.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
@ -1,182 +0,0 @@
|
|||||||
#[cfg(target_os = "macos")]
|
|
||||||
mod mac {
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct InfoPlist {
|
|
||||||
#[serde(rename = "CFBundleDevelopmentRegion")]
|
|
||||||
cf_bundle_development_region: String,
|
|
||||||
#[serde(rename = "CFBundleDisplayName")]
|
|
||||||
cf_bundle_display_name: String,
|
|
||||||
#[serde(rename = "CFBundleExecutable")]
|
|
||||||
cf_bundle_executable: String,
|
|
||||||
#[serde(rename = "CFBundleIdentifier")]
|
|
||||||
cf_bundle_identifier: String,
|
|
||||||
#[serde(rename = "CFBundleInfoDictionaryVersion")]
|
|
||||||
cf_bundle_info_dictionary_version: String,
|
|
||||||
#[serde(rename = "CFBundleName")]
|
|
||||||
cf_bundle_name: String,
|
|
||||||
#[serde(rename = "CFBundlePackageType")]
|
|
||||||
cf_bundle_package_type: String,
|
|
||||||
#[serde(rename = "CFBundleSignature")]
|
|
||||||
cf_bundle_signature: String,
|
|
||||||
#[serde(rename = "CFBundleVersion")]
|
|
||||||
cf_bundle_version: String,
|
|
||||||
#[serde(rename = "CFBundleShortVersionString")]
|
|
||||||
cf_bundle_short_version_string: String,
|
|
||||||
#[serde(rename = "LSEnvironment")]
|
|
||||||
ls_environment: HashMap<String, String>,
|
|
||||||
#[serde(rename = "LSFileQuarantineEnabled")]
|
|
||||||
ls_file_quarantine_enabled: bool,
|
|
||||||
#[serde(rename = "LSMinimumSystemVersion")]
|
|
||||||
ls_minimum_system_version: String,
|
|
||||||
#[serde(rename = "LSUIElement")]
|
|
||||||
ls_ui_element: Option<String>,
|
|
||||||
#[serde(rename = "NSBluetoothAlwaysUsageDescription")]
|
|
||||||
ns_bluetooth_always_usage_description: String,
|
|
||||||
#[serde(rename = "NSSupportsAutomaticGraphicsSwitching")]
|
|
||||||
ns_supports_automatic_graphics_switching: bool,
|
|
||||||
#[serde(rename = "NSWebBrowserPublicKeyCredentialUsageDescription")]
|
|
||||||
ns_web_browser_publickey_credential_usage_description: String,
|
|
||||||
#[serde(rename = "NSCameraUsageDescription")]
|
|
||||||
ns_camera_usage_description: String,
|
|
||||||
#[serde(rename = "NSMicrophoneUsageDescription")]
|
|
||||||
ns_microphone_usage_description: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
const EXEC_PATH: &str = "Contents/MacOS";
|
|
||||||
const FRAMEWORKS_PATH: &str = "Contents/Frameworks";
|
|
||||||
const RESOURCES_PATH: &str = "Contents/Resources";
|
|
||||||
const FRAMEWORK: &str = "Chromium Embedded Framework.framework";
|
|
||||||
const HELPERS: &[&str] = &[
|
|
||||||
"app Helper (GPU)",
|
|
||||||
"app Helper (Renderer)",
|
|
||||||
"app Helper (Plugin)",
|
|
||||||
"app Helper (Alerts)",
|
|
||||||
"app Helper",
|
|
||||||
];
|
|
||||||
|
|
||||||
fn create_app_layout(app_path: &Path) -> PathBuf {
|
|
||||||
[EXEC_PATH, RESOURCES_PATH, FRAMEWORKS_PATH]
|
|
||||||
.iter()
|
|
||||||
.for_each(|p| fs::create_dir_all(app_path.join(p)).unwrap());
|
|
||||||
app_path.join("Contents")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_app(app_path: &Path, exec_name: &str, bin: &Path, is_helper: bool) -> PathBuf {
|
|
||||||
let app_path = app_path.join(exec_name).with_extension("app");
|
|
||||||
let contents_path = create_app_layout(&app_path);
|
|
||||||
create_info_plist(&contents_path, exec_name, is_helper).unwrap();
|
|
||||||
fs::copy(bin, app_path.join(EXEC_PATH).join(exec_name)).unwrap();
|
|
||||||
app_path
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-macos
|
|
||||||
fn bundle(app_path: &Path) {
|
|
||||||
let example_path = PathBuf::from(app_path);
|
|
||||||
let main_app_path = create_app(
|
|
||||||
app_path,
|
|
||||||
"app",
|
|
||||||
&example_path.join("app"),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let cef_path = cef_dll_sys::get_cef_dir().unwrap();
|
|
||||||
let to = main_app_path.join(FRAMEWORKS_PATH).join(FRAMEWORK);
|
|
||||||
if to.exists() {
|
|
||||||
fs::remove_dir_all(&to).unwrap();
|
|
||||||
}
|
|
||||||
copy_directory(&cef_path.join(FRAMEWORK), &to);
|
|
||||||
HELPERS.iter().for_each(|helper| {
|
|
||||||
create_app(
|
|
||||||
&main_app_path.join(FRAMEWORKS_PATH),
|
|
||||||
helper,
|
|
||||||
&example_path.join("app_helper"),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_info_plist(
|
|
||||||
contents_path: &Path,
|
|
||||||
exec_name: &str,
|
|
||||||
is_helper: bool,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let info_plist = InfoPlist {
|
|
||||||
cf_bundle_development_region: "en".to_string(),
|
|
||||||
cf_bundle_display_name: exec_name.to_string(),
|
|
||||||
cf_bundle_executable: exec_name.to_string(),
|
|
||||||
cf_bundle_identifier: "app.jan.ai.helper".to_string(),
|
|
||||||
cf_bundle_info_dictionary_version: "6.0".to_string(),
|
|
||||||
cf_bundle_name: "cef-rs".to_string(),
|
|
||||||
cf_bundle_package_type: "APPL".to_string(),
|
|
||||||
cf_bundle_signature: "????".to_string(),
|
|
||||||
cf_bundle_version: "1.0.0".to_string(),
|
|
||||||
cf_bundle_short_version_string: "1.0".to_string(),
|
|
||||||
ls_environment: [("MallocNanoZone".to_string(), "0".to_string())]
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect(),
|
|
||||||
ls_file_quarantine_enabled: true,
|
|
||||||
ls_minimum_system_version: "11.0".to_string(),
|
|
||||||
ls_ui_element: if is_helper {
|
|
||||||
Some("1".to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
ns_bluetooth_always_usage_description: exec_name.to_string(),
|
|
||||||
ns_supports_automatic_graphics_switching: true,
|
|
||||||
ns_web_browser_publickey_credential_usage_description: exec_name.to_string(),
|
|
||||||
ns_camera_usage_description: exec_name.to_string(),
|
|
||||||
ns_microphone_usage_description: exec_name.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
plist::to_file_xml(contents_path.join("Info.plist"), &info_plist)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn copy_directory(src: &Path, dst: &Path) {
|
|
||||||
fs::create_dir_all(dst).unwrap();
|
|
||||||
for entry in fs::read_dir(src).unwrap() {
|
|
||||||
let entry = entry.unwrap();
|
|
||||||
let dst_path = dst.join(entry.file_name());
|
|
||||||
if entry.file_type().unwrap().is_dir() {
|
|
||||||
copy_directory(&entry.path(), &dst_path);
|
|
||||||
} else {
|
|
||||||
fs::copy(&entry.path(), &dst_path).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_command(args: &[&str]) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let status = Command::new("cargo")
|
|
||||||
.args(args)
|
|
||||||
.stdout(Stdio::inherit())
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let app_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../target/debug");
|
|
||||||
run_command(&["build", "--bin", "app"])?;
|
|
||||||
run_command(&["build", "--bin", "app_helper"])?;
|
|
||||||
bundle(&app_path);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
mac::main()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
fn main() {}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
use cef::{args::Args, *};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = Args::new();
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", feature = "sandbox"))]
|
|
||||||
let _sandbox = {
|
|
||||||
let mut sandbox = cef::sandbox::Sandbox::new();
|
|
||||||
sandbox.initialize(args.as_main_args());
|
|
||||||
sandbox
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let _loader = {
|
|
||||||
let loader = library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), true);
|
|
||||||
assert!(loader.load());
|
|
||||||
loader
|
|
||||||
};
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
Some(args.as_main_args()),
|
|
||||||
None::<&mut App>,
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,782 +0,0 @@
|
|||||||
use cef::{args::Args, rc::*, *};
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use winit::{
|
|
||||||
application::ApplicationHandler,
|
|
||||||
event::WindowEvent,
|
|
||||||
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
|
|
||||||
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
|
||||||
window::{Window, WindowAttributes, WindowId},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Shared state between CEF and rendering
|
|
||||||
static TEXTURE_BUFFER: once_cell::sync::Lazy<Arc<RwLock<Vec<u8>>>> =
|
|
||||||
once_cell::sync::Lazy::new(|| Arc::new(RwLock::new(Vec::new())));
|
|
||||||
static TEXTURE_SIZE: once_cell::sync::Lazy<Arc<RwLock<(u32, u32)>>> =
|
|
||||||
once_cell::sync::Lazy::new(|| Arc::new(RwLock::new((1024, 768))));
|
|
||||||
static CEF_INITIALIZED: once_cell::sync::Lazy<Arc<RwLock<bool>>> =
|
|
||||||
once_cell::sync::Lazy::new(|| Arc::new(RwLock::new(false)));
|
|
||||||
static BROWSER_INSTANCE: once_cell::sync::Lazy<Arc<RwLock<Option<Browser>>>> =
|
|
||||||
once_cell::sync::Lazy::new(|| Arc::new(RwLock::new(None)));
|
|
||||||
|
|
||||||
// CEF App implementation
|
|
||||||
struct DemoApp {
|
|
||||||
object: *mut RcImpl<cef_dll_sys::_cef_app_t, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DemoApp {
|
|
||||||
fn new_app() -> App {
|
|
||||||
App::new(Self {
|
|
||||||
object: std::ptr::null_mut(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapApp for DemoApp {
|
|
||||||
fn wrap_rc(&mut self, object: *mut RcImpl<cef_dll_sys::_cef_app_t, Self>) {
|
|
||||||
self.object = object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for DemoApp {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
let rc_impl = &mut *self.object;
|
|
||||||
rc_impl.interface.add_ref();
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
object: self.object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rc for DemoApp {
|
|
||||||
fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t {
|
|
||||||
unsafe {
|
|
||||||
let base = &*self.object;
|
|
||||||
std::mem::transmute(&base.cef_object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImplApp for DemoApp {
|
|
||||||
fn get_raw(&self) -> *mut cef_dll_sys::_cef_app_t {
|
|
||||||
self.object.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn browser_process_handler(&self) -> Option<BrowserProcessHandler> {
|
|
||||||
Some(DemoBrowserProcessHandler::new_browser_process_handler())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browser Process Handler
|
|
||||||
struct DemoBrowserProcessHandler {
|
|
||||||
object: *mut RcImpl<cef_dll_sys::cef_browser_process_handler_t, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DemoBrowserProcessHandler {
|
|
||||||
fn new_browser_process_handler() -> BrowserProcessHandler {
|
|
||||||
BrowserProcessHandler::new(Self {
|
|
||||||
object: std::ptr::null_mut(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rc for DemoBrowserProcessHandler {
|
|
||||||
fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t {
|
|
||||||
unsafe {
|
|
||||||
let base = &*self.object;
|
|
||||||
std::mem::transmute(&base.cef_object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapBrowserProcessHandler for DemoBrowserProcessHandler {
|
|
||||||
fn wrap_rc(
|
|
||||||
&mut self,
|
|
||||||
object: *mut RcImpl<cef_dll_sys::_cef_browser_process_handler_t, Self>,
|
|
||||||
) {
|
|
||||||
self.object = object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for DemoBrowserProcessHandler {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
let rc_impl = &mut *self.object;
|
|
||||||
rc_impl.interface.add_ref();
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
object: self.object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImplBrowserProcessHandler for DemoBrowserProcessHandler {
|
|
||||||
fn get_raw(&self) -> *mut cef_dll_sys::_cef_browser_process_handler_t {
|
|
||||||
self.object.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_context_initialized(&self) {
|
|
||||||
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
||||||
println!("CEF context initialized");
|
|
||||||
|
|
||||||
*CEF_INITIALIZED.write() = true;
|
|
||||||
|
|
||||||
let mut client = DemoClient::new_client();
|
|
||||||
let url = CefString::from("https://www.google.com");
|
|
||||||
|
|
||||||
let window_info = WindowInfo {
|
|
||||||
windowless_rendering_enabled: 1,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let settings = BrowserSettings::default();
|
|
||||||
|
|
||||||
let _browser = browser_host_create_browser(
|
|
||||||
Some(&window_info),
|
|
||||||
Some(&mut client),
|
|
||||||
Some(&url),
|
|
||||||
Some(&settings),
|
|
||||||
Option::<&mut DictionaryValue>::None,
|
|
||||||
Option::<&mut RequestContext>::None,
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client
|
|
||||||
struct DemoClient {
|
|
||||||
object: *mut RcImpl<cef_dll_sys::_cef_client_t, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DemoClient {
|
|
||||||
fn new_client() -> Client {
|
|
||||||
Client::new(Self {
|
|
||||||
object: std::ptr::null_mut(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapClient for DemoClient {
|
|
||||||
fn wrap_rc(&mut self, object: *mut RcImpl<cef_dll_sys::_cef_client_t, Self>) {
|
|
||||||
self.object = object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for DemoClient {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
let rc_impl = &mut *self.object;
|
|
||||||
rc_impl.interface.add_ref();
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
object: self.object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rc for DemoClient {
|
|
||||||
fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t {
|
|
||||||
unsafe {
|
|
||||||
let base = &*self.object;
|
|
||||||
std::mem::transmute(&base.cef_object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImplClient for DemoClient {
|
|
||||||
fn get_raw(&self) -> *mut cef_dll_sys::_cef_client_t {
|
|
||||||
self.object.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_handler(&self) -> Option<RenderHandler> {
|
|
||||||
Some(DemoRenderHandler::new_render_handler())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn life_span_handler(&self) -> Option<LifeSpanHandler> {
|
|
||||||
Some(DemoLifeSpanHandler::new_life_span_handler())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LifeSpan Handler to capture browser instance
|
|
||||||
struct DemoLifeSpanHandler {
|
|
||||||
object: *mut RcImpl<cef_dll_sys::_cef_life_span_handler_t, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DemoLifeSpanHandler {
|
|
||||||
fn new_life_span_handler() -> LifeSpanHandler {
|
|
||||||
LifeSpanHandler::new(Self {
|
|
||||||
object: std::ptr::null_mut(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapLifeSpanHandler for DemoLifeSpanHandler {
|
|
||||||
fn wrap_rc(&mut self, object: *mut RcImpl<cef_dll_sys::_cef_life_span_handler_t, Self>) {
|
|
||||||
self.object = object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for DemoLifeSpanHandler {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
let rc_impl = &mut *self.object;
|
|
||||||
rc_impl.interface.add_ref();
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
object: self.object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rc for DemoLifeSpanHandler {
|
|
||||||
fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t {
|
|
||||||
unsafe {
|
|
||||||
let base = &*self.object;
|
|
||||||
std::mem::transmute(&base.cef_object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImplLifeSpanHandler for DemoLifeSpanHandler {
|
|
||||||
fn get_raw(&self) -> *mut cef_dll_sys::_cef_life_span_handler_t {
|
|
||||||
self.object.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_after_created(&self, browser: Option<&mut Browser>) {
|
|
||||||
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
||||||
if let Some(browser) = browser {
|
|
||||||
// Store the browser instance for later use
|
|
||||||
*BROWSER_INSTANCE.write() = Some(browser.clone());
|
|
||||||
println!("Browser created and stored!");
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render Handler
|
|
||||||
struct DemoRenderHandler {
|
|
||||||
object: *mut RcImpl<cef_dll_sys::_cef_render_handler_t, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DemoRenderHandler {
|
|
||||||
fn new_render_handler() -> RenderHandler {
|
|
||||||
RenderHandler::new(Self {
|
|
||||||
object: std::ptr::null_mut(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapRenderHandler for DemoRenderHandler {
|
|
||||||
fn wrap_rc(&mut self, object: *mut RcImpl<cef_dll_sys::_cef_render_handler_t, Self>) {
|
|
||||||
self.object = object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for DemoRenderHandler {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
let rc_impl = &mut *self.object;
|
|
||||||
rc_impl.interface.add_ref();
|
|
||||||
}
|
|
||||||
Self {
|
|
||||||
object: self.object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rc for DemoRenderHandler {
|
|
||||||
fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t {
|
|
||||||
unsafe {
|
|
||||||
let base = &*self.object;
|
|
||||||
std::mem::transmute(&base.cef_object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImplRenderHandler for DemoRenderHandler {
|
|
||||||
fn get_raw(&self) -> *mut cef_dll_sys::_cef_render_handler_t {
|
|
||||||
self.object.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) {
|
|
||||||
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
||||||
if let Some(rect) = rect {
|
|
||||||
let size = TEXTURE_SIZE.read();
|
|
||||||
rect.x = 0;
|
|
||||||
rect.y = 0;
|
|
||||||
rect.width = size.0 as i32;
|
|
||||||
rect.height = size.1 as i32;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_paint(
|
|
||||||
&self,
|
|
||||||
_browser: Option<&mut Browser>,
|
|
||||||
_type_: PaintElementType,
|
|
||||||
_dirty_rects_count: usize,
|
|
||||||
_dirty_rects: Option<&Rect>,
|
|
||||||
buffer: *const u8,
|
|
||||||
width: ::std::os::raw::c_int,
|
|
||||||
height: ::std::os::raw::c_int,
|
|
||||||
) {
|
|
||||||
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
||||||
if buffer.is_null() || width <= 0 || height <= 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let size = (width * height * 4) as usize;
|
|
||||||
let slice = unsafe { std::slice::from_raw_parts(buffer, size) };
|
|
||||||
|
|
||||||
let mut texture_buffer = TEXTURE_BUFFER.write();
|
|
||||||
texture_buffer.clear();
|
|
||||||
texture_buffer.extend_from_slice(slice);
|
|
||||||
|
|
||||||
let mut texture_size = TEXTURE_SIZE.write();
|
|
||||||
*texture_size = (width as u32, height as u32);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// egui + wgpu Application
|
|
||||||
struct EguiCefApp {
|
|
||||||
window: Option<Arc<Window>>,
|
|
||||||
egui_state: Option<EguiState>,
|
|
||||||
url_input: String,
|
|
||||||
frame_count: u64,
|
|
||||||
last_update: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EguiState {
|
|
||||||
device: wgpu::Device,
|
|
||||||
queue: wgpu::Queue,
|
|
||||||
surface: wgpu::Surface<'static>,
|
|
||||||
surface_config: wgpu::SurfaceConfiguration,
|
|
||||||
egui_renderer: egui_wgpu::Renderer,
|
|
||||||
egui_state: egui_winit::State,
|
|
||||||
cef_texture: Option<wgpu::Texture>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EguiCefApp {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
window: None,
|
|
||||||
egui_state: None,
|
|
||||||
url_input: String::from("https://www.google.com"),
|
|
||||||
frame_count: 0,
|
|
||||||
last_update: Instant::now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApplicationHandler for EguiCefApp {
|
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
if self.window.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let window_attrs = WindowAttributes::default()
|
|
||||||
.with_title("egui + CEF Browser")
|
|
||||||
.with_inner_size(winit::dpi::LogicalSize::new(1024, 768));
|
|
||||||
|
|
||||||
let window = Arc::new(event_loop.create_window(window_attrs).unwrap());
|
|
||||||
|
|
||||||
// Initialize wgpu and egui
|
|
||||||
let egui_state = pollster::block_on(async {
|
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
||||||
backends: wgpu::Backends::all(),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let surface = instance.create_surface(window.clone()).unwrap();
|
|
||||||
|
|
||||||
let adapter = instance
|
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: wgpu::PowerPreference::default(),
|
|
||||||
compatible_surface: Some(&surface),
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let (device, queue) = adapter
|
|
||||||
.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
label: Some("Device"),
|
|
||||||
required_features: wgpu::Features::empty(),
|
|
||||||
required_limits: wgpu::Limits::default(),
|
|
||||||
memory_hints: Default::default(),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let surface_caps = surface.get_capabilities(&adapter);
|
|
||||||
let surface_format = surface_caps
|
|
||||||
.formats
|
|
||||||
.iter()
|
|
||||||
.find(|f| f.is_srgb())
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(surface_caps.formats[0]);
|
|
||||||
|
|
||||||
let size = window.inner_size();
|
|
||||||
let surface_config = wgpu::SurfaceConfiguration {
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
format: surface_format,
|
|
||||||
width: size.width,
|
|
||||||
height: size.height,
|
|
||||||
present_mode: wgpu::PresentMode::AutoVsync,
|
|
||||||
alpha_mode: surface_caps.alpha_modes[0],
|
|
||||||
view_formats: vec![],
|
|
||||||
desired_maximum_frame_latency: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
surface.configure(&device, &surface_config);
|
|
||||||
|
|
||||||
let egui_ctx = egui::Context::default();
|
|
||||||
let viewport_id = egui_ctx.viewport_id();
|
|
||||||
let max_texture_side = Some(device.limits().max_texture_dimension_2d as usize);
|
|
||||||
let egui_state = egui_winit::State::new(
|
|
||||||
egui_ctx,
|
|
||||||
viewport_id,
|
|
||||||
&window,
|
|
||||||
Some(window.scale_factor() as f32),
|
|
||||||
Some(winit::window::Theme::Dark),
|
|
||||||
max_texture_side,
|
|
||||||
);
|
|
||||||
|
|
||||||
let egui_renderer = egui_wgpu::Renderer::new(&device, surface_format, None, 1, false);
|
|
||||||
|
|
||||||
EguiState {
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
surface,
|
|
||||||
surface_config,
|
|
||||||
egui_renderer,
|
|
||||||
egui_state,
|
|
||||||
cef_texture: None,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.egui_state = Some(egui_state);
|
|
||||||
self.window = Some(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
|
||||||
let Some(window) = &self.window else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(state) = &mut self.egui_state else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Let egui handle the event first
|
|
||||||
let response = state.egui_state.on_window_event(window, &event);
|
|
||||||
|
|
||||||
if response.repaint {
|
|
||||||
window.request_redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.consumed {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match event {
|
|
||||||
WindowEvent::CloseRequested => {
|
|
||||||
event_loop.exit();
|
|
||||||
}
|
|
||||||
WindowEvent::Resized(new_size) => {
|
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
|
||||||
state.surface_config.width = new_size.width;
|
|
||||||
state.surface_config.height = new_size.height;
|
|
||||||
state.surface.configure(&state.device, &state.surface_config);
|
|
||||||
window.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WindowEvent::RedrawRequested => {
|
|
||||||
self.render();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EguiCefApp {
|
|
||||||
fn render(&mut self) {
|
|
||||||
let Some(window) = &self.window else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(state) = &mut self.egui_state else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.frame_count += 1;
|
|
||||||
|
|
||||||
// Build egui UI
|
|
||||||
let raw_input = state.egui_state.take_egui_input(window);
|
|
||||||
let egui_output = state.egui_state.egui_ctx().run(raw_input, |ctx| {
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
|
||||||
ui.heading("🦀 egui + CEF Browser Integration");
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label("URL:");
|
|
||||||
ui.text_edit_singleline(&mut self.url_input);
|
|
||||||
if ui.button("Navigate").clicked() {
|
|
||||||
// Navigate to the URL
|
|
||||||
let url = self.url_input.clone();
|
|
||||||
if let Some(browser) = BROWSER_INSTANCE.read().as_ref() {
|
|
||||||
if let Some(main_frame) = browser.main_frame() {
|
|
||||||
let cef_url = CefString::from(url.as_str());
|
|
||||||
main_frame.load_url(Some(&cef_url));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
let cef_ready = *CEF_INITIALIZED.read();
|
|
||||||
ui.label(format!("CEF Status: {}", if cef_ready { "✅ Ready" } else { "⏳ Initializing..." }));
|
|
||||||
ui.label(format!("Frame: {}", self.frame_count));
|
|
||||||
ui.label(format!("FPS: {:.1}", 1.0 / self.last_update.elapsed().as_secs_f32()));
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
// Display CEF texture
|
|
||||||
let texture_buffer = TEXTURE_BUFFER.read();
|
|
||||||
let size = TEXTURE_SIZE.read();
|
|
||||||
|
|
||||||
if !texture_buffer.is_empty() && size.0 > 0 && size.1 > 0 {
|
|
||||||
// Update or create wgpu texture from CEF buffer
|
|
||||||
if state.cef_texture.is_none()
|
|
||||||
|| state.cef_texture.as_ref().unwrap().width() != size.0
|
|
||||||
|| state.cef_texture.as_ref().unwrap().height() != size.1
|
|
||||||
{
|
|
||||||
state.cef_texture = Some(state.device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label: Some("CEF Texture"),
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: size.0,
|
|
||||||
height: size.1,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
|
||||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
|
||||||
view_formats: &[],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert BGRA to RGBA
|
|
||||||
let mut rgba_buffer = Vec::with_capacity(texture_buffer.len());
|
|
||||||
for chunk in texture_buffer.chunks_exact(4) {
|
|
||||||
rgba_buffer.push(chunk[2]); // R
|
|
||||||
rgba_buffer.push(chunk[1]); // G
|
|
||||||
rgba_buffer.push(chunk[0]); // B
|
|
||||||
rgba_buffer.push(chunk[3]); // A
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(texture) = &state.cef_texture {
|
|
||||||
state.queue.write_texture(
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
texture,
|
|
||||||
mip_level: 0,
|
|
||||||
origin: wgpu::Origin3d::ZERO,
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
},
|
|
||||||
&rgba_buffer,
|
|
||||||
wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(4 * size.0),
|
|
||||||
rows_per_image: Some(size.1),
|
|
||||||
},
|
|
||||||
wgpu::Extent3d {
|
|
||||||
width: size.0,
|
|
||||||
height: size.1,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Register texture with egui
|
|
||||||
let texture_id = state.egui_renderer.register_native_texture(
|
|
||||||
&state.device,
|
|
||||||
&texture.create_view(&wgpu::TextureViewDescriptor::default()),
|
|
||||||
wgpu::FilterMode::Linear,
|
|
||||||
);
|
|
||||||
|
|
||||||
ui.image(egui::ImageSource::Texture(egui::load::SizedTexture::new(
|
|
||||||
texture_id,
|
|
||||||
[size.0 as f32, size.1 as f32],
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ui.label("⏳ Waiting for CEF to render...");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
state.egui_state.handle_platform_output(window, egui_output.platform_output);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
let output = state.surface.get_current_texture().unwrap();
|
|
||||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
|
|
||||||
let mut encoder = state.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("Render Encoder"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let screen_descriptor = egui_wgpu::ScreenDescriptor {
|
|
||||||
size_in_pixels: [state.surface_config.width, state.surface_config.height],
|
|
||||||
pixels_per_point: window.scale_factor() as f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
let primitives = state.egui_state.egui_ctx().tessellate(egui_output.shapes, egui_output.pixels_per_point);
|
|
||||||
|
|
||||||
// Update textures
|
|
||||||
for (id, image_delta) in &egui_output.textures_delta.set {
|
|
||||||
state.egui_renderer.update_texture(&state.device, &state.queue, *id, image_delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update buffers
|
|
||||||
state.egui_renderer.update_buffers(
|
|
||||||
&state.device,
|
|
||||||
&state.queue,
|
|
||||||
&mut encoder,
|
|
||||||
&primitives,
|
|
||||||
&screen_descriptor,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
{
|
|
||||||
let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("Render Pass"),
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: &view,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
|
||||||
r: 0.1,
|
|
||||||
g: 0.1,
|
|
||||||
b: 0.1,
|
|
||||||
a: 1.0,
|
|
||||||
}),
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
},
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
occlusion_query_set: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render egui
|
|
||||||
state.egui_renderer.render(
|
|
||||||
&mut render_pass.forget_lifetime(),
|
|
||||||
&primitives,
|
|
||||||
&screen_descriptor,
|
|
||||||
);
|
|
||||||
} // render_pass dropped here automatically
|
|
||||||
|
|
||||||
for id in &egui_output.textures_delta.free {
|
|
||||||
state.egui_renderer.free_texture(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.queue.submit(std::iter::once(encoder.finish()));
|
|
||||||
output.present();
|
|
||||||
|
|
||||||
self.last_update = Instant::now();
|
|
||||||
window.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
env_logger::init();
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let _loader = {
|
|
||||||
let loader = library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), false);
|
|
||||||
if !loader.load() {
|
|
||||||
eprintln!("Failed to load CEF library");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loader
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = api_hash(sys::CEF_API_VERSION_LAST, 0);
|
|
||||||
|
|
||||||
let args = Args::new();
|
|
||||||
let cmd = args.as_cmd_line().unwrap();
|
|
||||||
|
|
||||||
let switch = CefString::from("type");
|
|
||||||
let is_browser_process = cmd.has_switch(Some(&switch)) != 1;
|
|
||||||
|
|
||||||
let mut app = DemoApp::new_app();
|
|
||||||
|
|
||||||
let ret = execute_process(
|
|
||||||
Some(args.as_main_args()),
|
|
||||||
Some(&mut app),
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle non-browser processes (renderer, GPU, etc.)
|
|
||||||
if !is_browser_process {
|
|
||||||
let process_type = CefString::from(&cmd.switch_value(Some(&switch)));
|
|
||||||
println!("launch process {process_type}");
|
|
||||||
assert!(ret >= 0, "cannot execute non-browser process");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browser process - initialize CEF with external message pump
|
|
||||||
println!("launch browser process");
|
|
||||||
assert!(ret == -1, "cannot execute browser process");
|
|
||||||
|
|
||||||
let mut settings = Settings {
|
|
||||||
no_sandbox: !cfg!(feature = "sandbox") as _,
|
|
||||||
windowless_rendering_enabled: 1,
|
|
||||||
external_message_pump: 1, // Key: allows manual event loop control
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let init_result = initialize(
|
|
||||||
Some(args.as_main_args()),
|
|
||||||
Some(&mut settings),
|
|
||||||
Some(&mut app),
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if init_result != 1 {
|
|
||||||
eprintln!("Failed to initialize CEF: {}", init_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("CEF initialized successfully with external message pump");
|
|
||||||
|
|
||||||
// Run event loop with manual pumping
|
|
||||||
let mut event_loop = EventLoop::new().unwrap();
|
|
||||||
event_loop.set_control_flow(ControlFlow::Poll);
|
|
||||||
|
|
||||||
let mut egui_app = EguiCefApp::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Pump CEF messages
|
|
||||||
do_message_loop_work();
|
|
||||||
|
|
||||||
// Pump winit events
|
|
||||||
let status = event_loop.pump_app_events(Some(Duration::ZERO), &mut egui_app);
|
|
||||||
|
|
||||||
if let PumpStatus::Exit(code) = status {
|
|
||||||
println!("Exiting with code: {}", code);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Small sleep to prevent busy-waiting
|
|
||||||
std::thread::sleep(Duration::from_millis(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
|
||||||
|
|
||||||
<!-- https://cef-builds.spotifycdn.com/cef_binary_132.3.2+g4997b2f+chromium-132.0.6834.161_windows64.tar.bz2 -->
|
|
||||||
<!-- This compatibility section is from the standar pre - built binary package, specifically from %CEF_ROOT%/tests/app/win/compatibility.manifest -->
|
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
|
||||||
<application>
|
|
||||||
<!--The ID below indicates application support for Windows Vista -->
|
|
||||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
|
||||||
<!--The ID below indicates application support for Windows 7 -->
|
|
||||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
|
||||||
<!--The ID below indicates application support for Windows 8 -->
|
|
||||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
|
||||||
<!--The ID below indicates application support for Windows 8.1 -->
|
|
||||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
|
||||||
<!--The ID below indicates application support for Windows 10 -->
|
|
||||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
|
||||||
<!-- This tag is required for XAML islands usage in the process for media scenarios. -->
|
|
||||||
<!-- This version corresponds to the Windows 10 May 2019 Update. -->
|
|
||||||
<maxversiontested Id="10.0.18362.0"/>
|
|
||||||
</application>
|
|
||||||
</compatibility>
|
|
||||||
|
|
||||||
<!--The compatibility section will be merged from build/win/compatibility.manifest -->
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
|
|
||||||
</dependentAssembly>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<security>
|
|
||||||
<requestedPrivileges>
|
|
||||||
<requestedExecutionLevel level="asInvoker" />
|
|
||||||
</requestedPrivileges>
|
|
||||||
</security>
|
|
||||||
</trustInfo>
|
|
||||||
|
|
||||||
</assembly>
|
|
||||||
@ -1,215 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## [140.3.1+140.1.14](https://github.com/tauri-apps/cef-rs/compare/cef-v140.3.0+140.1.14...cef-v140.3.1+140.1.14) - 2025-10-03
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- copy wrapped out-params back to pointers ([#224](https://github.com/tauri-apps/cef-rs/pull/224))
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update bindings
|
|
||||||
- *(test)* test out-params
|
|
||||||
|
|
||||||
## [140.2.0+140.1.14](https://github.com/tauri-apps/cef-rs/compare/cef-v140.1.0+140.1.13...cef-v140.2.0+140.1.14) - 2025-09-21
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 140.1.14
|
|
||||||
|
|
||||||
## [140.1.0+140.1.13](https://github.com/tauri-apps/cef-rs/compare/cef-v140.0.0+140.1.13...cef-v140.1.0+140.1.13) - 2025-09-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
|
|
||||||
## [140.0.0+140.1.13](https://github.com/tauri-apps/cef-rs/compare/cef-v139.8.0+139.0.40...cef-v140.0.0+140.1.13) - 2025-09-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update bindings
|
|
||||||
- *(release)* update CEF version to 140.1.13
|
|
||||||
|
|
||||||
## [139.8.0+139.0.40](https://github.com/tauri-apps/cef-rs/compare/cef-v139.7.2+139.0.38...cef-v139.8.0+139.0.40) - 2025-09-12
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.40
|
|
||||||
|
|
||||||
## [139.7.2+139.0.38](https://github.com/tauri-apps/cef-rs/compare/cef-v139.7.1+139.0.38...cef-v139.7.2+139.0.38) - 2025-09-08
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- cleanup logic for copying back out-params
|
|
||||||
- handle out-params ([#173](https://github.com/tauri-apps/cef-rs/issues/173))
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.7.2+139.0.38
|
|
||||||
- update bindings
|
|
||||||
|
|
||||||
## [139.7.1+139.0.38](https://github.com/tauri-apps/cef-rs/compare/cef-v139.7.0+139.0.38...cef-v139.7.1+139.0.38) - 2025-09-07
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.7.1+139.0.38
|
|
||||||
- *(deps)* update rust crate windows-sys to 0.61
|
|
||||||
|
|
||||||
## [139.7.0+139.0.38](https://github.com/tauri-apps/cef-rs/compare/cef-v139.6.0+139.0.37...cef-v139.7.0+139.0.38) - 2025-08-31
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.38
|
|
||||||
|
|
||||||
## [139.6.0+139.0.37](https://github.com/tauri-apps/cef-rs/compare/cef-v139.5.0+139.0.30...cef-v139.6.0+139.0.37) - 2025-08-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.37
|
|
||||||
|
|
||||||
## [139.5.0+139.0.30](https://github.com/tauri-apps/cef-rs/compare/cef-v139.4.0+139.0.28...cef-v139.5.0+139.0.30) - 2025-08-28
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.30
|
|
||||||
|
|
||||||
## [139.4.0+139.0.28](https://github.com/tauri-apps/cef-rs/compare/cef-v139.3.0+139.0.26...cef-v139.4.0+139.0.28) - 2025-08-23
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.28
|
|
||||||
|
|
||||||
## [139.3.0+139.0.26](https://github.com/tauri-apps/cef-rs/compare/cef-v139.2.1+139.0.23...cef-v139.3.0+139.0.26) - 2025-08-22
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.26
|
|
||||||
|
|
||||||
## [139.2.1+139.0.23](https://github.com/tauri-apps/cef-rs/compare/cef-v139.2.0+139.0.23...cef-v139.2.1+139.0.23) - 2025-08-16
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- warnings about usize < 0 comparisons
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
|
|
||||||
## [139.2.0+139.0.23](https://github.com/tauri-apps/cef-rs/compare/cef-v139.1.0+139.0.20...cef-v139.2.0+139.0.23) - 2025-08-16
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.23
|
|
||||||
|
|
||||||
## [139.1.0+139.0.20](https://github.com/tauri-apps/cef-rs/compare/cef-v139.0.1+139.0.17...cef-v139.1.0+139.0.20) - 2025-08-15
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.20
|
|
||||||
|
|
||||||
## [139.0.1+139.0.17](https://github.com/tauri-apps/cef-rs/compare/cef-v139.0.0+139.0.17...cef-v139.0.1+139.0.17) - 2025-08-08
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.0.1+139.0.17
|
|
||||||
|
|
||||||
## [139.0.0+139.0.17](https://github.com/tauri-apps/cef-rs/compare/cef-v138.9.0+138.0.36...cef-v139.0.0+139.0.17) - 2025-08-08
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.17
|
|
||||||
|
|
||||||
## [138.9.0+138.0.36](https://github.com/tauri-apps/cef-rs/compare/cef-v138.8.0+138.0.34...cef-v138.9.0+138.0.36) - 2025-08-07
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.36
|
|
||||||
|
|
||||||
## [138.8.0+138.0.34](https://github.com/tauri-apps/cef-rs/compare/cef-v138.7.1+138.0.33...cef-v138.8.0+138.0.34) - 2025-08-02
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- remove cef version from example dependencies
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.34
|
|
||||||
|
|
||||||
## [138.7.1+138.0.33](https://github.com/tauri-apps/cef-rs/compare/cef-v138.7.0+138.0.33...cef-v138.7.1+138.0.33) - 2025-07-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v138.7.1+138.0.33
|
|
||||||
- move examples into separate crates
|
|
||||||
|
|
||||||
## [138.7.0+138.0.33](https://github.com/tauri-apps/cef-rs/compare/cef-v138.6.1+138.0.27...cef-v138.7.0+138.0.33) - 2025-07-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.33
|
|
||||||
|
|
||||||
## [138.6.1+138.0.27](https://github.com/tauri-apps/cef-rs/compare/cef-v138.6.0+138.0.27...cef-v138.6.1+138.0.27) - 2025-07-28
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- embed git-cliff as a library in get-latest
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* bump version for get-latest updates
|
|
||||||
|
|
||||||
## [138.6.0+138.0.27](https://github.com/tauri-apps/cef-rs/compare/cef-v138.5.1+138.0.26...cef-v138.6.0+138.0.27) - 2025-07-28
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- update CEF version to 138.0.27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- bump version for release
|
|
||||||
|
|
||||||
## [138.5.1+138.0.26](https://github.com/tauri-apps/cef-rs/compare/cef-v138.5.0+138.0.26...cef-v138.5.1+138.0.26) - 2025-07-22
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
- *(doc)* regenerate CHANGELOG.md
|
|
||||||
|
|
||||||
## [138.5.0+138.0.26](https://github.com/tauri-apps/cef-rs/compare/cef-v138.4.0+138.0.25...cef-v138.5.0+138.0.26) - 2025-07-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.4.0+138.0.25](https://github.com/tauri-apps/cef-rs/compare/cef-v138.3.0+138.0.23...cef-v138.4.0+138.0.25) - 2025-07-18
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.3.0+138.0.23](https://github.com/tauri-apps/cef-rs/compare/cef-v138.2.2+138.0.21...cef-v138.3.0+138.0.23) - 2025-07-17
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.2.2+138.0.21](https://github.com/tauri-apps/cef-rs/compare/cef-v138.2.1+138.0.21...cef-v138.2.2+138.0.21) - 2025-07-14
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
- seed CHANGELOG.md files
|
|
||||||
|
|
||||||
## [138.2.1+138.0.21](https://github.com/tauri-apps/cef-rs/compare/cef-v138.2.0+138.0.21...cef-v138.2.1+138.0.21) - 2025-07-14
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- bump major version of download-cef [#145](https://github.com/tauri-apps/cef-rs/issues/145)
|
|
||||||
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "cef"
|
|
||||||
description = "Use cef in Rust"
|
|
||||||
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["sandbox"]
|
|
||||||
dox = ["cef-dll-sys/dox"]
|
|
||||||
sandbox = ["cef-dll-sys/sandbox"]
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
features = ["dox"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
cef-dll-sys.workspace = true
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
|
||||||
windows-sys.workspace = true
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
|
||||||
libloading = "0.8"
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# cef
|
|
||||||
|
|
||||||
Use the [Chromium Embedded Framework](https://github.com/chromiumembedded/cef) in Rust.
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
use std::ffi::{c_char, CString};
|
|
||||||
|
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
pub struct Args {
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
_source: Vec<CString>,
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
_argv: Vec<*const c_char>,
|
|
||||||
main_args: MainArgs,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args {
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let main_args = MainArgs {
|
|
||||||
instance: cef_dll_sys::HINSTANCE(
|
|
||||||
unsafe {
|
|
||||||
windows_sys::Win32::System::LibraryLoader::GetModuleHandleW(std::ptr::null())
|
|
||||||
}
|
|
||||||
.cast(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { main_args }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let args = std::env::args();
|
|
||||||
let _source = args
|
|
||||||
.into_iter()
|
|
||||||
.map(|arg| CString::new(arg).unwrap())
|
|
||||||
.collect::<Vec<CString>>();
|
|
||||||
let _argv = _source
|
|
||||||
.iter()
|
|
||||||
.map(|arg| arg.as_ptr())
|
|
||||||
.collect::<Vec<*const c_char>>();
|
|
||||||
let main_args = MainArgs {
|
|
||||||
argc: _argv.len() as i32,
|
|
||||||
argv: _argv.as_ptr() as *mut *mut _,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
_source,
|
|
||||||
_argv,
|
|
||||||
main_args,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_main_args(&self) -> &MainArgs {
|
|
||||||
&self.main_args
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
|
||||||
pub fn as_cmd_line(&self) -> Option<CommandLine> {
|
|
||||||
let Some(cmd_line) = command_line_create() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
cmd_line.init_from_argv(self.as_main_args().argc, self.as_main_args().argv.cast());
|
|
||||||
Some(cmd_line)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn as_cmd_line(&self) -> Option<CommandLine> {
|
|
||||||
let cmd_line = command_line_create().and_then(|cmd_line| {
|
|
||||||
unsafe {
|
|
||||||
std::ffi::CStr::from_ptr(
|
|
||||||
windows_sys::Win32::System::Environment::GetCommandLineA().cast(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.to_str()
|
|
||||||
.ok()
|
|
||||||
.map(|args| {
|
|
||||||
cmd_line.init_from_string(Some(&CefString::from(args)));
|
|
||||||
cmd_line
|
|
||||||
})
|
|
||||||
});
|
|
||||||
cmd_line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,177 +0,0 @@
|
|||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
|
||||||
mod x86_64_unknown_linux_gnu;
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
|
||||||
pub use x86_64_unknown_linux_gnu::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
|
||||||
mod aarch64_unknown_linux_gnu;
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
|
||||||
pub use aarch64_unknown_linux_gnu::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "arm"))]
|
|
||||||
mod arm_unknown_linux_gnueabi;
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "arm"))]
|
|
||||||
pub use arm_unknown_linux_gnueabi::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
|
||||||
mod x86_64_pc_windows_msvc;
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
|
||||||
pub use x86_64_pc_windows_msvc::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86"))]
|
|
||||||
mod i686_pc_windows_msvc;
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86"))]
|
|
||||||
pub use i686_pc_windows_msvc::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
|
|
||||||
mod aarch64_pc_windows_msvc;
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
|
|
||||||
pub use aarch64_pc_windows_msvc::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
|
||||||
mod x86_64_apple_darwin;
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
|
||||||
pub use x86_64_apple_darwin::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
||||||
mod aarch64_apple_darwin;
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
||||||
pub use aarch64_apple_darwin::*;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use crate::{rc::*, sys};
|
|
||||||
use std::{cell::RefCell, ptr};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct CallInfo {
|
|
||||||
extra_info: RefCell<Option<DictionaryValue>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestLifeSpanHandler {
|
|
||||||
object: *mut RcImpl<sys::_cef_life_span_handler_t, Self>,
|
|
||||||
call_info: std::rc::Rc<CallInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImplLifeSpanHandler for TestLifeSpanHandler {
|
|
||||||
fn get_raw(&self) -> *mut sys::_cef_life_span_handler_t {
|
|
||||||
self.object.cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_before_popup(
|
|
||||||
&self,
|
|
||||||
_browser: Option<&mut Browser>,
|
|
||||||
_frame: Option<&mut Frame>,
|
|
||||||
_popup_id: ::std::os::raw::c_int,
|
|
||||||
_target_url: Option<&CefString>,
|
|
||||||
_target_frame_name: Option<&CefString>,
|
|
||||||
_target_disposition: WindowOpenDisposition,
|
|
||||||
_user_gesture: ::std::os::raw::c_int,
|
|
||||||
_popup_features: Option<&PopupFeatures>,
|
|
||||||
_window_info: Option<&mut WindowInfo>,
|
|
||||||
_client: Option<&mut Option<Client>>,
|
|
||||||
_settings: Option<&mut BrowserSettings>,
|
|
||||||
extra_info: Option<&mut Option<DictionaryValue>>,
|
|
||||||
_no_javascript_access: Option<&mut ::std::os::raw::c_int>,
|
|
||||||
) -> ::std::os::raw::c_int {
|
|
||||||
let extra_info = extra_info.expect("extra_info is required");
|
|
||||||
*extra_info = self.call_info.extra_info.take();
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapLifeSpanHandler for TestLifeSpanHandler {
|
|
||||||
fn wrap_rc(&mut self, object: *mut RcImpl<sys::_cef_life_span_handler_t, Self>) {
|
|
||||||
self.object = object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestLifeSpanHandler {
|
|
||||||
fn new(call_info: std::rc::Rc<CallInfo>) -> LifeSpanHandler {
|
|
||||||
LifeSpanHandler::new(Self {
|
|
||||||
object: std::ptr::null_mut(),
|
|
||||||
call_info,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for TestLifeSpanHandler {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let object = unsafe {
|
|
||||||
let rc_impl = &mut *self.object;
|
|
||||||
rc_impl.interface.add_ref();
|
|
||||||
self.object
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
|
||||||
object,
|
|
||||||
call_info: self.call_info.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rc for TestLifeSpanHandler {
|
|
||||||
fn as_base(&self) -> &sys::cef_base_ref_counted_t {
|
|
||||||
unsafe {
|
|
||||||
let base = &*self.object;
|
|
||||||
std::mem::transmute(&base.cef_object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dictionary_value_out_param() {
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
unsafe {
|
|
||||||
use std::{ffi::CString, os::unix::ffi::OsStrExt};
|
|
||||||
|
|
||||||
let cef_dir = sys::get_cef_dir().expect("CEF not found");
|
|
||||||
let framework_dir = cef_dir
|
|
||||||
.join(sys::FRAMEWORK_PATH)
|
|
||||||
.canonicalize()
|
|
||||||
.expect("failed to get framework path");
|
|
||||||
let framework_dir =
|
|
||||||
CString::new(framework_dir.as_os_str().as_bytes()).expect("invalid path");
|
|
||||||
|
|
||||||
assert_eq!(sys::cef_load_library(framework_dir.as_ptr().cast()), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(initialize(None, None, None, ptr::null_mut()), 0);
|
|
||||||
|
|
||||||
let _ = api_hash(sys::CEF_API_VERSION_LAST, 0);
|
|
||||||
|
|
||||||
let call_info = std::rc::Rc::new(CallInfo::default());
|
|
||||||
let extra_info = dictionary_value_create().expect("failed to create dictionary");
|
|
||||||
let test_key = CefString::from("testKey");
|
|
||||||
let test_value = CefString::from("testValue");
|
|
||||||
extra_info.set_string(Some(&test_key), Some(&test_value));
|
|
||||||
*call_info.extra_info.borrow_mut() = Some(extra_info);
|
|
||||||
let mut extra_info = None;
|
|
||||||
|
|
||||||
let handler = TestLifeSpanHandler::new(call_info);
|
|
||||||
assert_eq!(
|
|
||||||
1,
|
|
||||||
handler.on_before_popup(
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
1,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
sys::cef_window_open_disposition_t::CEF_WOD_CURRENT_TAB.into(),
|
|
||||||
0,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(&mut extra_info),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
let extra_info = extra_info.as_ref().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
"testValue",
|
|
||||||
CefString::from(&extra_info.string(Some(&test_key))).to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,17 +0,0 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
|
||||||
|
|
||||||
pub mod args;
|
|
||||||
pub mod rc;
|
|
||||||
pub mod string;
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
pub mod library_loader;
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
pub mod sandbox;
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
mod bindings;
|
|
||||||
pub use bindings::*;
|
|
||||||
|
|
||||||
pub use cef_dll_sys as sys;
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
use crate::{load_library, unload_library};
|
|
||||||
|
|
||||||
pub struct LibraryLoader {
|
|
||||||
path: std::path::PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LibraryLoader {
|
|
||||||
const FRAMEWORK_PATH: &str =
|
|
||||||
"Chromium Embedded Framework.framework/Chromium Embedded Framework";
|
|
||||||
|
|
||||||
pub fn new(path: &std::path::Path, helper: bool) -> Self {
|
|
||||||
let resolver = if helper {
|
|
||||||
"../../../.."
|
|
||||||
} else {
|
|
||||||
"../../Frameworks"
|
|
||||||
};
|
|
||||||
let path = path
|
|
||||||
.join(resolver)
|
|
||||||
.join(Self::FRAMEWORK_PATH)
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Self { path }
|
|
||||||
}
|
|
||||||
|
|
||||||
// See [cef_load_library] for more documentation.
|
|
||||||
pub fn load(&self) -> bool {
|
|
||||||
Self::load_library(&self.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_library(name: &std::path::Path) -> bool {
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
let Ok(name) = std::ffi::CString::new(name.as_os_str().as_bytes()) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
unsafe { load_library(Some(&*name.as_ptr().cast())) == 1 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LibraryLoader {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if unload_library() != 1 {
|
|
||||||
eprintln!("cannot unload framework {}", self.path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,380 +0,0 @@
|
|||||||
//! Reference counted module
|
|
||||||
//!
|
|
||||||
//! Many cef types are reference counted, this module is the building block to create them. Users
|
|
||||||
//! typically don't need to uses these types, the `update-bindings` tool generates all the code
|
|
||||||
//! which should ever call them.
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
fmt::Debug,
|
|
||||||
mem,
|
|
||||||
ops::Deref,
|
|
||||||
ptr::{self, NonNull},
|
|
||||||
sync::atomic::{fence, AtomicUsize, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use cef_dll_sys::cef_base_ref_counted_t;
|
|
||||||
|
|
||||||
/// Reference counted trait for types has [`cef_base_ref_counted_t`].
|
|
||||||
pub trait Rc {
|
|
||||||
/// Increase the reference count by 1.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Calling this method when you need to manually handle the reference count.
|
|
||||||
/// Otherwise, these methods shouldn't be called externally in most cases.
|
|
||||||
unsafe fn add_ref(&self) {
|
|
||||||
self.as_base().add_ref();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decrease reference count by 1 and release the value if the count meets 0.
|
|
||||||
/// Reuturn `True` if it is released.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Calling this method when you need to manually handle the reference count.
|
|
||||||
/// Otherwise, these methods shouldn't be called externally in most cases.
|
|
||||||
unsafe fn release(&self) -> bool {
|
|
||||||
self.as_base().release()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `True` if the reference count is exactly 1.
|
|
||||||
fn has_one_ref(&self) -> bool {
|
|
||||||
self.as_base().has_one_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `True` if the reference count is larger than 0.
|
|
||||||
fn has_at_least_one_ref(&self) -> bool {
|
|
||||||
self.as_base().has_at_least_one_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the reference of [cef_base_ref_counted_t].
|
|
||||||
fn as_base(&self) -> &cef_base_ref_counted_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rc for cef_base_ref_counted_t {
|
|
||||||
unsafe fn add_ref(&self) {
|
|
||||||
if let Some(add_ref) = self.add_ref {
|
|
||||||
add_ref(ptr::from_ref(self) as *mut _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_one_ref(&self) -> bool {
|
|
||||||
if let Some(has_one_ref) = self.has_one_ref {
|
|
||||||
let result = unsafe { has_one_ref(ptr::from_ref(self) as *mut _) };
|
|
||||||
return result == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_at_least_one_ref(&self) -> bool {
|
|
||||||
if let Some(has_at_least_one_ref) = self.has_at_least_one_ref {
|
|
||||||
let result = unsafe { has_at_least_one_ref(ptr::from_ref(self) as *mut _) };
|
|
||||||
return result == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn release(&self) -> bool {
|
|
||||||
if let Some(release) = self.release {
|
|
||||||
return release(ptr::from_ref(self) as *mut _) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_base(&self) -> &Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ConvertParam<T: Sized> {
|
|
||||||
fn into_raw(self) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> ConvertParam<U> for T
|
|
||||||
where
|
|
||||||
T: Sized + Into<U>,
|
|
||||||
U: Sized,
|
|
||||||
{
|
|
||||||
fn into_raw(self) -> U {
|
|
||||||
self.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ConvertParam<*mut T> for &RefGuard<T>
|
|
||||||
where
|
|
||||||
T: Sized + Rc,
|
|
||||||
{
|
|
||||||
/// Access the [RefGuard] and return the raw pointer without decreasing the reference count.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This should be used when you need to pass wrapper type to the FFI function as **parameter**, and it is **not**
|
|
||||||
/// the `self` type (usually the first parameter). This means we pass the ownership of the
|
|
||||||
/// value to the function call. Using this method elsewehre may cause incorrect reference count
|
|
||||||
/// and memory safety issues.
|
|
||||||
fn into_raw(self) -> *mut T {
|
|
||||||
unsafe { self.into_raw() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WrapParamRef<T, P>
|
|
||||||
where
|
|
||||||
T: Sized + Into<P>,
|
|
||||||
P: Sized + Copy + Into<T>,
|
|
||||||
{
|
|
||||||
value: T,
|
|
||||||
output: Option<NonNull<P>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, P> Drop for WrapParamRef<T, P>
|
|
||||||
where
|
|
||||||
T: Sized + Into<P>,
|
|
||||||
P: Sized + Copy + Into<T>,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(output) = &mut self.output {
|
|
||||||
let output = unsafe { output.as_mut() };
|
|
||||||
let mut value = unsafe { mem::zeroed() };
|
|
||||||
mem::swap(&mut self.value, &mut value);
|
|
||||||
*output = value.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, P> From<*mut P> for WrapParamRef<T, P>
|
|
||||||
where
|
|
||||||
T: Sized + Into<P>,
|
|
||||||
P: Sized + Copy + Into<T>,
|
|
||||||
{
|
|
||||||
fn from(value: *mut P) -> Self {
|
|
||||||
let mut output = NonNull::new(value);
|
|
||||||
let value = output
|
|
||||||
.as_mut()
|
|
||||||
.map(|p| {
|
|
||||||
let mut value = unsafe { mem::zeroed() };
|
|
||||||
mem::swap(unsafe { p.as_mut() }, &mut value);
|
|
||||||
value.into()
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| unsafe { mem::zeroed() });
|
|
||||||
|
|
||||||
Self { value, output }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, P> From<*const P> for WrapParamRef<T, P>
|
|
||||||
where
|
|
||||||
T: Sized + Into<P>,
|
|
||||||
P: Sized + Copy + Into<T>,
|
|
||||||
{
|
|
||||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
|
||||||
fn from(value: *const P) -> Self {
|
|
||||||
let value = unsafe { value.as_ref() }
|
|
||||||
.map(|value| (*value).into())
|
|
||||||
.unwrap_or_else(|| unsafe { mem::zeroed() });
|
|
||||||
|
|
||||||
Self {
|
|
||||||
value,
|
|
||||||
output: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, P> AsMut<T> for WrapParamRef<T, P>
|
|
||||||
where
|
|
||||||
T: Sized + Into<P>,
|
|
||||||
P: Sized + Copy + Into<T>,
|
|
||||||
{
|
|
||||||
fn as_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, P> AsRef<T> for WrapParamRef<T, P>
|
|
||||||
where
|
|
||||||
T: Sized + Into<P>,
|
|
||||||
P: Sized + Copy + Into<T>,
|
|
||||||
{
|
|
||||||
fn as_ref(&self) -> &T {
|
|
||||||
&self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ConvertReturnValue<T: Sized> {
|
|
||||||
fn wrap_result(self) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> ConvertReturnValue<U> for T
|
|
||||||
where
|
|
||||||
T: Sized + Into<U>,
|
|
||||||
U: Sized,
|
|
||||||
{
|
|
||||||
fn wrap_result(self) -> U {
|
|
||||||
self.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A smart pointer for types from cef library.
|
|
||||||
pub struct RefGuard<T: Rc> {
|
|
||||||
object: *mut T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug + Rc> Debug for RefGuard<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let object_ref = unsafe { self.object.as_ref() };
|
|
||||||
write!(f, "RefGuard({object_ref:#?})")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Rc> RefGuard<T> {
|
|
||||||
/// Create [RefGuard] from a raw C pointer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This should be used to get the **return value** of the FFI function. This means we get the
|
|
||||||
/// ownership of the value. The reference count of the return value is already increased when
|
|
||||||
/// you get it. So we don't need to increase it again manually. Using this method elsewhere may
|
|
||||||
/// cause incorrect reference count and memory safety issues.
|
|
||||||
pub unsafe fn from_raw(ptr: *mut T) -> RefGuard<T> {
|
|
||||||
RefGuard { object: ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create [RefGuard] from a raw C pointer and increase a reference count. This should be used
|
|
||||||
/// when you want to copy the value and create another wrapper type.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// THis should be used when you want to manually increase the reference count upon getting the
|
|
||||||
/// raw pointer. Using this method elsewhere may cause incorrect reference count and memory
|
|
||||||
/// safety issues.
|
|
||||||
pub unsafe fn from_raw_add_ref(ptr: *mut T) -> RefGuard<T> {
|
|
||||||
let guard = RefGuard { object: ptr };
|
|
||||||
|
|
||||||
guard.add_ref();
|
|
||||||
|
|
||||||
guard
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the raw pointer of [RefGuard].
|
|
||||||
//
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This should be used when you need to pass wrapper type to the FFI function as **parameter**, and it **is**
|
|
||||||
/// the `self` type (usually the first parameter). This means we pass the ownership of the
|
|
||||||
/// value to the function call. Using this method elsewhere may cause incorrect reference count
|
|
||||||
/// and memory safety issues.
|
|
||||||
pub unsafe fn into_raw(&self) -> *mut T {
|
|
||||||
self.object
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the value to another value that is also reference counted.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This should be used when the type has type `U` as its base type. Using this method
|
|
||||||
/// elsewhere may cause memory safety issues.
|
|
||||||
pub unsafe fn convert<U: Rc>(&self) -> RefGuard<U> {
|
|
||||||
RefGuard::from_raw_add_ref(self.into_raw().cast())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Rc> Send for RefGuard<T> {}
|
|
||||||
unsafe impl<T: Rc> Sync for RefGuard<T> {}
|
|
||||||
|
|
||||||
impl<T: Rc> Clone for RefGuard<T> {
|
|
||||||
fn clone(&self) -> RefGuard<T> {
|
|
||||||
unsafe { self.add_ref() };
|
|
||||||
|
|
||||||
RefGuard {
|
|
||||||
object: self.object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Rc> Deref for RefGuard<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
unsafe { &*self.object }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Rc> Drop for RefGuard<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { self.release() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// There are some types require users to implement one their own in Rust and then create a raw type around it to
|
|
||||||
/// pass to sys level api. This is the wrapper type for it.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct RcImpl<T, I> {
|
|
||||||
/// Raw cef types
|
|
||||||
pub cef_object: T,
|
|
||||||
/// Rust interface of such type
|
|
||||||
pub interface: I,
|
|
||||||
ref_count: AtomicUsize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, I> RcImpl<T, I> {
|
|
||||||
pub fn new(mut cef_object: T, interface: I) -> *mut RcImpl<T, I> {
|
|
||||||
let base: &mut cef_base_ref_counted_t =
|
|
||||||
unsafe { &mut *(ptr::from_mut(&mut cef_object).cast()) };
|
|
||||||
|
|
||||||
base.size = std::mem::size_of::<T>();
|
|
||||||
base.add_ref = Some(add_ref::<T, I>);
|
|
||||||
base.has_one_ref = Some(has_one_ref::<T, I>);
|
|
||||||
base.has_at_least_one_ref = Some(has_at_least_one_ref::<T, I>);
|
|
||||||
base.release = Some(release::<T, I>);
|
|
||||||
|
|
||||||
Box::into_raw(Box::new(RcImpl {
|
|
||||||
cef_object,
|
|
||||||
interface,
|
|
||||||
ref_count: AtomicUsize::new(1),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<'a>(ptr: *mut T) -> &'a mut RcImpl<T, I> {
|
|
||||||
unsafe { &mut *(ptr.cast()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn add_ref<T, I>(this: *mut cef_base_ref_counted_t) {
|
|
||||||
let obj = RcImpl::<T, I>::get(this.cast());
|
|
||||||
|
|
||||||
obj.ref_count.fetch_add(1, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn has_one_ref<T, I>(this: *mut cef_base_ref_counted_t) -> i32 {
|
|
||||||
let obj = RcImpl::<T, I>::get(this.cast());
|
|
||||||
|
|
||||||
if obj.ref_count.load(Ordering::Relaxed) == 1 {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn has_at_least_one_ref<T, I>(this: *mut cef_base_ref_counted_t) -> i32 {
|
|
||||||
let obj = RcImpl::<T, I>::get(this.cast());
|
|
||||||
|
|
||||||
if obj.ref_count.load(Ordering::Relaxed) >= 1 {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn release<T, I>(this: *mut cef_base_ref_counted_t) -> i32 {
|
|
||||||
let obj = RcImpl::<T, I>::get(this.cast());
|
|
||||||
|
|
||||||
if obj.ref_count.fetch_sub(1, Ordering::Release) != 1 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
fence(Ordering::Acquire);
|
|
||||||
let _: Box<RcImpl<T, I>> = unsafe { Box::from_raw(this.cast()) };
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
use libloading::Library;
|
|
||||||
use std::{ffi::c_void, ptr::NonNull};
|
|
||||||
|
|
||||||
use crate::MainArgs;
|
|
||||||
|
|
||||||
pub struct Sandbox {
|
|
||||||
lib: Library,
|
|
||||||
context: Option<NonNull<c_void>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sandbox {
|
|
||||||
const LIBCEF_SANDBOX_PATH: &str =
|
|
||||||
"../../../../Chromium Embedded Framework.framework/Libraries/libcef_sandbox.dylib";
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
unsafe {
|
|
||||||
let lib = Library::new(
|
|
||||||
std::env::current_exe()
|
|
||||||
.unwrap()
|
|
||||||
.join(Self::LIBCEF_SANDBOX_PATH)
|
|
||||||
.canonicalize()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
Self { lib, context: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize(&mut self, args: &MainArgs) {
|
|
||||||
let inner = unsafe {
|
|
||||||
self.lib
|
|
||||||
.get::<extern "C" fn(
|
|
||||||
argc: std::os::raw::c_int,
|
|
||||||
argv: *mut *mut ::std::os::raw::c_char,
|
|
||||||
) -> *mut c_void>(b"cef_sandbox_initialize")
|
|
||||||
.unwrap()(args.argc, args.argv)
|
|
||||||
};
|
|
||||||
self.context = NonNull::new(inner);
|
|
||||||
assert!(self.context.is_some());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Sandbox {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
if let Some(inner) = self.context {
|
|
||||||
self.lib
|
|
||||||
.get::<extern "C" fn(context: *mut c_void)>(b"cef_sandbox_destroy")
|
|
||||||
.unwrap()(inner.as_ptr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,49 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## [2.2.0](https://github.com/tauri-apps/cef-rs/compare/download-cef-v2.1.0...download-cef-v2.2.0) - 2025-09-23
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- add --mirror-url cli args
|
|
||||||
- Allow to download CEF binaries with custom base url set in env variable
|
|
||||||
|
|
||||||
## [2.1.0](https://github.com/tauri-apps/cef-rs/compare/download-cef-v2.0.3...download-cef-v2.1.0) - 2025-09-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- support pre-downloaded archive ([#197](https://github.com/tauri-apps/cef-rs/pull/197))
|
|
||||||
|
|
||||||
## [2.0.3](https://github.com/tauri-apps/cef-rs/compare/download-cef-v2.0.2...download-cef-v2.0.3) - 2025-08-16
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- #183
|
|
||||||
|
|
||||||
## [2.0.2](https://github.com/tauri-apps/cef-rs/compare/download-cef-v2.0.1...download-cef-v2.0.2) - 2025-07-22
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(doc)* regenerate CHANGELOG.md
|
|
||||||
|
|
||||||
## [2.0.1](https://github.com/tauri-apps/cef-rs/compare/download-cef-v2.0.0...download-cef-v2.0.1) - 2025-07-14
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
|
|
||||||
## [2.0.0](https://github.com/tauri-apps/cef-rs/compare/download-cef-v1.6.0...download-cef-v2.0.0) - 2025-07-14
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- bump major version of download-cef [#145](https://github.com/tauri-apps/cef-rs/issues/145)
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "download-cef"
|
|
||||||
description = "Download and extract pre-built CEF (Chromium Embedded Framework) archives."
|
|
||||||
version = "2.2.0"
|
|
||||||
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
clap.workspace = true
|
|
||||||
regex.workspace = true
|
|
||||||
semver.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
|
|
||||||
bzip2 = "0.6"
|
|
||||||
indicatif = "0.18"
|
|
||||||
sha1_smol = "1"
|
|
||||||
tar = "0.4"
|
|
||||||
ureq = { version = "3", features = ["json", "socks-proxy"] }
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
# download-cef
|
|
||||||
|
|
||||||
Utility functions to download and extract prebuilt [Chromium Embedded Framework](https://github.com/chromiumembedded/cef)
|
|
||||||
archives on any supported platform.
|
|
||||||
@ -1,644 +0,0 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
|
||||||
|
|
||||||
use bzip2::bufread::BzDecoder;
|
|
||||||
use clap::ValueEnum;
|
|
||||||
use regex::Regex;
|
|
||||||
use semver::Version;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sha1_smol::Sha1;
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
env,
|
|
||||||
fmt::{self, Display},
|
|
||||||
fs::{self, File},
|
|
||||||
io::{self, BufReader, IsTerminal, Write},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::{Mutex, OnceLock},
|
|
||||||
thread,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate thiserror;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Unsupported target triplet: {0}")]
|
|
||||||
UnsupportedTarget(String),
|
|
||||||
#[error("HTTP request error: {0}")]
|
|
||||||
Request(#[from] ureq::Error),
|
|
||||||
#[error("Invalid version: {0}")]
|
|
||||||
InvalidVersion(#[from] semver::Error),
|
|
||||||
#[error("Version not found: {0}")]
|
|
||||||
VersionNotFound(String),
|
|
||||||
#[error("Missing Content-Length header")]
|
|
||||||
MissingContentLength,
|
|
||||||
#[error("Opaque Content-Length header: {0}")]
|
|
||||||
OpaqueContentLength(#[from] ureq::http::header::ToStrError),
|
|
||||||
#[error("Invalid Content-Length header: {0}")]
|
|
||||||
InvalidContentLength(String),
|
|
||||||
#[error("File I/O error: {0}")]
|
|
||||||
Io(#[from] std::io::Error),
|
|
||||||
#[error("Unexpected file size: downloaded {downloaded} expected {expected}")]
|
|
||||||
UnexpectedFileSize { downloaded: u64, expected: u64 },
|
|
||||||
#[error("Bad SHA1 file hash: {0}")]
|
|
||||||
CorruptedFile(String),
|
|
||||||
#[error("Invalid archive file path: {0}")]
|
|
||||||
InvalidArchiveFile(String),
|
|
||||||
#[error("JSON serialization error: {0}")]
|
|
||||||
Json(#[from] serde_json::Error),
|
|
||||||
#[error(
|
|
||||||
"Undexpected archive version: location: {location} archive {archive} expected {expected}"
|
|
||||||
)]
|
|
||||||
VersionMismatch {
|
|
||||||
location: String,
|
|
||||||
archive: String,
|
|
||||||
expected: String,
|
|
||||||
},
|
|
||||||
#[error("Invalid regex pattern: {0}")]
|
|
||||||
InvalidRegexPattern(#[from] regex::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub const LINUX_TARGETS: &[&str] = &[
|
|
||||||
"x86_64-unknown-linux-gnu",
|
|
||||||
"arm-unknown-linux-gnueabi",
|
|
||||||
"aarch64-unknown-linux-gnu",
|
|
||||||
];
|
|
||||||
|
|
||||||
pub const MACOS_TARGETS: &[&str] = &["aarch64-apple-darwin", "x86_64-apple-darwin"];
|
|
||||||
|
|
||||||
pub const WINDOWS_TARGETS: &[&str] = &[
|
|
||||||
"x86_64-pc-windows-msvc",
|
|
||||||
"aarch64-pc-windows-msvc",
|
|
||||||
"i686-pc-windows-msvc",
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn default_version(version: &str) -> String {
|
|
||||||
unwrap_cef_version(version).unwrap_or_else(|_| version.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unwrap_cef_version(version: &str) -> Result<String> {
|
|
||||||
static VERSIONS: OnceLock<Mutex<HashMap<Version, String>>> = OnceLock::new();
|
|
||||||
let mut versions = VERSIONS
|
|
||||||
.get_or_init(Default::default)
|
|
||||||
.lock()
|
|
||||||
.expect("Lock error");
|
|
||||||
Ok(versions
|
|
||||||
.entry(Version::parse(version)?)
|
|
||||||
.or_insert_with_key(|v| {
|
|
||||||
if v.build.is_empty() {
|
|
||||||
version.to_string()
|
|
||||||
} else {
|
|
||||||
v.build.to_string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_archive_json(version: &str, location: &str) -> Result<()> {
|
|
||||||
let expected = Version::parse(&unwrap_cef_version(version)?)?;
|
|
||||||
|
|
||||||
static PATTERN: OnceLock<core::result::Result<Regex, regex::Error>> = OnceLock::new();
|
|
||||||
let pattern = PATTERN
|
|
||||||
.get_or_init(|| Regex::new(r"^cef_binary_([^+]+)(:?\+.+)?$"))
|
|
||||||
.as_ref()
|
|
||||||
.map_err(Clone::clone)?;
|
|
||||||
let archive_json: CefFile = serde_json::from_reader(File::open(archive_json_path(location))?)?;
|
|
||||||
let archive_version = pattern.replace(&archive_json.name, "$1");
|
|
||||||
let archive = Version::parse(&archive_version)?;
|
|
||||||
|
|
||||||
if archive <= expected {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::VersionMismatch {
|
|
||||||
location: location.to_string(),
|
|
||||||
expected: expected.to_string(),
|
|
||||||
archive: archive.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn archive_json_path<P>(location: P) -> PathBuf
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
location.as_ref().join("archive.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const DEFAULT_CDN_URL: &str = "https://cef-builds.spotifycdn.com";
|
|
||||||
|
|
||||||
pub fn default_download_url() -> String {
|
|
||||||
env::var("CEF_DOWNLOAD_URL").unwrap_or(DEFAULT_CDN_URL.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Deserialize, Serialize, ValueEnum)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum Channel {
|
|
||||||
Stable,
|
|
||||||
Beta,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Channel {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Channel::Stable => write!(f, "stable"),
|
|
||||||
Channel::Beta => write!(f, "beta"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Default)]
|
|
||||||
pub struct CefIndex {
|
|
||||||
pub macosarm64: CefPlatform,
|
|
||||||
pub macosx64: CefPlatform,
|
|
||||||
pub windows64: CefPlatform,
|
|
||||||
pub windowsarm64: CefPlatform,
|
|
||||||
pub windows32: CefPlatform,
|
|
||||||
pub linux64: CefPlatform,
|
|
||||||
pub linuxarm64: CefPlatform,
|
|
||||||
pub linuxarm: CefPlatform,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CefIndex {
|
|
||||||
pub fn download() -> Result<Self> {
|
|
||||||
Self::download_from(DEFAULT_CDN_URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn download_from(url: &str) -> Result<Self> {
|
|
||||||
Ok(ureq::get(&format!("{url}/index.json"))
|
|
||||||
.call()?
|
|
||||||
.into_body()
|
|
||||||
.read_json()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn platform(&self, target: &str) -> Result<&CefPlatform> {
|
|
||||||
match target {
|
|
||||||
"aarch64-apple-darwin" => Ok(&self.macosarm64),
|
|
||||||
"x86_64-apple-darwin" => Ok(&self.macosx64),
|
|
||||||
"x86_64-pc-windows-msvc" => Ok(&self.windows64),
|
|
||||||
"aarch64-pc-windows-msvc" => Ok(&self.windowsarm64),
|
|
||||||
"i686-pc-windows-msvc" => Ok(&self.windows32),
|
|
||||||
"x86_64-unknown-linux-gnu" => Ok(&self.linux64),
|
|
||||||
"aarch64-unknown-linux-gnu" => Ok(&self.linuxarm64),
|
|
||||||
"arm-unknown-linux-gnueabi" => Ok(&self.linuxarm),
|
|
||||||
v => Err(Error::UnsupportedTarget(v.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Default)]
|
|
||||||
pub struct CefPlatform {
|
|
||||||
pub versions: Vec<CefVersion>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CefPlatform {
|
|
||||||
pub fn version(&self, cef_version: &str) -> Result<&CefVersion> {
|
|
||||||
let version_prefix = format!("{cef_version}+");
|
|
||||||
self.versions
|
|
||||||
.iter()
|
|
||||||
.find(|v| v.cef_version.starts_with(&version_prefix))
|
|
||||||
.ok_or_else(|| Error::VersionNotFound(cef_version.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn latest(&self, channel: Channel) -> Result<&CefVersion> {
|
|
||||||
static PATTERN: OnceLock<core::result::Result<Regex, regex::Error>> = OnceLock::new();
|
|
||||||
let pattern = PATTERN
|
|
||||||
.get_or_init(|| Regex::new(r"^([^+]+)(:?\+.+)?$"))
|
|
||||||
.as_ref()
|
|
||||||
.map_err(Clone::clone)?;
|
|
||||||
|
|
||||||
self.versions
|
|
||||||
.iter()
|
|
||||||
.filter_map(|value| {
|
|
||||||
if value.channel == channel {
|
|
||||||
let key = Version::parse(&pattern.replace(&value.cef_version, "$1")).ok()?;
|
|
||||||
Some((key, value))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.max_by(|(a, _), (b, _)| a.cmp(b))
|
|
||||||
.map(|(_, v)| v)
|
|
||||||
.ok_or_else(|| Error::VersionNotFound("latest".to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct CefVersion {
|
|
||||||
pub channel: Channel,
|
|
||||||
pub cef_version: String,
|
|
||||||
pub files: Vec<CefFile>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CefVersion {
|
|
||||||
pub fn download_archive<P>(&self, location: P, show_progress: bool) -> Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
self.download_archive_from(DEFAULT_CDN_URL, location, show_progress)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn download_archive_from<P>(
|
|
||||||
&self,
|
|
||||||
url: &str,
|
|
||||||
location: P,
|
|
||||||
show_progress: bool,
|
|
||||||
) -> Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let file = self.minimal()?;
|
|
||||||
let (file, sha) = (file.name.as_str(), file.sha1.as_str());
|
|
||||||
|
|
||||||
fs::create_dir_all(&location)?;
|
|
||||||
let download_file = location.as_ref().join(file);
|
|
||||||
|
|
||||||
if download_file.exists() {
|
|
||||||
if calculate_file_sha1(&download_file) == sha {
|
|
||||||
if show_progress {
|
|
||||||
println!("Verified archive: {}", download_file.display());
|
|
||||||
}
|
|
||||||
return Ok(download_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
if show_progress {
|
|
||||||
println!("Cleaning corrupted archive: {}", download_file.display());
|
|
||||||
}
|
|
||||||
let corrupted_file = location.as_ref().join(format!("corrupted_{file}"));
|
|
||||||
fs::rename(&download_file, &corrupted_file)?;
|
|
||||||
fs::remove_file(&corrupted_file)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cef_url = format!("{url}/{file}");
|
|
||||||
if show_progress {
|
|
||||||
println!("Using archive url: {cef_url}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file = File::create(&download_file)?;
|
|
||||||
|
|
||||||
let resp = ureq::get(&cef_url).call()?;
|
|
||||||
let expected = resp
|
|
||||||
.headers()
|
|
||||||
.get("Content-Length")
|
|
||||||
.ok_or(Error::MissingContentLength)?;
|
|
||||||
let expected = expected.to_str()?;
|
|
||||||
let expected = expected
|
|
||||||
.parse::<u64>()
|
|
||||||
.map_err(|_| Error::InvalidContentLength(expected.to_owned()))?;
|
|
||||||
|
|
||||||
let downloaded = if show_progress && io::stdout().is_terminal() {
|
|
||||||
const DOWNLOAD_TEMPLATE: &str = "{msg} {spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})";
|
|
||||||
|
|
||||||
let bar = indicatif::ProgressBar::new(expected);
|
|
||||||
bar.set_style(
|
|
||||||
indicatif::ProgressStyle::with_template(DOWNLOAD_TEMPLATE)
|
|
||||||
.expect("invalid template")
|
|
||||||
.progress_chars("##-"),
|
|
||||||
);
|
|
||||||
bar.set_message("Downloading");
|
|
||||||
std::io::copy(
|
|
||||||
&mut bar.wrap_read(resp.into_body().into_reader()),
|
|
||||||
&mut file,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let mut reader = resp.into_body().into_reader();
|
|
||||||
std::io::copy(&mut reader, &mut file)
|
|
||||||
}?;
|
|
||||||
|
|
||||||
if downloaded != expected {
|
|
||||||
return Err(Error::UnexpectedFileSize {
|
|
||||||
downloaded,
|
|
||||||
expected,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if show_progress {
|
|
||||||
println!("Verifying SHA1 hash: {sha}...");
|
|
||||||
}
|
|
||||||
if calculate_file_sha1(&download_file) != sha {
|
|
||||||
return Err(Error::CorruptedFile(download_file.display().to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if show_progress {
|
|
||||||
println!("Downloaded archive: {}", download_file.display());
|
|
||||||
}
|
|
||||||
Ok(download_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn download_archive_with_retry<P>(
|
|
||||||
&self,
|
|
||||||
location: P,
|
|
||||||
show_progress: bool,
|
|
||||||
retry_delay: Duration,
|
|
||||||
max_retries: u32,
|
|
||||||
) -> Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
self.download_archive_with_retry_from(
|
|
||||||
DEFAULT_CDN_URL,
|
|
||||||
location,
|
|
||||||
show_progress,
|
|
||||||
retry_delay,
|
|
||||||
max_retries,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn download_archive_with_retry_from<P>(
|
|
||||||
&self,
|
|
||||||
url: &str,
|
|
||||||
location: P,
|
|
||||||
show_progress: bool,
|
|
||||||
retry_delay: Duration,
|
|
||||||
max_retries: u32,
|
|
||||||
) -> Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let mut result = self.download_archive_from(&url, &location, show_progress);
|
|
||||||
|
|
||||||
let mut retry = 0;
|
|
||||||
while let Err(Error::Io(_)) = &result {
|
|
||||||
if retry >= max_retries {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
retry += 1;
|
|
||||||
thread::sleep(retry_delay * retry);
|
|
||||||
|
|
||||||
result = self.download_archive_from(&url, &location, show_progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn minimal(&self) -> Result<&CefFile> {
|
|
||||||
self.files
|
|
||||||
.iter()
|
|
||||||
.find(|f| f.file_type == "minimal")
|
|
||||||
.ok_or_else(|| Error::VersionNotFound(self.cef_version.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_archive_json<P>(&self, location: P) -> Result<()>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
self.minimal()?.write_archive_json(location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize)]
|
|
||||||
pub struct CefFile {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub file_type: String,
|
|
||||||
pub name: String,
|
|
||||||
pub sha1: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CefFile {
|
|
||||||
pub fn write_archive_json<P>(&self, location: P) -> Result<()>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let archive_version = serde_json::to_string_pretty(self)?;
|
|
||||||
let mut archive_json = File::create(archive_json_path(location))?;
|
|
||||||
archive_json.write_all(archive_version.as_bytes())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Path> for CefFile {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(location: &Path) -> Result<Self> {
|
|
||||||
let file_type = "minimal".to_string();
|
|
||||||
let name = location
|
|
||||||
.file_name()
|
|
||||||
.map(|f| f.display().to_string())
|
|
||||||
.ok_or_else(|| Error::InvalidArchiveFile(location.display().to_string()))?;
|
|
||||||
let sha1 = calculate_file_sha1(location);
|
|
||||||
Ok(Self {
|
|
||||||
file_type,
|
|
||||||
name,
|
|
||||||
sha1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn download_target_archive<P>(
|
|
||||||
target: &str,
|
|
||||||
cef_version: &str,
|
|
||||||
location: P,
|
|
||||||
show_progress: bool,
|
|
||||||
) -> Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
download_target_archive_from(
|
|
||||||
DEFAULT_CDN_URL,
|
|
||||||
target,
|
|
||||||
cef_version,
|
|
||||||
location,
|
|
||||||
show_progress,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn download_target_archive_from<P>(
|
|
||||||
url: &str,
|
|
||||||
target: &str,
|
|
||||||
cef_version: &str,
|
|
||||||
location: P,
|
|
||||||
show_progress: bool,
|
|
||||||
) -> Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
if show_progress {
|
|
||||||
println!("Downloading CEF archive for {target}...");
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = CefIndex::download_from(&url)?;
|
|
||||||
let platform = index.platform(target)?;
|
|
||||||
let version = platform.version(cef_version)?;
|
|
||||||
|
|
||||||
version.download_archive_with_retry_from(
|
|
||||||
url,
|
|
||||||
location,
|
|
||||||
show_progress,
|
|
||||||
Duration::from_secs(15),
|
|
||||||
3,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extract_target_archive<P, Q>(
|
|
||||||
target: &str,
|
|
||||||
archive: P,
|
|
||||||
location: Q,
|
|
||||||
show_progress: bool,
|
|
||||||
) -> Result<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
Q: AsRef<Path>,
|
|
||||||
{
|
|
||||||
if show_progress {
|
|
||||||
println!("Extracting archive: {}", archive.as_ref().display());
|
|
||||||
}
|
|
||||||
let decoder = BzDecoder::new(BufReader::new(File::open(&archive)?));
|
|
||||||
tar::Archive::new(decoder).unpack(&location)?;
|
|
||||||
|
|
||||||
let extracted_dir = archive.as_ref().display().to_string();
|
|
||||||
let extracted_dir = extracted_dir
|
|
||||||
.strip_suffix(".tar.bz2")
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.ok_or(Error::InvalidArchiveFile(extracted_dir))?;
|
|
||||||
|
|
||||||
let os_and_arch = OsAndArch::try_from(target)?;
|
|
||||||
let OsAndArch { os, arch } = os_and_arch;
|
|
||||||
let cef_dir = os_and_arch.to_string();
|
|
||||||
let cef_dir = location.as_ref().join(cef_dir);
|
|
||||||
|
|
||||||
if cef_dir.exists() {
|
|
||||||
let old_dir = location.as_ref().join(format!("old_{os}_{arch}"));
|
|
||||||
if show_progress {
|
|
||||||
println!("Cleaning up: {}", old_dir.display());
|
|
||||||
}
|
|
||||||
fs::rename(&cef_dir, &old_dir)?;
|
|
||||||
fs::remove_dir_all(old_dir)?;
|
|
||||||
}
|
|
||||||
const RELEASE_DIR: &str = "Release";
|
|
||||||
fs::rename(extracted_dir.join(RELEASE_DIR), &cef_dir)?;
|
|
||||||
|
|
||||||
if os != "macos" {
|
|
||||||
let resources = extracted_dir.join("Resources");
|
|
||||||
|
|
||||||
for entry in fs::read_dir(&resources)? {
|
|
||||||
let entry = entry?;
|
|
||||||
fs::rename(entry.path(), cef_dir.join(entry.file_name()))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CMAKE_LISTS_TXT: &str = "CMakeLists.txt";
|
|
||||||
fs::rename(
|
|
||||||
extracted_dir.join(CMAKE_LISTS_TXT),
|
|
||||||
cef_dir.join(CMAKE_LISTS_TXT),
|
|
||||||
)?;
|
|
||||||
const CMAKE_DIR: &str = "cmake";
|
|
||||||
fs::rename(extracted_dir.join(CMAKE_DIR), cef_dir.join(CMAKE_DIR))?;
|
|
||||||
const INCLUDE_DIR: &str = "include";
|
|
||||||
fs::rename(extracted_dir.join(INCLUDE_DIR), cef_dir.join(INCLUDE_DIR))?;
|
|
||||||
const LIBCEF_DLL_DIR: &str = "libcef_dll";
|
|
||||||
fs::rename(
|
|
||||||
extracted_dir.join(LIBCEF_DLL_DIR),
|
|
||||||
cef_dir.join(LIBCEF_DLL_DIR),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if show_progress {
|
|
||||||
println!("Moved contents to: {}", cef_dir.display());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup whatever is left in the extracted directory.
|
|
||||||
let old_dir = extracted_dir
|
|
||||||
.parent()
|
|
||||||
.map(|parent| parent.join(format!("extracted_{os}_{arch}")))
|
|
||||||
.ok_or_else(|| Error::InvalidArchiveFile(extracted_dir.display().to_string()))?;
|
|
||||||
if show_progress {
|
|
||||||
println!("Cleaning up: {}", old_dir.display());
|
|
||||||
}
|
|
||||||
fs::rename(&extracted_dir, &old_dir)?;
|
|
||||||
fs::remove_dir_all(old_dir)?;
|
|
||||||
|
|
||||||
Ok(cef_dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_file_sha1(path: &Path) -> String {
|
|
||||||
use std::io::Read;
|
|
||||||
let mut file = BufReader::new(File::open(path).unwrap());
|
|
||||||
let mut sha1 = Sha1::new();
|
|
||||||
let mut buffer = [0; 8192];
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let count = file.read(&mut buffer).unwrap();
|
|
||||||
if count == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sha1.update(&buffer[..count]);
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1.digest().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OsAndArch {
|
|
||||||
pub os: &'static str,
|
|
||||||
pub arch: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for OsAndArch {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let os = self.os;
|
|
||||||
let arch = self.arch;
|
|
||||||
write!(f, "cef_{os}_{arch}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for OsAndArch {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(target: &str) -> Result<Self> {
|
|
||||||
match target {
|
|
||||||
"aarch64-apple-darwin" => Ok(OsAndArch {
|
|
||||||
os: "macos",
|
|
||||||
arch: "aarch64",
|
|
||||||
}),
|
|
||||||
"x86_64-apple-darwin" => Ok(OsAndArch {
|
|
||||||
os: "macos",
|
|
||||||
arch: "x86_64",
|
|
||||||
}),
|
|
||||||
"x86_64-pc-windows-msvc" => Ok(OsAndArch {
|
|
||||||
os: "windows",
|
|
||||||
arch: "x86_64",
|
|
||||||
}),
|
|
||||||
"aarch64-pc-windows-msvc" => Ok(OsAndArch {
|
|
||||||
os: "windows",
|
|
||||||
arch: "aarch64",
|
|
||||||
}),
|
|
||||||
"i686-pc-windows-msvc" => Ok(OsAndArch {
|
|
||||||
os: "windows",
|
|
||||||
arch: "x86",
|
|
||||||
}),
|
|
||||||
"x86_64-unknown-linux-gnu" => Ok(OsAndArch {
|
|
||||||
os: "linux",
|
|
||||||
arch: "x86_64",
|
|
||||||
}),
|
|
||||||
"aarch64-unknown-linux-gnu" => Ok(OsAndArch {
|
|
||||||
os: "linux",
|
|
||||||
arch: "aarch64",
|
|
||||||
}),
|
|
||||||
"arm-unknown-linux-gnueabi" => Ok(OsAndArch {
|
|
||||||
os: "linux",
|
|
||||||
arch: "arm",
|
|
||||||
}),
|
|
||||||
v => Err(Error::UnsupportedTarget(v.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "aarch64-unknown-linux-gnu";
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "arm"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "arm-unknown-linux-gnueabi";
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "x86_64-pc-windows-msvc";
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "i686-pc-windows-msvc";
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "aarch64-pc-windows-msvc";
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "x86_64-apple-darwin";
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
||||||
pub const DEFAULT_TARGET: &str = "aarch64-apple-darwin";
|
|
||||||
@ -1,208 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## [140.3.0+140.1.14](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v140.2.0+140.1.14...export-cef-dir-v140.3.0+140.1.14) - 2025-09-23
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- add --mirror-url cli args
|
|
||||||
- Allow to download CEF binaries with custom base url set in env variable
|
|
||||||
|
|
||||||
## [140.2.0+140.1.14](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v140.1.0+140.1.13...export-cef-dir-v140.2.0+140.1.14) - 2025-09-21
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 140.1.14
|
|
||||||
|
|
||||||
## [140.1.0+140.1.13](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v140.0.0+140.1.13...export-cef-dir-v140.1.0+140.1.13) - 2025-09-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- support pre-downloaded archive ([#197](https://github.com/tauri-apps/cef-rs/issues/197))
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
|
|
||||||
## [140.0.0+140.1.13](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.8.0+139.0.40...export-cef-dir-v140.0.0+140.1.13) - 2025-09-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 140.1.13
|
|
||||||
|
|
||||||
## [139.8.0+139.0.40](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.7.2+139.0.38...export-cef-dir-v139.8.0+139.0.40) - 2025-09-12
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.40
|
|
||||||
|
|
||||||
## [139.7.2+139.0.38](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.7.1+139.0.38...export-cef-dir-v139.7.2+139.0.38) - 2025-09-08
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.7.2+139.0.38
|
|
||||||
|
|
||||||
## [139.7.1+139.0.38](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.7.0+139.0.38...export-cef-dir-v139.7.1+139.0.38) - 2025-09-07
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.7.1+139.0.38
|
|
||||||
- *(deps)* update rust crate windows-sys to 0.61
|
|
||||||
|
|
||||||
## [139.7.0+139.0.38](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.6.0+139.0.37...export-cef-dir-v139.7.0+139.0.38) - 2025-08-31
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.38
|
|
||||||
|
|
||||||
## [139.6.0+139.0.37](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.5.0+139.0.30...export-cef-dir-v139.6.0+139.0.37) - 2025-08-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.37
|
|
||||||
|
|
||||||
## [139.5.0+139.0.30](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.4.0+139.0.28...export-cef-dir-v139.5.0+139.0.30) - 2025-08-28
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.30
|
|
||||||
|
|
||||||
## [139.4.0+139.0.28](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.3.0+139.0.26...export-cef-dir-v139.4.0+139.0.28) - 2025-08-23
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.28
|
|
||||||
|
|
||||||
## [139.3.0+139.0.26](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.2.1+139.0.23...export-cef-dir-v139.3.0+139.0.26) - 2025-08-22
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.26
|
|
||||||
|
|
||||||
## [139.2.1+139.0.23](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.2.0+139.0.23...export-cef-dir-v139.2.1+139.0.23) - 2025-08-16
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- [#183](https://github.com/tauri-apps/cef-rs/issues/183)
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
|
|
||||||
## [139.2.0+139.0.23](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.1.0+139.0.20...export-cef-dir-v139.2.0+139.0.23) - 2025-08-16
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.23
|
|
||||||
|
|
||||||
## [139.1.0+139.0.20](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.0.1+139.0.17...export-cef-dir-v139.1.0+139.0.20) - 2025-08-15
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.20
|
|
||||||
|
|
||||||
## [139.0.1+139.0.17](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v139.0.0+139.0.17...export-cef-dir-v139.0.1+139.0.17) - 2025-08-08
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.0.1+139.0.17
|
|
||||||
|
|
||||||
## [139.0.0+139.0.17](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.9.0+138.0.36...export-cef-dir-v139.0.0+139.0.17) - 2025-08-08
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.17
|
|
||||||
|
|
||||||
## [138.9.0+138.0.36](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.8.0+138.0.34...export-cef-dir-v138.9.0+138.0.36) - 2025-08-07
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.36
|
|
||||||
|
|
||||||
## [138.8.0+138.0.34](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.7.1+138.0.33...export-cef-dir-v138.8.0+138.0.34) - 2025-08-02
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- remove cef version from example dependencies
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.34
|
|
||||||
|
|
||||||
## [138.7.1+138.0.33](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.7.0+138.0.33...export-cef-dir-v138.7.1+138.0.33) - 2025-07-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v138.7.1+138.0.33
|
|
||||||
- move examples into separate crates
|
|
||||||
|
|
||||||
## [138.7.0+138.0.33](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.6.1+138.0.27...export-cef-dir-v138.7.0+138.0.33) - 2025-07-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.33
|
|
||||||
|
|
||||||
## [138.6.1+138.0.27](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.6.0+138.0.27...export-cef-dir-v138.6.1+138.0.27) - 2025-07-28
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- embed git-cliff as a library in get-latest
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* bump version for get-latest updates
|
|
||||||
|
|
||||||
## [138.6.0+138.0.27](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.5.1+138.0.26...export-cef-dir-v138.6.0+138.0.27) - 2025-07-28
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- update CEF version to 138.0.27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- bump version for release
|
|
||||||
|
|
||||||
## [138.5.1+138.0.26](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.5.0+138.0.26...export-cef-dir-v138.5.1+138.0.26) - 2025-07-22
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
- *(doc)* regenerate CHANGELOG.md
|
|
||||||
|
|
||||||
## [138.5.0+138.0.26](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.4.0+138.0.25...export-cef-dir-v138.5.0+138.0.26) - 2025-07-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.4.0+138.0.25](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.3.0+138.0.23...export-cef-dir-v138.4.0+138.0.25) - 2025-07-18
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.3.0+138.0.23](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.2.2+138.0.21...export-cef-dir-v138.3.0+138.0.23) - 2025-07-17
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.2.2+138.0.21](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.2.1+138.0.21...export-cef-dir-v138.2.2+138.0.21) - 2025-07-14
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
- seed CHANGELOG.md files
|
|
||||||
|
|
||||||
## [138.2.1+138.0.21](https://github.com/tauri-apps/cef-rs/compare/export-cef-dir-v138.2.0+138.0.21...export-cef-dir-v138.2.1+138.0.21) - 2025-07-14
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- bump major version of download-cef [#145](https://github.com/tauri-apps/cef-rs/issues/145)
|
|
||||||
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "export-cef-dir"
|
|
||||||
description = "Export pre-built CEF (Chromium Embedded Framework) archives."
|
|
||||||
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
clap.workspace = true
|
|
||||||
download-cef.workspace = true
|
|
||||||
semver.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
# export-cef-dir
|
|
||||||
|
|
||||||
Export files from the prebuilt [Chromium Embedded Framework](https://github.com/chromiumembedded/cef)
|
|
||||||
archive on any supported platform. The structure of the exported directory matches the way that
|
|
||||||
the `cef-dll-sys` crate expects to see them.
|
|
||||||
|
|
||||||
To use the target directory when building, set the `CEF_PATH` environment variable to the path of the
|
|
||||||
exported directory, e.g., `~/.local/share/cef`.
|
|
||||||
|
|
||||||
To use the DLLs in this directory at runtime, the library loader path varies by platform:
|
|
||||||
|
|
||||||
- Linux
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$CEF_PATH"
|
|
||||||
```
|
|
||||||
|
|
||||||
- macOS
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$CEF_PATH"
|
|
||||||
```
|
|
||||||
|
|
||||||
- Windows (using PowerShell)
|
|
||||||
|
|
||||||
```pwsh
|
|
||||||
$env:PATH = "$env:PATH;$env:CEF_PATH"
|
|
||||||
```
|
|
||||||
@ -1,136 +0,0 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
use download_cef::{CefFile, CefIndex, OsAndArch, DEFAULT_TARGET};
|
|
||||||
use std::{
|
|
||||||
fs,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::OnceLock,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn default_version() -> &'static str {
|
|
||||||
static DEFAULT_VERSION: OnceLock<String> = OnceLock::new();
|
|
||||||
DEFAULT_VERSION
|
|
||||||
.get_or_init(|| download_cef::default_version(env!("CARGO_PKG_VERSION")))
|
|
||||||
.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_download_url() -> &'static str {
|
|
||||||
static DEFAULT_DOWNLOAD_URL: OnceLock<String> = OnceLock::new();
|
|
||||||
DEFAULT_DOWNLOAD_URL
|
|
||||||
.get_or_init(|| download_cef::default_download_url())
|
|
||||||
.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[command(about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
#[arg(short, long)]
|
|
||||||
force: bool,
|
|
||||||
#[arg(short, long)]
|
|
||||||
save_archive: bool,
|
|
||||||
#[arg(short, long, default_value = DEFAULT_TARGET)]
|
|
||||||
target: String,
|
|
||||||
#[arg(short, long, default_value = default_version())]
|
|
||||||
version: String,
|
|
||||||
#[arg(short, long, default_value = default_download_url())]
|
|
||||||
mirror_url: String,
|
|
||||||
#[arg(short, long)]
|
|
||||||
archive: Option<String>,
|
|
||||||
output: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
|
||||||
let args = Args::parse();
|
|
||||||
let output = PathBuf::from(args.output);
|
|
||||||
let url = args.mirror_url.as_str();
|
|
||||||
|
|
||||||
let parent = PathBuf::from(
|
|
||||||
output
|
|
||||||
.parent()
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("invalid target directory: {}", output.display()))?,
|
|
||||||
);
|
|
||||||
|
|
||||||
if fs::exists(&output)? {
|
|
||||||
if !args.force {
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"target directory already exists: {}",
|
|
||||||
output.display()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let dir = output
|
|
||||||
.file_name()
|
|
||||||
.and_then(|dir| dir.to_str())
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("invalid target directory: {}", output.display()))?;
|
|
||||||
let old_output = parent.join(format!("old_{dir}"));
|
|
||||||
fs::rename(&output, &old_output)?;
|
|
||||||
println!("Cleaning up: {}", old_output.display());
|
|
||||||
fs::remove_dir_all(old_output)?
|
|
||||||
}
|
|
||||||
|
|
||||||
let target = args.target.as_str();
|
|
||||||
let os_arch = OsAndArch::try_from(target)?;
|
|
||||||
let cef_dir = os_arch.to_string();
|
|
||||||
let cef_dir = parent.join(&cef_dir);
|
|
||||||
|
|
||||||
if fs::exists(&cef_dir)? {
|
|
||||||
let dir = cef_dir
|
|
||||||
.file_name()
|
|
||||||
.and_then(|dir| dir.to_str())
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("invalid target directory: {}", output.display()))?;
|
|
||||||
let old_cef_dir = parent.join(format!("old_{dir}"));
|
|
||||||
fs::rename(&cef_dir, &old_cef_dir)?;
|
|
||||||
println!("Cleaning up: {}", old_cef_dir.display());
|
|
||||||
fs::remove_dir_all(old_cef_dir)?
|
|
||||||
}
|
|
||||||
|
|
||||||
let (archive, extracted_dir) = match args.archive {
|
|
||||||
Some(archive) => {
|
|
||||||
let extracted_dir =
|
|
||||||
download_cef::extract_target_archive(target, &archive, &parent, true)?;
|
|
||||||
let archive = CefFile::try_from(Path::new(&archive))?;
|
|
||||||
(archive, extracted_dir)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let cef_version = args.version.as_str();
|
|
||||||
let index = CefIndex::download_from(url)?;
|
|
||||||
let platform = index.platform(target)?;
|
|
||||||
let version = platform.version(cef_version)?;
|
|
||||||
|
|
||||||
let archive = version.download_archive_with_retry_from(
|
|
||||||
url,
|
|
||||||
&parent,
|
|
||||||
true,
|
|
||||||
Duration::from_secs(15),
|
|
||||||
3,
|
|
||||||
)?;
|
|
||||||
let extracted_dir =
|
|
||||||
download_cef::extract_target_archive(target, &archive, &parent, true)?;
|
|
||||||
|
|
||||||
if !args.save_archive {
|
|
||||||
println!("Cleaning up: {}", archive.display());
|
|
||||||
fs::remove_file(archive)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let archive = version.minimal()?.clone();
|
|
||||||
(archive, extracted_dir)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if extracted_dir != cef_dir {
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"extracted dir {extracted_dir:?} does not match cef_dir {cef_dir:?}",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
archive.write_archive_json(extracted_dir)?;
|
|
||||||
|
|
||||||
if output != cef_dir {
|
|
||||||
println!("Renaming: {}", output.display());
|
|
||||||
fs::rename(cef_dir, output)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "get-latest"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
download-cef.workspace = true
|
|
||||||
|
|
||||||
clap.workspace = true
|
|
||||||
git-cliff.workspace = true
|
|
||||||
git-cliff-core.workspace = true
|
|
||||||
regex.workspace = true
|
|
||||||
semver.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
toml_edit.workspace = true
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# get-latest
|
|
||||||
|
|
||||||
Download the list of versions available from the [Chromium Embedded Framework (CEF) Automated
|
|
||||||
Builds](https://cef-builds.spotifycdn.com/index.html) page and determine the latest version that is
|
|
||||||
available for every platform we support.
|
|
||||||
@ -1,112 +0,0 @@
|
|||||||
# git-cliff ~ configuration file
|
|
||||||
# https://git-cliff.org/docs/configuration
|
|
||||||
|
|
||||||
|
|
||||||
[changelog]
|
|
||||||
# A Tera template to be rendered as the changelog's footer.
|
|
||||||
# See https://keats.github.io/tera/docs/#introduction
|
|
||||||
header = """# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
"""
|
|
||||||
# A Tera template to be rendered for each release in the changelog.
|
|
||||||
# See https://keats.github.io/tera/docs/#introduction
|
|
||||||
body = """
|
|
||||||
|
|
||||||
{%- if version %}
|
|
||||||
## [{{ version }}]\
|
|
||||||
{%- if previous.version == version -%}\
|
|
||||||
(<REPO>/releases/tag/{{ version }})\
|
|
||||||
{%- else -%}\
|
|
||||||
(<REPO>/compare/{{ previous.version }}...{{ version }})\
|
|
||||||
{% endif %} \
|
|
||||||
- {{ timestamp | date(format="%Y-%m-%d") }}
|
|
||||||
{% for group, commits in commits | group_by(attribute="group") %}
|
|
||||||
### {{ group | upper_first }}
|
|
||||||
|
|
||||||
{% for commit in commits %}
|
|
||||||
{%- if commit.scope -%}
|
|
||||||
- *({{commit.scope}})* {% if commit.breaking %}[**breaking**] {% endif %}\
|
|
||||||
{{ commit.message }}\
|
|
||||||
{%- if commit.links %} \
|
|
||||||
({% for link in commit.links %}[{{link.text}}]({{link.href}}) {% endfor -%})\
|
|
||||||
{% endif %}
|
|
||||||
{% else -%}
|
|
||||||
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message }}
|
|
||||||
{% endif -%}
|
|
||||||
{% endfor -%}
|
|
||||||
{% endfor %}\
|
|
||||||
{%- else -%}
|
|
||||||
{% endif -%}
|
|
||||||
"""
|
|
||||||
# A Tera template to be rendered as the changelog's footer.
|
|
||||||
# See https://keats.github.io/tera/docs/#introduction
|
|
||||||
footer = ""
|
|
||||||
# Remove leading and trailing whitespaces from the changelog's body.
|
|
||||||
trim = true
|
|
||||||
# Render body even when there are no releases to process.
|
|
||||||
render_always = true
|
|
||||||
# An array of regex based postprocessors to modify the changelog.
|
|
||||||
postprocessors = [
|
|
||||||
# Replace the placeholder <REPO> with a URL.
|
|
||||||
{ pattern = '<REPO>', replace = "https://github.com/tauri-apps/cef-rs" },
|
|
||||||
{ pattern = '\[[\w\-]+-v([0-9.+]+)\]', replace = "[${1}]" },
|
|
||||||
]
|
|
||||||
# render body even when there are no releases to process
|
|
||||||
# render_always = true
|
|
||||||
# output file path
|
|
||||||
# output = "test.md"
|
|
||||||
|
|
||||||
[git]
|
|
||||||
# Parse commits according to the conventional commits specification.
|
|
||||||
# See https://www.conventionalcommits.org
|
|
||||||
conventional_commits = true
|
|
||||||
# Exclude commits that do not match the conventional commits specification.
|
|
||||||
filter_unconventional = true
|
|
||||||
# Require all commits to be conventional.
|
|
||||||
# Takes precedence over filter_unconventional.
|
|
||||||
require_conventional = false
|
|
||||||
# Split commits on newlines, treating each line as an individual commit.
|
|
||||||
split_commits = false
|
|
||||||
# An array of regex based parsers to modify commit messages prior to further processing.
|
|
||||||
commit_preprocessors = [
|
|
||||||
# Replace issue numbers with link templates to be updated in `changelog.postprocessors`.
|
|
||||||
{ pattern = '(\w+\s)?#([0-9]+)', replace = "${1}[#${2}](<REPO>/issues/${2})" },
|
|
||||||
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))" },
|
|
||||||
# Check spelling of the commit message using https://github.com/crate-ci/typos.
|
|
||||||
# If the spelling is incorrect, it will be fixed automatically.
|
|
||||||
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
|
|
||||||
]
|
|
||||||
# Prevent commits that are breaking from being excluded by commit parsers.
|
|
||||||
protect_breaking_commits = false
|
|
||||||
# An array of regex based parsers for extracting data from the commit message.
|
|
||||||
# Assigns commits to groups.
|
|
||||||
# Optionally sets the commit's scope and can decide to exclude commits from further processing.
|
|
||||||
commit_parsers = [
|
|
||||||
{ message = "^feat", group = "added" },
|
|
||||||
{ message = "^changed", group = "changed" },
|
|
||||||
{ message = "^deprecated", group = "deprecated" },
|
|
||||||
{ message = "^fix", group = "fixed" },
|
|
||||||
{ message = "^security", group = "security" },
|
|
||||||
{ message = "^.*", group = "other" },
|
|
||||||
]
|
|
||||||
# Exclude commits that are not matched by any commit parser.
|
|
||||||
filter_commits = false
|
|
||||||
# An array of link parsers for extracting external references, and turning them into URLs, using regex.
|
|
||||||
link_parsers = []
|
|
||||||
# Include only the tags that belong to the current branch.
|
|
||||||
use_branch_tags = false
|
|
||||||
# Order releases topologically instead of chronologically.
|
|
||||||
topo_order = false
|
|
||||||
# Order releases topologically instead of chronologically.
|
|
||||||
topo_order_commits = true
|
|
||||||
# Order of commits in each group/release within the changelog.
|
|
||||||
# Allowed values: newest, oldest
|
|
||||||
sort_commits = "newest"
|
|
||||||
# Process submodules commits
|
|
||||||
recurse_submodules = false
|
|
||||||
@ -1,235 +0,0 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate thiserror;
|
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
use download_cef::{CefIndex, Channel, LINUX_TARGETS, MACOS_TARGETS, WINDOWS_TARGETS};
|
|
||||||
use git_cliff::args::*;
|
|
||||||
use regex::Regex;
|
|
||||||
use semver::{BuildMetadata, Version};
|
|
||||||
use std::{
|
|
||||||
env, fs,
|
|
||||||
io::Write,
|
|
||||||
path::PathBuf,
|
|
||||||
process::{Command, ExitStatus},
|
|
||||||
sync::OnceLock,
|
|
||||||
};
|
|
||||||
use toml_edit::{value, DocumentMut};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
enum Error {
|
|
||||||
#[error("Download error: {0}")]
|
|
||||||
Download(#[from] download_cef::Error),
|
|
||||||
#[error("Invalid regex pattern: {0}")]
|
|
||||||
InvalidRegexPattern(#[from] regex::Error),
|
|
||||||
#[error("Invalid version: {0}")]
|
|
||||||
InvalidVersion(#[from] semver::Error),
|
|
||||||
#[error("No versions found")]
|
|
||||||
NoVersionsFound,
|
|
||||||
#[error("I/O error: {0}")]
|
|
||||||
Io(#[from] std::io::Error),
|
|
||||||
#[error("Invalid manifest file: {0}")]
|
|
||||||
InvalidManifest(#[from] toml_edit::TomlError),
|
|
||||||
#[error("Error invoking git: {0:?}")]
|
|
||||||
GitInvocation(ExitStatus),
|
|
||||||
#[error("Error running git-cliff: {0:?}")]
|
|
||||||
InvalidGitCliffArgs(#[from] clap::Error),
|
|
||||||
#[error("Error updating change log: {0:?}")]
|
|
||||||
UpdateChangeLog(#[from] git_cliff_core::error::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
fn default_download_url() -> &'static str {
|
|
||||||
static DEFAULT_DOWNLOAD_URL: OnceLock<String> = OnceLock::new();
|
|
||||||
DEFAULT_DOWNLOAD_URL
|
|
||||||
.get_or_init(|| download_cef::default_download_url())
|
|
||||||
.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
#[arg(short, long, default_value = default_download_url())]
|
|
||||||
mirror_url: String,
|
|
||||||
#[arg(short, long, default_value = "stable")]
|
|
||||||
channel: Channel,
|
|
||||||
#[arg(short, long)]
|
|
||||||
update_version: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let pattern = Regex::new(r"^([^+]+)(:?\+.+)?$")?;
|
|
||||||
|
|
||||||
let args = Args::parse();
|
|
||||||
let channel = args.channel;
|
|
||||||
let url = args.mirror_url.as_str();
|
|
||||||
|
|
||||||
let index = CefIndex::download_from(url)?;
|
|
||||||
let latest_versions: Vec<_> = LINUX_TARGETS
|
|
||||||
.iter()
|
|
||||||
.chain(MACOS_TARGETS.iter())
|
|
||||||
.chain(WINDOWS_TARGETS.iter())
|
|
||||||
.map(|target| {
|
|
||||||
index
|
|
||||||
.platform(target)
|
|
||||||
.and_then(|platform| platform.latest(channel.clone()))
|
|
||||||
.map(|version| pattern.replace(&version.cef_version, "$1"))
|
|
||||||
})
|
|
||||||
.collect::<download_cef::Result<Vec<_>>>()?;
|
|
||||||
let latest_versions = latest_versions
|
|
||||||
.into_iter()
|
|
||||||
.map(|version| Ok(Version::parse(&version)?))
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
let latest_version = latest_versions
|
|
||||||
.into_iter()
|
|
||||||
.min()
|
|
||||||
.ok_or(Error::NoVersionsFound)?;
|
|
||||||
|
|
||||||
println!("Latest available {channel} version: {latest_version}");
|
|
||||||
|
|
||||||
if args.update_version {
|
|
||||||
let current_version =
|
|
||||||
Version::parse(&download_cef::default_version(env!("CARGO_PKG_VERSION")))?;
|
|
||||||
if current_version < latest_version {
|
|
||||||
let latest_build = BuildMetadata::new(&latest_version.to_string())?;
|
|
||||||
let mut next_version = Version::parse(env!("CARGO_PKG_VERSION"))?;
|
|
||||||
if next_version.major < latest_version.major {
|
|
||||||
next_version.major = latest_version.major;
|
|
||||||
next_version.minor = 0;
|
|
||||||
} else {
|
|
||||||
next_version.minor += 1;
|
|
||||||
}
|
|
||||||
next_version.patch = 0;
|
|
||||||
next_version.build = latest_build.clone();
|
|
||||||
|
|
||||||
let mut manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
||||||
manifest.pop();
|
|
||||||
let manifest = manifest.join("Cargo.toml");
|
|
||||||
let mut doc = fs::read_to_string(&manifest)?.parse::<DocumentMut>()?;
|
|
||||||
doc["workspace"]["package"]["version"] = value(next_version.to_string());
|
|
||||||
let workspace_version = Version {
|
|
||||||
build: BuildMetadata::EMPTY,
|
|
||||||
..next_version.clone()
|
|
||||||
};
|
|
||||||
doc["workspace"]["dependencies"]["cef-dll-sys"]["version"] =
|
|
||||||
value(workspace_version.to_string());
|
|
||||||
fs::write(&manifest, doc.to_string().as_bytes())?;
|
|
||||||
|
|
||||||
if let Ok(output) = env::var("GITHUB_OUTPUT") {
|
|
||||||
let mut output = fs::OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.append(true)
|
|
||||||
.open(output)?;
|
|
||||||
|
|
||||||
let commit_message =
|
|
||||||
format!("chore(release): update CEF version to {latest_version}");
|
|
||||||
writeln!(output, "commit-message={commit_message}",)?;
|
|
||||||
|
|
||||||
let output = Command::new("git")
|
|
||||||
.args(["commit", "-a", "-m", commit_message.as_str()])
|
|
||||||
.output()?;
|
|
||||||
std::io::stdout().write_all(&output.stdout)?;
|
|
||||||
std::io::stderr().write_all(&output.stderr)?;
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(Error::GitInvocation(output.status));
|
|
||||||
}
|
|
||||||
|
|
||||||
let export_cef_dir_tag = format!("export-cef-dir-v{next_version}");
|
|
||||||
let output = Command::new("git")
|
|
||||||
.args(["tag", "--no-sign", "-f", export_cef_dir_tag.as_str()])
|
|
||||||
.output()?;
|
|
||||||
std::io::stdout().write_all(&output.stdout)?;
|
|
||||||
std::io::stderr().write_all(&output.stderr)?;
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(Error::GitInvocation(output.status));
|
|
||||||
}
|
|
||||||
|
|
||||||
let cef_dll_sys_tag = format!("cef-dll-sys-v{next_version}");
|
|
||||||
let output = Command::new("git")
|
|
||||||
.args(["tag", "--no-sign", "-f", cef_dll_sys_tag.as_str()])
|
|
||||||
.output()?;
|
|
||||||
std::io::stdout().write_all(&output.stdout)?;
|
|
||||||
std::io::stderr().write_all(&output.stderr)?;
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(Error::GitInvocation(output.status));
|
|
||||||
}
|
|
||||||
|
|
||||||
let cef_tag = format!("cef-v{next_version}");
|
|
||||||
let output = Command::new("git")
|
|
||||||
.args(["tag", "--no-sign", "-f", cef_tag.as_str()])
|
|
||||||
.output()?;
|
|
||||||
std::io::stdout().write_all(&output.stdout)?;
|
|
||||||
std::io::stderr().write_all(&output.stderr)?;
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(Error::GitInvocation(output.status));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
||||||
config_path.push("cliff.toml");
|
|
||||||
|
|
||||||
let common_opts = ["--strip", "footer", "--include-path", "Cargo.toml"];
|
|
||||||
|
|
||||||
let export_cef_dir_opts = Opt {
|
|
||||||
config: config_path.clone(),
|
|
||||||
range: Some("export-cef-dir-v138.2.0+138.0.21..".to_string()),
|
|
||||||
..Opt::try_parse_from(
|
|
||||||
common_opts.iter().chain(
|
|
||||||
[
|
|
||||||
"--include-path",
|
|
||||||
"export-cef-dir/*",
|
|
||||||
"--tag-pattern",
|
|
||||||
"^export-cef-dir-v",
|
|
||||||
"--output",
|
|
||||||
"export-cef-dir/CHANGELOG.md",
|
|
||||||
]
|
|
||||||
.iter(),
|
|
||||||
),
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
git_cliff::run(export_cef_dir_opts)?;
|
|
||||||
|
|
||||||
let cef_dll_sys_opts = Opt {
|
|
||||||
config: config_path.clone(),
|
|
||||||
range: Some("cef-dll-sys-v138.2.0+138.0.21..".to_string()),
|
|
||||||
..Opt::try_parse_from(
|
|
||||||
common_opts.iter().chain(
|
|
||||||
[
|
|
||||||
"--include-path",
|
|
||||||
"sys/*",
|
|
||||||
"--tag-pattern",
|
|
||||||
"^cef-dll-sys-v",
|
|
||||||
"--output",
|
|
||||||
"sys/CHANGELOG.md",
|
|
||||||
]
|
|
||||||
.iter(),
|
|
||||||
),
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
git_cliff::run(cef_dll_sys_opts)?;
|
|
||||||
|
|
||||||
let cef_opts = Opt {
|
|
||||||
config: config_path,
|
|
||||||
range: Some("cef-v138.2.0+138.0.21..".to_string()),
|
|
||||||
..Opt::try_parse_from(
|
|
||||||
common_opts.iter().chain(
|
|
||||||
[
|
|
||||||
"--include-path",
|
|
||||||
"cef/*",
|
|
||||||
"--tag-pattern",
|
|
||||||
"^cef-v",
|
|
||||||
"--output",
|
|
||||||
"cef/CHANGELOG.md",
|
|
||||||
]
|
|
||||||
.iter(),
|
|
||||||
),
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
git_cliff::run(cef_opts)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## [140.2.0+140.1.14](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v140.1.0+140.1.13...cef-dll-sys-v140.2.0+140.1.14) - 2025-09-21
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 140.1.14
|
|
||||||
|
|
||||||
## [140.1.0+140.1.13](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v140.0.0+140.1.13...cef-dll-sys-v140.1.0+140.1.13) - 2025-09-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
|
|
||||||
## [140.0.0+140.1.13](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.8.0+139.0.40...cef-dll-sys-v140.0.0+140.1.13) - 2025-09-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update bindings
|
|
||||||
- *(release)* update CEF version to 140.1.13
|
|
||||||
|
|
||||||
## [139.8.0+139.0.40](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.7.2+139.0.38...cef-dll-sys-v139.8.0+139.0.40) - 2025-09-12
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update bindings
|
|
||||||
- *(release)* update CEF version to 139.0.40
|
|
||||||
|
|
||||||
## [139.7.2+139.0.38](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.7.1+139.0.38...cef-dll-sys-v139.7.2+139.0.38) - 2025-09-08
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- handle out-params ([#173](https://github.com/tauri-apps/cef-rs/issues/173))
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.7.2+139.0.38
|
|
||||||
- update bindings
|
|
||||||
|
|
||||||
## [139.7.1+139.0.38](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.7.0+139.0.38...cef-dll-sys-v139.7.1+139.0.38) - 2025-09-07
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.7.1+139.0.38
|
|
||||||
- *(deps)* update rust crate windows-sys to 0.61
|
|
||||||
|
|
||||||
## [139.7.0+139.0.38](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.6.0+139.0.37...cef-dll-sys-v139.7.0+139.0.38) - 2025-08-31
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.38
|
|
||||||
|
|
||||||
## [139.6.0+139.0.37](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.5.0+139.0.30...cef-dll-sys-v139.6.0+139.0.37) - 2025-08-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.37
|
|
||||||
|
|
||||||
## [139.5.0+139.0.30](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.4.0+139.0.28...cef-dll-sys-v139.5.0+139.0.30) - 2025-08-28
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.30
|
|
||||||
|
|
||||||
## [139.4.0+139.0.28](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.3.0+139.0.26...cef-dll-sys-v139.4.0+139.0.28) - 2025-08-23
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.28
|
|
||||||
|
|
||||||
## [139.3.0+139.0.26](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.2.1+139.0.23...cef-dll-sys-v139.3.0+139.0.26) - 2025-08-22
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.26
|
|
||||||
|
|
||||||
## [139.2.1+139.0.23](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.2.0+139.0.23...cef-dll-sys-v139.2.1+139.0.23) - 2025-08-16
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
|
|
||||||
## [139.2.0+139.0.23](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.1.0+139.0.20...cef-dll-sys-v139.2.0+139.0.23) - 2025-08-16
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.23
|
|
||||||
|
|
||||||
## [139.1.0+139.0.20](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.0.1+139.0.17...cef-dll-sys-v139.1.0+139.0.20) - 2025-08-15
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 139.0.20
|
|
||||||
|
|
||||||
## [139.0.1+139.0.17](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v139.0.0+139.0.17...cef-dll-sys-v139.0.1+139.0.17) - 2025-08-08
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v139.0.1+139.0.17
|
|
||||||
|
|
||||||
## [139.0.0+139.0.17](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.9.0+138.0.36...cef-dll-sys-v139.0.0+139.0.17) - 2025-08-08
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update bindings
|
|
||||||
- *(release)* update CEF version to 139.0.17
|
|
||||||
|
|
||||||
## [138.9.0+138.0.36](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.8.0+138.0.34...cef-dll-sys-v138.9.0+138.0.36) - 2025-08-07
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.36
|
|
||||||
|
|
||||||
## [138.8.0+138.0.34](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.7.1+138.0.33...cef-dll-sys-v138.8.0+138.0.34) - 2025-08-02
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- remove cef version from example dependencies
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* update CEF version to 138.0.34
|
|
||||||
|
|
||||||
## [138.7.1+138.0.33](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.7.0+138.0.33...cef-dll-sys-v138.7.1+138.0.33) - 2025-07-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release v138.7.1+138.0.33
|
|
||||||
- move examples into separate crates
|
|
||||||
|
|
||||||
## [138.7.0+138.0.33](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.6.1+138.0.27...cef-dll-sys-v138.7.0+138.0.33) - 2025-07-29
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update bindings
|
|
||||||
- *(release)* update CEF version to 138.0.33
|
|
||||||
|
|
||||||
## [138.6.1+138.0.27](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.6.0+138.0.27...cef-dll-sys-v138.6.1+138.0.27) - 2025-07-28
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- embed git-cliff as a library in get-latest
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- *(release)* bump version for get-latest updates
|
|
||||||
|
|
||||||
## [138.6.0+138.0.27](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.5.1+138.0.26...cef-dll-sys-v138.6.0+138.0.27) - 2025-07-28
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- update CEF version to 138.0.27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- bump version for release
|
|
||||||
|
|
||||||
## [138.5.1+138.0.26](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.5.0+138.0.26...cef-dll-sys-v138.5.1+138.0.26) - 2025-07-22
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
- *(doc)* regenerate CHANGELOG.md
|
|
||||||
|
|
||||||
## [138.5.0+138.0.26](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.4.0+138.0.25...cef-dll-sys-v138.5.0+138.0.26) - 2025-07-19
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.4.0+138.0.25](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.3.0+138.0.23...cef-dll-sys-v138.4.0+138.0.25) - 2025-07-18
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.3.0+138.0.23](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.2.2+138.0.21...cef-dll-sys-v138.3.0+138.0.23) - 2025-07-17
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- update CEF version
|
|
||||||
|
|
||||||
## [138.2.2+138.0.21](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.2.1+138.0.21...cef-dll-sys-v138.2.2+138.0.21) - 2025-07-14
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
- release
|
|
||||||
- seed CHANGELOG.md files
|
|
||||||
|
|
||||||
## [138.2.1+138.0.21](https://github.com/tauri-apps/cef-rs/compare/cef-dll-sys-v138.2.0+138.0.21...cef-dll-sys-v138.2.1+138.0.21) - 2025-07-14
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- bump major version of download-cef [#145](https://github.com/tauri-apps/cef-rs/issues/145)
|
|
||||||
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "cef-dll-sys"
|
|
||||||
description = "cef-rs sys crate"
|
|
||||||
links = "cef_dll_wrapper"
|
|
||||||
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
doctest = false
|
|
||||||
|
|
||||||
[features]
|
|
||||||
dox = []
|
|
||||||
sandbox = []
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
features = ["dox"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
cmake.workspace = true
|
|
||||||
download-cef.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
# cef-dll-sys
|
|
||||||
|
|
||||||
Generated bindings for the prebuilt [Chromium Embedded Framework](https://github.com/chromiumembedded/cef)
|
|
||||||
C API on any supported platform.
|
|
||||||
@ -1,131 +0,0 @@
|
|||||||
#[cfg(not(feature = "dox"))]
|
|
||||||
fn main() -> anyhow::Result<()> {
|
|
||||||
use download_cef::{CefIndex, OsAndArch};
|
|
||||||
use std::{env, fs, path::PathBuf};
|
|
||||||
|
|
||||||
println!("cargo::rerun-if-changed=build.rs");
|
|
||||||
|
|
||||||
let target = env::var("TARGET")?;
|
|
||||||
let os_arch = OsAndArch::try_from(target.as_str())?;
|
|
||||||
|
|
||||||
println!("cargo::rerun-if-env-changed=FLATPAK");
|
|
||||||
println!("cargo::rerun-if-env-changed=CEF_PATH");
|
|
||||||
let cef_path_env = env::var("FLATPAK")
|
|
||||||
.map(|_| String::from("/usr/lib"))
|
|
||||||
.or_else(|_| env::var("CEF_PATH"));
|
|
||||||
|
|
||||||
let cef_dir = match cef_path_env {
|
|
||||||
Ok(cef_path) => {
|
|
||||||
// Allow overriding the CEF path with environment variables.
|
|
||||||
println!("Using CEF path from environment: {cef_path}");
|
|
||||||
download_cef::check_archive_json(&env::var("CARGO_PKG_VERSION")?, &cef_path)?;
|
|
||||||
PathBuf::from(cef_path)
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
|
|
||||||
let cef_dir = os_arch.to_string();
|
|
||||||
let cef_dir = out_dir.join(&cef_dir);
|
|
||||||
|
|
||||||
if !fs::exists(&cef_dir)? {
|
|
||||||
let cef_version = download_cef::default_version(&env::var("CARGO_PKG_VERSION")?);
|
|
||||||
let index = CefIndex::download()?;
|
|
||||||
let platform = index.platform(&target)?;
|
|
||||||
let version = platform.version(&cef_version)?;
|
|
||||||
|
|
||||||
let archive = version.download_archive(&out_dir, false)?;
|
|
||||||
let extracted_dir =
|
|
||||||
download_cef::extract_target_archive(&target, &archive, &out_dir, false)?;
|
|
||||||
if extracted_dir != cef_dir {
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"extracted dir {extracted_dir:?} does not match cef_dir {cef_dir:?}",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
version.write_archive_json(extracted_dir)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
cef_dir
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let cef_dir = cef_dir.display().to_string();
|
|
||||||
|
|
||||||
println!("cargo::metadata=CEF_DIR={cef_dir}");
|
|
||||||
println!("cargo::rustc-link-search=native={cef_dir}");
|
|
||||||
|
|
||||||
let mut cef_dll_wrapper = cmake::Config::new(&cef_dir);
|
|
||||||
cef_dll_wrapper
|
|
||||||
.generator("Ninja")
|
|
||||||
.profile("RelWithDebInfo")
|
|
||||||
.build_target("libcef_dll_wrapper");
|
|
||||||
|
|
||||||
let project_arch = match os_arch.arch {
|
|
||||||
"aarch64" => "arm64",
|
|
||||||
arch => arch,
|
|
||||||
};
|
|
||||||
|
|
||||||
let sandbox = if cfg!(feature = "sandbox") {
|
|
||||||
"ON"
|
|
||||||
} else {
|
|
||||||
"OFF"
|
|
||||||
};
|
|
||||||
|
|
||||||
match os_arch.os {
|
|
||||||
"linux" => {
|
|
||||||
println!("cargo::rustc-link-lib=dylib=cef");
|
|
||||||
}
|
|
||||||
"windows" => {
|
|
||||||
let sdk_libs = [
|
|
||||||
"comctl32.lib",
|
|
||||||
"delayimp.lib",
|
|
||||||
"mincore.lib",
|
|
||||||
"powrprof.lib",
|
|
||||||
"propsys.lib",
|
|
||||||
"runtimeobject.lib",
|
|
||||||
"setupapi.lib",
|
|
||||||
"shcore.lib",
|
|
||||||
"shell32.lib",
|
|
||||||
"shlwapi.lib",
|
|
||||||
"user32.lib",
|
|
||||||
"version.lib",
|
|
||||||
"wbemuuid.lib",
|
|
||||||
"winmm.lib",
|
|
||||||
]
|
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
let build_dir = cef_dll_wrapper
|
|
||||||
.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded")
|
|
||||||
.define("CMAKE_OBJECT_PATH_MAX", "500")
|
|
||||||
.define("CMAKE_STATIC_LINKER_FLAGS", &sdk_libs)
|
|
||||||
.define("PROJECT_ARCH", project_arch)
|
|
||||||
.define("USE_SANDBOX", sandbox)
|
|
||||||
.build()
|
|
||||||
.display()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
println!("cargo::rustc-link-search=native={build_dir}/build/libcef_dll_wrapper");
|
|
||||||
println!("cargo::rustc-link-lib=static=libcef_dll_wrapper");
|
|
||||||
|
|
||||||
println!("cargo::rustc-link-lib=dylib=libcef");
|
|
||||||
}
|
|
||||||
"macos" => {
|
|
||||||
println!("cargo::rustc-link-lib=framework=AppKit");
|
|
||||||
|
|
||||||
let build_dir = cef_dll_wrapper
|
|
||||||
.no_default_flags(true)
|
|
||||||
.define("PROJECT_ARCH", project_arch)
|
|
||||||
.define("USE_SANDBOX", sandbox)
|
|
||||||
.build()
|
|
||||||
.display()
|
|
||||||
.to_string();
|
|
||||||
println!("cargo::rustc-link-search=native={build_dir}/build/libcef_dll_wrapper");
|
|
||||||
println!("cargo::rustc-link-lib=static=cef_dll_wrapper");
|
|
||||||
}
|
|
||||||
os => unimplemented!("unknown target {os}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dox")]
|
|
||||||
fn main() {}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,39 +0,0 @@
|
|||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
|
||||||
mod x86_64_unknown_linux_gnu;
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
|
||||||
pub use x86_64_unknown_linux_gnu::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
|
||||||
mod aarch64_unknown_linux_gnu;
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
|
|
||||||
pub use aarch64_unknown_linux_gnu::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "arm"))]
|
|
||||||
mod arm_unknown_linux_gnueabi;
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "arm"))]
|
|
||||||
pub use arm_unknown_linux_gnueabi::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
|
||||||
mod x86_64_pc_windows_msvc;
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
|
||||||
pub use x86_64_pc_windows_msvc::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86"))]
|
|
||||||
mod i686_pc_windows_msvc;
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "x86"))]
|
|
||||||
pub use i686_pc_windows_msvc::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
|
|
||||||
mod aarch64_pc_windows_msvc;
|
|
||||||
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
|
|
||||||
pub use aarch64_pc_windows_msvc::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
|
||||||
mod x86_64_apple_darwin;
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
|
||||||
pub use x86_64_apple_darwin::*;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
||||||
mod aarch64_apple_darwin;
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
|
||||||
pub use aarch64_apple_darwin::*;
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,91 +0,0 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
|
||||||
|
|
||||||
#[allow(
|
|
||||||
non_snake_case,
|
|
||||||
non_camel_case_types,
|
|
||||||
non_upper_case_globals,
|
|
||||||
dead_code,
|
|
||||||
clippy::all
|
|
||||||
)]
|
|
||||||
mod bindings;
|
|
||||||
pub use bindings::*;
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
impl Default for HWND {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(std::ptr::null_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
impl Default for HINSTANCE {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(std::ptr::null_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
pub const FRAMEWORK_PATH: &str =
|
|
||||||
"Chromium Embedded Framework.framework/Chromium Embedded Framework";
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
env::{
|
|
||||||
self,
|
|
||||||
consts::{ARCH, OS},
|
|
||||||
},
|
|
||||||
fs,
|
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn get_cef_dir() -> Option<PathBuf> {
|
|
||||||
let cef_path_env = env::var("FLATPAK")
|
|
||||||
.map(|_| String::from("/usr/lib"))
|
|
||||||
.or_else(|_| env::var("CEF_PATH"));
|
|
||||||
|
|
||||||
match cef_path_env {
|
|
||||||
Ok(cef_path) => {
|
|
||||||
// Allow overriding the CEF path with environment variables.
|
|
||||||
PathBuf::from(cef_path).canonicalize().ok()
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
let out_dir = PathBuf::from(env::var("OUT_DIR").ok()?);
|
|
||||||
let cef_dir = format!("cef_{OS}_{ARCH}");
|
|
||||||
let cef_dir = out_dir.join(&cef_dir).canonicalize().ok()?;
|
|
||||||
fs::exists(&cef_dir).ok()?.then_some(cef_dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cef_dir() {
|
|
||||||
let _ = get_cef_dir().expect("CEF not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_init() {
|
|
||||||
use std::ptr::*;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
|
|
||||||
let cef_dir = get_cef_dir().expect("CEF not found");
|
|
||||||
let framework_dir = cef_dir
|
|
||||||
.join(FRAMEWORK_PATH)
|
|
||||||
.canonicalize()
|
|
||||||
.expect("failed to get framework path");
|
|
||||||
let framework_dir = std::ffi::CString::new(framework_dir.as_os_str().as_bytes())
|
|
||||||
.expect("invalid path");
|
|
||||||
|
|
||||||
assert_eq!(cef_load_library(framework_dir.as_ptr().cast()), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(cef_initialize(null(), null(), null_mut(), null_mut()), 0);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
#ifndef CEF_RUST_SYS_WRAPPER_H
|
|
||||||
#define CEF_RUST_SYS_WRAPPER_H
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include "include/wrapper/cef_library_loader.h"
|
|
||||||
#include "include/cef_sandbox_mac.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "include/cef_api_hash.h"
|
|
||||||
#include "include/cef_version.h"
|
|
||||||
|
|
||||||
#include "include/capi/cef_base_capi.h"
|
|
||||||
|
|
||||||
#include "include/capi/cef_app_capi.h"
|
|
||||||
#include "include/capi/cef_client_capi.h"
|
|
||||||
#include "include/capi/cef_urlrequest_capi.h"
|
|
||||||
|
|
||||||
#include "include/capi/views/cef_layout_capi.h"
|
|
||||||
#include "include/capi/views/cef_box_layout_capi.h"
|
|
||||||
#include "include/capi/views/cef_fill_layout_capi.h"
|
|
||||||
|
|
||||||
#include "include/capi/views/cef_button_capi.h"
|
|
||||||
#include "include/capi/views/cef_label_button_capi.h"
|
|
||||||
#include "include/capi/views/cef_menu_button_capi.h"
|
|
||||||
|
|
||||||
#include "include/capi/views/cef_textfield_capi.h"
|
|
||||||
|
|
||||||
#include "include/capi/views/cef_browser_view_capi.h"
|
|
||||||
#include "include/capi/views/cef_scroll_view_capi.h"
|
|
||||||
|
|
||||||
#include "include/capi/views/cef_window_capi.h"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "update-bindings"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
version.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
download-cef.workspace = true
|
|
||||||
|
|
||||||
bindgen.workspace = true
|
|
||||||
clap.workspace = true
|
|
||||||
convert_case.workspace = true
|
|
||||||
proc-macro2.workspace = true
|
|
||||||
quote.workspace = true
|
|
||||||
regex.workspace = true
|
|
||||||
semver.workspace = true
|
|
||||||
syn.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
# update-bindings
|
|
||||||
|
|
||||||
Download the prebuilt [Chromium Embedded Framework](https://github.com/chromiumembedded/cef)
|
|
||||||
archive on any supported platform and run `bindgen` on the C API for the `cef-dll-sys` crate,
|
|
||||||
then regenerate the safe bindings in the `cef` crate.
|
|
||||||
|
|
||||||
You can find the latest version of the prebuilt CEF archives on the [Chromium Embedded Framework
|
|
||||||
(CEF) Automated Builds](https://cef-builds.spotifycdn.com/index.html).
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
// Empty build.rs file to set OUT_DIR environment variable
|
|
||||||
fn main() {}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
use std::{convert::From, env, path::PathBuf};
|
|
||||||
|
|
||||||
pub fn get_manifest_dir() -> PathBuf {
|
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_out_dir() -> PathBuf {
|
|
||||||
PathBuf::from(env!("OUT_DIR"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cef_root(os: &str, arch: &str) -> PathBuf {
|
|
||||||
env::var(format!("CEF_PATH_{os}_{arch}"))
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
let mut out_dir = get_out_dir();
|
|
||||||
out_dir.push(format!("cef_{os}_{arch}"));
|
|
||||||
out_dir
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_sys_dir() -> crate::Result<PathBuf> {
|
|
||||||
let manifest_dir = get_manifest_dir();
|
|
||||||
let mut bindings_dir = get_manifest_dir().parent().map_or_else(
|
|
||||||
|| Err(crate::Error::MissingParent(manifest_dir)),
|
|
||||||
|parent| Ok(PathBuf::from(parent)),
|
|
||||||
)?;
|
|
||||||
bindings_dir.push("sys");
|
|
||||||
Ok(bindings_dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cef_dir() -> crate::Result<PathBuf> {
|
|
||||||
let manifest_dir = get_manifest_dir();
|
|
||||||
let mut webview2_com_dir = get_manifest_dir().parent().map_or_else(
|
|
||||||
|| Err(crate::Error::MissingParent(manifest_dir)),
|
|
||||||
|parent| Ok(PathBuf::from(parent)),
|
|
||||||
)?;
|
|
||||||
webview2_com_dir.push("cef");
|
|
||||||
Ok(webview2_com_dir)
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate thiserror;
|
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
use download_cef::DEFAULT_TARGET;
|
|
||||||
use std::{fs, io::Read, path::Path, sync::OnceLock};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Missing Parent")]
|
|
||||||
MissingParent(std::path::PathBuf),
|
|
||||||
#[error(transparent)]
|
|
||||||
Io(#[from] std::io::Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
Bindgen(#[from] bindgen::BindgenError),
|
|
||||||
#[error(transparent)]
|
|
||||||
Regex(#[from] regex::Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
Syn(#[from] syn::Error),
|
|
||||||
#[error("Parsing bindgen output failed")]
|
|
||||||
Parse(#[from] parse_tree::Unrecognized),
|
|
||||||
#[error("Missing Path")]
|
|
||||||
MissingPath(std::path::PathBuf),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
mod dirs;
|
|
||||||
mod parse_tree;
|
|
||||||
mod upgrade;
|
|
||||||
|
|
||||||
fn default_version() -> &'static str {
|
|
||||||
static DEFAULT_VERSION: OnceLock<String> = OnceLock::new();
|
|
||||||
DEFAULT_VERSION
|
|
||||||
.get_or_init(|| download_cef::default_version(env!("CARGO_PKG_VERSION")))
|
|
||||||
.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_download_url() -> &'static str {
|
|
||||||
static DEFAULT_DOWNLOAD_URL: OnceLock<String> = OnceLock::new();
|
|
||||||
DEFAULT_DOWNLOAD_URL
|
|
||||||
.get_or_init(|| download_cef::default_download_url())
|
|
||||||
.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[command(about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
#[arg(short, long)]
|
|
||||||
download: bool,
|
|
||||||
#[arg(short, long)]
|
|
||||||
bindgen: bool,
|
|
||||||
#[arg(short, long, default_value = DEFAULT_TARGET)]
|
|
||||||
target: String,
|
|
||||||
#[arg(short, long, default_value = default_version())]
|
|
||||||
version: String,
|
|
||||||
#[arg(short, long, default_value = default_download_url())]
|
|
||||||
mirror_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let args = Args::parse();
|
|
||||||
let target = args.target.as_str();
|
|
||||||
|
|
||||||
if args.bindgen {
|
|
||||||
if args.download {
|
|
||||||
let _ = upgrade::download(args.mirror_url.as_str(), target, args.version.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
upgrade::sys_bindgen(target)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bindings_file = upgrade::get_target_bindings(target);
|
|
||||||
let mut sys_bindings = dirs::get_sys_dir()?;
|
|
||||||
sys_bindings.push("src");
|
|
||||||
sys_bindings.push("bindings");
|
|
||||||
sys_bindings.push(&bindings_file);
|
|
||||||
let mut cef_bindings = dirs::get_cef_dir()?;
|
|
||||||
cef_bindings.push("src");
|
|
||||||
cef_bindings.push("bindings");
|
|
||||||
cef_bindings.push(&bindings_file);
|
|
||||||
|
|
||||||
let bindings = parse_tree::generate_bindings(&sys_bindings)?;
|
|
||||||
let source = read_bindings(&bindings)?;
|
|
||||||
let dest = read_bindings(&cef_bindings).unwrap_or_default();
|
|
||||||
|
|
||||||
if source != dest {
|
|
||||||
fs::copy(&bindings, &cef_bindings)?;
|
|
||||||
println!("Updated: {}", cef_bindings.display());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_bindings(source_path: &Path) -> crate::Result<String> {
|
|
||||||
let mut source_file = fs::File::open(source_path)?;
|
|
||||||
let mut updated = String::default();
|
|
||||||
source_file.read_to_string(&mut updated)?;
|
|
||||||
Ok(updated)
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,102 +0,0 @@
|
|||||||
use crate::dirs;
|
|
||||||
use std::{
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
const TARGETS: &[&str] = &[
|
|
||||||
// macos
|
|
||||||
"aarch64-apple-darwin",
|
|
||||||
"x86_64-apple-darwin",
|
|
||||||
// windows
|
|
||||||
"x86_64-pc-windows-msvc",
|
|
||||||
"aarch64-pc-windows-msvc",
|
|
||||||
"i686-pc-windows-msvc",
|
|
||||||
// linux
|
|
||||||
"x86_64-unknown-linux-gnu",
|
|
||||||
"aarch64-unknown-linux-gnu",
|
|
||||||
"arm-unknown-linux-gnueabi",
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn download(url: &str, target: &str, version: &str) -> PathBuf {
|
|
||||||
assert!(TARGETS.contains(&target), "unsupported target {target}");
|
|
||||||
|
|
||||||
let archive =
|
|
||||||
download_cef::download_target_archive_from(url, target, version, dirs::get_out_dir(), true)
|
|
||||||
.expect("download failed");
|
|
||||||
|
|
||||||
download_cef::extract_target_archive(target, &archive, dirs::get_out_dir(), true)
|
|
||||||
.expect("extraction failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sys_bindgen(target: &str) -> crate::Result<()> {
|
|
||||||
assert!(TARGETS.contains(&target), "unsupported target {target}");
|
|
||||||
let (os, arch) = target_to_os_arch(target);
|
|
||||||
let cef_path = dirs::get_cef_root(os, arch);
|
|
||||||
bindgen(target, &cef_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_target_bindings(target: &str) -> String {
|
|
||||||
assert!(TARGETS.contains(&target), "unsupported target {target}");
|
|
||||||
format!("{}.rs", target.replace('-', "_"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bindgen(target: &str, cef_path: &Path) -> crate::Result<()> {
|
|
||||||
let mut sys_bindings = dirs::get_sys_dir()?;
|
|
||||||
let mut wrapper = sys_bindings.clone();
|
|
||||||
sys_bindings.push("src");
|
|
||||||
sys_bindings.push("bindings");
|
|
||||||
sys_bindings.push(format!("{}.rs", target.replace('-', "_")));
|
|
||||||
wrapper.push("wrapper.h");
|
|
||||||
|
|
||||||
let mut bindings = bindgen::Builder::default()
|
|
||||||
.header(wrapper.display().to_string())
|
|
||||||
.default_enum_style(bindgen::EnumVariation::Rust {
|
|
||||||
non_exhaustive: true,
|
|
||||||
})
|
|
||||||
.allowlist_type("cef_.*")
|
|
||||||
.allowlist_function("cef_.*")
|
|
||||||
.allowlist_item("CEF_API_VERSION(_.+)?")
|
|
||||||
.allowlist_item("CEF_VERSION(_.+)?")
|
|
||||||
.allowlist_item("CHROME_VERSION(_.+)?")
|
|
||||||
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
|
||||||
.bitfield_enum(".*_mask_t")
|
|
||||||
.clang_args([
|
|
||||||
format!("-I{}", cef_path.display()),
|
|
||||||
format!("--target={target}"),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if target.contains("windows") {
|
|
||||||
bindings = bindings.new_type_alias("HINSTANCE").new_type_alias("HWND");
|
|
||||||
} else if target.contains("apple") {
|
|
||||||
let sdk_path = Command::new("xcrun")
|
|
||||||
.args(["--sdk", "macosx", "--show-sdk-path"])
|
|
||||||
.output()
|
|
||||||
.unwrap()
|
|
||||||
.stdout;
|
|
||||||
|
|
||||||
bindings = bindings.clang_arg(format!(
|
|
||||||
"--sysroot={}",
|
|
||||||
String::from_utf8_lossy(&sdk_path).trim()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let bindings = bindings.generate()?;
|
|
||||||
|
|
||||||
bindings.write_to_file(&sys_bindings)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn target_to_os_arch(target: &str) -> (&str, &str) {
|
|
||||||
match target {
|
|
||||||
"aarch64-apple-darwin" => ("macos", "aarch64"),
|
|
||||||
"x86_64-apple-darwin" => ("macos", "x86_64"),
|
|
||||||
"x86_64-pc-windows-msvc" => ("windows", "x86_64"),
|
|
||||||
"aarch64-pc-windows-msvc" => ("windows", "aarch64"),
|
|
||||||
"i686-pc-windows-msvc" => ("windows", "x86"),
|
|
||||||
"x86_64-unknown-linux-gnu" => ("linux", "x86_64"),
|
|
||||||
"aarch64-unknown-linux-gnu" => ("linux", "aarch64"),
|
|
||||||
"arm-unknown-linux-gnueabi" => ("linux", "arm"),
|
|
||||||
v => panic!("unsupported {v:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,7 +13,7 @@ import * as core from '@janhq/core'
|
|||||||
|
|
||||||
## Build an Extension
|
## Build an Extension
|
||||||
|
|
||||||
1. Download an extension template, for example, [https://github.com/menloresearch/extension-template](https://github.com/menloresearch/extension-template).
|
1. Download an extension template, for example, [https://github.com/janhq/extension-template](https://github.com/janhq/extension-template).
|
||||||
|
|
||||||
2. Update the source code:
|
2. Update the source code:
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
"@vitest/coverage-v8": "^2.1.8",
|
"@vitest/coverage-v8": "^2.1.8",
|
||||||
"@vitest/ui": "^2.1.8",
|
"@vitest/ui": "^2.1.8",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"happy-dom": "^15.11.6",
|
"happy-dom": "^20.0.0",
|
||||||
"pacote": "^21.0.0",
|
"pacote": "^21.0.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
|
|||||||
@ -11,6 +11,8 @@ export enum ExtensionTypeEnum {
|
|||||||
HuggingFace = 'huggingFace',
|
HuggingFace = 'huggingFace',
|
||||||
Engine = 'engine',
|
Engine = 'engine',
|
||||||
Hardware = 'hardware',
|
Hardware = 'hardware',
|
||||||
|
RAG = 'rag',
|
||||||
|
VectorDB = 'vectorDB',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionType {
|
export interface ExtensionType {
|
||||||
|
|||||||
@ -182,6 +182,7 @@ export interface SessionInfo {
|
|||||||
port: number // llama-server output port (corrected from portid)
|
port: number // llama-server output port (corrected from portid)
|
||||||
model_id: string //name of the model
|
model_id: string //name of the model
|
||||||
model_path: string // path of the loaded model
|
model_path: string // path of the loaded model
|
||||||
|
is_embedding: boolean
|
||||||
api_key: string
|
api_key: string
|
||||||
mmproj_path?: string
|
mmproj_path?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,3 +23,8 @@ export { MCPExtension } from './mcp'
|
|||||||
* Base AI Engines.
|
* Base AI Engines.
|
||||||
*/
|
*/
|
||||||
export * from './engines'
|
export * from './engines'
|
||||||
|
|
||||||
|
export { RAGExtension, RAG_INTERNAL_SERVER } from './rag'
|
||||||
|
export type { AttachmentInput, IngestAttachmentsResult } from './rag'
|
||||||
|
export { VectorDBExtension } from './vector-db'
|
||||||
|
export type { SearchMode, VectorDBStatus, VectorChunkInput, VectorSearchResult, AttachmentFileInfo, VectorDBFileInput, VectorDBIngestOptions } from './vector-db'
|
||||||
|
|||||||
36
core/src/browser/extensions/rag.ts
Normal file
36
core/src/browser/extensions/rag.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { BaseExtension, ExtensionTypeEnum } from '../extension'
|
||||||
|
import type { MCPTool, MCPToolCallResult } from '../../types'
|
||||||
|
import type { AttachmentFileInfo } from './vector-db'
|
||||||
|
|
||||||
|
export interface AttachmentInput {
|
||||||
|
path: string
|
||||||
|
name?: string
|
||||||
|
type?: string
|
||||||
|
size?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IngestAttachmentsResult {
|
||||||
|
filesProcessed: number
|
||||||
|
chunksInserted: number
|
||||||
|
files: AttachmentFileInfo[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RAG_INTERNAL_SERVER = 'rag-internal'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RAG extension base: exposes RAG tools and orchestration API.
|
||||||
|
*/
|
||||||
|
export abstract class RAGExtension extends BaseExtension {
|
||||||
|
type(): ExtensionTypeEnum | undefined {
|
||||||
|
return ExtensionTypeEnum.RAG
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getTools(): Promise<MCPTool[]>
|
||||||
|
/**
|
||||||
|
* Lightweight list of tool names for quick routing/lookup.
|
||||||
|
*/
|
||||||
|
abstract getToolNames(): Promise<string[]>
|
||||||
|
abstract callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult>
|
||||||
|
|
||||||
|
abstract ingestAttachments(threadId: string, files: AttachmentInput[]): Promise<IngestAttachmentsResult>
|
||||||
|
}
|
||||||
82
core/src/browser/extensions/vector-db.ts
Normal file
82
core/src/browser/extensions/vector-db.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { BaseExtension, ExtensionTypeEnum } from '../extension'
|
||||||
|
|
||||||
|
export type SearchMode = 'auto' | 'ann' | 'linear'
|
||||||
|
|
||||||
|
export interface VectorDBStatus {
|
||||||
|
ann_available: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VectorChunkInput {
|
||||||
|
text: string
|
||||||
|
embedding: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VectorSearchResult {
|
||||||
|
id: string
|
||||||
|
text: string
|
||||||
|
score?: number
|
||||||
|
file_id: string
|
||||||
|
chunk_file_order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AttachmentFileInfo {
|
||||||
|
id: string
|
||||||
|
name?: string
|
||||||
|
path?: string
|
||||||
|
type?: string
|
||||||
|
size?: number
|
||||||
|
chunk_count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// High-level input types for file ingestion
|
||||||
|
export interface VectorDBFileInput {
|
||||||
|
path: string
|
||||||
|
name?: string
|
||||||
|
type?: string
|
||||||
|
size?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VectorDBIngestOptions {
|
||||||
|
chunkSize: number
|
||||||
|
chunkOverlap: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vector DB extension base: abstraction over local vector storage and search.
|
||||||
|
*/
|
||||||
|
export abstract class VectorDBExtension extends BaseExtension {
|
||||||
|
type(): ExtensionTypeEnum | undefined {
|
||||||
|
return ExtensionTypeEnum.VectorDB
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getStatus(): Promise<VectorDBStatus>
|
||||||
|
abstract createCollection(threadId: string, dimension: number): Promise<void>
|
||||||
|
abstract insertChunks(
|
||||||
|
threadId: string,
|
||||||
|
fileId: string,
|
||||||
|
chunks: VectorChunkInput[]
|
||||||
|
): Promise<void>
|
||||||
|
abstract ingestFile(
|
||||||
|
threadId: string,
|
||||||
|
file: VectorDBFileInput,
|
||||||
|
opts: VectorDBIngestOptions
|
||||||
|
): Promise<AttachmentFileInfo>
|
||||||
|
abstract searchCollection(
|
||||||
|
threadId: string,
|
||||||
|
query_embedding: number[],
|
||||||
|
limit: number,
|
||||||
|
threshold: number,
|
||||||
|
mode?: SearchMode,
|
||||||
|
fileIds?: string[]
|
||||||
|
): Promise<VectorSearchResult[]>
|
||||||
|
abstract deleteChunks(threadId: string, ids: string[]): Promise<void>
|
||||||
|
abstract deleteFile(threadId: string, fileId: string): Promise<void>
|
||||||
|
abstract deleteCollection(threadId: string): Promise<void>
|
||||||
|
abstract listAttachments(threadId: string, limit?: number): Promise<AttachmentFileInfo[]>
|
||||||
|
abstract getChunks(
|
||||||
|
threadId: string,
|
||||||
|
fileId: string,
|
||||||
|
startOrder: number,
|
||||||
|
endOrder: number
|
||||||
|
): Promise<VectorSearchResult[]>
|
||||||
|
}
|
||||||
@ -12,6 +12,8 @@ export type SettingComponentProps = {
|
|||||||
extensionName?: string
|
extensionName?: string
|
||||||
requireModelReload?: boolean
|
requireModelReload?: boolean
|
||||||
configType?: ConfigType
|
configType?: ConfigType
|
||||||
|
titleKey?: string
|
||||||
|
descriptionKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConfigType = 'runtime' | 'setting'
|
export type ConfigType = 'runtime' | 'setting'
|
||||||
|
|||||||
@ -18,7 +18,7 @@ We try to **keep routes consistent** to maintain SEO.
|
|||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
|
|
||||||
Refer to the [Contributing Guide](https://github.com/menloresearch/jan/blob/main/CONTRIBUTING.md) for more comprehensive information on how to contribute to the Jan project.
|
Refer to the [Contributing Guide](https://github.com/janhq/jan/blob/main/CONTRIBUTING.md) for more comprehensive information on how to contribute to the Jan project.
|
||||||
|
|
||||||
### Pre-requisites and Installation
|
### Pre-requisites and Installation
|
||||||
|
|
||||||
|
|||||||
@ -1581,7 +1581,7 @@
|
|||||||
},
|
},
|
||||||
"cover": {
|
"cover": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "https://raw.githubusercontent.com/menloresearch/jan/main/models/trinity-v1.2-7b/cover.png"
|
"example": "https://raw.githubusercontent.com/janhq/jan/main/models/trinity-v1.2-7b/cover.png"
|
||||||
},
|
},
|
||||||
"engine": {
|
"engine": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export const APIReference = () => {
|
|||||||
<ApiReferenceReact
|
<ApiReferenceReact
|
||||||
configuration={{
|
configuration={{
|
||||||
spec: {
|
spec: {
|
||||||
url: 'https://raw.githubusercontent.com/menloresearch/docs/main/public/openapi/jan.json',
|
url: 'https://raw.githubusercontent.com/janhq/docs/main/public/openapi/jan.json',
|
||||||
},
|
},
|
||||||
theme: 'alternate',
|
theme: 'alternate',
|
||||||
hideModels: true,
|
hideModels: true,
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const Changelog = () => {
|
|||||||
<p className="text-base mt-2 leading-relaxed">
|
<p className="text-base mt-2 leading-relaxed">
|
||||||
Latest release updates from the Jan team. Check out our
|
Latest release updates from the Jan team. Check out our
|
||||||
<a
|
<a
|
||||||
href="https://github.com/orgs/menloresearch/projects/30"
|
href="https://github.com/orgs/janhq/projects/30"
|
||||||
className="text-blue-600 dark:text-blue-400 cursor-pointer"
|
className="text-blue-600 dark:text-blue-400 cursor-pointer"
|
||||||
>
|
>
|
||||||
Roadmap
|
Roadmap
|
||||||
@ -150,7 +150,7 @@ const Changelog = () => {
|
|||||||
|
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Link
|
<Link
|
||||||
href="https://github.com/menloresearch/jan/releases"
|
href="https://github.com/janhq/jan/releases"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="dark:nx-bg-neutral-900 dark:text-white bg-black text-white hover:text-white justify-center dark:border dark:border-neutral-800 flex-shrink-0 px-4 py-3 rounded-xl inline-flex items-center"
|
className="dark:nx-bg-neutral-900 dark:text-white bg-black text-white hover:text-white justify-center dark:border dark:border-neutral-800 flex-shrink-0 px-4 py-3 rounded-xl inline-flex items-center"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -72,7 +72,7 @@ export default function CardDownload({ lastRelease }: Props) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...system,
|
...system,
|
||||||
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
href: `https://github.com/janhq/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
||||||
size: asset ? formatFileSize(asset.size) : undefined,
|
size: asset ? formatFileSize(asset.size) : undefined,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -139,7 +139,7 @@ const DropdownDownload = ({ lastRelease }: Props) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...system,
|
...system,
|
||||||
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
href: `https://github.com/janhq/jan/releases/download/${lastRelease.tag_name}/${downloadUrl}`,
|
||||||
size: asset ? formatFileSize(asset.size) : undefined,
|
size: asset ? formatFileSize(asset.size) : undefined,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -23,7 +23,7 @@ const BuiltWithLove = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col lg:flex-row gap-8 mt-8 items-center justify-center">
|
<div className="flex flex-col lg:flex-row gap-8 mt-8 items-center justify-center">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/menloresearch/jan"
|
href="https://github.com/janhq/jan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="dark:bg-white bg-black inline-flex w-56 px-4 py-3 rounded-xl cursor-pointer justify-center items-start space-x-4 "
|
className="dark:bg-white bg-black inline-flex w-56 px-4 py-3 rounded-xl cursor-pointer justify-center items-start space-x-4 "
|
||||||
>
|
>
|
||||||
|
|||||||
@ -44,7 +44,7 @@ const Hero = () => {
|
|||||||
<div className="mt-10 text-center">
|
<div className="mt-10 text-center">
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
href="https://github.com/menloresearch/jan/releases"
|
href="https://github.com/janhq/jan/releases"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="hidden lg:inline-block"
|
className="hidden lg:inline-block"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -95,7 +95,7 @@ const Home = () => {
|
|||||||
<div className="container mx-auto relative z-10">
|
<div className="container mx-auto relative z-10">
|
||||||
<div className="flex justify-center items-center mt-14 lg:mt-20 px-4">
|
<div className="flex justify-center items-center mt-14 lg:mt-20 px-4">
|
||||||
<a
|
<a
|
||||||
href={`https://github.com/menloresearch/jan/releases/tag/${lastVersion}`}
|
href={`https://github.com/janhq/jan/releases/tag/${lastVersion}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="bg-black/40 px-3 lg:px-4 rounded-full h-10 inline-flex items-center max-w-full animate-fade-in delay-100"
|
className="bg-black/40 px-3 lg:px-4 rounded-full h-10 inline-flex items-center max-w-full animate-fade-in delay-100"
|
||||||
@ -270,7 +270,7 @@ const Home = () => {
|
|||||||
data-delay="600"
|
data-delay="600"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/menloresearch/jan"
|
href="https://github.com/janhq/jan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@ -387,7 +387,7 @@ const Home = () => {
|
|||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
className="hidden md:block"
|
className="hidden md:block"
|
||||||
href="https://github.com/menloresearch/jan"
|
href="https://github.com/janhq/jan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@ -413,7 +413,7 @@ const Home = () => {
|
|||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
className="md:hidden mt-4 block w-full"
|
className="md:hidden mt-4 block w-full"
|
||||||
href="https://github.com/menloresearch/jan"
|
href="https://github.com/janhq/jan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -95,7 +95,7 @@ const Navbar = ({ noScroll }: { noScroll?: boolean }) => {
|
|||||||
})}
|
})}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/menloresearch/jan/releases/latest"
|
href="https://github.com/janhq/jan/releases/latest"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@ -141,7 +141,7 @@ const Navbar = ({ noScroll }: { noScroll?: boolean }) => {
|
|||||||
<FaLinkedinIn className="size-5" />
|
<FaLinkedinIn className="size-5" />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/menloresearch/jan"
|
href="https://github.com/janhq/jan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="rounded-lg flex items-center justify-center"
|
className="rounded-lg flex items-center justify-center"
|
||||||
@ -156,7 +156,7 @@ const Navbar = ({ noScroll }: { noScroll?: boolean }) => {
|
|||||||
{/* Mobile Download Button and Hamburger */}
|
{/* Mobile Download Button and Hamburger */}
|
||||||
<div className="lg:hidden flex items-center gap-3">
|
<div className="lg:hidden flex items-center gap-3">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/menloresearch/jan/releases/latest"
|
href="https://github.com/janhq/jan/releases/latest"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@ -278,7 +278,7 @@ const Navbar = ({ noScroll }: { noScroll?: boolean }) => {
|
|||||||
<FaLinkedinIn className="size-5" />
|
<FaLinkedinIn className="size-5" />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/menloresearch/jan"
|
href="https://github.com/janhq/jan"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-black rounded-lg flex items-center justify-center"
|
className="text-black rounded-lg flex items-center justify-center"
|
||||||
@ -296,7 +296,7 @@ const Navbar = ({ noScroll }: { noScroll?: boolean }) => {
|
|||||||
asChild
|
asChild
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/menloresearch/jan/releases/latest"
|
href="https://github.com/janhq/jan/releases/latest"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -120,7 +120,7 @@ export function DropdownButton({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...option,
|
...option,
|
||||||
href: `https://github.com/menloresearch/jan/releases/download/${lastRelease.tag_name}/${fileName}`,
|
href: `https://github.com/janhq/jan/releases/download/${lastRelease.tag_name}/${fileName}`,
|
||||||
size: asset ? formatFileSize(asset.size) : 'N/A',
|
size: asset ? formatFileSize(asset.size) : 'N/A',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -18,7 +18,7 @@ description: Development setup, workflow, and contribution guidelines for Jan Se
|
|||||||
|
|
||||||
1. **Clone Repository**
|
1. **Clone Repository**
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/menloresearch/jan-server
|
git clone https://github.com/janhq/jan-server
|
||||||
cd jan-server
|
cd jan-server
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ Jan Server currently supports minikube for local development. Production Kuberne
|
|||||||
|
|
||||||
1. **Clone the repository**
|
1. **Clone the repository**
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/menloresearch/jan-server
|
git clone https://github.com/janhq/jan-server
|
||||||
cd jan-server
|
cd jan-server
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -24,4 +24,4 @@ Fixes 💫
|
|||||||
|
|
||||||
Update your product or download the latest: https://jan.ai
|
Update your product or download the latest: https://jan.ai
|
||||||
|
|
||||||
For more details, see the [GitHub release notes](https://github.com/menloresearch/jan/releases/tag/v0.5.5).
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.5).
|
||||||
@ -24,4 +24,4 @@ Jan now supports Mistral's new model Codestral. Thanks [Bartowski](https://huggi
|
|||||||
|
|
||||||
More GGUF models can run in Jan - we rebased to llama.cpp b3012.Big thanks to [ggerganov](https://github.com/ggerganov)
|
More GGUF models can run in Jan - we rebased to llama.cpp b3012.Big thanks to [ggerganov](https://github.com/ggerganov)
|
||||||
|
|
||||||
For more details, see the [GitHub release notes](https://github.com/menloresearch/jan/releases/tag/v0.5.0).
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.0).
|
||||||
|
|||||||
@ -28,4 +28,4 @@ Jan now understands LaTeX, allowing users to process and understand complex math
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
For more details, see the [GitHub release notes](https://github.com/menloresearch/jan/releases/tag/v0.4.12).
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.4.12).
|
||||||
|
|||||||
@ -28,4 +28,4 @@ Users can now connect to OpenAI's new model GPT-4o.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
For more details, see the [GitHub release notes.](https://github.com/menloresearch/jan/releases/tag/v0.4.13)
|
For more details, see the [GitHub release notes.](https://github.com/janhq/jan/releases/tag/v0.4.13)
|
||||||
|
|||||||
@ -16,4 +16,4 @@ More GGUF models can run in Jan - we rebased to llama.cpp b2961.
|
|||||||
|
|
||||||
Huge shoutouts to [ggerganov](https://github.com/ggerganov) and contributors for llama.cpp, and [Bartowski](https://huggingface.co/bartowski) for GGUF models.
|
Huge shoutouts to [ggerganov](https://github.com/ggerganov) and contributors for llama.cpp, and [Bartowski](https://huggingface.co/bartowski) for GGUF models.
|
||||||
|
|
||||||
For more details, see the [GitHub release notes](https://github.com/menloresearch/jan/releases/tag/v0.4.14).
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.4.14).
|
||||||
|
|||||||
@ -26,4 +26,4 @@ We've updated to llama.cpp b3088 for better performance - thanks to [GG](https:/
|
|||||||
- Reduced chat font weight (back to normal!)
|
- Reduced chat font weight (back to normal!)
|
||||||
- Restored the maximize button
|
- Restored the maximize button
|
||||||
|
|
||||||
For more details, see the [GitHub release notes](https://github.com/menloresearch/jan/releases/tag/v0.5.1).
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.1).
|
||||||
|
|||||||
@ -32,4 +32,4 @@ We've restored the tooltip hover functionality, which makes it easier to access
|
|||||||
|
|
||||||
The right-click options for thread settings are now fully operational again. You can now manage your threads with this fix.
|
The right-click options for thread settings are now fully operational again. You can now manage your threads with this fix.
|
||||||
|
|
||||||
For more details, see the [GitHub release notes](https://github.com/menloresearch/jan/releases/tag/v0.5.2).
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.2).
|
||||||
|
|||||||
@ -23,4 +23,4 @@ We've been working on stability issues over the last few weeks. Jan is now more
|
|||||||
- Fixed the GPU memory utilization bar
|
- Fixed the GPU memory utilization bar
|
||||||
- Some UX and copy improvements
|
- Some UX and copy improvements
|
||||||
|
|
||||||
For more details, see the [GitHub release notes](https://github.com/menloresearch/jan/releases/tag/v0.5.3).
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.3).
|
||||||
|
|||||||
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