Compare commits
6 Commits
v0.12.3-de
...
v0.12.3-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e22614648 | ||
|
|
5d87e1e563 | ||
|
|
d735b189f5 | ||
|
|
3d575f4f68 | ||
|
|
b58728dc0e | ||
|
|
672177f570 |
95
.github/workflows/build-and-upload.yml
vendored
95
.github/workflows/build-and-upload.yml
vendored
@@ -28,6 +28,21 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
default: true
|
default: true
|
||||||
type: boolean
|
type: boolean
|
||||||
|
upload_actions_artifacts:
|
||||||
|
description: "Upload built artifacts to GitHub Actions run artifacts"
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
actions_artifacts_retention_days:
|
||||||
|
description: "Retention (days) for GitHub Actions artifacts"
|
||||||
|
required: false
|
||||||
|
default: 7
|
||||||
|
type: number
|
||||||
|
actions_artifacts_name_prefix:
|
||||||
|
description: "Optional prefix for Actions artifact names"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
set_versions:
|
set_versions:
|
||||||
description: "Run npm version to set workspace versions"
|
description: "Run npm version to set workspace versions"
|
||||||
required: false
|
required: false
|
||||||
@@ -203,6 +218,15 @@ jobs:
|
|||||||
gh release upload "$TAG" "$file" --clobber
|
gh release upload "$TAG" "$file" --clobber
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Electron macOS)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}electron-macos
|
||||||
|
path: packages/electron-app/release/*.zip
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2025
|
runs-on: windows-2025
|
||||||
env:
|
env:
|
||||||
@@ -244,6 +268,15 @@ jobs:
|
|||||||
gh release upload $env:TAG $_.FullName --clobber
|
gh release upload $env:TAG $_.FullName --clobber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Electron Windows)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}electron-windows
|
||||||
|
path: packages/electron-app/release/*.zip
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
env:
|
env:
|
||||||
@@ -286,6 +319,15 @@ jobs:
|
|||||||
gh release upload "$TAG" "$file" --clobber
|
gh release upload "$TAG" "$file" --clobber
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Electron Linux)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}electron-linux
|
||||||
|
path: packages/electron-app/release/*.zip
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
build-tauri-macos:
|
build-tauri-macos:
|
||||||
runs-on: macos-15-intel
|
runs-on: macos-15-intel
|
||||||
env:
|
env:
|
||||||
@@ -339,7 +381,7 @@ jobs:
|
|||||||
run: npm exec -- tauri build
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (macOS)
|
- name: Package Tauri artifacts (macOS)
|
||||||
if: ${{ inputs.upload }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
BUNDLE_ROOT="packages/tauri-app/target/release/bundle"
|
BUNDLE_ROOT="packages/tauri-app/target/release/bundle"
|
||||||
@@ -350,6 +392,15 @@ jobs:
|
|||||||
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-x64.zip"
|
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-x64.zip"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Tauri macOS)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}tauri-macos
|
||||||
|
path: packages/tauri-app/release-tauri/*.zip
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
- name: Upload Tauri release assets (macOS)
|
- name: Upload Tauri release assets (macOS)
|
||||||
if: ${{ inputs.upload && inputs.tag != '' }}
|
if: ${{ inputs.upload && inputs.tag != '' }}
|
||||||
run: |
|
run: |
|
||||||
@@ -414,7 +465,7 @@ jobs:
|
|||||||
run: npm exec -- tauri build
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (macOS arm64)
|
- name: Package Tauri artifacts (macOS arm64)
|
||||||
if: ${{ inputs.upload }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
BUNDLE_ROOT="packages/tauri-app/target/release/bundle"
|
BUNDLE_ROOT="packages/tauri-app/target/release/bundle"
|
||||||
@@ -425,6 +476,15 @@ jobs:
|
|||||||
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-arm64.zip"
|
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-arm64.zip"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Tauri macOS arm64)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}tauri-macos-arm64
|
||||||
|
path: packages/tauri-app/release-tauri/*.zip
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
- name: Upload Tauri release assets (macOS arm64)
|
- name: Upload Tauri release assets (macOS arm64)
|
||||||
if: ${{ inputs.upload && inputs.tag != '' }}
|
if: ${{ inputs.upload && inputs.tag != '' }}
|
||||||
run: |
|
run: |
|
||||||
@@ -492,7 +552,7 @@ jobs:
|
|||||||
run: npm exec -- tauri build
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (Windows)
|
- name: Package Tauri artifacts (Windows)
|
||||||
if: ${{ inputs.upload }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$bundleRoot = "packages/tauri-app/target/release/bundle"
|
$bundleRoot = "packages/tauri-app/target/release/bundle"
|
||||||
@@ -505,6 +565,15 @@ jobs:
|
|||||||
Compress-Archive -Path $exe.Directory.FullName -DestinationPath $dest -Force
|
Compress-Archive -Path $exe.Directory.FullName -DestinationPath $dest -Force
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Tauri Windows)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}tauri-windows
|
||||||
|
path: packages/tauri-app/release-tauri/*.zip
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
- name: Upload Tauri release assets (Windows)
|
- name: Upload Tauri release assets (Windows)
|
||||||
if: ${{ inputs.upload && inputs.tag != '' }}
|
if: ${{ inputs.upload && inputs.tag != '' }}
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
@@ -582,7 +651,7 @@ jobs:
|
|||||||
run: npm exec -- tauri build
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (Linux)
|
- name: Package Tauri artifacts (Linux)
|
||||||
if: ${{ inputs.upload }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SEARCH_ROOT="packages/tauri-app/target"
|
SEARCH_ROOT="packages/tauri-app/target"
|
||||||
@@ -608,6 +677,15 @@ jobs:
|
|||||||
cp "$deb" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.deb"
|
cp "$deb" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.deb"
|
||||||
cp "$rpm" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.rpm"
|
cp "$rpm" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.rpm"
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Tauri Linux)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}tauri-linux
|
||||||
|
path: packages/tauri-app/release-tauri/*
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
- name: Upload Tauri release assets (Linux)
|
- name: Upload Tauri release assets (Linux)
|
||||||
if: ${{ inputs.upload && inputs.tag != '' }}
|
if: ${{ inputs.upload && inputs.tag != '' }}
|
||||||
run: |
|
run: |
|
||||||
@@ -766,3 +844,12 @@ jobs:
|
|||||||
echo "Uploading $file"
|
echo "Uploading $file"
|
||||||
gh release upload "$TAG" "$file" --clobber
|
gh release upload "$TAG" "$file" --clobber
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: Upload Actions artifacts (Electron Linux RPM)
|
||||||
|
if: ${{ inputs.upload_actions_artifacts }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.actions_artifacts_name_prefix }}electron-linux-rpm
|
||||||
|
path: packages/electron-app/release/*.rpm
|
||||||
|
retention-days: ${{ inputs.actions_artifacts_retention_days }}
|
||||||
|
if-no-files-found: error
|
||||||
|
|||||||
120
.github/workflows/comment-pr-artifacts.yml
vendored
Normal file
120
.github/workflows/comment-pr-artifacts.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
name: Comment PR Artifacts
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows:
|
||||||
|
- PR Build Validation
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment:
|
||||||
|
# Only runs for PR Build Validation runs triggered by PRs.
|
||||||
|
if: ${{ github.event.workflow_run.event == 'pull_request' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Comment with artifact download link
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const run = context.payload.workflow_run;
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
|
||||||
|
const prs = run.pull_requests || [];
|
||||||
|
let prNumber = prs[0]?.number;
|
||||||
|
|
||||||
|
// `workflow_run` payload does not reliably include pull request numbers.
|
||||||
|
// Resolve PR number(s) by asking GitHub for PRs associated with the head SHA.
|
||||||
|
if (!prNumber) {
|
||||||
|
const headSha = run.head_sha;
|
||||||
|
if (!headSha) {
|
||||||
|
core.info('No PR number and no head_sha found for this workflow_run; skipping.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const associated = await github.rest.repos.listPullRequestsAssociatedWithCommit({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
commit_sha: headSha,
|
||||||
|
});
|
||||||
|
|
||||||
|
const open = (associated.data || []).find((p) => p.state === 'open');
|
||||||
|
prNumber = open?.number;
|
||||||
|
if (!prNumber) {
|
||||||
|
core.info(`No open PR found associated with commit ${headSha}; skipping.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only comment when the PR build job actually ran (i.e. authorization passed).
|
||||||
|
// Unauthorized PRs targeting non-dev will skip the `build` job.
|
||||||
|
const jobs = await github.paginate(
|
||||||
|
github.rest.actions.listJobsForWorkflowRun,
|
||||||
|
{ owner, repo, run_id: run.id, per_page: 100 }
|
||||||
|
);
|
||||||
|
const buildJob = jobs.find((j) => j.name === 'build');
|
||||||
|
if (!buildJob) {
|
||||||
|
core.info('No `build` job found on this run; skipping.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (buildJob.conclusion === 'skipped') {
|
||||||
|
core.info('`build` job was skipped; skipping comment.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List artifacts from the run. If none exist (e.g. build failed before packaging),
|
||||||
|
// still comment with the run link so testers can see logs.
|
||||||
|
const artifacts = await github.paginate(
|
||||||
|
github.rest.actions.listWorkflowRunArtifacts,
|
||||||
|
{ owner, repo, run_id: run.id, per_page: 100 }
|
||||||
|
);
|
||||||
|
const active = artifacts.filter((a) => !a.expired);
|
||||||
|
|
||||||
|
const marker = '<!-- codenomad-pr-artifacts -->';
|
||||||
|
const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${run.id}`;
|
||||||
|
const retentionDays = 7;
|
||||||
|
|
||||||
|
const artifactsBlock = active.length
|
||||||
|
? ['Artifacts:', ...active.map((a) => `- ${a.name}`)].join('\n')
|
||||||
|
: 'Artifacts: (none found on this run)';
|
||||||
|
|
||||||
|
const body = [
|
||||||
|
marker,
|
||||||
|
'PR builds are available as GitHub Actions artifacts:',
|
||||||
|
'',
|
||||||
|
runUrl,
|
||||||
|
'',
|
||||||
|
`Artifacts expire in ${retentionDays} days.`,
|
||||||
|
artifactsBlock,
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const comments = await github.paginate(
|
||||||
|
github.rest.issues.listComments,
|
||||||
|
{ owner, repo, issue_number: prNumber, per_page: 100 }
|
||||||
|
);
|
||||||
|
const existing = comments.find((c) => (c.body || '').includes(marker));
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
comment_id: existing.id,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
core.info(`Updated existing artifacts comment: ${existing.html_url}`);
|
||||||
|
} else {
|
||||||
|
const created = await github.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: prNumber,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
core.info(`Created artifacts comment: ${created.data.html_url}`);
|
||||||
|
}
|
||||||
56
.github/workflows/pr-build.yml
vendored
Normal file
56
.github/workflows/pr-build.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
name: PR Build Validation
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- reopened
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
actions: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pr-build-${{ github.event.pull_request.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
authorize:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
allowed: ${{ steps.auth.outputs.allowed }}
|
||||||
|
env:
|
||||||
|
ALLOWED_ACTORS: ${{ vars.ALLOWED_NON_DEV_PR_ACTORS }}
|
||||||
|
ACTOR: ${{ github.actor }}
|
||||||
|
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||||
|
steps:
|
||||||
|
- name: Check PR authorization
|
||||||
|
id: auth
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ "$BASE_REF" = "dev" ]; then
|
||||||
|
echo "allowed=true" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
normalized=",${ALLOWED_ACTORS},"
|
||||||
|
if [[ "$normalized" == *",${ACTOR},"* ]]; then
|
||||||
|
echo "allowed=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "allowed=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Skipping builds for unauthorized PR targeting $BASE_REF" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: authorize
|
||||||
|
if: ${{ needs.authorize.outputs.allowed == 'true' }}
|
||||||
|
uses: ./.github/workflows/build-and-upload.yml
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
upload: false
|
||||||
|
upload_actions_artifacts: true
|
||||||
|
actions_artifacts_retention_days: 7
|
||||||
|
actions_artifacts_name_prefix: pr-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }}-
|
||||||
|
set_versions: false
|
||||||
54
.github/workflows/restrict-non-dev-prs.yml
vendored
Normal file
54
.github/workflows/restrict-non-dev-prs.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
name: Restrict Non-Dev PRs
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
restrict-non-dev-prs:
|
||||||
|
if: ${{ github.event.pull_request.base.ref != 'dev' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
ALLOWED_ACTORS: ${{ vars.ALLOWED_NON_DEV_PR_ACTORS }}
|
||||||
|
ACTOR: ${{ github.actor }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||||
|
steps:
|
||||||
|
- name: Check allowed actor
|
||||||
|
id: auth
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
normalized=",${ALLOWED_ACTORS},"
|
||||||
|
if [[ "$normalized" == *",${ACTOR},"* ]]; then
|
||||||
|
echo "authorized=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "authorized=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Comment on unauthorized PR
|
||||||
|
if: ${{ steps.auth.outputs.authorized != 'true' }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr comment "$PR_NUMBER" --body "Thanks for the contribution. PRs need to target \`dev\` branch. Please retarget this PR to the dev branch"
|
||||||
|
|
||||||
|
- name: Close unauthorized PR
|
||||||
|
if: ${{ steps.auth.outputs.authorized != 'true' }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr close "$PR_NUMBER"
|
||||||
|
|
||||||
|
- name: Fail unauthorized PR
|
||||||
|
if: ${{ steps.auth.outputs.authorized != 'true' }}
|
||||||
|
run: |
|
||||||
|
echo "Actor $ACTOR is not allowed to open PRs targeting $BASE_REF" >&2
|
||||||
|
exit 1
|
||||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -3253,9 +3253,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/api": {
|
"node_modules/@tauri-apps/api": {
|
||||||
"version": "2.9.1",
|
"version": "2.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz",
|
||||||
"integrity": "sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw==",
|
"integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==",
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -3322,6 +3322,15 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tauri-apps/plugin-dialog": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-q4Uq3eY87TdcYzXACiYSPhmpBA76shgmQswGkSVio4C82Sz2W4iehe9TnKYwbq7weHiL88Yw19XZm7v28+Micg==",
|
||||||
|
"license": "MIT OR Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tauri-apps/plugin-notification": {
|
"node_modules/@tauri-apps/plugin-notification": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.3.3.tgz",
|
||||||
@@ -10235,14 +10244,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/tauri-plugin-keepawake-api": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tauri-plugin-keepawake-api/-/tauri-plugin-keepawake-api-0.1.0.tgz",
|
|
||||||
"integrity": "sha512-XPUl66zUYiB7kCRxsTdmCoNjFM/++NWCJ4kdTo2NUOgBUa8UVYfayDWnnTzGIQbhT7qNAHs+jgKSjhqSKs/QHA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@tauri-apps/api": ">=2.0.0-beta.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/temp-dir": {
|
"node_modules/temp-dir": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
||||||
@@ -12098,6 +12099,8 @@
|
|||||||
"@suid/icons-material": "^0.9.0",
|
"@suid/icons-material": "^0.9.0",
|
||||||
"@suid/material": "^0.19.0",
|
"@suid/material": "^0.19.0",
|
||||||
"@suid/system": "^0.14.0",
|
"@suid/system": "^0.14.0",
|
||||||
|
"@tauri-apps/api": "^2.10.1",
|
||||||
|
"@tauri-apps/plugin-dialog": "^2.6.0",
|
||||||
"@tauri-apps/plugin-notification": "^2.3.3",
|
"@tauri-apps/plugin-notification": "^2.3.3",
|
||||||
"@tauri-apps/plugin-opener": "^2.5.3",
|
"@tauri-apps/plugin-opener": "^2.5.3",
|
||||||
"ansi-sequence-parser": "^1.1.3",
|
"ansi-sequence-parser": "^1.1.3",
|
||||||
@@ -12110,7 +12113,6 @@
|
|||||||
"shiki": "^3.13.0",
|
"shiki": "^3.13.0",
|
||||||
"solid-js": "^1.8.0",
|
"solid-js": "^1.8.0",
|
||||||
"solid-toast": "^0.5.0",
|
"solid-toast": "^0.5.0",
|
||||||
"tauri-plugin-keepawake-api": "^0.1.0",
|
|
||||||
"yaml": "^2.4.2"
|
"yaml": "^2.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
2430
packages/tauri-app/Cargo.lock
generated
2430
packages/tauri-app/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -19,11 +19,11 @@ thiserror = "1"
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
which = "4"
|
which = "4"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
keepawake = "0.6"
|
||||||
tauri-plugin-dialog = "2"
|
tauri-plugin-dialog = "2"
|
||||||
dirs = "5"
|
dirs = "5"
|
||||||
tauri-plugin-opener = "2"
|
tauri-plugin-opener = "2"
|
||||||
url = "2"
|
url = "2"
|
||||||
tauri-plugin-keepawake = "0.1.1"
|
|
||||||
tauri-plugin-notification = "2"
|
tauri-plugin-notification = "2"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -2378,36 +2378,6 @@
|
|||||||
"const": "dialog:deny-save",
|
"const": "dialog:deny-save",
|
||||||
"markdownDescription": "Denies the save command without any pre-configured scope."
|
"markdownDescription": "Denies the save command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-start`\n- `allow-stop`",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:default",
|
|
||||||
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-start`\n- `allow-stop`"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Enables the start command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:allow-start",
|
|
||||||
"markdownDescription": "Enables the start command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Enables the stop command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:allow-stop",
|
|
||||||
"markdownDescription": "Enables the stop command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Denies the start command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:deny-start",
|
|
||||||
"markdownDescription": "Denies the start command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Denies the stop command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:deny-stop",
|
|
||||||
"markdownDescription": "Denies the stop command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`",
|
"description": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -2378,36 +2378,6 @@
|
|||||||
"const": "dialog:deny-save",
|
"const": "dialog:deny-save",
|
||||||
"markdownDescription": "Denies the save command without any pre-configured scope."
|
"markdownDescription": "Denies the save command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-start`\n- `allow-stop`",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:default",
|
|
||||||
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-start`\n- `allow-stop`"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Enables the start command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:allow-start",
|
|
||||||
"markdownDescription": "Enables the start command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Enables the stop command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:allow-stop",
|
|
||||||
"markdownDescription": "Enables the stop command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Denies the start command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:deny-start",
|
|
||||||
"markdownDescription": "Denies the start command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Denies the stop command without any pre-configured scope.",
|
|
||||||
"type": "string",
|
|
||||||
"const": "keepawake:deny-stop",
|
|
||||||
"markdownDescription": "Denies the stop command without any pre-configured scope."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`",
|
"description": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
mod cli_manager;
|
mod cli_manager;
|
||||||
|
|
||||||
use cli_manager::{CliProcessManager, CliStatus};
|
use cli_manager::{CliProcessManager, CliStatus};
|
||||||
|
use keepawake::KeepAwake;
|
||||||
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Mutex;
|
||||||
use tauri::menu::{MenuBuilder, MenuItem, SubmenuBuilder};
|
use tauri::menu::{MenuBuilder, MenuItem, SubmenuBuilder};
|
||||||
use tauri::plugin::{Builder as PluginBuilder, TauriPlugin};
|
use tauri::plugin::{Builder as PluginBuilder, TauriPlugin};
|
||||||
use tauri::webview::Webview;
|
use tauri::webview::Webview;
|
||||||
@@ -26,9 +29,17 @@ static QUIT_REQUESTED: AtomicBool = AtomicBool::new(false);
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const WINDOWS_APP_USER_MODEL_ID: &str = "ai.neuralnomads.codenomad.client";
|
const WINDOWS_APP_USER_MODEL_ID: &str = "ai.neuralnomads.codenomad.client";
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub manager: CliProcessManager,
|
pub manager: CliProcessManager,
|
||||||
|
pub wake_lock: Mutex<Option<KeepAwake>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
struct WakeLockConfig {
|
||||||
|
display: bool,
|
||||||
|
idle: bool,
|
||||||
|
sleep: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -47,6 +58,39 @@ fn cli_restart(app: AppHandle, state: tauri::State<AppState>) -> Result<CliStatu
|
|||||||
Ok(state.manager.status())
|
Ok(state.manager.status())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn wake_lock_start(
|
||||||
|
state: tauri::State<AppState>,
|
||||||
|
config: Option<WakeLockConfig>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let config = config.unwrap_or(WakeLockConfig {
|
||||||
|
display: true,
|
||||||
|
idle: false,
|
||||||
|
sleep: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut builder = keepawake::Builder::default();
|
||||||
|
builder
|
||||||
|
.display(config.display)
|
||||||
|
.idle(config.idle)
|
||||||
|
.sleep(config.sleep)
|
||||||
|
.reason("CodeNomad active session")
|
||||||
|
.app_name("CodeNomad")
|
||||||
|
.app_reverse_domain("ai.neuralnomads.codenomad.client");
|
||||||
|
|
||||||
|
let wake_lock = builder.create().map_err(|err| err.to_string())?;
|
||||||
|
let mut state_lock = state.wake_lock.lock().map_err(|err| err.to_string())?;
|
||||||
|
*state_lock = Some(wake_lock);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn wake_lock_stop(state: tauri::State<AppState>) -> Result<(), String> {
|
||||||
|
let mut state_lock = state.wake_lock.lock().map_err(|err| err.to_string())?;
|
||||||
|
state_lock.take();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_dev_mode() -> bool {
|
fn is_dev_mode() -> bool {
|
||||||
cfg!(debug_assertions) || std::env::var("TAURI_DEV").is_ok()
|
cfg!(debug_assertions) || std::env::var("TAURI_DEV").is_ok()
|
||||||
}
|
}
|
||||||
@@ -137,11 +181,11 @@ fn main() {
|
|||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.plugin(tauri_plugin_keepawake::init())
|
|
||||||
.plugin(tauri_plugin_notification::init())
|
.plugin(tauri_plugin_notification::init())
|
||||||
.plugin(navigation_guard)
|
.plugin(navigation_guard)
|
||||||
.manage(AppState {
|
.manage(AppState {
|
||||||
manager: CliProcessManager::new(),
|
manager: CliProcessManager::new(),
|
||||||
|
wake_lock: Mutex::new(None),
|
||||||
})
|
})
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
set_windows_app_user_model_id();
|
set_windows_app_user_model_id();
|
||||||
@@ -156,7 +200,12 @@ fn main() {
|
|||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![cli_get_status, cli_restart])
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
cli_get_status,
|
||||||
|
cli_restart,
|
||||||
|
wake_lock_start,
|
||||||
|
wake_lock_stop
|
||||||
|
])
|
||||||
.on_menu_event(|app_handle, event| {
|
.on_menu_event(|app_handle, event| {
|
||||||
match event.id().0.as_str() {
|
match event.id().0.as_str() {
|
||||||
// File menu
|
// File menu
|
||||||
|
|||||||
@@ -18,8 +18,10 @@
|
|||||||
"@suid/icons-material": "^0.9.0",
|
"@suid/icons-material": "^0.9.0",
|
||||||
"@suid/material": "^0.19.0",
|
"@suid/material": "^0.19.0",
|
||||||
"@suid/system": "^0.14.0",
|
"@suid/system": "^0.14.0",
|
||||||
"@tauri-apps/plugin-opener": "^2.5.3",
|
"@tauri-apps/api": "^2.10.1",
|
||||||
|
"@tauri-apps/plugin-dialog": "^2.6.0",
|
||||||
"@tauri-apps/plugin-notification": "^2.3.3",
|
"@tauri-apps/plugin-notification": "^2.3.3",
|
||||||
|
"@tauri-apps/plugin-opener": "^2.5.3",
|
||||||
"ansi-sequence-parser": "^1.1.3",
|
"ansi-sequence-parser": "^1.1.3",
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"github-markdown-css": "^5.8.1",
|
"github-markdown-css": "^5.8.1",
|
||||||
@@ -30,7 +32,6 @@
|
|||||||
"shiki": "^3.13.0",
|
"shiki": "^3.13.0",
|
||||||
"solid-js": "^1.8.0",
|
"solid-js": "^1.8.0",
|
||||||
"solid-toast": "^0.5.0",
|
"solid-toast": "^0.5.0",
|
||||||
"tauri-plugin-keepawake-api": "^0.1.0",
|
|
||||||
"yaml": "^2.4.2"
|
"yaml": "^2.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
import { runtimeEnv } from "../runtime-env"
|
import { runtimeEnv } from "../runtime-env"
|
||||||
import { getLogger } from "../logger"
|
import { getLogger } from "../logger"
|
||||||
const log = getLogger("actions")
|
const log = getLogger("actions")
|
||||||
@@ -15,9 +16,8 @@ export async function restartCli(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (runtimeEnv.host === "tauri") {
|
if (runtimeEnv.host === "tauri") {
|
||||||
const tauri = (window as typeof window & { __TAURI__?: { invoke?: <T = unknown>(cmd: string, args?: Record<string, unknown>) => Promise<T> } }).__TAURI__
|
if (typeof window.__TAURI__?.core?.invoke === "function") {
|
||||||
if (tauri?.invoke) {
|
await invoke("cli_restart")
|
||||||
await tauri.invoke("cli_restart")
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { listen } from "@tauri-apps/api/event"
|
||||||
import { getLogger } from "../logger"
|
import { getLogger } from "../logger"
|
||||||
import { runtimeEnv } from "../runtime-env"
|
import { runtimeEnv } from "../runtime-env"
|
||||||
|
|
||||||
@@ -107,13 +108,8 @@ export async function listenForNativeFolderDrops(onDrop: (paths: string[]) => vo
|
|||||||
return () => {}
|
return () => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventApi = window.__TAURI__?.event
|
|
||||||
if (!eventApi?.listen) {
|
|
||||||
return () => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const unlisten = await eventApi.listen("desktop:folder-drop", (event) => {
|
const unlisten = await listen("desktop:folder-drop", (event) => {
|
||||||
const payload = (event.payload ?? {}) as TauriFolderDropPayload
|
const payload = (event.payload ?? {}) as TauriFolderDropPayload
|
||||||
const paths = normalizePathList(payload.paths)
|
const paths = normalizePathList(payload.paths)
|
||||||
if (paths.length > 0) {
|
if (paths.length > 0) {
|
||||||
@@ -134,15 +130,10 @@ export async function listenForNativeFolderDropState(onState: (state: NativeFold
|
|||||||
return () => {}
|
return () => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventApi = window.__TAURI__?.event
|
|
||||||
if (!eventApi?.listen) {
|
|
||||||
return () => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [unlistenEnter, unlistenLeave] = await Promise.all([
|
const [unlistenEnter, unlistenLeave] = await Promise.all([
|
||||||
eventApi.listen("desktop:folder-drag-enter", () => onState("enter")),
|
listen("desktop:folder-drag-enter", () => onState("enter")),
|
||||||
eventApi.listen("desktop:folder-drag-leave", () => onState("leave")),
|
listen("desktop:folder-drag-leave", () => onState("leave")),
|
||||||
])
|
])
|
||||||
return () => {
|
return () => {
|
||||||
unlistenEnter()
|
unlistenEnter()
|
||||||
|
|||||||
@@ -1,43 +1,21 @@
|
|||||||
|
import { open } from "@tauri-apps/plugin-dialog"
|
||||||
import type { NativeDialogOptions } from "../native-functions"
|
import type { NativeDialogOptions } from "../native-functions"
|
||||||
import { getLogger } from "../../logger"
|
import { getLogger } from "../../logger"
|
||||||
const log = getLogger("actions")
|
const log = getLogger("actions")
|
||||||
|
|
||||||
|
|
||||||
interface TauriDialogModule {
|
|
||||||
open?: (
|
|
||||||
options: {
|
|
||||||
title?: string
|
|
||||||
defaultPath?: string
|
|
||||||
filters?: { name?: string; extensions: string[] }[]
|
|
||||||
directory?: boolean
|
|
||||||
multiple?: boolean
|
|
||||||
},
|
|
||||||
) => Promise<string | string[] | null>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TauriBridge {
|
|
||||||
dialog?: TauriDialogModule
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function openTauriNativeDialog(options: NativeDialogOptions): Promise<string | null> {
|
export async function openTauriNativeDialog(options: NativeDialogOptions): Promise<string | null> {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const tauriBridge = (window as Window & { __TAURI__?: TauriBridge }).__TAURI__
|
|
||||||
const dialogApi = tauriBridge?.dialog
|
|
||||||
if (!dialogApi?.open) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await dialogApi.open({
|
const response = await open({
|
||||||
title: options.title,
|
title: options.title,
|
||||||
defaultPath: options.defaultPath,
|
defaultPath: options.defaultPath,
|
||||||
directory: options.mode === "directory",
|
directory: options.mode === "directory",
|
||||||
multiple: false,
|
multiple: false,
|
||||||
filters: options.filters?.map((filter) => ({
|
filters: options.filters?.map((filter) => ({
|
||||||
name: filter.name,
|
name: filter.name ?? "Files",
|
||||||
extensions: filter.extensions,
|
extensions: filter.extensions,
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
import { runtimeEnv } from "../runtime-env"
|
import { runtimeEnv } from "../runtime-env"
|
||||||
import { getLogger } from "../logger"
|
import { getLogger } from "../logger"
|
||||||
|
|
||||||
@@ -60,8 +61,7 @@ function hasAnyWakeLockSupport(): boolean {
|
|||||||
if (api?.setWakeLock) return true
|
if (api?.setWakeLock) return true
|
||||||
}
|
}
|
||||||
if (runtimeEnv.host === "tauri") {
|
if (runtimeEnv.host === "tauri") {
|
||||||
// We'll attempt dynamic import; treat as potentially supported.
|
return typeof window.__TAURI__?.core?.invoke === "function"
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return Boolean((navigator as any)?.wakeLock?.request)
|
return Boolean((navigator as any)?.wakeLock?.request)
|
||||||
}
|
}
|
||||||
@@ -84,21 +84,18 @@ async function setElectronWakeLock(enabled: boolean): Promise<boolean> {
|
|||||||
|
|
||||||
async function setTauriWakeLock(enabled: boolean): Promise<boolean> {
|
async function setTauriWakeLock(enabled: boolean): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const mod = await import("tauri-plugin-keepawake-api")
|
if (!hasAnyWakeLockSupport()) {
|
||||||
const start = (mod as any).start as ((config?: any) => Promise<void>) | undefined
|
|
||||||
const stop = (mod as any).stop as (() => Promise<void>) | undefined
|
|
||||||
if (!start || !stop) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
// Plugin config supports toggling display/idle/sleep. Use a conservative
|
// Match Electron's prevent-display-sleep behavior by keeping the display
|
||||||
// default to keep both system + display awake.
|
// awake without blocking explicit system sleep requests.
|
||||||
await start({ display: true, idle: true, sleep: true })
|
await invoke("wake_lock_start", { config: { display: true, idle: false, sleep: false } })
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
await stop()
|
await invoke("wake_lock_stop")
|
||||||
return false
|
return false
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.log("[wake-lock] tauri wake lock failed", error)
|
log.log("[wake-lock] tauri wake lock failed", error)
|
||||||
@@ -137,13 +134,12 @@ export function setWakeLockDesired(nextDesired: boolean): Promise<boolean> {
|
|||||||
inFlight = (async () => {
|
inFlight = (async () => {
|
||||||
try {
|
try {
|
||||||
const ok = await applyWakeLock(target)
|
const ok = await applyWakeLock(target)
|
||||||
// Treat disable attempts as applied even if the underlying API doesn't exist.
|
applied = target ? ok : false
|
||||||
applied = target
|
|
||||||
return ok
|
return ok
|
||||||
} finally {
|
} finally {
|
||||||
inFlight = null
|
inFlight = null
|
||||||
// If desired changed while in-flight, re-apply once.
|
// If desired changed while in-flight, re-apply once.
|
||||||
if (desired !== applied) {
|
if (desired !== target) {
|
||||||
void setWakeLockDesired(desired)
|
void setWakeLockDesired(desired)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,17 +9,14 @@ export interface RuntimeEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
interface TauriCoreModule {
|
||||||
|
invoke: <T = unknown>(cmd: string, args?: Record<string, unknown>) => Promise<T>
|
||||||
|
}
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
electronAPI?: unknown
|
electronAPI?: unknown
|
||||||
__TAURI__?: {
|
__TAURI__?: {
|
||||||
invoke?: <T = unknown>(cmd: string, args?: Record<string, unknown>) => Promise<T>
|
core?: TauriCoreModule
|
||||||
event?: {
|
|
||||||
listen: (event: string, handler: (payload: { payload: unknown }) => void) => Promise<() => void>
|
|
||||||
}
|
|
||||||
dialog?: {
|
|
||||||
open?: (options: Record<string, unknown>) => Promise<string | string[] | null>
|
|
||||||
save?: (options: Record<string, unknown>) => Promise<string | null>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
|
import { listen } from "@tauri-apps/api/event"
|
||||||
import { Show, createSignal, onCleanup, onMount } from "solid-js"
|
import { Show, createSignal, onCleanup, onMount } from "solid-js"
|
||||||
import { render } from "solid-js/web"
|
import { render } from "solid-js/web"
|
||||||
import iconUrl from "../../images/CodeNomad-Icon.png"
|
import iconUrl from "../../images/CodeNomad-Icon.png"
|
||||||
@@ -27,13 +29,6 @@ interface CliStatus {
|
|||||||
error?: string | null
|
error?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TauriBridge {
|
|
||||||
invoke?: <T = unknown>(cmd: string, args?: Record<string, unknown>) => Promise<T>
|
|
||||||
event?: {
|
|
||||||
listen: (event: string, handler: (payload: { payload: unknown }) => void) => Promise<() => void>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function pickPhraseKey(previous?: PhraseKey) {
|
function pickPhraseKey(previous?: PhraseKey) {
|
||||||
const filtered = phraseKeys.filter((key) => key !== previous)
|
const filtered = phraseKeys.filter((key) => key !== previous)
|
||||||
const source = filtered.length > 0 ? filtered : phraseKeys
|
const source = filtered.length > 0 ? filtered : phraseKeys
|
||||||
@@ -46,17 +41,6 @@ function navigateTo(url?: string | null) {
|
|||||||
window.location.replace(url)
|
window.location.replace(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTauriBridge(): TauriBridge | null {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const bridge = (window as { __TAURI__?: TauriBridge }).__TAURI__
|
|
||||||
if (!bridge || !bridge.event || !bridge.invoke) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return bridge
|
|
||||||
}
|
|
||||||
|
|
||||||
function annotateDocument() {
|
function annotateDocument() {
|
||||||
if (typeof document === "undefined") {
|
if (typeof document === "undefined") {
|
||||||
return
|
return
|
||||||
@@ -77,25 +61,22 @@ function LoadingApp() {
|
|||||||
setPhraseKey(pickPhraseKey())
|
setPhraseKey(pickPhraseKey())
|
||||||
const unsubscribers: Array<() => void> = []
|
const unsubscribers: Array<() => void> = []
|
||||||
|
|
||||||
async function bootstrapTauri(tauriBridge: TauriBridge | null) {
|
async function bootstrapTauri() {
|
||||||
if (!tauriBridge || !tauriBridge.event || !tauriBridge.invoke) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const readyUnlisten = await tauriBridge.event.listen("cli:ready", (event) => {
|
const readyUnlisten = await listen("cli:ready", (event) => {
|
||||||
const payload = (event?.payload as CliStatus) || {}
|
const payload = (event?.payload as CliStatus) || {}
|
||||||
setError(null)
|
setError(null)
|
||||||
setStatusKey(null)
|
setStatusKey(null)
|
||||||
navigateTo(payload.url)
|
navigateTo(payload.url)
|
||||||
})
|
})
|
||||||
const errorUnlisten = await tauriBridge.event.listen("cli:error", (event) => {
|
const errorUnlisten = await listen("cli:error", (event) => {
|
||||||
const payload = (event?.payload as CliStatus) || {}
|
const payload = (event?.payload as CliStatus) || {}
|
||||||
if (payload.error) {
|
if (payload.error) {
|
||||||
setError(payload.error)
|
setError(payload.error)
|
||||||
setStatusKey("loadingScreen.status.issue")
|
setStatusKey("loadingScreen.status.issue")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const statusUnlisten = await tauriBridge.event.listen("cli:status", (event) => {
|
const statusUnlisten = await listen("cli:status", (event) => {
|
||||||
const payload = (event?.payload as CliStatus) || {}
|
const payload = (event?.payload as CliStatus) || {}
|
||||||
if (payload.state === "error" && payload.error) {
|
if (payload.state === "error" && payload.error) {
|
||||||
setError(payload.error)
|
setError(payload.error)
|
||||||
@@ -109,7 +90,7 @@ function LoadingApp() {
|
|||||||
})
|
})
|
||||||
unsubscribers.push(readyUnlisten, errorUnlisten, statusUnlisten)
|
unsubscribers.push(readyUnlisten, errorUnlisten, statusUnlisten)
|
||||||
|
|
||||||
const result = await tauriBridge.invoke<CliStatus>("cli_get_status")
|
const result = await invoke<CliStatus>("cli_get_status")
|
||||||
if (result?.state === "ready" && result.url) {
|
if (result?.state === "ready" && result.url) {
|
||||||
navigateTo(result.url)
|
navigateTo(result.url)
|
||||||
} else if (result?.state === "error" && result.error) {
|
} else if (result?.state === "error" && result.error) {
|
||||||
@@ -123,7 +104,7 @@ function LoadingApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isTauriHost()) {
|
if (isTauriHost()) {
|
||||||
void bootstrapTauri(getTauriBridge())
|
void bootstrapTauri()
|
||||||
}
|
}
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
|
|||||||
11
packages/ui/src/types/global.d.ts
vendored
11
packages/ui/src/types/global.d.ts
vendored
@@ -47,16 +47,9 @@ declare global {
|
|||||||
webkitGetAsEntry?: () => FileSystemEntry | null
|
webkitGetAsEntry?: () => FileSystemEntry | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TauriDialogModule {
|
|
||||||
open?: (options: Record<string, unknown>) => Promise<string | string[] | null>
|
|
||||||
save?: (options: Record<string, unknown>) => Promise<string | null>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TauriBridge {
|
interface TauriBridge {
|
||||||
invoke?: <T = unknown>(cmd: string, args?: Record<string, unknown>) => Promise<T>
|
core?: {
|
||||||
dialog?: TauriDialogModule
|
invoke: <T = unknown>(cmd: string, args?: Record<string, unknown>) => Promise<T>
|
||||||
event?: {
|
|
||||||
listen: (event: string, handler: (payload: { payload: unknown }) => void) => Promise<() => void>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
declare module "tauri-plugin-keepawake-api" {
|
|
||||||
export interface KeepAwakeConfig {
|
|
||||||
display?: boolean
|
|
||||||
idle?: boolean
|
|
||||||
sleep?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export function start(config?: KeepAwakeConfig): Promise<void>
|
|
||||||
export function stop(): Promise<void>
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user