mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-11 18:01:14 +00:00
Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95d3e8eafc | ||
|
|
52eeb0f228 | ||
|
|
8145b0792f | ||
|
|
2ccd73759f | ||
|
|
cab7a7d75f | ||
|
|
bd125d32da | ||
|
|
7369674d8f | ||
|
|
70da7e89bb | ||
|
|
a671bf605d | ||
|
|
bae8f64b8b | ||
|
|
2df07b7ed8 | ||
|
|
295094a594 | ||
|
|
210bc927bc | ||
|
|
00cfb5b259 | ||
|
|
2bd105b0ce | ||
|
|
b81d761605 | ||
|
|
6922edfc20 | ||
|
|
7ec147603f | ||
|
|
8ee9e5915a | ||
|
|
cd5b786704 | ||
|
|
d715bb760c | ||
|
|
63918b44a8 | ||
|
|
64d09c9bca |
@@ -1,73 +1,198 @@
|
||||
name: Check Cache
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
required: true
|
||||
type: string
|
||||
arch:
|
||||
required: false
|
||||
type: string
|
||||
compiler:
|
||||
required: false
|
||||
type: string
|
||||
default: msvc
|
||||
build-deps-only:
|
||||
required: false
|
||||
type: boolean
|
||||
force-build:
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
check_cache: # determines if there is a cache and outputs variables used in caching process
|
||||
name: Check Cache
|
||||
runs-on: ${{ inputs.os }}
|
||||
outputs:
|
||||
cache-key: ${{ steps.set_outputs.outputs.cache-key }}
|
||||
cache-path: ${{ steps.set_outputs.outputs.cache-path }}
|
||||
valid-cache: ${{ steps.cache_deps.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
lfs: 'false'
|
||||
|
||||
- name: set outputs
|
||||
id: set_outputs
|
||||
env:
|
||||
# Anything that changes how the tree is built belongs in the key, or a job
|
||||
# restores one it cannot use. Linux amd64 passes no arch deliberately, so
|
||||
# 'linux-clang' keeps the cache it already has.
|
||||
cache-os: ${{ runner.os == 'macOS' && format('{0}-{1}', inputs.os, inputs.arch) || (runner.os == 'Windows' && format('windows-{0}-{1}', inputs.arch, inputs.compiler) || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
|
||||
# The Windows ARM64 deps build in build-arm64, all others under build;
|
||||
# build_deps.yml and build_orca.yml pass the Windows directory to build_win.bat.
|
||||
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || (runner.os == 'Windows' && inputs.arch == 'arm64') && '-arm64/OrcaSlicer_dep' || '/OrcaSlicer_dep' }}
|
||||
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
||||
run: |
|
||||
echo cache-key=${{ env.cache-os }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }}
|
||||
echo cache-path=${{ github.workspace }}/deps/build${{ env.dep-folder-name }} >> ${{ env.output-cmd }}
|
||||
|
||||
- name: load cache
|
||||
id: cache_deps
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.set_outputs.outputs.cache-path }}
|
||||
key: ${{ steps.set_outputs.outputs.cache-key }}
|
||||
lookup-only: true
|
||||
|
||||
build_deps: # call next step
|
||||
name: Build Deps
|
||||
needs: [check_cache]
|
||||
uses: ./.github/workflows/build_deps.yml
|
||||
with:
|
||||
cache-key: ${{ needs.check_cache.outputs.cache-key }}
|
||||
cache-path: ${{ needs.check_cache.outputs.cache-path }}
|
||||
valid-cache: ${{ needs.check_cache.outputs.valid-cache == 'true' }}
|
||||
os: ${{ inputs.os }}
|
||||
arch: ${{ inputs.arch }}
|
||||
compiler: ${{ inputs.compiler }}
|
||||
build-deps-only: ${{ inputs.build-deps-only }}
|
||||
force-build: ${{ inputs.force-build }}
|
||||
secrets: inherit
|
||||
name: Check Cache
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
required: true
|
||||
type: string
|
||||
arch:
|
||||
required: false
|
||||
type: string
|
||||
compiler:
|
||||
required: false
|
||||
type: string
|
||||
default: msvc
|
||||
build-deps-only:
|
||||
required: false
|
||||
type: boolean
|
||||
force-build:
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
# Read by scripts/ci_deps_cache.sh.
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
WORKFLOW_REF: ${{ github.workflow_ref }}
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
|
||||
jobs:
|
||||
check_cache: # determines if there is a cache and outputs variables used in caching process
|
||||
name: Check Cache
|
||||
# Hosted macOS runners are scarce, so their lookup runs on Linux.
|
||||
runs-on: ${{ startsWith(inputs.os, 'macos-') && 'ubuntu-24.04' || inputs.os }}
|
||||
outputs:
|
||||
cache-key: ${{ steps.set_outputs.outputs.cache-key }}
|
||||
cache-path: ${{ steps.set_outputs.outputs.cache-path }}
|
||||
valid-cache: ${{ steps.cache_deps.outputs.cache-hit }}
|
||||
macos-path: ${{ steps.macos_path.outputs.path }}
|
||||
key-inputs: ${{ steps.wait_check.outputs.key-inputs }}
|
||||
wait-for: ${{ steps.wait_check.outputs.wait-for }}
|
||||
wait-since: ${{ steps.wait_check.outputs.wait-since }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
lfs: 'false'
|
||||
# The wait check compares the merge commit with its first parent.
|
||||
fetch-depth: ${{ startsWith(inputs.os, 'macos-') && 2 || 1 }}
|
||||
# Fetches only deps/, which the key hashes, and the wait script.
|
||||
sparse-checkout: |
|
||||
/deps/
|
||||
/scripts/ci_deps_cache.sh
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: set outputs
|
||||
id: set_outputs
|
||||
env:
|
||||
# Anything that changes how the tree is built belongs in the key, or a job
|
||||
# restores one it cannot use. Linux amd64 passes no arch deliberately, so
|
||||
# 'linux-clang' keeps the cache it already has.
|
||||
cache-os: ${{ (runner.os == 'macOS' || startsWith(inputs.os, 'macos-')) && format('{0}-{1}', inputs.os, inputs.arch) || (runner.os == 'Windows' && format('windows-{0}-{1}', inputs.arch, inputs.compiler) || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
|
||||
# The Windows ARM64 deps build in build-arm64, all others under build;
|
||||
# build_deps.yml and build_orca.yml pass the Windows directory to build_win.bat.
|
||||
dep-folder-name: ${{ (runner.os == 'macOS' || startsWith(inputs.os, 'macos-')) && format('/{0}', inputs.arch) || (runner.os == 'Windows' && inputs.arch == 'arm64') && '-arm64/OrcaSlicer_dep' || '/OrcaSlicer_dep' }}
|
||||
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
||||
run: |
|
||||
echo cache-key=${{ env.cache-os }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }}
|
||||
# Relative to the workspace, which differs from the build's when this job runs on Linux.
|
||||
echo cache-path=deps/build${{ env.dep-folder-name }} >> ${{ env.output-cmd }}
|
||||
|
||||
# actions/cache matches the path too, so a Linux lookup for macOS uses the
|
||||
# workspace of a hosted macOS job.
|
||||
- name: macOS cache path
|
||||
id: macos_path
|
||||
if: ${{ startsWith(inputs.os, 'macos-') }}
|
||||
run: echo "path=/Users/runner/work/${GITHUB_REPOSITORY#*/}/${GITHUB_REPOSITORY#*/}/${{ steps.set_outputs.outputs.cache-path }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: load cache
|
||||
id: cache_deps
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.macos_path.outputs.path || format('{0}/{1}', github.workspace, steps.set_outputs.outputs.cache-path) }}
|
||||
key: ${{ steps.set_outputs.outputs.cache-key }}
|
||||
lookup-only: true
|
||||
|
||||
# A pull request that misses the cache can wait for a base branch build of the same deps.
|
||||
- name: check for a base branch build to wait for
|
||||
id: wait_check
|
||||
if: ${{ startsWith(inputs.os, 'macos-') && github.event_name == 'pull_request' && steps.cache_deps.outputs.cache-hit != 'true' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: bash scripts/ci_deps_cache.sh check
|
||||
|
||||
wait_for_deps:
|
||||
name: Wait for base branch deps
|
||||
needs: [check_cache]
|
||||
if: ${{ needs.check_cache.outputs.wait-for != '' }}
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
WAIT_MINUTES: 120
|
||||
# Must exceed WAIT_MINUTES.
|
||||
timeout-minutes: 130
|
||||
# Waiting runs queue here, and only the first one holds a runner.
|
||||
concurrency:
|
||||
group: deps-wait-${{ github.base_ref }}-${{ needs.check_cache.outputs.cache-key }}
|
||||
queue: max
|
||||
outputs:
|
||||
hit: ${{ steps.lookup.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
sparse-checkout: scripts/ci_deps_cache.sh
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: wait for the deps cache
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
KEY: ${{ needs.check_cache.outputs.cache-key }}
|
||||
KEY_INPUTS: ${{ needs.check_cache.outputs.key-inputs }}
|
||||
WAIT_FOR: ${{ needs.check_cache.outputs.wait-for }}
|
||||
WAIT_SINCE: ${{ needs.check_cache.outputs.wait-since }}
|
||||
ARCH: ${{ inputs.arch }}
|
||||
run: bash scripts/ci_deps_cache.sh wait
|
||||
|
||||
- name: load cache
|
||||
id: lookup
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ needs.check_cache.outputs.macos-path }}
|
||||
key: ${{ needs.check_cache.outputs.cache-key }}
|
||||
lookup-only: true
|
||||
|
||||
# Hosted macOS runners are scarce, so a pull request's macOS build waits until
|
||||
# one is free that push and nightly builds do not need. See the script.
|
||||
macos_admission:
|
||||
name: Wait for a macOS runner
|
||||
needs: [check_cache, wait_for_deps]
|
||||
if: ${{ !cancelled() && needs.check_cache.result == 'success' && startsWith(inputs.os, 'macos-') && github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'macos-priority') }}
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
WAIT_MINUTES: 240
|
||||
PRIORITY: false
|
||||
# Must exceed WAIT_MINUTES.
|
||||
timeout-minutes: 250
|
||||
# One line for every arch of every pull request, and only its first job polls.
|
||||
concurrency:
|
||||
group: macos-admission
|
||||
queue: max
|
||||
steps: &macos_admission_steps
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
sparse-checkout: scripts/ci_macos_admission.py
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: wait for a macOS runner
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
MACOS_RUNNER_LIMIT: ${{ vars.MACOS_RUNNER_LIMIT }}
|
||||
# -u, or the log shows nothing until the wait ends.
|
||||
run: python3 -u scripts/ci_macos_admission.py
|
||||
|
||||
# The macos-priority label, when the run starts, puts a pull request in its own
|
||||
# line, which the normal one yields to. The names are fixed, because a skipped
|
||||
# job is listed under its unevaluated name, and the script matches both.
|
||||
macos_admission_priority:
|
||||
name: Wait for a macOS runner (priority)
|
||||
needs: [check_cache, wait_for_deps]
|
||||
if: ${{ !cancelled() && needs.check_cache.result == 'success' && startsWith(inputs.os, 'macos-') && github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'macos-priority') }}
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
WAIT_MINUTES: 240
|
||||
PRIORITY: true
|
||||
# Must exceed WAIT_MINUTES.
|
||||
timeout-minutes: 250
|
||||
concurrency:
|
||||
group: macos-admission-priority
|
||||
queue: max
|
||||
steps: *macos_admission_steps
|
||||
|
||||
build_deps: # call next step
|
||||
name: Build Deps
|
||||
needs: [check_cache, wait_for_deps, macos_admission, macos_admission_priority]
|
||||
# A failed wait counts as no cache, so the deps are built here. A failed
|
||||
# admission still builds rather than skip macOS.
|
||||
if: ${{ !cancelled() && needs.check_cache.result == 'success' }}
|
||||
uses: ./.github/workflows/build_deps.yml
|
||||
with:
|
||||
cache-key: ${{ needs.check_cache.outputs.cache-key }}
|
||||
cache-path: ${{ needs.check_cache.outputs.cache-path }}
|
||||
valid-cache: ${{ needs.check_cache.outputs.valid-cache == 'true' || needs.wait_for_deps.outputs.hit == 'true' }}
|
||||
os: ${{ inputs.os }}
|
||||
arch: ${{ inputs.arch }}
|
||||
compiler: ${{ inputs.compiler }}
|
||||
build-deps-only: ${{ inputs.build-deps-only }}
|
||||
force-build: ${{ inputs.force-build }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: load cached deps
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ inputs.cache-path }}
|
||||
path: ${{ github.workspace }}/${{ inputs.cache-path }}
|
||||
key: ${{ inputs.cache-key }}
|
||||
|
||||
- uses: lukka/get-cmake@latest
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
choco install strawberryperl
|
||||
}
|
||||
# cache-path is the install directory inside the deps build directory.
|
||||
$deps = (Split-Path "${{ inputs.cache-path }}").Replace('\', '/')
|
||||
$deps = (Split-Path "${{ github.workspace }}/${{ inputs.cache-path }}").Replace('\', '/')
|
||||
# -l compiles with Visual Studio's clang-cl and -x builds with Ninja; --msvc --msbuild is cl under the Visual Studio generator.
|
||||
$flags = if ("${{ inputs.compiler }}" -eq "clang") { "-l", "-x" } else { "--msvc", "--msbuild" }
|
||||
.\build_win.bat -d --arch ${{ inputs.arch }} --deps-dir $deps @flags
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
if: ${{ !(runner.os == 'macOS' && inputs.macos-combine-only) }}
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ inputs.cache-path }}
|
||||
path: ${{ github.workspace }}/${{ inputs.cache-path }}
|
||||
key: ${{ inputs.cache-key }}
|
||||
fail-on-cache-miss: true
|
||||
|
||||
@@ -179,6 +179,13 @@ jobs:
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only && !vars.SELF_HOSTED
|
||||
run: |
|
||||
df -hI /
|
||||
# Deleting the unused Xcodes takes minutes, so only do it when the
|
||||
# build might not fit. A cold build uses under 4 GiB after this step.
|
||||
avail_gib=$(df -g / | awk 'NR == 2 { print $4 }')
|
||||
if [ "$avail_gib" -ge 15 ]; then
|
||||
echo "$avail_gib GiB free, enough for the build"
|
||||
exit 0
|
||||
fi
|
||||
# Keep only the selected Xcode
|
||||
xcode=$(basename "$(cd "$(xcode-select -p)/../.." && pwd -P)")
|
||||
sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "$xcode" -exec rm -rf {} +
|
||||
@@ -460,7 +467,7 @@ jobs:
|
||||
# --tests builds the unit tests too; the unit_tests_windows_* jobs run them.
|
||||
run: |
|
||||
# cache-path is the install directory inside the deps build directory.
|
||||
$deps = (Split-Path "${{ inputs.cache-path }}").Replace('\', '/')
|
||||
$deps = (Split-Path "${{ github.workspace }}/${{ inputs.cache-path }}").Replace('\', '/')
|
||||
# -l compiles with Visual Studio's clang-cl and -x builds with Ninja; --msvc --msbuild is cl under the Visual Studio generator.
|
||||
$flags = if ("${{ inputs.compiler }}" -eq "clang") { "-l", "-x" } else { "--msvc", "--msbuild" }
|
||||
.\build_win.bat -s --tests -i --arch ${{ inputs.arch }} --build-dir $env:BUILD_DIR --deps-dir $deps @flags
|
||||
|
||||
+1148
-247
File diff suppressed because it is too large
Load Diff
+1480
-361
File diff suppressed because it is too large
Load Diff
+1476
-357
File diff suppressed because it is too large
Load Diff
+1476
-356
File diff suppressed because it is too large
Load Diff
+1148
-247
File diff suppressed because it is too large
Load Diff
+1471
-354
File diff suppressed because it is too large
Load Diff
+1473
-353
File diff suppressed because it is too large
Load Diff
+1474
-354
File diff suppressed because it is too large
Load Diff
+1475
-355
File diff suppressed because it is too large
Load Diff
+1477
-358
File diff suppressed because it is too large
Load Diff
+1480
-360
File diff suppressed because it is too large
Load Diff
+1481
-361
File diff suppressed because it is too large
Load Diff
@@ -333,6 +333,8 @@ src/slic3r/GUI/OAuthDialog.cpp
|
||||
src/slic3r/GUI/PluginPickerDialog.cpp
|
||||
src/slic3r/GUI/PluginsConfigDialog.cpp
|
||||
src/slic3r/GUI/PluginsDialog.cpp
|
||||
src/slic3r/GUI/PrinterWebView.cpp
|
||||
src/slic3r/GUI/Project.cpp
|
||||
src/slic3r/GUI/PublishSettingsDialog.hpp
|
||||
src/slic3r/GUI/PurgeModeDialog.cpp
|
||||
src/slic3r/GUI/TerminalDialog.cpp
|
||||
@@ -341,7 +343,6 @@ src/slic3r/GUI/TextureLibrary.cpp
|
||||
src/slic3r/GUI/TextureProjectorFrame.cpp
|
||||
src/slic3r/GUI/UVEditorCanvas.cpp
|
||||
src/slic3r/GUI/UserManager.cpp
|
||||
src/slic3r/GUI/WebDownPluginDlg.cpp
|
||||
src/slic3r/GUI/WebViewDialog.cpp
|
||||
src/slic3r/GUI/Widgets/AMSItem.cpp
|
||||
src/slic3r/GUI/Widgets/DialogButtons.cpp
|
||||
|
||||
+1474
-352
File diff suppressed because it is too large
Load Diff
+1488
-368
File diff suppressed because it is too large
Load Diff
+1482
-363
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1471
-352
File diff suppressed because it is too large
Load Diff
+1486
-366
File diff suppressed because it is too large
Load Diff
+1474
-354
File diff suppressed because it is too large
Load Diff
+1480
-361
File diff suppressed because it is too large
Load Diff
+1481
-361
File diff suppressed because it is too large
Load Diff
+1484
-364
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,6 +1,6 @@
|
||||
{
|
||||
"name": "Anker",
|
||||
"version": "02.04.00.05",
|
||||
"version": "02.04.00.06",
|
||||
"force_update": "0",
|
||||
"description": "Anker configurations",
|
||||
"machine_model_list": [
|
||||
@@ -304,6 +304,26 @@
|
||||
"name": "Generic PLA Silk @Anker 0.25 nozzle",
|
||||
"sub_path": "filament/Generic PLA Silk @Anker 0.25 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Basic @Anker base",
|
||||
"sub_path": "filament/AnkerMake PLA+ Basic @Anker base.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Glitter @Anker base",
|
||||
"sub_path": "filament/AnkerMake PLA+ Glitter @Anker base.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Matte @Anker base",
|
||||
"sub_path": "filament/AnkerMake PLA+ Matte @Anker base.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Metallic @Anker base",
|
||||
"sub_path": "filament/AnkerMake PLA+ Metallic @Anker base.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Silk @Anker base",
|
||||
"sub_path": "filament/AnkerMake PLA+ Silk @Anker base.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PLA+ @Anker",
|
||||
"sub_path": "filament/Generic PLA+ @Anker.json"
|
||||
@@ -327,6 +347,26 @@
|
||||
{
|
||||
"name": "Generic TPU @Anker",
|
||||
"sub_path": "filament/Generic TPU @Anker.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Basic @Anker",
|
||||
"sub_path": "filament/AnkerMake PLA+ Basic @Anker.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Glitter @Anker",
|
||||
"sub_path": "filament/AnkerMake PLA+ Glitter @Anker.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Matte @Anker",
|
||||
"sub_path": "filament/AnkerMake PLA+ Matte @Anker.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Metallic @Anker",
|
||||
"sub_path": "filament/AnkerMake PLA+ Metallic @Anker.json"
|
||||
},
|
||||
{
|
||||
"name": "AnkerMake PLA+ Silk @Anker",
|
||||
"sub_path": "filament/AnkerMake PLA+ Silk @Anker.json"
|
||||
}
|
||||
],
|
||||
"machine_list": [
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Basic @Anker base",
|
||||
"inherits": "Generic PLA+ @Anker base",
|
||||
"from": "system",
|
||||
"filament_id": "OFUuZlQL",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"AnkerMake"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"200"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"230"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"1"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"60"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"8"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"30"
|
||||
],
|
||||
"default_filament_colour": [
|
||||
"#FFFFFF"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"22"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Basic @Anker",
|
||||
"inherits": "AnkerMake PLA+ Basic @Anker base",
|
||||
"from": "system",
|
||||
"setting_id": "EfM5rmn4w6pMBajC",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"Anker M5 0.4 nozzle",
|
||||
"Anker M5 0.6 nozzle",
|
||||
"Anker M5 All-Metal 0.4 nozzle",
|
||||
"Anker M5 All-Metal 0.6 nozzle",
|
||||
"Anker M5C 0.4 nozzle",
|
||||
"Anker M5C 0.6 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Glitter @Anker base",
|
||||
"inherits": "Generic PLA+ @Anker base",
|
||||
"from": "system",
|
||||
"filament_id": "OFxCURdg",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"AnkerMake"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"200"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"230"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"1"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"60"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"6"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"30"
|
||||
],
|
||||
"default_filament_colour": [
|
||||
"#00AD66"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"22"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Glitter @Anker",
|
||||
"inherits": "AnkerMake PLA+ Glitter @Anker base",
|
||||
"from": "system",
|
||||
"setting_id": "blGHp0htgHEj7khE",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"Anker M5 0.4 nozzle",
|
||||
"Anker M5 0.6 nozzle",
|
||||
"Anker M5 All-Metal 0.4 nozzle",
|
||||
"Anker M5 All-Metal 0.6 nozzle",
|
||||
"Anker M5C 0.4 nozzle",
|
||||
"Anker M5C 0.6 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Matte @Anker base",
|
||||
"inherits": "Generic PLA+ @Anker base",
|
||||
"from": "system",
|
||||
"filament_id": "OFKDOyNK",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"AnkerMake"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"210"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"230"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"1"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"60"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"6"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"30"
|
||||
],
|
||||
"default_filament_colour": [
|
||||
"#FFFFFF"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"22"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Matte @Anker",
|
||||
"inherits": "AnkerMake PLA+ Matte @Anker base",
|
||||
"from": "system",
|
||||
"setting_id": "tPgwC1eEXyWPOn9S",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"Anker M5 0.4 nozzle",
|
||||
"Anker M5 All-Metal 0.4 nozzle",
|
||||
"Anker M5C 0.4 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Metallic @Anker base",
|
||||
"inherits": "Generic PLA+ @Anker base",
|
||||
"from": "system",
|
||||
"filament_id": "OFBfbmtb",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"AnkerMake"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"200"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"60"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"60"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"1"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"60"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"6"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"30"
|
||||
],
|
||||
"overhang_fan_speed": [
|
||||
"50"
|
||||
],
|
||||
"reduce_fan_stop_start_freq": [
|
||||
"0"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"1"
|
||||
],
|
||||
"filament_retraction_speed": [
|
||||
"50"
|
||||
],
|
||||
"filament_deretraction_speed": [
|
||||
"50"
|
||||
],
|
||||
"default_filament_colour": [
|
||||
"#ACAC8A"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"20"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Metallic @Anker",
|
||||
"inherits": "AnkerMake PLA+ Metallic @Anker base",
|
||||
"from": "system",
|
||||
"setting_id": "jlbAMNNaEacdUASZ",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"Anker M5 0.4 nozzle",
|
||||
"Anker M5 All-Metal 0.4 nozzle",
|
||||
"Anker M5C 0.4 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Silk @Anker base",
|
||||
"inherits": "Generic PLA+ @Anker base",
|
||||
"from": "system",
|
||||
"filament_id": "OFkxTGQc",
|
||||
"instantiation": "false",
|
||||
"filament_vendor": [
|
||||
"AnkerMake"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"210"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"230"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"65"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"65"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"1"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"60"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"2"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"20"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"0.5"
|
||||
],
|
||||
"filament_retraction_speed": [
|
||||
"20"
|
||||
],
|
||||
"filament_wipe": [
|
||||
"0"
|
||||
],
|
||||
"default_filament_colour": [
|
||||
"#99ADA3"
|
||||
],
|
||||
"filament_deretraction_speed": [
|
||||
"20"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"13"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "AnkerMake PLA+ Silk @Anker",
|
||||
"inherits": "AnkerMake PLA+ Silk @Anker base",
|
||||
"from": "system",
|
||||
"setting_id": "eVM9Ckwjl5vEvEKN",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"Anker M5 0.4 nozzle",
|
||||
"Anker M5 All-Metal 0.4 nozzle",
|
||||
"Anker M5 All-Metal 0.6 nozzle",
|
||||
"Anker M5C 0.4 nozzle",
|
||||
"Anker M5C 0.6 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Anycubic",
|
||||
"version": "02.04.00.08",
|
||||
"version": "02.04.00.09",
|
||||
"force_update": "0",
|
||||
"description": "Anycubic configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @Anycubic"
|
||||
],
|
||||
"machine_start_gcode": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG0 Z0.2 F1800 ; move nozzle to print position\nG92 E0 ; specify current extruder position as zero\nG1 Y10 X180 E50 F1200 ; extrude a line in front of the printer\nG92 E0 ; specify current extruder position as zero\nG0 Z20 F6000 ; move head up\nG1 E-7 F2400 ; retract\nG04 S2 ; wait 2s\nG0 X0 F6000 ; wipe from oozed filament\nG1 E-1 F2400 ; undo some of the retraction to avoid oozing\nG1 F6000 ; set travel speed to move to start printing point\nM117",
|
||||
"machine_start_gcode": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG0 Z0.2 F1800 ; move nozzle to print position\nG92 E0 ; specify current extruder position as zero\nM109 S[nozzle_temperature_initial_layer] ; wait for nozzle temp to stabilize\nG1 Y10 X180 E50 F1200 ; extrude a line in front of the printer\nG92 E0 ; specify current extruder position as zero\nG0 Z20 F6000 ; move head up\nG1 E-7 F2400 ; retract\nG04 S2 ; wait 2s\nG0 X0 F6000 ; wipe from oozed filament\nG1 E-1 F2400 ; undo some of the retraction to avoid oozing\nG1 F6000 ; set travel speed to move to start printing point\nM117",
|
||||
"machine_end_gcode": "G4 ; wait\nG92 E0\nG1{if max_layer_z < printable_height} Z{z_offset+min(max_layer_z+30, printable_height)}{endif} ; move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors",
|
||||
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]",
|
||||
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Bambulab",
|
||||
"url": "http://www.bambulab.com/Parameters/vendor/BBL.json",
|
||||
"version": "02.08.00.13",
|
||||
"version": "02.08.00.14",
|
||||
"force_update": "0",
|
||||
"description": "the initial version of BBL configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"0.4"
|
||||
],
|
||||
"printer_variant": "0.4",
|
||||
"use_3mf": "1",
|
||||
"bed_exclude_area": [
|
||||
"0x0",
|
||||
"28x0",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"0.4"
|
||||
],
|
||||
"printer_variant": "0.4",
|
||||
"use_3mf": "1",
|
||||
"best_object_pos": "0.3x0.5",
|
||||
"bed_exclude_area": [],
|
||||
"bed_temperature_formula": "by_highest_temp",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Prusa",
|
||||
"version": "02.04.00.16",
|
||||
"version": "02.04.00.17",
|
||||
"force_update": "0",
|
||||
"description": "Prusa configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"35"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"110"
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"35"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"110"
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"0.8"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_wipe": [
|
||||
"1"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"15"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One|CORE One L).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_z_hop": [
|
||||
"0.6"
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"7"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[0]==0.4}0.03{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[0]==0.4}0.03{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_z_hop": [
|
||||
"0.6",
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"ASA"
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"PA11-CF"
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"PC"
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"PC-CF"
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"PETG"
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One|CORE One L).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA"
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"1"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"PVB"
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../css/theme.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
@@ -23,14 +23,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="1"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t15">printer</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="printer">Printer</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="MachineList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" mode="all" onClick="ChooseAllMachine()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" mode="all" onClick="ChooseAllMachine()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,14 +38,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="2"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t16">filament</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="filament">Filament</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="FilatypeList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllFilament()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" onClick="ChooseAllFilament()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -53,22 +53,22 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="3"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t17">presets</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="process">Process</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="PresetList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllPreset()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" onClick="ChooseAllPreset()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="AcceptArea">
|
||||
<div id="export_cloud_btn" class="ButtonStyleConfirm ButtonTypeChoice">Export to OrcaCloud</div>
|
||||
<div id="export_local_btn" class="ButtonStyleRegular ButtonTypeChoice">Export to folder</div>
|
||||
<div id="close_btn" class="ButtonStyleRegular ButtonTypeChoice">Close</div>
|
||||
<div id="export_cloud_btn" class="ButtonStyleConfirm ButtonTypeChoice" data-i18n="export_cloud">Export to OrcaCloud</div>
|
||||
<div id="export_local_btn" class="ButtonStyleRegular ButtonTypeChoice" data-i18n="export_folder">Export to folder</div>
|
||||
<div id="close_btn" class="ButtonStyleRegular ButtonTypeChoice" data-i18n="close">Close</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -12,8 +12,7 @@ var g_search = {
|
||||
|
||||
function OnInit()
|
||||
{
|
||||
if (typeof TranslatePage === "function")
|
||||
TranslatePage();
|
||||
TranslatePage();
|
||||
|
||||
InstallInputSafeKeydown();
|
||||
BindSearchInputs();
|
||||
|
||||
@@ -16,29 +16,31 @@
|
||||
<h1 id="pagePresetName" class="page-title"></h1>
|
||||
</header>
|
||||
|
||||
<div id="configEmpty" class="detail-empty">This preset does not use any plugin capabilities</div>
|
||||
<div id="configEmpty" class="detail-empty" data-i18n="no_capabilities">This preset does not use any plugin capabilities</div>
|
||||
|
||||
<div id="configLayout" class="config-layout" hidden>
|
||||
<div id="configSidebar" class="config-sidebar thin-scroll" role="listbox"
|
||||
aria-label="Capabilities used by this preset"></div>
|
||||
aria-label="Capabilities used by this preset" data-i18n-aria-label="capabilities_label"></div>
|
||||
<div class="config-view">
|
||||
<div id="configError" class="config-error" role="status" aria-live="polite" hidden></div>
|
||||
<div id="configEditor" class="config-editor" hidden>
|
||||
<textarea id="configText" class="config-textarea thin-scroll" spellcheck="false"
|
||||
autocomplete="off" autocapitalize="off" aria-label="Capability configuration (JSON)"></textarea>
|
||||
autocomplete="off" autocapitalize="off" aria-label="Capability configuration (JSON)"
|
||||
data-i18n-aria-label="config_json_label"></textarea>
|
||||
</div>
|
||||
<!-- Custom capability UI. Sandboxed without allow-same-origin, so the plugin's HTML runs in
|
||||
an opaque origin and reaches the host only through the injected window.orca bridge. -->
|
||||
<iframe id="configCustom" class="config-custom" title="Plugin configuration"
|
||||
<iframe id="configCustom" class="config-custom" title="Plugin configuration" data-i18n-title="config_frame_title"
|
||||
sandbox="allow-scripts" referrerpolicy="no-referrer" hidden></iframe>
|
||||
<div id="configFooter" class="config-view-footer" hidden>
|
||||
<span id="configValidation" class="config-validation" role="status" aria-live="polite"></span>
|
||||
<div class="config-actions">
|
||||
<button id="configRestoreBtn" class="ButtonStyleRegular ButtonTypeChoice" type="button"
|
||||
title="Discard this preset's override and use the global configuration again">
|
||||
title="Discard this preset's override and use the global configuration again"
|
||||
data-i18n-title="restore_title" data-i18n="restore_defaults">
|
||||
Restore defaults
|
||||
</button>
|
||||
<button id="configSaveBtn" class="ButtonStyleConfirm ButtonTypeChoice" type="button">Save</button>
|
||||
<button id="configSaveBtn" class="ButtonStyleConfirm ButtonTypeChoice" type="button" data-i18n="save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,6 +50,7 @@
|
||||
<span id="statusText" class="status-text"></span>
|
||||
</footer>
|
||||
</main>
|
||||
<script src="../../include/i18n.js"></script>
|
||||
<script src="../js/plugin-config-ui.js"></script>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -304,7 +304,7 @@ function ValidateConfigText() {
|
||||
SetConfigValidation("");
|
||||
return true;
|
||||
} catch (err) {
|
||||
SetConfigValidation(String(err?.message || "Invalid JSON"));
|
||||
SetConfigValidation(String(err?.message || T("invalid_json", "Invalid JSON")));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -393,6 +393,8 @@ function OnCustomConfigMessage(event) {
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
TranslatePage();
|
||||
|
||||
const sidebar = document.getElementById("configSidebar");
|
||||
if (sidebar)
|
||||
sidebar.addEventListener("click", OnConfigSidebarClick);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../css/theme.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script src="../js/plugin-config-ui.js"></script>
|
||||
@@ -32,8 +32,10 @@
|
||||
</svg>
|
||||
</span>
|
||||
<input id="plugin_search_input" class="plugin-search-input" type="text"
|
||||
placeholder="Search plugins" autocomplete="off" spellcheck="false" aria-label="Search plugins" />
|
||||
<button id="plugin_search_clear" class="plugin-search-clear" type="button" title="Clear" aria-label="Clear search">
|
||||
placeholder="Search plugins" autocomplete="off" spellcheck="false" aria-label="Search plugins"
|
||||
data-i18n-placeholder="search_plugins" data-i18n-aria-label="search_plugins" />
|
||||
<button id="plugin_search_clear" class="plugin-search-clear" type="button" title="Clear" aria-label="Clear search"
|
||||
data-i18n-title="clear" data-i18n-aria-label="clear_search">
|
||||
<svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor"
|
||||
stroke-width="1.6" stroke-linecap="round" aria-hidden="true">
|
||||
<line x1="5" y1="5" x2="11" y2="11" />
|
||||
@@ -41,27 +43,28 @@
|
||||
</svg>
|
||||
</button>
|
||||
<button id="plugin_search_cc" class="plugin-search-toggle" type="button"
|
||||
aria-pressed="false" title="Match case">Aa</button>
|
||||
aria-pressed="false" title="Match case" data-i18n-title="match_case">Aa</button>
|
||||
<button id="plugin_search_w" class="plugin-search-toggle" type="button"
|
||||
aria-pressed="false" title="Match whole word"><span class="plugin-search-underline">ab</span></button>
|
||||
aria-pressed="false" title="Match whole word" data-i18n-title="match_whole_word"><span class="plugin-search-underline">ab</span></button>
|
||||
</div>
|
||||
<!-- <button id="open_terminal" class="ButtonStyleRegular ButtonTypeChoice left-btn">-->
|
||||
<!-- Open Terminal-->
|
||||
<!-- </button>-->
|
||||
<button id="refresh_btn" class="ButtonStyleRegular ButtonTypeChoice">
|
||||
<button id="refresh_btn" class="ButtonStyleRegular ButtonTypeChoice" data-i18n="refresh">
|
||||
Refresh
|
||||
</button>
|
||||
<div id="exploreDropdown" class="explore-dropdown">
|
||||
<button id="explore_menu_btn" class="ButtonStyleConfirm ButtonTypeChoice explore-menu-btn" type="button"
|
||||
aria-haspopup="true" aria-expanded="false" aria-controls="exploreMenu" title="More plugin install options">
|
||||
aria-haspopup="true" aria-expanded="false" aria-controls="exploreMenu" title="More plugin install options"
|
||||
data-i18n-title="install_options">
|
||||
<span class="explore-menu-icon" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="explore_btn" class="ButtonStyleConfirm ButtonTypeChoice explore-main-btn" type="button">
|
||||
Install plugin
|
||||
</button>
|
||||
<div id="exploreMenu" class="explore-menu" role="menu" hidden>
|
||||
<button type="button" class="explore-menu-item" role="menuitem" data-install-action="explore">Install plugin</button>
|
||||
<button type="button" class="explore-menu-item" role="menuitem" data-install-action="install-local">Install local plugin</button>
|
||||
<button type="button" class="explore-menu-item" role="menuitem" data-install-action="explore" data-i18n="install_plugin">Install plugin</button>
|
||||
<button type="button" class="explore-menu-item" role="menuitem" data-install-action="install-local" data-i18n="install_local_plugin">Install local plugin</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,15 +72,15 @@
|
||||
<main class="content">
|
||||
<section class="pane plugin-list-pane">
|
||||
<div class="hdr plugin-cols">
|
||||
<span>Activate</span>
|
||||
<span data-i18n="activate">Activate</span>
|
||||
<span class="sort-th" data-sort-field="name" role="button" tabindex="0"
|
||||
title="Sort by name">Name<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
title="Sort by name" data-i18n-title="sort_by_name" data-i18n="name">Name<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
<span class="sort-th" data-sort-field="version" role="button" tabindex="0"
|
||||
title="Sort by version">Plugin Version<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
title="Sort by version" data-i18n-title="sort_by_version" data-i18n="plugin_version">Plugin Version<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
<span class="sort-th" data-sort-field="source" role="button" tabindex="0"
|
||||
title="Sort by source">Source<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
title="Sort by source" data-i18n-title="sort_by_source" data-i18n="source">Source<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
<span class="sort-th" data-sort-field="status" role="button" tabindex="0"
|
||||
title="Sort by status">Status<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
title="Sort by status" data-i18n-title="sort_by_status" data-i18n="status">Status<span class="sort-tri" aria-hidden="true"></span></span>
|
||||
</div>
|
||||
<div id="pluginList" class="body thin-scroll"></div>
|
||||
</section>
|
||||
@@ -85,20 +88,21 @@
|
||||
<!-- Drag to redistribute the dialog's height between the list and the details pane; the hit
|
||||
area is the whole strip, the visible divider is the line drawn inside it. -->
|
||||
<div id="paneSplitter" class="pane-splitter" role="separator" aria-orientation="horizontal"
|
||||
aria-label="Resize the plugin list" title="Drag to resize; double-click to reset"></div>
|
||||
aria-label="Resize the plugin list" title="Drag to resize; double-click to reset"
|
||||
data-i18n-aria-label="resize_list" data-i18n-title="resize_hint"></div>
|
||||
|
||||
<section class="pane details-pane">
|
||||
<div class="detail-tabs" role="tablist" aria-label="Plugin details">
|
||||
<div class="detail-tabs" role="tablist" aria-label="Plugin details" data-i18n-aria-label="details_label">
|
||||
<button id="pluginInfoTab" class="detail-tab active" type="button" role="tab"
|
||||
aria-selected="true" aria-controls="pluginInfoPanel" data-tab="plugin-info">Plugin Info</button>
|
||||
aria-selected="true" aria-controls="pluginInfoPanel" data-tab="plugin-info" data-i18n="tab_info">Plugin Info</button>
|
||||
<button id="descriptionTab" class="detail-tab" type="button" role="tab" tabindex="-1"
|
||||
aria-selected="false" aria-controls="descriptionPanel" data-tab="description">Description</button>
|
||||
aria-selected="false" aria-controls="descriptionPanel" data-tab="description" data-i18n="description">Description</button>
|
||||
<button id="configTab" class="detail-tab" type="button" role="tab" tabindex="-1"
|
||||
aria-selected="false" aria-controls="configPanel" data-tab="config">Config</button>
|
||||
aria-selected="false" aria-controls="configPanel" data-tab="config" data-i18n="tab_config">Config</button>
|
||||
<button id="changelogTab" class="detail-tab" type="button" role="tab" tabindex="-1"
|
||||
aria-selected="false" aria-controls="changelogPanel" data-tab="changelog">Changelog</button>
|
||||
aria-selected="false" aria-controls="changelogPanel" data-tab="changelog" data-i18n="tab_changelog">Changelog</button>
|
||||
<button id="diagnosticsTab" class="detail-tab" type="button" role="tab" tabindex="-1"
|
||||
aria-selected="false" aria-controls="diagnosticsPanel" data-tab="diagnostics">Diagnostics</button>
|
||||
aria-selected="false" aria-controls="diagnosticsPanel" data-tab="diagnostics" data-i18n="tab_diagnostics">Diagnostics</button>
|
||||
</div>
|
||||
|
||||
<div class="detail-tab-panels">
|
||||
@@ -110,31 +114,32 @@
|
||||
</div>
|
||||
<div class="detail-fields">
|
||||
<div class="detail-field-row">
|
||||
<div class="detail-label">Source</div>
|
||||
<div class="detail-label" data-i18n="source">Source</div>
|
||||
<div id="detailSource" class="detail-value">-</div>
|
||||
</div>
|
||||
<div class="detail-field-row">
|
||||
<div class="detail-label">Types</div>
|
||||
<div class="detail-label" data-i18n="types">Types</div>
|
||||
<div id="detailTypes" class="detail-value">-</div>
|
||||
</div>
|
||||
<div class="detail-field-row">
|
||||
<div class="detail-label">Author</div>
|
||||
<div class="detail-label" data-i18n="author">Author</div>
|
||||
<div id="detailAuthor" class="detail-value">-</div>
|
||||
</div>
|
||||
<div class="detail-field-row">
|
||||
<div class="detail-label">Installed Version</div>
|
||||
<div class="detail-label" data-i18n="installed_version">Installed Version</div>
|
||||
<div class="detail-value">
|
||||
<span id="detailInstalledVersion">-</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-field-row">
|
||||
<div class="detail-label">Latest Version</div>
|
||||
<div class="detail-label" data-i18n="latest_version">Latest Version</div>
|
||||
<div class="detail-value detail-version-row">
|
||||
<span id="detailLatestVersion">-</span>
|
||||
<span id="detailUpdateBadge" class="version-update-badge" hidden
|
||||
aria-label="Update available" title="Update available"></span>
|
||||
aria-label="Update available" title="Update available"
|
||||
data-i18n-aria-label="update_available" data-i18n-title="update_available"></span>
|
||||
<button id="detailUpdateBtn" type="button" class="plugin-update-btn" hidden
|
||||
title="Update to the latest version">Update</button>
|
||||
title="Update to the latest version" data-i18n-title="update_title" data-i18n="update">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -143,26 +148,27 @@
|
||||
|
||||
<section id="descriptionPanel" class="detail-tab-panel thin-scroll" role="tabpanel" tabindex="0"
|
||||
aria-labelledby="descriptionTab" data-panel="description" hidden>
|
||||
<h2 class="detail-section-title">Description</h2>
|
||||
<div id="detailDescription" class="detail-description">No description available</div>
|
||||
<h2 class="detail-section-title" data-i18n="description">Description</h2>
|
||||
<div id="detailDescription" class="detail-description" data-i18n="no_description">No description available</div>
|
||||
</section>
|
||||
|
||||
<section id="configPanel" class="detail-tab-panel" role="tabpanel" tabindex="0"
|
||||
aria-labelledby="configTab" data-panel="config" hidden>
|
||||
<div id="configEmpty" class="detail-empty">Select a plugin to configure its capabilities</div>
|
||||
<div id="configEmpty" class="detail-empty" data-i18n="config_select_plugin">Select a plugin to configure its capabilities</div>
|
||||
<div id="configLayout" class="config-layout" hidden>
|
||||
<div id="configSidebar" class="config-sidebar thin-scroll" role="listbox"
|
||||
aria-label="Configurable capabilities"></div>
|
||||
aria-label="Configurable capabilities" data-i18n-aria-label="configurable_capabilities"></div>
|
||||
<div class="config-view">
|
||||
<div id="configError" class="config-error" role="status" aria-live="polite" hidden></div>
|
||||
<div id="configEditor" class="config-editor" hidden>
|
||||
<textarea id="configText" class="config-textarea thin-scroll" spellcheck="false"
|
||||
autocomplete="off" autocapitalize="off" aria-label="Capability configuration (JSON)"></textarea>
|
||||
autocomplete="off" autocapitalize="off" aria-label="Capability configuration (JSON)"
|
||||
data-i18n-aria-label="config_json_label"></textarea>
|
||||
</div>
|
||||
<!-- Custom capability UI. Sandboxed without allow-same-origin, so the plugin's HTML
|
||||
runs in an opaque origin and reaches the host only through the injected
|
||||
window.orca bridge. -->
|
||||
<iframe id="configCustom" class="config-custom" title="Plugin configuration"
|
||||
<iframe id="configCustom" class="config-custom" title="Plugin configuration" data-i18n-title="config_frame_title"
|
||||
sandbox="allow-scripts" referrerpolicy="no-referrer" hidden></iframe>
|
||||
<!-- JSON-editor chrome only: a custom UI renders its own save/restore controls and
|
||||
drives them through the window.orca bridge. -->
|
||||
@@ -170,10 +176,11 @@
|
||||
<span id="configValidation" class="config-validation" role="status" aria-live="polite"></span>
|
||||
<div class="config-actions">
|
||||
<button id="configRestoreBtn" class="ButtonStyleRegular ButtonTypeChoice" type="button"
|
||||
title="Discard the settings saved for this capability and restore the plugin's defaults">
|
||||
title="Discard the settings saved for this capability and restore the plugin's defaults"
|
||||
data-i18n-title="restore_title" data-i18n="restore_defaults">
|
||||
Restore defaults
|
||||
</button>
|
||||
<button id="configSaveBtn" class="ButtonStyleConfirm ButtonTypeChoice" type="button">Save</button>
|
||||
<button id="configSaveBtn" class="ButtonStyleConfirm ButtonTypeChoice" type="button" data-i18n="save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -182,22 +189,22 @@
|
||||
|
||||
<section id="changelogPanel" class="detail-tab-panel thin-scroll" role="tabpanel" tabindex="0"
|
||||
aria-labelledby="changelogTab" data-panel="changelog" hidden>
|
||||
<table id="changelogTable" class="detail-table changelog-table" aria-label="Plugin changelog">
|
||||
<table id="changelogTable" class="detail-table changelog-table" aria-label="Plugin changelog" data-i18n-aria-label="changelog_label">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Version</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Changes</th>
|
||||
<th scope="col" data-i18n="version">Version</th>
|
||||
<th scope="col" data-i18n="date">Date</th>
|
||||
<th scope="col" data-i18n="changes">Changes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="changelogBody"></tbody>
|
||||
</table>
|
||||
<div id="changelogEmpty" class="detail-empty" hidden>No changelog available</div>
|
||||
<div id="changelogEmpty" class="detail-empty" data-i18n="no_changelog" hidden>No changelog available</div>
|
||||
</section>
|
||||
|
||||
<section id="diagnosticsPanel" class="detail-tab-panel thin-scroll" role="tabpanel" tabindex="0"
|
||||
aria-labelledby="diagnosticsTab" data-panel="diagnostics" hidden>
|
||||
<div id="detailStatusBody" class="detail-status-body">No issues detected</div>
|
||||
<div id="detailStatusBody" class="detail-status-body" data-i18n="no_issues">No issues detected</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const pluginsById = new Map();
|
||||
const pluginInstallActions = {
|
||||
"explore": {
|
||||
label: "Install plugin",
|
||||
label: T("install_plugin", "Install plugin"),
|
||||
command: "open_plugin_hub"
|
||||
},
|
||||
"install-local": {
|
||||
label: "Install local plugin",
|
||||
label: T("install_local_plugin", "Install local plugin"),
|
||||
command: "install_local_plugin"
|
||||
}
|
||||
};
|
||||
@@ -47,8 +47,7 @@ function OnInit() {
|
||||
exploreMenu = document.getElementById("exploreMenu");
|
||||
exploreMenuButton = document.getElementById("explore_menu_btn");
|
||||
|
||||
if (typeof TranslatePage === "function")
|
||||
TranslatePage();
|
||||
TranslatePage();
|
||||
|
||||
document.getElementById("refresh_btn")?.addEventListener("click", () => SendMessage("refresh_plugins"));
|
||||
document.getElementById("explore_btn")?.addEventListener("click", () => {
|
||||
@@ -449,7 +448,7 @@ function RenderPlugins() {
|
||||
if (pluginsById.size === 0) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "empty-state";
|
||||
empty.textContent = "No plugins found";
|
||||
empty.textContent = T("no_plugins", "No plugins found");
|
||||
pluginList.appendChild(empty);
|
||||
SyncPluginListHeaderGutter();
|
||||
return;
|
||||
@@ -504,11 +503,9 @@ function RenderPlugins() {
|
||||
if (searching && shown === 0) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "empty-state";
|
||||
empty.appendChild(document.createTextNode('No plugins match "'));
|
||||
const term = document.createElement("b");
|
||||
term.textContent = pluginSearch.query;
|
||||
empty.appendChild(term);
|
||||
empty.appendChild(document.createTextNode('"'));
|
||||
AppendTemplate(empty, T("no_match", 'No plugins match "%s"'), term);
|
||||
pluginList.appendChild(empty);
|
||||
}
|
||||
|
||||
@@ -599,13 +596,13 @@ function IsPluginLoading(plugin) {
|
||||
function SourceLabel(source) {
|
||||
switch (String(source || "").toLowerCase()) {
|
||||
case "mine":
|
||||
return "Mine";
|
||||
return T("source_mine", "Mine");
|
||||
case "subscribed":
|
||||
return "Subscribed";
|
||||
return T("source_subscribed", "Subscribed");
|
||||
case "orphaned":
|
||||
return "Orphaned";
|
||||
return T("source_orphaned", "Orphaned");
|
||||
default:
|
||||
return "Local";
|
||||
return T("source_local", "Local");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,7 +638,7 @@ function CheckCell(row, plugin) {
|
||||
checkbox.dataset.pluginKey = row.dataset.pluginKey;
|
||||
if (checkbox.indeterminate) {
|
||||
checkbox.setAttribute("aria-checked", "mixed");
|
||||
checkbox.setAttribute("aria-label", "Some plugin capabilities are enabled");
|
||||
checkbox.setAttribute("aria-label", T("mixed_capabilities", "Some plugin capabilities are enabled"));
|
||||
}
|
||||
const checkboxMark = document.createElement("span");
|
||||
checkboxMark.className = "plugin-checkbox-mark";
|
||||
@@ -664,9 +661,14 @@ function LabelCell(plugin, isExpanded = false, capabilityCount = 0, nameRanges =
|
||||
const expandButton = document.createElement("button");
|
||||
expandButton.type = "button";
|
||||
expandButton.className = "plugin-expand-btn";
|
||||
expandButton.setAttribute("aria-label", `${isExpanded ? "Collapse" : "Expand"} ${pluginLabelText || "plugin"} capabilities`);
|
||||
const labelName = pluginLabelText || "plugin";
|
||||
expandButton.setAttribute("aria-label", isExpanded
|
||||
? T("collapse_named", "Collapse %s capabilities", labelName)
|
||||
: T("expand_named", "Expand %s capabilities", labelName));
|
||||
expandButton.setAttribute("aria-expanded", isExpanded ? "true" : "false");
|
||||
expandButton.title = isExpanded ? "Collapse capabilities" : "Expand capabilities";
|
||||
expandButton.title = isExpanded
|
||||
? T("collapse", "Collapse capabilities")
|
||||
: T("expand", "Expand capabilities");
|
||||
const icon = document.createElement("span");
|
||||
icon.className = "plugin-expand-icon";
|
||||
expandButton.appendChild(icon);
|
||||
@@ -688,7 +690,7 @@ function LabelCell(plugin, isExpanded = false, capabilityCount = 0, nameRanges =
|
||||
if (hasCloudLink) {
|
||||
labelElement.href = "#";
|
||||
labelElement.classList.add("plugin-cloud-link");
|
||||
labelElement.title = "Open this plugin in your browser";
|
||||
labelElement.title = T("open_in_browser", "Open this plugin in your browser");
|
||||
}
|
||||
|
||||
nameWrap.appendChild(labelElement);
|
||||
@@ -696,7 +698,7 @@ function LabelCell(plugin, isExpanded = false, capabilityCount = 0, nameRanges =
|
||||
const countBadge = document.createElement("span");
|
||||
countBadge.className = "plugin-capability-count";
|
||||
countBadge.textContent = String(capabilityCount);
|
||||
countBadge.title = `${capabilityCount} capabilities`;
|
||||
countBadge.title = T("capability_count", "Capabilities: %s", capabilityCount);
|
||||
nameWrap.appendChild(countBadge);
|
||||
}
|
||||
labelCell.appendChild(nameWrap);
|
||||
@@ -722,7 +724,7 @@ function RenderCapabilityTree(plugin, capabilities, capRanges = null) {
|
||||
const tree = document.createElement("div");
|
||||
tree.className = "capabilities-tree";
|
||||
tree.setAttribute("role", "group");
|
||||
tree.setAttribute("aria-label", "Capabilities");
|
||||
tree.setAttribute("aria-label", T("capabilities", "Capabilities"));
|
||||
|
||||
capabilities.forEach((capability, index) => {
|
||||
tree.appendChild(RenderCapabilityRow(plugin, capability, index === capabilities.length - 1, capRanges));
|
||||
@@ -769,7 +771,7 @@ function RenderCapabilityRow(plugin, capability, isLast, capRanges = null) {
|
||||
const runButton = document.createElement("button");
|
||||
runButton.type = "button";
|
||||
runButton.className = "capability-run-btn script-run-btn";
|
||||
runButton.title = `Run "${capabilityName}"`;
|
||||
runButton.title = T("run_named", 'Run "%s"', capabilityName);
|
||||
runButton.setAttribute("aria-label", runButton.title);
|
||||
const runIcon = document.createElement("span");
|
||||
runIcon.className = "icon16 plugin-run-icon";
|
||||
@@ -804,7 +806,7 @@ function CapabilityCheckCell(plugin, capability) {
|
||||
checkbox.dataset.capabilityToggle = "true";
|
||||
checkbox.dataset.capabilityName = capabilityName;
|
||||
checkbox.dataset.capabilityType = capabilityType;
|
||||
checkbox.setAttribute("aria-label", `Enable ${capabilityName || "capability"}`);
|
||||
checkbox.setAttribute("aria-label", T("enable_named", "Enable %s", capabilityName));
|
||||
const checkboxMark = document.createElement("span");
|
||||
checkboxMark.className = "plugin-checkbox-mark";
|
||||
checkboxLabel.appendChild(checkbox);
|
||||
@@ -826,7 +828,7 @@ function VersionCell(plugin) {
|
||||
|
||||
const versionLabel = document.createElement("span");
|
||||
versionLabel.className = "version-value";
|
||||
versionLabel.textContent = GetDisplayVersion(plugin) || "N/A";
|
||||
versionLabel.textContent = GetDisplayVersion(plugin) || T("not_available", "N/A");
|
||||
cell.appendChild(versionLabel);
|
||||
|
||||
const updateBadge = UpdateStatusBadge(plugin);
|
||||
@@ -845,11 +847,11 @@ function UpdateStatusBadge(plugin) {
|
||||
badge.className = "version-update-badge";
|
||||
if (updateStatus === "unauthorized") {
|
||||
badge.classList.add("is-warning");
|
||||
badge.title = "Unauthorized for updates";
|
||||
badge.setAttribute("aria-label", "Unauthorized for updates");
|
||||
badge.title = T("unauthorized", "Unauthorized for updates");
|
||||
badge.setAttribute("aria-label", badge.title);
|
||||
} else {
|
||||
badge.title = "Update available";
|
||||
badge.setAttribute("aria-label", "Update available");
|
||||
badge.title = T("update_available", "Update available");
|
||||
badge.setAttribute("aria-label", badge.title);
|
||||
}
|
||||
|
||||
return badge;
|
||||
@@ -862,7 +864,7 @@ function StatusCell(plugin) {
|
||||
|
||||
const statusLabel = document.createElement("span");
|
||||
statusLabel.className = "status-label";
|
||||
statusLabel.textContent = GetStatus(plugin);
|
||||
statusLabel.textContent = StatusLabel(plugin);
|
||||
cell.appendChild(statusLabel);
|
||||
|
||||
return cell;
|
||||
@@ -875,7 +877,7 @@ function RenderDetails() {
|
||||
const detailStatusBody = document.getElementById("detailStatusBody");
|
||||
|
||||
SetText("detailInstalledVersion",
|
||||
plugin ? (IsPluginInstalled(plugin) ? (GetInstalledVersion(plugin) || "-") : "Not installed") : "-");
|
||||
plugin ? (IsPluginInstalled(plugin) ? (GetInstalledVersion(plugin) || "-") : T("not_installed", "Not installed")) : "-");
|
||||
SetText("detailLatestVersion", plugin ? (GetLatestVersion(plugin) || "-") : "-");
|
||||
const detailSource = document.getElementById("detailSource");
|
||||
if (detailSource) {
|
||||
@@ -933,12 +935,12 @@ function RenderConfig(plugin) {
|
||||
// Capabilities only exist once the plugin is activated: say that, rather than claiming it has
|
||||
// none to configure.
|
||||
empty.textContent = !plugin
|
||||
? "Select a plugin to configure its capabilities"
|
||||
? T("config_select_plugin", "Select a plugin to configure its capabilities")
|
||||
: (IsPluginLoading(plugin)
|
||||
? "Loading the plugin…"
|
||||
? T("config_loading", "Loading the plugin…")
|
||||
: (GetStatus(plugin) === "Activated"
|
||||
? "This plugin exposes no capabilities"
|
||||
: "Activate this plugin to configure its capabilities"));
|
||||
? T("config_no_capabilities", "This plugin exposes no capabilities")
|
||||
: T("config_activate", "Activate this plugin to configure its capabilities")));
|
||||
empty.hidden = false;
|
||||
layout.hidden = true;
|
||||
sidebar.replaceChildren();
|
||||
@@ -1121,7 +1123,7 @@ function ValidateConfigText() {
|
||||
SetConfigValidation("");
|
||||
return true;
|
||||
} catch (err) {
|
||||
SetConfigValidation(String(err?.message || "Invalid JSON"));
|
||||
SetConfigValidation(String(err?.message || T("invalid_json", "Invalid JSON")));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1237,21 +1239,19 @@ function RenderDescription(plugin) {
|
||||
|
||||
const isCloud = plugin && (plugin.source === "mine" || plugin.source === "subscribed" || plugin.source === "orphaned");
|
||||
if (isCloud && String(plugin?.sharing_token || "")) {
|
||||
node.appendChild(document.createTextNode("View on OrcaCloud "));
|
||||
const link = document.createElement("a");
|
||||
link.href = "#";
|
||||
link.className = "plugin-cloud-link";
|
||||
link.textContent = "here";
|
||||
link.title = "Open this plugin in your browser";
|
||||
link.title = T("open_in_browser", "Open this plugin in your browser");
|
||||
link.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
sendOpenPluginOnCloud(String(plugin.plugin_key || ""));
|
||||
});
|
||||
node.appendChild(link);
|
||||
AppendTemplate(node, T("view_on_cloud", "View on OrcaCloud <a>here</a>"), link);
|
||||
return;
|
||||
}
|
||||
|
||||
node.textContent = "No description available";
|
||||
node.textContent = T("no_description", "No description available");
|
||||
}
|
||||
|
||||
function RenderChangelog(plugin) {
|
||||
@@ -1305,8 +1305,8 @@ function ApplyDetailUpdateBadge(node, plugin) {
|
||||
if (updateStatus === "unauthorized") {
|
||||
node.hidden = false;
|
||||
node.classList.add("is-warning");
|
||||
node.title = "Unauthorized for updates";
|
||||
node.setAttribute("aria-label", "Unauthorized for updates");
|
||||
node.title = T("unauthorized", "Unauthorized for updates");
|
||||
node.setAttribute("aria-label", node.title);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1339,19 +1339,37 @@ function UpdateSelectedPlugin() {
|
||||
});
|
||||
}
|
||||
|
||||
// The status arrives as an identifier, which also names the status-* CSS classes.
|
||||
function StatusLabel(plugin) {
|
||||
switch (GetStatus(plugin)) {
|
||||
case "Activated":
|
||||
return T("status_activated", "Activated");
|
||||
case "Loading":
|
||||
return T("status_loading", "Loading");
|
||||
case "Error":
|
||||
return T("status_error", "Error");
|
||||
case "RuntimeError":
|
||||
return T("status_runtime_error", "Runtime error");
|
||||
case "Inactive":
|
||||
return T("status_inactive", "Inactive");
|
||||
default:
|
||||
return GetStatus(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
function StatusDescription(plugin) {
|
||||
switch (GetStatus(plugin)) {
|
||||
case "Activated":
|
||||
return "This plugin is active and ready to be used.";
|
||||
return T("status_activated_desc", "This plugin is active and ready to be used.");
|
||||
case "Loading":
|
||||
return "This plugin is still loading.";
|
||||
return T("status_loading_desc", "This plugin is still loading.");
|
||||
case "Error":
|
||||
return "This plugin is blocked until its error is fixed.";
|
||||
return T("status_error_desc", "This plugin is blocked until its error is fixed.");
|
||||
case "RuntimeError":
|
||||
return "This plugin is loaded but a capability reported an error.";
|
||||
return T("status_runtime_error_desc", "This plugin is loaded but a capability reported an error.");
|
||||
case "Inactive":
|
||||
default:
|
||||
return "This plugin is inactive. Activate it to install or load it.";
|
||||
return T("status_inactive_desc", "This plugin is inactive. Activate it to install or load it.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1359,13 +1377,13 @@ function RenderDetailSummary(container, plugin) {
|
||||
container.replaceChildren();
|
||||
|
||||
if (!plugin) {
|
||||
container.textContent = "Select a plugin to view its status and details.";
|
||||
container.textContent = T("select_plugin", "Select a plugin to view its status and details.");
|
||||
return;
|
||||
}
|
||||
|
||||
const statusChip = document.createElement("span");
|
||||
statusChip.className = `detail-status-chip status-${GetStatus(plugin).toLowerCase()}`;
|
||||
statusChip.textContent = GetStatus(plugin);
|
||||
statusChip.textContent = StatusLabel(plugin);
|
||||
container.appendChild(statusChip);
|
||||
|
||||
const message = document.createElement("div");
|
||||
@@ -1377,7 +1395,7 @@ function RenderDetailSummary(container, plugin) {
|
||||
if (plugin.orphaned === true) {
|
||||
const warning = document.createElement("div");
|
||||
warning.className = "detail-description detail-warning-text";
|
||||
warning.textContent = "Orphaned: This plugin is no longer subscribed or available in OrcaCloud. The local copy remains installed and can still be used.";
|
||||
warning.textContent = T("orphaned_note", "Orphaned: This plugin is no longer subscribed or available in OrcaCloud. The local copy remains installed and can still be used.");
|
||||
container.appendChild(warning);
|
||||
}
|
||||
|
||||
@@ -1385,14 +1403,14 @@ function RenderDetailSummary(container, plugin) {
|
||||
if (updateStatus === "update_available") {
|
||||
const note = document.createElement("div");
|
||||
note.className = "detail-description detail-note-text";
|
||||
note.textContent = "A newer plugin version is available.";
|
||||
note.textContent = T("newer_version_note", "A newer plugin version is available.");
|
||||
container.appendChild(note);
|
||||
}
|
||||
|
||||
if (updateStatus === "unauthorized") {
|
||||
const note = document.createElement("div");
|
||||
note.className = "detail-description detail-note-text is-warning";
|
||||
note.textContent = "Cloud updates are unavailable because this plugin is unauthorized for updates.";
|
||||
note.textContent = T("unauthorized_note", "Cloud updates are unavailable because this plugin is unauthorized for updates.");
|
||||
container.appendChild(note);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../css/theme.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<!-- <script type="text/javascript" src="./23.js"></script> -->
|
||||
@@ -21,7 +21,7 @@
|
||||
<div class="app">
|
||||
<div class="split">
|
||||
<div class="top-toolbar">
|
||||
<button id="refresh_btn" class="ButtonStyleConfirm ButtonTypeChoice toolbar-btn">Refresh</button>
|
||||
<button id="refresh_btn" class="ButtonStyleConfirm ButtonTypeChoice toolbar-btn" data-i18n="refresh">Refresh</button>
|
||||
|
||||
<!-- <label class="auto-update-switch" for="auto_update_toggle">
|
||||
<span class="auto-update-label">Auto update</span>
|
||||
@@ -35,18 +35,18 @@
|
||||
|
||||
<section class="pane top">
|
||||
<div class="hdr top-cols">
|
||||
<span>Name</span>
|
||||
<span>Type</span>
|
||||
<span>Version</span>
|
||||
<span>Update</span>
|
||||
<span data-i18n="name">Name</span>
|
||||
<span data-i18n="type">Type</span>
|
||||
<span data-i18n="version">Version</span>
|
||||
<span data-i18n="update">Update</span>
|
||||
</div>
|
||||
<div id="topList" class="body"></div>
|
||||
</section>
|
||||
|
||||
<section class="pane bottom">
|
||||
<div class="hdr bot-cols">
|
||||
<span>Name</span>
|
||||
<span>Type</span>
|
||||
<span data-i18n="name">Name</span>
|
||||
<span data-i18n="type">Type</span>
|
||||
</div>
|
||||
<div id="bottomList" class="body"></div>
|
||||
</section>
|
||||
@@ -55,15 +55,15 @@
|
||||
|
||||
<!-- Footer -->
|
||||
<div id="AcceptArea">
|
||||
<div id="export_btn" class="ButtonStyleConfirm ButtonTypeChoice" hidden>Export Presets</div>
|
||||
<div id="close_btn" class="ButtonStyleRegular ButtonTypeChoice">Close</div>
|
||||
<div id="export_btn" class="ButtonStyleConfirm ButtonTypeChoice" data-i18n="export_presets" hidden>Export Presets</div>
|
||||
<div id="close_btn" class="ButtonStyleRegular ButtonTypeChoice" data-i18n="close">Close</div>
|
||||
</div>
|
||||
|
||||
<!-- Context Menu -->
|
||||
<div id="ctxMenu" class="ctx" hidden>
|
||||
<button class="ctx-item" data-action="open_folder">Open folder in explorer</button>
|
||||
<button id = "delete_btn" class="ctx-item-delete" data-action="delete_bundle" hidden>Delete bundle</button>
|
||||
<button id = "unsubscribe_btn" class="ctx-item-subscribed" data-action="unsubscribe_bundle" hidden>Unsubscribe bundle</button>
|
||||
<button class="ctx-item" data-action="open_folder" data-i18n="open_folder">Open folder in explorer</button>
|
||||
<button id = "delete_btn" class="ctx-item-delete" data-action="delete_bundle" data-i18n="delete_bundle" hidden>Delete bundle</button>
|
||||
<button id = "unsubscribe_btn" class="ctx-item-subscribed" data-action="unsubscribe_bundle" data-i18n="unsubscribe_bundle" hidden>Unsubscribe bundle</button>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,14 @@ const bundlesById = new Map(); // bundleId -> bundle object
|
||||
const printersByBundle = new Map(); // bundleId -> Map(index -> printerName)
|
||||
const filamentsByBundle = new Map(); // bundleId -> Map(index -> filamentName)
|
||||
const processesByBundle = new Map(); // bundleId -> Map(index -> presetName)
|
||||
const UPDATE_TOOLTIP = "Update available";
|
||||
const UNAUTHORIZED_TOOLTIP = "Unauthorized bundle";
|
||||
const UPDATE_TOOLTIP = T("update_available", "Update available");
|
||||
const UNAUTHORIZED_TOOLTIP = T("unauthorized_bundle", "Unauthorized bundle");
|
||||
// Display names for the bundle types the native side sends (also kept raw in data-bundle-type).
|
||||
const BUNDLE_TYPE_LABELS = {
|
||||
Subscribed: T("type_subscribed", "Subscribed"),
|
||||
Local: T("type_local", "Local"),
|
||||
Default: T("type_default", "Default")
|
||||
};
|
||||
|
||||
// ========= DOM =========
|
||||
let topList = null;
|
||||
@@ -230,11 +236,11 @@ function renderTop() {
|
||||
: `<span class="bundle-status-icon-spacer" aria-hidden="true"></span>`}
|
||||
${
|
||||
b.type === "Subscribed" ?
|
||||
`<a href="#" class="bundle-name-text bundle-cloud-link" title="Open this bundle in your browser">${escapeHtml(b.name)}</a>`
|
||||
`<a href="#" class="bundle-name-text bundle-cloud-link" title="${escapeAttr(T("open_in_browser", "Open this bundle in your browser"))}">${escapeHtml(b.name)}</a>`
|
||||
: `<span class="bundle-name-text">${escapeHtml(b.name)}</span>`
|
||||
}
|
||||
</div>
|
||||
<span title="${escapeAttr(b.type)}">${escapeHtml(b.type)}</span>
|
||||
<span title="${escapeAttr(BundleTypeLabel(b.type))}">${escapeHtml(BundleTypeLabel(b.type))}</span>
|
||||
<span title="${escapeAttr(b.version)}">${escapeHtml(b.version)}</span>
|
||||
<div class="cell bundle-update-cell">
|
||||
<button
|
||||
@@ -242,7 +248,7 @@ function renderTop() {
|
||||
class="bundle-update-btn ${(!b.unauthorized && b.update_available) ? "is-enabled" : "is-disabled"}"
|
||||
${(!b.unauthorized && b.update_available) ? "" : "disabled"}
|
||||
data-id="${escapeAttr(b.id)}"
|
||||
>Update</button>
|
||||
>${escapeHtml(T("update", "Update"))}</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join("");
|
||||
@@ -282,9 +288,9 @@ function renderBottomForBundle(bundleId) {
|
||||
// Convert to a flat list of rows { typeLabel, name }
|
||||
const rows = [];
|
||||
|
||||
for (const [, name] of printers) rows.push({ type: "Printer", name });
|
||||
for (const [, name] of filaments) rows.push({ type: "Filament", name });
|
||||
for (const [, name] of processes) rows.push({ type: "Process", name });
|
||||
for (const [, name] of printers) rows.push({ type: T("printer", "Printer"), name });
|
||||
for (const [, name] of filaments) rows.push({ type: T("filament", "Filament"), name });
|
||||
for (const [, name] of processes) rows.push({ type: T("process", "Process"), name });
|
||||
|
||||
bottomList.innerHTML = rows.map((r, idx) => `
|
||||
<div class="row" data-id="${escapeAttr(bundleId)}" data-idx="${idx}">
|
||||
@@ -351,6 +357,10 @@ function hideMenu() {
|
||||
contextRow = null;
|
||||
}
|
||||
// ========= Utilities =========
|
||||
function BundleTypeLabel(type) {
|
||||
return BUNDLE_TYPE_LABELS[type] || type;
|
||||
}
|
||||
|
||||
function safeJsonParse(s) {
|
||||
try { return JSON.parse(s); } catch { return null; }
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
// - action.input "percent"/"tab" -> NativeCommands catalog (phases handled in activateEntry)
|
||||
// - action.icon SVG base name -> AppAction::icon / resources/images/<name>.svg
|
||||
// - action.desc/wiki -> AppAction::tooltip / help_url (footer detail strip)
|
||||
// - action.*_en / tab.title_en -> ActionRegistry action_json / tab_options (English search aliases,
|
||||
// sent only when they differ from the displayed text)
|
||||
// - payload.tooltip_expanded -> ActionRegistry::tooltip_expanded (persisted footer state)
|
||||
// - action list is frecency-sorted -> ActionRegistry::snapshot()
|
||||
|
||||
@@ -97,45 +99,85 @@ var ghostEl = null, ghostTypedEl = null, ghostSuffixEl = null;
|
||||
var activeCompletion = null;
|
||||
|
||||
// ---- pure helpers (no DOM; unit-tested) -------------------------------------
|
||||
// Pre-normalized haystacks, cached on the action object. The fold is length-preserving (1:1 per
|
||||
// char) so the ranges FuzzyRangesNorm returns slice the ORIGINAL title/group/source text correctly.
|
||||
// The action objects arrive from C++ and are stable for the dialog's lifetime, so we compute these once.
|
||||
function titleNorm(a) {
|
||||
if (a._tn === undefined)
|
||||
a._tn = NormText(a.title, false);
|
||||
return a._tn;
|
||||
// Every searchable field of an action: its text, its ranking weight (title > group/category >
|
||||
// source/aliases) and the matchIndex key its highlight ranges go to (null for a search-only field).
|
||||
// - group vs source: the eyebrow (header) line shows group when present, else source. Settings keep
|
||||
// an empty group so their source path is the eyebrow; commands / plates / recents carry a non-empty
|
||||
// group. Splitting the two lets a match range stay aligned to whichever string the eyebrow renders.
|
||||
// - full_label: search-only alias for the descriptive name when the title differs (e.g. "Reverse on
|
||||
// even" vs "Overhang reversal"). Never rendered, so no highlight ranges.
|
||||
// - plugin category / type: the visible "Plugins" category, plus the plugin kind as searchable
|
||||
// metadata, so typing "plugin" finds runnable plugin actions even when neither their capability
|
||||
// nor plugin name contains that word. Keep both category forms.
|
||||
// - *_en: English counterparts of the translated fields, sent by C++ only when they differ from the
|
||||
// displayed text (a non-English UI), so the page can still be searched in English. Each carries the
|
||||
// weight of the localized field it mirrors, so a match ranks the same in either language. Search-only,
|
||||
// except their ranges, which drive the row's English hint (see ENGLISH_HINT_FIELDS).
|
||||
var SEARCH_FIELDS = [
|
||||
{ text: function (a) { return a.title; }, weight: SCORE_TITLE, range: "title" },
|
||||
{ text: function (a) { return a.group; }, weight: SCORE_GROUP, range: "group" },
|
||||
{ text: function (a) { return a.source; }, weight: 0, range: "source" },
|
||||
{ text: function (a) { return a.full_label; }, weight: 0, range: null },
|
||||
{ text: function (a) { return a.kind === "plugin" ? T("sd_plugins", "Plugins") : ""; }, weight: SCORE_GROUP, range: "plugin" },
|
||||
{ text: function (a) { return a.kind === "plugin" ? "plugin plugins" : ""; }, weight: SCORE_GROUP, range: null },
|
||||
{ text: function (a) { return a.title_en; }, weight: SCORE_TITLE, range: "title_en" },
|
||||
{ text: function (a) { return a.group_en; }, weight: SCORE_GROUP, range: "group_en" },
|
||||
{ text: function (a) { return a.source_en; }, weight: 0, range: "source_en" },
|
||||
{ text: function (a) { return a.full_label_en; }, weight: 0, range: "full_label_en" }
|
||||
];
|
||||
var MATCH_RANGE_KEYS = SEARCH_FIELDS.map(function (f) { return f.range; }).filter(Boolean);
|
||||
|
||||
// English aliases a row can match through. `range` names the matchIndex key matchFields fills for that
|
||||
// field; the hint shows the action's English name (title_en), falling back to whichever alias matched.
|
||||
var ENGLISH_HINT_FIELDS = [
|
||||
{ range: "title_en", text: function (a) { return a.title_en; } },
|
||||
{ range: "group_en", text: function (a) { return a.group_en; } },
|
||||
{ range: "source_en", text: function (a) { return a.source_en; } },
|
||||
{ range: "full_label_en", text: function (a) { return a.full_label_en; } }
|
||||
];
|
||||
|
||||
// The English alias to show as a muted hint on an action row, or null. Only a row whose match came
|
||||
// through an English alias gets one (a visible translated highlight, or a local-only alias, needs
|
||||
// none). The hint is the action's English NAME - highlighted when the name itself matched, otherwise
|
||||
// shown plain - so the row identifies the action instead of repeating its translated eyebrow: a
|
||||
// "Process : Speed" breadcrumb match still names the setting ("Inner wall"), not the breadcrumb again.
|
||||
function englishHint(a, mi, visibleMatch) {
|
||||
if (!mi || visibleMatch) return null;
|
||||
var matched = null;
|
||||
for (var i = 0; i < ENGLISH_HINT_FIELDS.length; i++) {
|
||||
if (mi[ENGLISH_HINT_FIELDS[i].range]) { matched = ENGLISH_HINT_FIELDS[i]; break; }
|
||||
}
|
||||
if (!matched) return null;
|
||||
if (a.title_en) return { text: a.title_en, ranges: mi.title_en || null };
|
||||
var text = matched.text(a);
|
||||
return text ? { text: text, ranges: mi[matched.range] } : null;
|
||||
}
|
||||
// The eyebrow (header) line shows group when present, else source. Settings keep an empty group so
|
||||
// their source path is the eyebrow; commands / plates / recents carry a non-empty group. Splitting the
|
||||
// two lets a match range stay aligned to whichever string the eyebrow actually renders.
|
||||
function groupNorm(a) {
|
||||
if (a._gn === undefined)
|
||||
a._gn = NormText(a.group || "", false);
|
||||
return a._gn;
|
||||
|
||||
// The English breadcrumb a row matched through (a command's group, a setting's source path), with its
|
||||
// highlight ranges, or null when neither matched. Shown beside the translated eyebrow so an English
|
||||
// hit is visible where it landed, alongside the English name hint on the row-name line.
|
||||
function englishBreadcrumb(a, mi) {
|
||||
if (!mi) return null;
|
||||
if (mi.group_en && a.group_en) return { text: a.group_en, ranges: mi.group_en };
|
||||
if (mi.source_en && a.source_en) return { text: a.source_en, ranges: mi.source_en };
|
||||
return null;
|
||||
}
|
||||
function sourceNorm(a) {
|
||||
if (a._sn === undefined)
|
||||
a._sn = NormText(a.source || "", false);
|
||||
return a._sn;
|
||||
}
|
||||
function pluginCategoryNorm(a) {
|
||||
if (a._pcn === undefined)
|
||||
a._pcn = a.kind === "plugin" ? NormText(T("sd_plugins", "Plugins"), false) : "";
|
||||
return a._pcn;
|
||||
}
|
||||
// Plugin type is searchable metadata, so typing "plugin" can find runnable plugin actions even
|
||||
// when neither their capability nor plugin name contains that word. Keep both category forms.
|
||||
function pluginTypeNorm(a) {
|
||||
if (a._pn === undefined)
|
||||
a._pn = a.kind === "plugin" ? NormText("plugin plugins", false) : "";
|
||||
return a._pn;
|
||||
}
|
||||
// Search-only alias for the descriptive name when the title differs (e.g. "Reverse on even" vs
|
||||
// "Overhang reversal"). Never rendered, so no highlight ranges.
|
||||
function fullNorm(a) {
|
||||
if (a._fn === undefined)
|
||||
a._fn = NormText(a.full_label || "", false);
|
||||
return a._fn;
|
||||
|
||||
// The action's non-empty SEARCH_FIELDS as pre-normalized haystacks ({norm, weight, range}), cached on
|
||||
// the action object. The action objects arrive from C++ and are stable for the dialog's lifetime, so
|
||||
// this runs once per action; skipping empty fields keeps the per-keystroke scan to the text an action
|
||||
// actually has (an English UI pays nothing for the aliases it never receives). The fold is
|
||||
// length-preserving (1:1 per char) so the ranges FuzzyRangesNorm returns slice the ORIGINAL text.
|
||||
function searchFields(a) {
|
||||
if (a._sf === undefined) {
|
||||
a._sf = [];
|
||||
for (var i = 0; i < SEARCH_FIELDS.length; i++) {
|
||||
var norm = NormText(SEARCH_FIELDS[i].text(a) || "", false);
|
||||
if (norm)
|
||||
a._sf.push({ norm: norm, weight: SEARCH_FIELDS[i].weight, range: SEARCH_FIELDS[i].range });
|
||||
}
|
||||
}
|
||||
return a._sf;
|
||||
}
|
||||
|
||||
// Match one pre-normalized field vs one pre-normalized needle. Returns {score, ranges, contiguous}
|
||||
@@ -175,26 +217,22 @@ function tokenWordRe(token) {
|
||||
return new RegExp("\\b" + EscapeRegExp(token) + "\\b");
|
||||
}
|
||||
|
||||
// One token's best match across an action's searchable fields, keeping per-field ranges for
|
||||
// highlighting. Returns {score, title, group, source}, or null if no field matches.
|
||||
function tokenMatch(a, token, wwRe) {
|
||||
var t = fieldMatchScore(titleNorm(a), token, wwRe);
|
||||
var g = fieldMatchScore(groupNorm(a), token, wwRe);
|
||||
var s = fieldMatchScore(sourceNorm(a), token, wwRe);
|
||||
var f = fieldMatchScore(fullNorm(a), token, wwRe);
|
||||
var p = fieldMatchScore(pluginCategoryNorm(a), token, wwRe);
|
||||
var typeMatch = fieldMatchScore(pluginTypeNorm(a), token, wwRe);
|
||||
if (!t && !g && !s && !f && !p && !typeMatch) return null;
|
||||
var score = Math.max(
|
||||
t ? (t.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_TITLE + t.score : -Infinity,
|
||||
g ? (g.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_GROUP + g.score : -Infinity,
|
||||
s ? (s.contiguous ? SCORE_CONTIGUOUS : 0) + s.score : -Infinity,
|
||||
f ? (f.contiguous ? SCORE_CONTIGUOUS : 0) + f.score : -Infinity,
|
||||
p ? (p.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_GROUP + p.score : -Infinity,
|
||||
typeMatch ? (typeMatch.contiguous ? SCORE_CONTIGUOUS : 0) + SCORE_GROUP + typeMatch.score : -Infinity
|
||||
);
|
||||
return { score: score, title: t ? t.ranges : null, group: g ? g.ranges : null, source: s ? s.ranges : null,
|
||||
plugin: p ? p.ranges : null };
|
||||
// One needle (the whole query, or one token of it) against every SEARCH_FIELDS field of an action.
|
||||
// Returns {score, ranges}: the best weighted field score (contiguous > fuzzy, then field weight, then
|
||||
// start/gaps) and the ranges of each matching highlighted field keyed by its range name. Null when no
|
||||
// field matches.
|
||||
function matchFields(a, needle, wwRe) {
|
||||
var fields = searchFields(a);
|
||||
var best = null, ranges = null; // allocated on the first match: most actions match nothing
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var field = fields[i];
|
||||
var m = fieldMatchScore(field.norm, needle, wwRe);
|
||||
if (!m) continue;
|
||||
var v = (m.contiguous ? SCORE_CONTIGUOUS : 0) + field.weight + m.score;
|
||||
if (best === null || v > best) best = v;
|
||||
if (field.range) (ranges || (ranges = {}))[field.range] = m.ranges;
|
||||
}
|
||||
return best === null ? null : { score: best, ranges: ranges || {} };
|
||||
}
|
||||
|
||||
// Merge per-field token ranges into sorted, coalesced ranges for highlighting. Overlapping or adjacent
|
||||
@@ -217,24 +255,6 @@ function mergeRanges(ranges) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Combine per-field scores into one value, or null when nothing matched.
|
||||
// Ranking: contiguous > fuzzy, then title > group/category > source/full alias, then start/gaps.
|
||||
function scoreFields(t, g, s, f, p, typeMatch) {
|
||||
var best = null;
|
||||
function consider(m, weight) {
|
||||
if (!m) return;
|
||||
var v = (m.contiguous ? SCORE_CONTIGUOUS : 0) + weight + m.score;
|
||||
best = best === null ? v : Math.max(best, v);
|
||||
}
|
||||
consider(t, SCORE_TITLE);
|
||||
consider(g, SCORE_GROUP);
|
||||
consider(s, 0);
|
||||
consider(f, 0);
|
||||
consider(p, SCORE_GROUP);
|
||||
consider(typeMatch, SCORE_GROUP);
|
||||
return best;
|
||||
}
|
||||
|
||||
// The unified main-phase search: every action (command/plugin/setting) matching the query, ranked
|
||||
// by relevance (not by action type). Sets matchIndex so rows highlight their match ranges. The query
|
||||
// is normalized ONCE per pass - FuzzyRangesNorm then runs against each action's pre-normalized
|
||||
@@ -245,8 +265,8 @@ function scoreFields(t, g, s, f, p, typeMatch) {
|
||||
// - tokens: every whitespace-separated word must match SOME field, but different words may match
|
||||
// different fields. This is what lets "speed acceleration inner" find "Inner wall" whose path is
|
||||
// "Process : Speed : Acceleration" (title + source breadcrumb together).
|
||||
// full_label and canonical plugin kind are searchable aliases. The visible category is also searchable
|
||||
// so its own match can be highlighted in plugin rows.
|
||||
// full_label, canonical plugin kind and the English counterparts (*_en) are searchable aliases. The
|
||||
// visible category is also searchable so its own match can be highlighted in plugin rows.
|
||||
// A phrase match always outranks a distributed token match.
|
||||
function searchActions(actions, query) {
|
||||
var q = (query || "").trim();
|
||||
@@ -262,50 +282,43 @@ function searchActions(actions, query) {
|
||||
// Compiled once per pass, reused over every field: non-global so no exec()/lastIndex state leaks
|
||||
// between fields, and EscapeRegExp keeps regex metachars in the query literal.
|
||||
var wwRe = new RegExp("\\b" + EscapeRegExp(searchNeedle) + "\\b");
|
||||
// A one-word query's only token is the needle itself, so its token pass would repeat the failed
|
||||
// phrase match exactly; such an action is dropped without it.
|
||||
var tokenPass = searchTokens.length !== 1 || searchTokens[0] !== searchNeedle;
|
||||
|
||||
var scored = [];
|
||||
var seen = {};
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var a = list[i];
|
||||
var t = fieldMatchScore(titleNorm(a), searchNeedle, wwRe);
|
||||
var g = fieldMatchScore(groupNorm(a), searchNeedle, wwRe);
|
||||
var s = fieldMatchScore(sourceNorm(a), searchNeedle, wwRe);
|
||||
var f = fieldMatchScore(fullNorm(a), searchNeedle, wwRe);
|
||||
var p = fieldMatchScore(pluginCategoryNorm(a), searchNeedle, wwRe);
|
||||
var typeMatch = fieldMatchScore(pluginTypeNorm(a), searchNeedle, wwRe);
|
||||
var phrase = scoreFields(t, g, s, f, p, typeMatch);
|
||||
var score, ranges;
|
||||
if (phrase !== null) {
|
||||
score = phrase + SCORE_PHRASE;
|
||||
ranges = { title: t ? t.ranges : null, group: g ? g.ranges : null, source: s ? s.ranges : null,
|
||||
plugin: p ? p.ranges : null };
|
||||
var phrase = matchFields(a, searchNeedle, wwRe);
|
||||
var score, perField;
|
||||
if (phrase) {
|
||||
score = phrase.score + SCORE_PHRASE;
|
||||
} else {
|
||||
if (!tokenPass) continue;
|
||||
// Require every token; a token that matches nothing drops the action immediately. Ranges
|
||||
// from all matching tokens are merged per field so each matched word highlights.
|
||||
var sum = 0, titleR = null, groupR = null, sourceR = null, pluginR = null, all = true;
|
||||
var sum = 0, all = true;
|
||||
perField = {};
|
||||
for (var tokenIndex = 0; tokenIndex < searchTokens.length; tokenIndex++) {
|
||||
var m = tokenMatch(a, searchTokens[tokenIndex], searchTokenRes[tokenIndex]);
|
||||
var m = matchFields(a, searchTokens[tokenIndex], searchTokenRes[tokenIndex]);
|
||||
if (!m) { all = false; break; }
|
||||
sum += m.score;
|
||||
if (m.title) (titleR || (titleR = [])).push(m.title);
|
||||
if (m.group) (groupR || (groupR = [])).push(m.group);
|
||||
if (m.source) (sourceR || (sourceR = [])).push(m.source);
|
||||
if (m.plugin) (pluginR || (pluginR = [])).push(m.plugin);
|
||||
for (var key in m.ranges)
|
||||
(perField[key] || (perField[key] = [])).push(m.ranges[key]);
|
||||
}
|
||||
if (!all) continue;
|
||||
score = sum;
|
||||
ranges = { title: mergeRanges(titleR), group: mergeRanges(groupR), source: mergeRanges(sourceR),
|
||||
plugin: mergeRanges(pluginR) };
|
||||
}
|
||||
// Ranges are per-field against the ACTUAL text drawn: title for the row-name, and group (or
|
||||
// source when group is empty) for the eyebrow - so highlight offsets stay aligned to the label.
|
||||
matchIndex[a.id] = {
|
||||
title: ranges.title,
|
||||
group: ranges.group,
|
||||
source: ranges.source,
|
||||
plugin: ranges.plugin,
|
||||
useEyebrowGroup: !!(a.group)
|
||||
};
|
||||
// Ranges are per-field against the ACTUAL text drawn: title for the row-name, group (or source
|
||||
// when group is empty) for the eyebrow, and title_en for the English hint - so highlight offsets
|
||||
// stay aligned to the label.
|
||||
var entry = { useEyebrowGroup: !!(a.group) };
|
||||
for (var k = 0; k < MATCH_RANGE_KEYS.length; k++) {
|
||||
var rangeKey = MATCH_RANGE_KEYS[k];
|
||||
entry[rangeKey] = phrase ? phrase.ranges[rangeKey] || null : mergeRanges(perField[rangeKey]);
|
||||
}
|
||||
matchIndex[a.id] = entry;
|
||||
scored.push({ a: a, s: score });
|
||||
seen[a.id] = true;
|
||||
}
|
||||
@@ -350,7 +363,8 @@ function completionFor(query, list) {
|
||||
var top = (list || []).slice(0, 10);
|
||||
for (var i = 0; i < top.length; i++) {
|
||||
var a = top[i];
|
||||
var fields = [a.title, a.group, a.source, a.full_label];
|
||||
// English aliases after the displayed text, so English typing completes in a localized UI.
|
||||
var fields = [a.title, a.group, a.source, a.full_label, a.title_en, a.group_en, a.source_en, a.full_label_en];
|
||||
for (var f = 0; f < fields.length; f++) {
|
||||
var words = completionWords(fields[f]);
|
||||
for (var w = 0; w < words.length; w++) {
|
||||
@@ -427,16 +441,30 @@ function tabTitle(t) {
|
||||
return title || prettySource((t && t.id) || "");
|
||||
}
|
||||
|
||||
// Filter the tab list by a fuzzy title/id match. Pure so the node-vm test can exercise it.
|
||||
// Filter the tab list by a fuzzy title/id/English-title match (title_en is sent only when the page
|
||||
// label is translated). Pure so the node-vm test can exercise it.
|
||||
function filterTabs(tabs, query) {
|
||||
var q = (query || "").trim();
|
||||
if (!q)
|
||||
return (tabs || []).slice(0);
|
||||
return (tabs || []).filter(function (t) {
|
||||
return FuzzyRanges(tabTitle(t), q, false) || FuzzyRanges(t.id, q, false);
|
||||
return FuzzyRanges(tabTitle(t), q, false) || FuzzyRanges(t.id, q, false) ||
|
||||
(t.title_en && FuzzyRanges(t.title_en, q, false));
|
||||
});
|
||||
}
|
||||
|
||||
// The English title of a tab matched only through it, with its highlight ranges, or null when the tab
|
||||
// matched its translated title or id (so it already shows why) or has no English title.
|
||||
function tabAliasHint(t, query) {
|
||||
var q = (query || "").trim();
|
||||
if (!q || !t || !t.title_en)
|
||||
return null;
|
||||
if (FuzzyRanges(tabTitle(t), q, false) || FuzzyRanges(t.id, q, false))
|
||||
return null;
|
||||
var ranges = FuzzyRanges(t.title_en, q, false);
|
||||
return ranges ? { text: t.title_en, ranges: ranges } : null;
|
||||
}
|
||||
|
||||
function shouldRenderActionList(query) {
|
||||
return !!((query || "").trim());
|
||||
}
|
||||
@@ -971,8 +999,21 @@ function renderActionRow(a, i) {
|
||||
var showPluginCategory = a.kind === "plugin" && (typedQuery || isRecent);
|
||||
var eyebrow = actionEyebrow(a, query, isRecent);
|
||||
var eyebrowMatch = mi ? (showPluginCategory ? mi.plugin : (mi.useEyebrowGroup ? mi.group : mi.source)) : null;
|
||||
shell.left.insertBefore(markedText("row-eyebrow", eyebrow, eyebrowMatch), shell.line);
|
||||
// The eyebrow line: the translated breadcrumb, and beside it the English breadcrumb when the query
|
||||
// matched that (non-English UI), so an English hit on a breadcrumb is visible where it landed.
|
||||
var eyebrowLine = document.createElement("div");
|
||||
eyebrowLine.className = "row-eyebrow-line";
|
||||
eyebrowLine.appendChild(markedText("row-eyebrow", eyebrow, eyebrowMatch));
|
||||
var enBreadcrumb = englishBreadcrumb(a, mi);
|
||||
if (enBreadcrumb)
|
||||
eyebrowLine.appendChild(markedText("row-eyebrow-en", enBreadcrumb.text, enBreadcrumb.ranges));
|
||||
shell.left.insertBefore(eyebrowLine, shell.line);
|
||||
shell.line.appendChild(markedText("row-name", a.title, mi ? mi.title : null));
|
||||
// A row found only through an English alias shows the action's English name, highlighted when the
|
||||
// name itself matched, so the match makes sense even when the translated row resembles nothing.
|
||||
var hint = englishHint(a, mi, mi ? (mi.title || eyebrowMatch) : null);
|
||||
if (hint)
|
||||
shell.line.appendChild(markedText("row-alias", hint.text, hint.ranges));
|
||||
var badge = modeBadge(a, USER_MODE);
|
||||
if (badge) {
|
||||
var tag = document.createElement("span");
|
||||
@@ -1150,6 +1191,11 @@ function renderTabRow(t, i) {
|
||||
name.className = "row-name";
|
||||
name.textContent = label;
|
||||
shell.line.appendChild(name);
|
||||
// A tab found only through its English title shows that title, highlighted, so the match is
|
||||
// explained even when the translated label looks nothing like the query.
|
||||
var hint = tabAliasHint(t, query);
|
||||
if (hint)
|
||||
shell.line.appendChild(markedText("row-alias", hint.text, hint.ranges));
|
||||
shell.row.onclick = function () { sel = { zone: "list", i: i }; render({ resize: true }); };
|
||||
shell.row.ondblclick = function () { sel = { zone: "list", i: i }; jumpToTab(t); };
|
||||
return shell.row;
|
||||
@@ -1421,8 +1467,8 @@ function focusInput() { setTimeout(function () { if (qEl) qEl.focus(); }, 0); }
|
||||
function OnInit() {
|
||||
qEl = $("q"); listEl = $("list"); favEl = $("favBar"); clearEl = $("clear"); eyeEl = $("favEyebrow"); countEl = $("count"); detailEl = $("detail");
|
||||
ghostEl = $("ghost"); ghostTypedEl = $("ghostTyped"); ghostSuffixEl = $("ghostSuffix");
|
||||
// text.js's TranslatePage() targets jQuery `.trans` nodes; this page has none and defines its own
|
||||
// `$`, so don't call it. Runtime strings go through T() instead.
|
||||
// This page has no data-i18n markup and defines its own `$` and T(), so it does not load
|
||||
// include/i18n.js. Its strings all go through T().
|
||||
qEl.placeholder = T("sd_search", "Search actions");
|
||||
qEl.setAttribute("aria-label", T("sd_search", "Search actions"));
|
||||
if (clearEl) {
|
||||
|
||||
@@ -50,6 +50,25 @@ assert.equal(ctx.filterTabs(tabOptions, "Device").length, 1,
|
||||
"a typed query matches a tab title");
|
||||
assert.deepEqual(ctx.filterTabs(tabOptions, "zzz"), [],
|
||||
"a typed query with no match returns an empty list");
|
||||
// A translated tab whose id is not its English name carries title_en, so English still finds it.
|
||||
const localizedTabs = [
|
||||
{ id: "monitor", title: "设备", title_en: "Device" },
|
||||
{ id: "prepare", title: "准备" }
|
||||
];
|
||||
assert.deepEqual(ctx.filterTabs(localizedTabs, "device").map(function (t) { return t.id; }), ["monitor"],
|
||||
"a localized tab matches its English title");
|
||||
assert.deepEqual(ctx.filterTabs(localizedTabs, "设备").map(function (t) { return t.id; }), ["monitor"],
|
||||
"a localized tab still matches its translated title");
|
||||
// A tab found only through its English title shows it as a hint; one that matched its translated
|
||||
// title or id already shows why, and one without an English title has nothing to show.
|
||||
assert.deepEqual(ctx.tabAliasHint(localizedTabs[0], "device"), { text: "Device", ranges: [[0, 6]] },
|
||||
"an English-only tab match yields the English title and its highlight ranges");
|
||||
assert.equal(ctx.tabAliasHint(localizedTabs[0], "设备"), null,
|
||||
"a translated-title tab match draws no English hint");
|
||||
assert.equal(ctx.tabAliasHint(localizedTabs[0], "monitor"), null,
|
||||
"a tab matched by its id draws no English hint");
|
||||
assert.equal(ctx.tabAliasHint(localizedTabs[1], "Device"), null,
|
||||
"a tab with no English title has no hint");
|
||||
|
||||
// tabTitle: pages added with an empty title (e.g. MainFrame's Home tab) fall back to the id.
|
||||
assert.equal(ctx.tabTitle({ id: "home", title: "" }), "Home",
|
||||
@@ -192,6 +211,89 @@ assert.equal(ctx.completionFor("", compPool), null, "an empty query has no compl
|
||||
assert.equal(ctx.completionFor("zzz", ctx.searchActions(compPool, "zzz")), null,
|
||||
"a query with no match has no completion");
|
||||
|
||||
// English aliases: in a non-English UI each translated field may carry its English counterpart
|
||||
// (*_en), searched with the weight of the field it mirrors. An English UI sends none.
|
||||
const zhPool = [
|
||||
{ id: "rev", title: "反转偶数层悬垂方向", full_label: "悬垂反转", source: "工艺 : 质量 : 悬垂",
|
||||
title_en: "Reverse on even", full_label_en: "Overhang reversal", source_en: "Process : Quality : Overhangs",
|
||||
group: "", input: "" },
|
||||
{ id: "slice", title: "切片并预览", source: "OrcaSlicer", group: "切片和导出",
|
||||
title_en: "Slice and Preview", group_en: "Slice & Export", input: "" },
|
||||
{ id: "brim", title: "Brim宽度", source: "工艺 : 其他 : Brim", source_en: "Process : Others : Brim",
|
||||
group: "", input: "" }
|
||||
];
|
||||
assert.deepEqual(ctx.searchActions(zhPool, "reverse on even").map(function (a) { return a.id; }), ["rev"],
|
||||
"an English query finds a translated setting through its English title");
|
||||
assert.equal(ctx.matchIndex.rev.title, null, "an English-only match does not highlight the translated title");
|
||||
assert.deepEqual(ctx.matchIndex.rev.title_en, [[0, 15]], "the English title keeps its ranges for the hint");
|
||||
assert.deepEqual(ctx.searchActions(zhPool, "偶数层").map(function (a) { return a.id; }), ["rev"],
|
||||
"the translated title is still searchable");
|
||||
assert.equal(ctx.matchIndex.rev.title_en, null, "a translated-title match leaves no English hint ranges");
|
||||
assert.deepEqual(ctx.searchActions(zhPool, "reverse 悬垂").map(function (a) { return a.id; }), ["rev"],
|
||||
"a mixed query may match one token in English and another in the translation");
|
||||
assert.deepEqual(ctx.searchActions(zhPool, "overhang reversal").map(function (a) { return a.id; }), ["rev"],
|
||||
"the English full_label alias is searchable");
|
||||
assert.deepEqual(ctx.searchActions(zhPool, "export").map(function (a) { return a.id; }), ["slice"],
|
||||
"the English group alias is searchable");
|
||||
assert.deepEqual(ctx.searchActions(zhPool, "brim").map(function (a) { return a.id; }), ["brim"],
|
||||
"a translation that keeps the English word matches the displayed title");
|
||||
assert.deepEqual(ctx.matchIndex.brim.title, [[0, 4]], "that match highlights the displayed title, so no hint is drawn");
|
||||
// Weights mirror the localized fields: an English title match outranks an English breadcrumb match.
|
||||
const zhRankPool = [
|
||||
{ id: "src", title: "内壁", source: "工艺 : 速度", source_en: "Process : Speed", group: "", input: "" },
|
||||
{ id: "ttl", title: "速度", source: "工艺 : 其他", title_en: "Speed", group: "", input: "" }
|
||||
];
|
||||
assert.deepEqual(ctx.searchActions(zhRankPool, "speed").map(function (a) { return a.id; }), ["ttl", "src"],
|
||||
"an English title match outranks an English breadcrumb match, as their localized fields do");
|
||||
// Absent aliases (an English UI) leave no English hint; every pool above has none and ranks as before.
|
||||
ctx.searchActions(pool, "layer");
|
||||
assert.equal(ctx.matchIndex.c1.title_en, null, "an action without English aliases has no English hint ranges");
|
||||
assert.equal(ctx.completionFor("reverse o", ctx.searchActions(zhPool, "reverse o")).suffix, "n",
|
||||
"completion extends an English word from title_en");
|
||||
assert.equal(ctx.completionFor("quality overh", ctx.searchActions(zhPool, "quality overh")).suffix, "angs",
|
||||
"completion also reaches the English breadcrumb");
|
||||
// A row matched only through an English group/source/full_label alias still names the action in
|
||||
// English: the hint is title_en (the English name), not the alias that matched, so a breadcrumb hit
|
||||
// does not just repeat the translated eyebrow.
|
||||
ctx.searchActions(zhPool, "export");
|
||||
assert.deepEqual(ctx.matchIndex.slice.group_en, [[8, 14]], "the English group keeps its ranges");
|
||||
assert.deepEqual(ctx.englishHint(zhPool[1], ctx.matchIndex.slice, null), { text: "Slice and Preview", ranges: null },
|
||||
"an English group match names the action, unhighlighted");
|
||||
assert.deepEqual(ctx.englishBreadcrumb(zhPool[1], ctx.matchIndex.slice), { text: "Slice & Export", ranges: [[8, 14]] },
|
||||
"an English group match shows the English breadcrumb beside the eyebrow");
|
||||
ctx.searchActions(zhPool, "overhang reversal");
|
||||
assert.deepEqual(ctx.matchIndex.rev.full_label_en, [[0, 17]], "the English full_label keeps its ranges");
|
||||
assert.deepEqual(ctx.englishHint(zhPool[0], ctx.matchIndex.rev, null), { text: "Reverse on even", ranges: null },
|
||||
"an English full_label match names the action, unhighlighted");
|
||||
ctx.searchActions(zhPool, "quality");
|
||||
assert.deepEqual(ctx.matchIndex.rev.source_en, [[10, 17]], "the English breadcrumb keeps its ranges");
|
||||
assert.deepEqual(ctx.englishHint(zhPool[0], ctx.matchIndex.rev, null), { text: "Reverse on even", ranges: null },
|
||||
"an English breadcrumb match names the action, not the breadcrumb");
|
||||
assert.deepEqual(ctx.englishBreadcrumb(zhPool[0], ctx.matchIndex.rev), { text: "Process : Quality : Overhangs", ranges: [[10, 17]] },
|
||||
"an English breadcrumb match shows the English breadcrumb beside the eyebrow");
|
||||
// The name itself matching is the one case that highlights the hint.
|
||||
ctx.searchActions(zhPool, "reverse on even");
|
||||
assert.deepEqual(ctx.englishHint(zhPool[0], ctx.matchIndex.rev, null), { text: "Reverse on even", ranges: [[0, 15]] },
|
||||
"an English title match names and highlights the action");
|
||||
assert.equal(ctx.englishBreadcrumb(zhPool[0], ctx.matchIndex.rev), null,
|
||||
"an English title match shows no breadcrumb hint");
|
||||
// A breadcrumb match with no English name falls back to the matched alias.
|
||||
assert.deepEqual(ctx.englishHint({ title_en: "", source_en: "Process : Speed" }, { source_en: [[10, 15]] }, null),
|
||||
{ text: "Process : Speed", ranges: [[10, 15]] }, "without an English name the matched alias is shown");
|
||||
// A visible translated highlight suppresses the hint even when an English alias also matched.
|
||||
ctx.searchActions(zhPool, "brim");
|
||||
assert.equal(ctx.englishHint(zhPool[2], ctx.matchIndex.brim, ctx.matchIndex.brim.title), null,
|
||||
"a row that highlights its translated title draws no English hint");
|
||||
// The reported case: "process speed" matches only the English breadcrumb of "Inner wall", so the hint
|
||||
// names the setting rather than echoing the breadcrumb.
|
||||
const zhInner = [{ id: "inner", title: "内壁", title_en: "Inner wall", source: "工艺 : 速度",
|
||||
source_en: "Process : Speed", group: "", input: "" }];
|
||||
ctx.searchActions(zhInner, "process speed");
|
||||
assert.deepEqual(ctx.englishHint(zhInner[0], ctx.matchIndex.inner, null), { text: "Inner wall", ranges: null },
|
||||
"a breadcrumb-only match names the setting in English");
|
||||
assert.deepEqual(ctx.englishBreadcrumb(zhInner[0], ctx.matchIndex.inner), { text: "Process : Speed", ranges: [[0, 8], [10, 15]] },
|
||||
"a breadcrumb-only match shows the English breadcrumb beside the eyebrow");
|
||||
|
||||
// actionCategory: a command/dynamic action's group is its category; a setting uses the top-level
|
||||
// source segment; every plugin shares one header; a category-less action falls back to "Other".
|
||||
assert.equal(ctx.actionCategory({ id: "c", group: "Help", source: "OrcaSlicer", kind: "command" }), "Help",
|
||||
@@ -456,9 +558,9 @@ assert.equal(ctx.stateFromPayload({}).tooltipExpanded, true, "expansion defaults
|
||||
assert.equal(ctx.stateFromPayload({ tooltip_expanded: false }).tooltipExpanded, false, "a collapsed payload is honored");
|
||||
assert.equal(ctx.stateFromPayload({ tooltip_expanded: true }).tooltipExpanded, true, "an expanded payload is honored");
|
||||
|
||||
// resultCountText: a search counts the shown matches only ("Showing N actions"); the total is used
|
||||
// resultCountText: a search counts the shown matches only ("Matching actions: N"); the total is used
|
||||
// solely for the empty-query count.
|
||||
assert.equal(ctx.resultCountText(100, 3, "lay"), "Showing 3 actions", "a search reports the shown match count only");
|
||||
assert.equal(ctx.resultCountText(100, 100, ""), "100 actions", "an empty query reports the total");
|
||||
assert.equal(ctx.resultCountText(100, 3, "lay"), "Matching actions: 3", "a search reports the shown match count only");
|
||||
assert.equal(ctx.resultCountText(100, 100, ""), "Actions: 100", "an empty query reports the total");
|
||||
|
||||
console.log("ok");
|
||||
|
||||
@@ -390,7 +390,31 @@ body {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Eyebrow line: the translated breadcrumb, plus the English breadcrumb beside it when it matched. */
|
||||
.row-eyebrow-line {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.row-eyebrow {
|
||||
font-size: 10px;
|
||||
line-height: 1.3;
|
||||
color: var(--muted, var(--orca-muted, #6b7280));
|
||||
opacity: .85;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* English breadcrumb of a row found only through it (non-English UI); gives way to the translation. */
|
||||
.row-eyebrow-en {
|
||||
flex: 0 100 auto;
|
||||
min-width: 0;
|
||||
font-size: 10px;
|
||||
line-height: 1.3;
|
||||
color: var(--muted, var(--orca-muted, #6b7280));
|
||||
@@ -414,8 +438,21 @@ body {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* English title of a row found only through it (non-English UI); gives way to the title first. */
|
||||
.row-alias {
|
||||
flex: 0 100 auto;
|
||||
min-width: 0;
|
||||
font-size: 11px;
|
||||
color: var(--muted, var(--orca-muted, #6b7280));
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.row-name mark,
|
||||
.row-eyebrow mark {
|
||||
.row-eyebrow mark,
|
||||
.row-eyebrow-en mark,
|
||||
.row-alias mark {
|
||||
background: var(--plugin-status-warn-bg);
|
||||
color: var(--plugin-status-warn);
|
||||
border-radius: 2px;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../../include/xterm/xterm.js"></script>
|
||||
<script type="text/javascript" src="../../include/xterm/xterm-addon-fit.js"></script>
|
||||
@@ -21,8 +21,8 @@
|
||||
<div class="app">
|
||||
<div id="terminal-container"></div>
|
||||
<div class="input-row">
|
||||
<input type="text" id="cmd-input" placeholder="python ... or uv ..." />
|
||||
<button id="run-btn">Run</button>
|
||||
<input type="text" id="cmd-input" placeholder="python ... or uv ..." data-i18n-placeholder="command_hint" />
|
||||
<button id="run-btn" data-i18n="run">Run</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -36,8 +36,7 @@ function WatchXtermTheme() {
|
||||
}
|
||||
|
||||
function OnInit() {
|
||||
if (typeof TranslatePage === "function")
|
||||
TranslatePage();
|
||||
TranslatePage();
|
||||
|
||||
term = new Terminal({
|
||||
cursorBlink: true,
|
||||
@@ -77,7 +76,7 @@ function OnInit() {
|
||||
onRun();
|
||||
});
|
||||
|
||||
term.writeln("Plugin terminal ready.");
|
||||
term.writeln(T("ready", "Plugin terminal ready."));
|
||||
}
|
||||
|
||||
function onRun() {
|
||||
@@ -122,7 +121,7 @@ function HandleStudio(value) {
|
||||
|
||||
case "process_done":
|
||||
setRunning(false);
|
||||
term.writeln("\x1b[90mProcess exited with code: " + payload.exit_code + "\x1b[0m");
|
||||
term.writeln("\x1b[90m" + T("exit_code", "Process exited with code: %s", payload.exit_code) + "\x1b[0m");
|
||||
break;
|
||||
|
||||
case "process_error":
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../css/dark.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="load.js"></script>
|
||||
@@ -17,7 +17,7 @@
|
||||
<body onLoad="OnInit()">
|
||||
<div id="LoadBlock">
|
||||
<img id="LoadingSvg" src="loading.svg" />
|
||||
<div id="LoadTip" class="trans" tid="t126">Loading……</div>
|
||||
<div id="LoadTip" data-i18n="loading">Loading…</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -9,7 +9,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../css/dark.css" />
|
||||
<link rel="stylesheet" type="text/css" href="1.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
</head>
|
||||
@@ -17,10 +17,10 @@
|
||||
<div id="SlicerIcon" style="text-align: center;">
|
||||
<img src="../../image/logo.png"/>
|
||||
</div>
|
||||
<div id="Title" class="trans" tid="t1">Welcome to Orca Slicer</div>
|
||||
<div id="STip" class="trans" tid="t2">Orca Slicer will be setup in several steps. Let's start!</div>
|
||||
<div id="Title" data-i18n="welcome">Welcome to Orca Slicer</div>
|
||||
<div id="STip" data-i18n="setup_intro">Orca Slicer will be set up in several steps. Let's start!</div>
|
||||
<div id="BtnArea">
|
||||
<a id="StartBtn" class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t24" href="../11/index.html">Get Started</a>
|
||||
<a id="StartBtn" class="ButtonStyleConfirm ButtonTypeChoice" href="../11/index.html" data-i18n="get_started">Get Started</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -10,23 +10,23 @@
|
||||
<link rel="stylesheet" type="text/css" href="../css/dark.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="11.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="t47">Please select your login region</div>
|
||||
<div data-i18n="login_region">Please select your login region</div>
|
||||
</div>
|
||||
<div id="Content">
|
||||
<div id="EarthIcon"><img src="earth.png"/></div>
|
||||
<div id="RegionList">
|
||||
<div class="trans RegionItem RegionSelected TextS1" tid="t48" region="Asia-Pacific" onclick="ChooseRegion('Asia-Pacific')">Asia-Pacific</div>
|
||||
<div class="trans RegionItem" tid="t49" region="China" onclick="ChooseRegion('China')">China</div>
|
||||
<div class="trans RegionItem" tid="t60" region="Europe" onclick="ChooseRegion('Europe')">Europe</div>
|
||||
<div class="trans RegionItem" tid="t61" region="North America" onclick="ChooseRegion('North America')">North America</div>
|
||||
<div class="trans RegionItem" tid="t62" region="Others" onclick="ChooseRegion('Others')">Others</div>
|
||||
<div class="RegionItem RegionSelected TextS1" region="Asia-Pacific" onclick="ChooseRegion('Asia-Pacific')" data-i18n="asia_pacific">Asia-Pacific</div>
|
||||
<div class="RegionItem" region="China" onclick="ChooseRegion('China')" data-i18n="china">China</div>
|
||||
<div class="RegionItem" region="Europe" onclick="ChooseRegion('Europe')" data-i18n="europe">Europe</div>
|
||||
<div class="RegionItem" region="North America" onclick="ChooseRegion('North America')" data-i18n="north_america">North America</div>
|
||||
<div class="RegionItem" region="Others" onclick="ChooseRegion('Others')" data-i18n="others">Others</div>
|
||||
</div>
|
||||
<!-- <div id="ChangeTip">
|
||||
<div><img class="TipIcon" src="tip.png" /></div>
|
||||
@@ -34,7 +34,7 @@
|
||||
</div>-->
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" id="AcceptBtn" tid="t9" onclick="GotoPolicyPage()">Next</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" id="AcceptBtn" onclick="GotoPolicyPage()" data-i18n="next">Next</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -50,17 +50,17 @@ function HandleStudio( pVal )
|
||||
let hasError = pVal.hasOwnProperty("error");
|
||||
noticeBar.classList.toggle("notice-error", hasError);
|
||||
if (hasError) {
|
||||
noticeBar.textContent = "Error";
|
||||
noticeBar.textContent = T("error", "Error");
|
||||
noticeText.textContent = pVal["error"];
|
||||
} else if (pVal["vendors"] && pVal["vendors"].length > 0) {
|
||||
noticeBar.textContent = "New printers found";
|
||||
noticeText.textContent = "New printer vendors installed: " + pVal["vendors"].join(", ");
|
||||
noticeBar.textContent = T("new_printers_found", "New printers found");
|
||||
noticeText.textContent = T("new_vendors_installed", "New printer vendors installed: %s", pVal["vendors"].join(", "));
|
||||
} else if (pVal["declined"]) {
|
||||
noticeBar.textContent = "Information";
|
||||
noticeText.textContent = "New printer vendors were found, but installation was cancelled.";
|
||||
noticeBar.textContent = T("information", "Information");
|
||||
noticeText.textContent = T("vendors_declined", "New printer vendors were found, but installation was cancelled.");
|
||||
} else {
|
||||
noticeBar.textContent = "Information";
|
||||
noticeText.textContent = "No new printers found.";
|
||||
noticeBar.textContent = T("information", "Information");
|
||||
noticeText.textContent = T("no_new_printers", "No new printers found.");
|
||||
}
|
||||
|
||||
ShowNotice(1);
|
||||
@@ -272,7 +272,7 @@ function CreateVendorBlock(vendorName)
|
||||
if( alt == "BBL" )
|
||||
alt = "Bambu Lab";
|
||||
if( alt == "Custom")
|
||||
alt = "Custom Printer";
|
||||
alt = T("custom_printer", "Custom Printer");
|
||||
if( alt == "Other")
|
||||
alt = "Orca colosseum";
|
||||
|
||||
@@ -315,7 +315,7 @@ function CreatePrinterBlock(OneModel)
|
||||
' </div>'+
|
||||
' <div class="PrinterInfoMark">?</div>'+
|
||||
' <div class="PrinterInfo">'+
|
||||
' <div class="title trans">Nozzle</div>'+
|
||||
' <div class="title">' + T("nozzle", "Nozzle") + '</div>'+
|
||||
' <div class="value">' + OneModel['nozzle_diameter'].replaceAll(";", " · ") + '</div>'+
|
||||
' </div>'+
|
||||
' <div style="display: flex;">'+
|
||||
@@ -377,7 +377,8 @@ function OnExit()
|
||||
if( nTotal==0 ) {
|
||||
let noticeBar = document.getElementById("NoticeBar");
|
||||
noticeBar.classList.add("notice-error");
|
||||
noticeBar.textContent = "Error";
|
||||
noticeBar.textContent = T("error", "Error");
|
||||
document.getElementById("NoticeText").textContent = T("printer_required", "At least one printer must be selected.");
|
||||
ShowNotice(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="../21/common.js"></script> <!-- ORCA use common sources for setup guide and standalone dialog -->
|
||||
@@ -20,12 +20,12 @@
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="t10">Printer Selection</div>
|
||||
<div data-i18n="printer_selection">Printer Selection</div>
|
||||
</div>
|
||||
|
||||
<div id="SidebarContainer">
|
||||
<div id="Sidebar" class="thin-scroll">
|
||||
<div class="title">Vendors</div>
|
||||
<div class="title" data-i18n="vendors">Vendors</div>
|
||||
<div class="area" id="SidebarVendors">
|
||||
<!-- <div class="SidebarItem"></div> -->
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="search" hasvalue="0">
|
||||
<div class="search-icon icon16"></div>
|
||||
<input type="text" class="searchTerm" placeholder=" " oninput="textInput(this)">
|
||||
<span class="search-placeholder trans">Search...</span>
|
||||
<span class="search-placeholder" data-i18n="search">Search</span>
|
||||
</div>
|
||||
<div class="LayoutSelector">
|
||||
<div class="TabGroup">
|
||||
@@ -81,17 +81,17 @@
|
||||
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" id="PreBtn" tid="t8" onclick="window.open('../1/index.html','_self')">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" id="AcceptBtn" tid="t9" onclick="GotoFilamentPage()">Next</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="PreBtn" onclick="window.open('../1/index.html','_self')" data-i18n="back">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" id="AcceptBtn" onclick="GotoFilamentPage()" data-i18n="next">Next</div>
|
||||
</div>
|
||||
|
||||
<div id="NoticeMask"></div>
|
||||
<div id="NoticeBody">
|
||||
<div id="NoticeBar" class="trans" tid="t18">error</div>
|
||||
<div id="NoticeBar" data-i18n="error">Error</div>
|
||||
<div id="NoticeContent">
|
||||
<div id="NoticeText" class="trans" tid="t37">At least one printer must be selected.</div>
|
||||
<div id="NoticeText" data-i18n="printer_required">At least one printer must be selected.</div>
|
||||
<div id="NoticeBtns">
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t36" onClick="ShowNotice(0)">ok</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" onClick="ShowNotice(0)" data-i18n="ok">OK</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="../22/common.js"></script> <!-- ORCA use common sources for setup guide and standalone dialog -->
|
||||
@@ -20,7 +20,7 @@
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="t14">Filament Selection</div>
|
||||
<div data-i18n="filament_selection">Filament Selection</div>
|
||||
</div>
|
||||
<div id="Content" class="ZScrol">
|
||||
<!-- ORCA column browser -->
|
||||
@@ -29,14 +29,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="1"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t15">printer</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="printer">Printer</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="MachineList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" mode="all" onClick="ChooseAllMachine()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" mode="all" onClick="ChooseAllMachine()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,14 +44,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="2"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t16">filament type</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="filament_type">Filament type</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="FilatypeList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllFilament()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" onClick="ChooseAllFilament()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,14 +59,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="3"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t17">vendor</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="vendor">Vendor</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="VendorList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllVendor()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" onClick="ChooseAllVendor()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,48 +75,48 @@
|
||||
<div class="cbr-filter-box">
|
||||
<div class="filter-icon"></div>
|
||||
<input type="text" class="cbr-filter-bar" placeholder=" " tabindex="4"/>
|
||||
<span class="cbr-filter-placeholder trans">Filter items</span>
|
||||
<span class="cbr-filter-placeholder" data-i18n="filter_items">Filter items</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="ComboBox NoLabel">
|
||||
<div class="arrow-icon"></div>
|
||||
<select id="filter-tags">
|
||||
<option value="0" disabled selected hidden></option>
|
||||
<option value="1">Checked</option>
|
||||
<option value="2">Unchecked</option>
|
||||
<option value="1" data-i18n="checked">Checked</option>
|
||||
<option value="2" data-i18n="unchecked">Unchecked</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="list-item-count"></div>
|
||||
<div class="cbr-filter-btns">
|
||||
<span class="cbr-filter-mode-filter trans">Select filtered</span>
|
||||
<span class="cbr-filter-mode-visible trans">Select visible</span>
|
||||
<div class="ButtonStyleConfirm ButtonTypeWindow trans" tid="t11" onClick="SelectAllFilament(1)">all</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeWindow trans" tid="t12" onClick="SelectAllFilament(0)">Clear all</div>
|
||||
<span class="cbr-filter-mode-filter" data-i18n="select_filtered">Select filtered</span>
|
||||
<span class="cbr-filter-mode-visible" data-i18n="select_visible">Select visible</span>
|
||||
<div class="ButtonStyleConfirm ButtonTypeWindow" onClick="SelectAllFilament(1)" data-i18n="all">All</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeWindow" onClick="SelectAllFilament(0)" data-i18n="clear_all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="ItemBlockArea">
|
||||
<!-- <label class="MItem"><input type="checkbox" class="trans" tid="t11" onClick="SelectAllFilament()" /><span class="trans" tid="t11">all</span></label> -->
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t8" id="PreBtn" onclick="ReturnPreviewPage()">Back</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t25" id="AcceptBtn" onclick="FinishGuide()">finish</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t9" id="GotoNetPluginBtn" onclick="GotoNetPluginPage()">Next</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="PreBtn" onclick="ReturnPreviewPage()" data-i18n="back">Back</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="AcceptBtn" onclick="FinishGuide()" data-i18n="finish">Finish</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" id="GotoNetPluginBtn" onclick="GotoNetPluginPage()" data-i18n="next">Next</div>
|
||||
</div>
|
||||
<div id="NoticeMask">
|
||||
</div>
|
||||
<div id="NoticeBody">
|
||||
<div id="NoticeBar" class="trans" tid="t18">error</div>
|
||||
<div id="NoticeBar" data-i18n="error">Error</div>
|
||||
<div id="NoticeContent">
|
||||
<div id="NoticeText" class="trans" tid="t19">At least one filament must be selected. </div>
|
||||
<div id="NoticeText" class="trans" tid="t20">Do you want to use default filament ?</div>
|
||||
<div id="NoticeText" data-i18n="filament_required">At least one filament must be selected.</div>
|
||||
<div id="NoticeText" data-i18n="use_default_filament">Do you want to use the default filament?</div>
|
||||
<div id="NoticeBtns">
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t21" onClick="ChooseDefaultFilament()">Yes</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t22" onClick="ShowNotice(0)">No</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" onClick="ChooseDefaultFilament()" data-i18n="yes">Yes</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" onClick="ShowNotice(0)" data-i18n="no">No</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../css/dark.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="../22/common.js"></script> <!-- ORCA use common sources for setup guide and standalone dialog -->
|
||||
@@ -19,8 +19,8 @@
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div id="SystemFilamentBtn" class="TitleSelected trans" tid="t109" onClick="OnSelectMenu(1)">System Filaments</div>
|
||||
<div id="CustomFilamentBtn" class="TitleUnselected trans" tid="t110" onClick="OnSelectMenu(2)">Custom Filaments</div>
|
||||
<div id="SystemFilamentBtn" class="TitleSelected" onClick="OnSelectMenu(1)" data-i18n="system_filaments">System Filaments</div>
|
||||
<div id="CustomFilamentBtn" class="TitleUnselected" onClick="OnSelectMenu(2)" data-i18n="custom_filaments">Custom Filaments</div>
|
||||
</div>
|
||||
<div id="Content" class="ZScrol">
|
||||
|
||||
@@ -32,14 +32,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="1"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t15">printer</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="printer">Printer</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="MachineList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" mode="all" onClick="ChooseAllMachine()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" mode="all" onClick="ChooseAllMachine()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,14 +47,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="2"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t16">filament type</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="filament_type">Filament type</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="FilatypeList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllFilament()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" onClick="ChooseAllFilament()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -62,14 +62,14 @@
|
||||
<div class="cbr-column-title-container">
|
||||
<div class="search-icon"></div>
|
||||
<input type="text" class="cbr-search-bar" placeholder=" " tabindex="3"/>
|
||||
<span class="cbr-search-placeholder trans" tid="t17">vendor</span>
|
||||
<span class="cbr-search-placeholder" data-i18n="vendor">Vendor</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="VendorList">
|
||||
<div class="CValues">
|
||||
<label><input type="checkbox" class="trans" tid="t11" onClick="ChooseAllVendor()" /><span class="trans" tid="t11">all</span></label>
|
||||
<label><input type="checkbox" onClick="ChooseAllVendor()" /><span data-i18n="all">All</span></label>
|
||||
</div>
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -78,28 +78,28 @@
|
||||
<div class="cbr-filter-box">
|
||||
<div class="filter-icon"></div>
|
||||
<input type="text" class="cbr-filter-bar" placeholder=" " tabindex="4"/>
|
||||
<span class="cbr-filter-placeholder trans">Filter items</span>
|
||||
<span class="cbr-filter-placeholder" data-i18n="filter_items">Filter items</span>
|
||||
<div class="clear-icon"></div>
|
||||
</div>
|
||||
<div class="ComboBox NoLabel">
|
||||
<div class="arrow-icon"></div>
|
||||
<select id="filter-tags">
|
||||
<option value="0" disabled selected hidden></option>
|
||||
<option value="1">Checked</option>
|
||||
<option value="2">Unchecked</option>
|
||||
<option value="1" data-i18n="checked">Checked</option>
|
||||
<option value="2" data-i18n="unchecked">Unchecked</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="list-item-count"></div>
|
||||
<div class="cbr-filter-btns">
|
||||
<span class="cbr-filter-mode-filter trans">Select filtered</span>
|
||||
<span class="cbr-filter-mode-visible trans">Select visible</span>
|
||||
<div class="ButtonStyleConfirm ButtonTypeWindow trans" tid="t11" onClick="SelectAllFilament(1)">all</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeWindow trans" tid="t12" onClick="SelectAllFilament(0)">Clear all</div>
|
||||
<span class="cbr-filter-mode-filter" data-i18n="select_filtered">Select filtered</span>
|
||||
<span class="cbr-filter-mode-visible" data-i18n="select_visible">Select visible</span>
|
||||
<div class="ButtonStyleConfirm ButtonTypeWindow" onClick="SelectAllFilament(1)" data-i18n="all">All</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeWindow" onClick="SelectAllFilament(0)" data-i18n="clear_all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cbr-content thin-scroll" id="ItemBlockArea">
|
||||
<!-- <label class="MItem"><input type="checkbox" class="trans" tid="t11" onClick="SelectAllFilament()" /><span class="trans" tid="t11">all</span></label> -->
|
||||
<div class="cbr-no-items">No items</div>
|
||||
<div class="cbr-no-items" data-i18n="no_items">No items</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
<div id="CustomFilamentsArea">
|
||||
<div id="CFilament_Btn_Area">
|
||||
<div class="ButtonStyleConfirm ButtonTypeWindow trans" tid="t111" onClick="OnClickCustomFilamentAdd()">Create New</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeWindow" onClick="OnClickCustomFilamentAdd()" data-i18n="create_new">Create New</div>
|
||||
</div>
|
||||
<div id="CFilament_List" class="thin-scroll">
|
||||
</div>
|
||||
@@ -116,19 +116,19 @@
|
||||
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t39" id="AcceptBtn" onclick="ConfirmSelect()">Confirm</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t38" id="PreBtn" onclick="CancelSelect()">Cancel</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" id="AcceptBtn" onclick="ConfirmSelect()" data-i18n="confirm">Confirm</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="PreBtn" onclick="CancelSelect()" data-i18n="cancel">Cancel</div>
|
||||
</div>
|
||||
<div id="NoticeMask">
|
||||
</div>
|
||||
<div id="NoticeBody">
|
||||
<div id="NoticeBar" class="trans" tid="t18">error</div>
|
||||
<div id="NoticeBar" data-i18n="error">Error</div>
|
||||
<div id="NoticeContent">
|
||||
<div id="NoticeText" class="trans" tid="t19">At least one filament must be selected. </div>
|
||||
<div id="NoticeText" class="trans" tid="t20">Do you want to use default filament ?</div>
|
||||
<div id="NoticeText" data-i18n="filament_required">At least one filament must be selected.</div>
|
||||
<div id="NoticeText" data-i18n="use_default_filament">Do you want to use the default filament?</div>
|
||||
<div id="NoticeBtns">
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t21" onClick="ChooseDefaultFilament()">yes</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t22" onClick="ShowNotice(0)">no</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" onClick="ChooseDefaultFilament()" data-i18n="yes">Yes</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" onClick="ShowNotice(0)" data-i18n="no">No</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- <script type="text/javascript" src="test.js"></script> -->
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="../21/common.js"></script> <!-- ORCA use common sources for setup guide and standalone dialog -->
|
||||
@@ -20,12 +20,12 @@
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="t10">Printer Selection</div>
|
||||
<div data-i18n="printer_selection">Printer Selection</div>
|
||||
</div>
|
||||
|
||||
<div id="SidebarContainer">
|
||||
<div id="Sidebar" class="thin-scroll">
|
||||
<div class="title">Vendors</div>
|
||||
<div class="title" data-i18n="vendors">Vendors</div>
|
||||
<div class="area" id="SidebarVendors">
|
||||
<!-- <div class="SidebarItem"></div> -->
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="search" hasvalue="0">
|
||||
<div class="search-icon icon16"></div>
|
||||
<input type="text" class="searchTerm" placeholder=" " oninput="textInput(this)">
|
||||
<span class="search-placeholder trans">Search...</span>
|
||||
<span class="search-placeholder" data-i18n="search">Search</span>
|
||||
</div>
|
||||
<div class="LayoutSelector">
|
||||
<div class="TabGroup">
|
||||
@@ -81,19 +81,19 @@
|
||||
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" id="CreateBtn" onclick="CreateNewPrinter()">Create</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="CheckNewPrintersBtn" onclick="CheckForNewPrinters()">Check for new printers</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t39" id="AcceptBtn" onclick="ConfirmSelect()">Confirm</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t38" id="PreBtn" onclick="CancelSelect()">Cancel</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="CreateBtn" onclick="CreateNewPrinter()" data-i18n="create">Create</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="CheckNewPrintersBtn" onclick="CheckForNewPrinters()" data-i18n="check_new_printers">Check for new printers</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" id="AcceptBtn" onclick="ConfirmSelect()" data-i18n="confirm">Confirm</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="PreBtn" onclick="CancelSelect()" data-i18n="cancel">Cancel</div>
|
||||
</div>
|
||||
|
||||
<div id="NoticeMask"></div>
|
||||
<div id="NoticeBody">
|
||||
<div id="NoticeBar" class="trans" tid="t18">error</div>
|
||||
<div id="NoticeBar" data-i18n="error">Error</div>
|
||||
<div id="NoticeContent">
|
||||
<div id="NoticeText" class="trans" tid="t37">At least one printer must be selected.</div>
|
||||
<div id="NoticeText" data-i18n="printer_required">At least one printer must be selected.</div>
|
||||
<div id="NoticeBtns">
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t36" onClick="ShowNotice(0)">ok</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" onClick="ShowNotice(0)" data-i18n="ok">OK</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
.PolicyArea
|
||||
{
|
||||
background-color: #4A4A51;
|
||||
}
|
||||
|
||||
#PolicyTxt
|
||||
{
|
||||
padding: 12px;
|
||||
word-wrap:break-word;
|
||||
overflow-y: auto;
|
||||
min-height: 200px;
|
||||
text-indent: 2em;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #D7D7D7;
|
||||
}
|
||||
|
||||
#PolicyTxt::-webkit-scrollbar {/*滚动条整体样式*/
|
||||
width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
#PolicyTxt::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
background-color: #AAAAAA;
|
||||
}
|
||||
|
||||
#PolicyTxt::-webkit-scrollbar-track {/*滚动条里面轨道*/
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
border-radius: 10px;
|
||||
background: #EDEDED;
|
||||
}
|
||||
|
||||
#C_List li
|
||||
{
|
||||
margin-left: 4em;
|
||||
}
|
||||
|
||||
p
|
||||
{
|
||||
text-indent:2em;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
|
||||
function OnInit()
|
||||
{
|
||||
TranslatePage();
|
||||
|
||||
SendPrivacySelect();
|
||||
}
|
||||
|
||||
|
||||
function SendPrivacySelect()
|
||||
{
|
||||
let nVal="refuse";
|
||||
if( $('#ChoosePrivacy').is(':checked') )
|
||||
nVal="agree";
|
||||
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="user_private_choice";
|
||||
tSend['data']={};
|
||||
tSend['data']['action']=nVal;
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
|
||||
function GotoNextPage()
|
||||
{
|
||||
RequestProfile();
|
||||
}
|
||||
|
||||
function RequestProfile()
|
||||
{
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="request_userguide_profile";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
function HandleStudio( pVal )
|
||||
{
|
||||
let strCmd=pVal['command'];
|
||||
//alert(strCmd);
|
||||
|
||||
if(strCmd=='response_userguide_profile')
|
||||
{
|
||||
HandleModelInfo(pVal['response']);
|
||||
}
|
||||
}
|
||||
|
||||
function HandleModelInfo( pVal )
|
||||
{
|
||||
let Modellist=pVal["model"];
|
||||
let nModel=Modellist.length;
|
||||
|
||||
if(nModel==1)
|
||||
{
|
||||
// let pModel=Modellist[0];
|
||||
//
|
||||
// var tSend={};
|
||||
// tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
// tSend['command']="save_userguide_models";
|
||||
// tSend['data']={};
|
||||
//
|
||||
// let ModelName=pModel['model'];
|
||||
//
|
||||
// tSend['data'][ModelName]={};
|
||||
// tSend['data'][ModelName]['model']=pModel['model'];
|
||||
// tSend['data'][ModelName]['nozzle_diameter']=pModel['nozzle_diameter'];
|
||||
// tSend['data'][ModelName]['vendor']=pModel['vendor'];
|
||||
//
|
||||
// SendWXMessage( JSON.stringify(tSend) );
|
||||
|
||||
window.location.href="../22/index.html";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href="../21/index.html";
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Cache-Control" content="max-age=7200" />
|
||||
<title>引导_P1</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../include/global.css" /> <!-- ORCA One for all-->
|
||||
<link rel="stylesheet" type="text/css" href="../css/common.css" />
|
||||
<link rel="stylesheet" type="text/css" href="3.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../css/dark.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="./3.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="t6">We kindly request your help to improve<br/> everyone's printing</div>
|
||||
</div>
|
||||
<div id="Content">
|
||||
<div id="PolicyTxt" class="TextS2 TextArea1 ZScrol">
|
||||
<a class="trans" tid="t54">In the 3D Printing community, we learn from each other’s successes and failures to adjust our own slicing parameters and settings. Orca Slicer follows the same principle and uses machine learning to improve its performance from the successes and failures of the vast number of prints by our users. We are training Orca Slicer to be smarter by feeding them the real-world data. If you are willing, this service will access information from your error logs and usage logs, which may include information described in </a><a class="HyperLink trans" tid="t55" href="https://bambulab.com/policies/privacy" target="_blank">Privacy Policy</a><a class="trans" tid="t56">
|
||||
. We will not collect any Personal Data by which an individual can be identified directly or indirectly, including without limitation names, addresses, payment information, or phone numbers. By enabling this service, you agree to these terms and the statement about Privacy Policy.
|
||||
</a>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<label> <!-- ORCA use label tag to allow checkbox to toggle when user clicked to text -->
|
||||
<input id="ChoosePrivacy" type="checkbox" onClick="SendPrivacySelect()" onChange="SendPrivacySelect()" />
|
||||
<span class="trans TextS1" tid="t7">Allow sending anonymous data</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t8" id="PreBtn" onclick="window.open('../11/index.html','_self')">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t9" id="AcceptBtn" onclick="GotoNextPage()">Next</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
#PolicyTxt
|
||||
{
|
||||
background-color: #EBEBEB;
|
||||
padding: 12px;
|
||||
word-wrap:break-word;
|
||||
overflow-y: scroll;
|
||||
min-height: 200px;
|
||||
text-indent: 2em;
|
||||
}
|
||||
|
||||
#PolicyTxt::-webkit-scrollbar {/*滚动条整体样式*/
|
||||
width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
#PolicyTxt::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
background-color: #AAAAAA;
|
||||
}
|
||||
|
||||
#PolicyTxt::-webkit-scrollbar-track {/*滚动条里面轨道*/
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
|
||||
border-radius: 10px;
|
||||
background: #EDEDED;
|
||||
}
|
||||
|
||||
#C_List li
|
||||
{
|
||||
margin-left: 4em;
|
||||
}
|
||||
|
||||
p
|
||||
{
|
||||
text-indent:2em;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
|
||||
function OnInit()
|
||||
{
|
||||
TranslatePage();
|
||||
|
||||
SendPrivacySelect();
|
||||
}
|
||||
|
||||
|
||||
function SendPrivacySelect()
|
||||
{
|
||||
let nVal="refuse";
|
||||
if( $('#ChoosePrivacy').is(':checked') )
|
||||
nVal="agree";
|
||||
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="user_private_choice";
|
||||
tSend['data']={};
|
||||
tSend['data']['action']=nVal;
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
|
||||
function GotoNextPage()
|
||||
{
|
||||
RequestProfile();
|
||||
}
|
||||
|
||||
function RequestProfile()
|
||||
{
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="request_userguide_profile";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
function HandleStudio( pVal )
|
||||
{
|
||||
let strCmd=pVal['command'];
|
||||
//alert(strCmd);
|
||||
|
||||
if(strCmd=='response_userguide_profile')
|
||||
{
|
||||
HandleModelInfo(pVal['response']);
|
||||
}
|
||||
}
|
||||
|
||||
function HandleModelInfo( pVal )
|
||||
{
|
||||
let Modellist=pVal["model"];
|
||||
let nModel=Modellist.length;
|
||||
|
||||
if(nModel==1)
|
||||
{
|
||||
// let pModel=Modellist[0];
|
||||
//
|
||||
// var tSend={};
|
||||
// tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
// tSend['command']="save_userguide_models";
|
||||
// tSend['data']={};
|
||||
//
|
||||
// let ModelName=pModel['model'];
|
||||
//
|
||||
// tSend['data'][ModelName]={};
|
||||
// tSend['data'][ModelName]['model']=pModel['model'];
|
||||
// tSend['data'][ModelName]['nozzle_diameter']=pModel['nozzle_diameter'];
|
||||
// tSend['data'][ModelName]['vendor']=pModel['vendor'];
|
||||
//
|
||||
// SendWXMessage( JSON.stringify(tSend) );
|
||||
|
||||
window.location.href="../22/index.html";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href="../21/index.html";
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Cache-Control" content="max-age=7200" />
|
||||
<title>引导_P1</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../include/global.css" /> <!-- ORCA One for all-->
|
||||
<link rel="stylesheet" type="text/css" href="../css/common.css" />
|
||||
<link rel="stylesheet" type="text/css" href="31.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="./31.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="t6">We kindly request your help to improve<br/> everyone's printing</div>
|
||||
</div>
|
||||
<div id="Content">
|
||||
<div id="PolicyTxt">
|
||||
<a class="trans" tid="t54">In the 3D Printing community, we learn from each other’s successes and failures to adjust our own slicing parameters and settings. Orca Slicer follows the same principle and uses machine learning to improve its performance from the successes and failures of the vast number of prints by our users. We are training Orca Slicer to be smarter by feeding them the real-world data. If you are willing, this service will access information from your error logs and usage logs, which may include information described in </a><a class="HyperLink trans" tid="t55" href="https://bambulab.com/policies/privacy" target="_blank">Privacy Policy</a><a class="trans" tid="t56">
|
||||
. We will not collect any Personal Data by which an individual can be identified directly or indirectly, including without limitation names, addresses, payment information, or phone numbers. By enabling this service, you agree to these terms and the statement about Privacy Policy.
|
||||
</a>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<label><!-- ORCA use label tag to allow checkbox to toggle when user clicked to text -->
|
||||
<input id="ChoosePrivacy" type="checkbox" onClick="SendPrivacySelect()" onChange="SendPrivacySelect()" />
|
||||
<span class="trans" tid="t7">Allow sending anonymous data</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t8" id="PreBtn" onclick="window.open('../11/index.html','_self')">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t9" id="AcceptBtn" onclick="GotoNextPage()">Next</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,32 +10,32 @@
|
||||
<link rel="stylesheet" type="text/css" href="4orca.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="4orca.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="orca3">Stealth Mode</div>
|
||||
<div data-i18n="stealth_mode">Stealth Mode</div>
|
||||
</div>
|
||||
<div id="Content">
|
||||
|
||||
<div id="FeatureText">
|
||||
<div><a tid="orca4" class="trans TextS1">This disables all cloud features, including Orca Cloud profile syncing. Users who prefer to work entirely offline can enable this option.</a></div>
|
||||
<div><a class="TextS1" data-i18n="stealth_mode_desc">This disables all cloud features, including Orca Cloud profile syncing. Users who prefer to work entirely offline can enable this option.</a></div>
|
||||
<br/>
|
||||
<div><a tid="orca12" class="trans TextS2">Note: When Stealth Mode is enabled, your user profiles will not be backed up to Orca Cloud.</a></div>
|
||||
<div><a class="TextS2" data-i18n="stealth_mode_note">Note: When Stealth Mode is enabled, your user profiles will not be backed up to Orca Cloud.</a></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ORCA use label tag to allow checkbox to toggle when user clicked to text -->
|
||||
<label id="CheckArea"><input id="StealthMode" onClick="SendStealthModeCheck()" type="checkbox"><a tid="orca5" class="trans TextS1">Enable Stealth Mode.</a></label>
|
||||
<div id="RestartText" tid="t70" class="trans" ></div>
|
||||
<label id="CheckArea"><input id="StealthMode" onClick="SendStealthModeCheck()" type="checkbox"><a class="TextS1" data-i18n="enable_stealth_mode">Enable Stealth Mode.</a></label>
|
||||
<div id="RestartText"></div>
|
||||
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t8" id="PreBtn" onclick="window.history.back()">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t9" id="GotoNetPluginBtn" onclick="GotoNetPluginPage()">Next</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="PreBtn" onclick="window.history.back()" data-i18n="back">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" id="GotoNetPluginBtn" onclick="GotoNetPluginPage()" data-i18n="next">Next</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
<link rel="stylesheet" type="text/css" href="5.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="5.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div id="Title">
|
||||
<div class="trans" tid="t64">Proprietary Plugins</div>
|
||||
<div data-i18n="proprietary_plugins">Proprietary Plugins</div>
|
||||
</div>
|
||||
<div id="Content">
|
||||
|
||||
<div id="FeatureText">
|
||||
<div><a tid="t75" class="trans TextS1">Some printer vendors require proprietary plugins for communication with their printers. Please select the corresponding plugin if you use such printers.</a></div>
|
||||
<div><a tid="t65" class="trans TextS1">Please be aware that these plugins are not developed or maintained by OrcaSlicer. They should be used at your own discretion and risk.</a></div>
|
||||
<div><a class="TextS1" data-i18n="proprietary_plugins_desc">Some printer vendors require proprietary plugins for communication with their printers. Please select the corresponding plugin if you use such printers.</a></div>
|
||||
<div><a class="TextS1" data-i18n="proprietary_plugins_risk">Please be aware that these plugins are not developed or maintained by OrcaSlicer. They should be used at your own discretion and risk.</a></div>
|
||||
|
||||
<!-- <div><span class="TextPoint">●</span> <a tid="t65" class="trans" ></a></div>
|
||||
<div><span class="TextPoint">●</span> <a tid="t66" class="trans" ></a></div>
|
||||
@@ -33,13 +33,13 @@
|
||||
|
||||
|
||||
<!-- ORCA use label tag to allow checkbox to toggle when user clicked to text -->
|
||||
<label id="CheckArea"><input id="InstallCheck" onClick="SendInstallPluginCheck()" type="checkbox"><a tid="t69" class="trans TextS1">Install Bambu Network Plug-in. </a></label>
|
||||
<div id="RestartText" tid="t70" class="trans" ></div>
|
||||
<label id="CheckArea"><input id="InstallCheck" onClick="SendInstallPluginCheck()" type="checkbox"><a class="TextS1" data-i18n="install_bambu_plugin">Install Bambu Network plug-in</a></label>
|
||||
<div id="RestartText"></div>
|
||||
|
||||
</div>
|
||||
<div id="AcceptArea">
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice trans" tid="t8" id="PreBtn" onclick="window.history.back()">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice trans" tid="t25" id="AcceptBtn" onclick="FinishGuide()">Finish</div>
|
||||
<div class="ButtonStyleRegular ButtonTypeChoice" id="PreBtn" onclick="window.history.back()" data-i18n="back">Back</div>
|
||||
<div class="ButtonStyleConfirm ButtonTypeChoice" id="AcceptBtn" onclick="FinishGuide()" data-i18n="finish">Finish</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
*
|
||||
{
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
font-family: "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-sans;
|
||||
}
|
||||
|
||||
html,body
|
||||
{
|
||||
font-size: 14px;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.LinkBtn
|
||||
{
|
||||
color: #1200FF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.LinkBtn:hover
|
||||
{
|
||||
text-decoration:underline;
|
||||
}
|
||||
|
||||
.Btn_Green
|
||||
{
|
||||
padding: 1.5mm 6mm;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
background-color: #009688;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
border: 1px solid #02c7b3;
|
||||
}
|
||||
|
||||
.Btn_Green:hover
|
||||
{
|
||||
background-color:#4db6ac;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#DownBlock
|
||||
{
|
||||
height: 60%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#DownArea
|
||||
{
|
||||
width:80%;
|
||||
height: 50%;
|
||||
line-height: 50px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#DownText
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#RetryBtn
|
||||
{
|
||||
background-image:url("restart.png");
|
||||
background-size: 100% 100%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#DownPercent
|
||||
{
|
||||
height:20px;
|
||||
}
|
||||
|
||||
#PercentBkg
|
||||
{
|
||||
height:20px;
|
||||
background-color:#CFCFCF;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
#PercentTip
|
||||
{
|
||||
float: left;
|
||||
height:20px;
|
||||
width: 0%;
|
||||
background-color:#009688;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
#DownSuccessTip
|
||||
{
|
||||
width:80%;
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#InstallFailedTip
|
||||
{
|
||||
display:none;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
/*------Btn Area------*/
|
||||
|
||||
#BtnBlock
|
||||
{
|
||||
height:40%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#CancelBtn
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#RestartBtn
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#CloseBtn
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
|
||||
|
||||
function OnInit()
|
||||
{
|
||||
//let strInput=JSON.stringify(cData);
|
||||
//HandleStudio(strInput);
|
||||
|
||||
// let tVal={};
|
||||
// tVal['status']=3;
|
||||
// tVal['percent']=30;
|
||||
//
|
||||
// HandStatusPercent(tVal);
|
||||
|
||||
TranslatePage();
|
||||
SendDownloadCmd();
|
||||
}
|
||||
|
||||
function SendDownloadCmd()
|
||||
{
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="Begin_Download_network_plugin";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
|
||||
function HandleStudio( pVal )
|
||||
{
|
||||
let strCmd=pVal['command'];
|
||||
//alert(strCmd);
|
||||
|
||||
if(strCmd=='ShowStatusPercent')
|
||||
{
|
||||
HandStatusPercent(pVal['data']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function HandStatusPercent( pVal )
|
||||
{
|
||||
let nStatus=pVal['status']*1;
|
||||
let nPercent=pVal['percent'];
|
||||
|
||||
if(nStatus==0)
|
||||
{
|
||||
//正常下载
|
||||
$('#DownStepText').attr("tid","t71");
|
||||
$('#RetryBtn').hide();
|
||||
|
||||
$('#PercentTip').css("width",nPercent+'%');
|
||||
}
|
||||
else if(nStatus==1)
|
||||
{
|
||||
//下载失败 或 解压缩补丁包失败
|
||||
$('#DownStepText').attr("tid","t72");
|
||||
$('#PercentTip').css("width",0+'%');
|
||||
$('#RetryBtn').show();
|
||||
}
|
||||
else if(nStatus==2)
|
||||
{
|
||||
//下载完成
|
||||
$('#PercentTip').css("width",100+'%');
|
||||
|
||||
setTimeout("SendInstallPluginCmd()",100);
|
||||
}
|
||||
else if(nStatus==3)
|
||||
{
|
||||
//解压缩补丁包失败
|
||||
$('#DownArea').hide();
|
||||
$("#InstallFailedTip").show();
|
||||
|
||||
$('#CancelBtn').hide();
|
||||
$('#RestartBtn').hide();
|
||||
$('#CloseBtn').show();
|
||||
}
|
||||
else if(nStatus==4)
|
||||
{
|
||||
//安装补丁包完成
|
||||
$('#DownArea').hide();
|
||||
$('#DownSuccessTip').show();
|
||||
|
||||
$('#CancelBtn').hide();
|
||||
$('#CloseBtn').hide();
|
||||
$('#RestartBtn').show();
|
||||
|
||||
//pTimer=setInterval("RunInverse()",1000);
|
||||
}
|
||||
|
||||
TranslatePage();
|
||||
}
|
||||
|
||||
var nCount=3;
|
||||
var pTimer=null;
|
||||
function RunInverse()
|
||||
{
|
||||
$('#CountNumber').text(nCount+'');
|
||||
nCount--;
|
||||
|
||||
if(nCount==-1)
|
||||
{
|
||||
RestartBambuStudio();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function RetryDownload()
|
||||
{
|
||||
$('#DownStepText').attr("tid","t71");
|
||||
$('#PercentTip').css("width",0+'%');
|
||||
SendDownloadCmd();
|
||||
}
|
||||
|
||||
|
||||
function CancelDownload()
|
||||
{
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="netplugin_download_cancel";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
|
||||
}
|
||||
|
||||
function SendInstallPluginCmd()
|
||||
{
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="begin_install_plugin";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
function RestartBambuStudio()
|
||||
{
|
||||
if( pTimer!=null )
|
||||
{
|
||||
clearInterval(pTimer);
|
||||
pTimer=null;
|
||||
}
|
||||
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="restart_studio";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
function CloseDownDialog()
|
||||
{
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="close_download_dialog";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
|
||||
function OpenPluginFolder()
|
||||
{
|
||||
var tSend={};
|
||||
tSend['sequence_id']=Math.round(new Date() / 1000);
|
||||
tSend['command']="open_plugin_folder";
|
||||
|
||||
SendWXMessage( JSON.stringify(tSend) );
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Cache-Control" content="max-age=7200" />
|
||||
<title>引导_P1</title>
|
||||
<link rel="stylesheet" type="text/css" href="../../include/global.css" /> <!-- ORCA One for all-->
|
||||
<link rel="stylesheet" type="text/css" href="6.css" />
|
||||
<script type="text/javascript" src="../../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../js/json2.js"></script>
|
||||
<script type="text/javascript" src="../../data/text.js"></script>
|
||||
<script type="text/javascript" src="../js/globalapi.js"></script>
|
||||
<script type="text/javascript" src="../js/common.js"></script>
|
||||
<script type="text/javascript" src="6.js"></script>
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
|
||||
<div id="DownBlock">
|
||||
<div id="DownArea">
|
||||
<div id="DownText">
|
||||
<div id="DownStepText" class="trans" tid="t71">Downloading</div>
|
||||
<div id="RetryBtn" onClick="RetryDownload()"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div id="PercentTip"></div>
|
||||
<div id="PercentBkg"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="DownSuccessTip" class="trans" tid="t73">Installation successful.</div>
|
||||
|
||||
<div id="InstallFailedTip">
|
||||
<div><a class="trans" tid="t79">Failed to install plug-in. </a><a class="trans" tid="t80">Try the following steps:</a></div>
|
||||
<div><a class="trans" tid="t81">1, Click </a><a class="trans LinkBtn" tid="t77" onClick="OpenPluginFolder()">here</a><a class="trans" tid="t82"> to open the plug-in directory</a> </div>
|
||||
<div class="trans" tid="t83">2, Close all open Orca Slicer</div>
|
||||
<div class="trans" tid="t84">3, Delete all files under the plug-in directory</div>
|
||||
<div class="trans" tid="t85">4, Reopen Orca Slicer and install the plug-in again</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="BtnBlock">
|
||||
<div id="CancelBtn" class="ButtonStyleRegular ButtonTypeChoice trans" tid="t38" onClick="CancelDownload()" >Cancel</div>
|
||||
<div id="RestartBtn" class="ButtonStyleRegular ButtonTypeChoice trans" tid="t25" onClick="RestartBambuStudio()">Finish</div>
|
||||
<div id="CloseBtn" class="ButtonStyleRegular ButtonTypeChoice trans" tid="t86" onClick="CloseDownDialog()" >Close </div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
@@ -15,7 +15,7 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../include/swiper/swiper-bundle.min.css" />
|
||||
<script type="text/javascript" src="../include/swiper/swiper-bundle.min.js"></script>
|
||||
<script type="text/javascript" src="../data/text.js"></script>
|
||||
<script type="text/javascript" src="../include/i18n.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/home.js"></script>
|
||||
</head>
|
||||
@@ -33,7 +33,7 @@
|
||||
<span id="UserName" class="AccountName"></span>
|
||||
<span class="AccountSecondary">Orca Cloud</span>
|
||||
</span>
|
||||
<span class="AccountSignIn trans" tid="t26">Login</span>
|
||||
<span class="AccountSignIn" data-i18n="login">Login</span>
|
||||
<svg class="AccountChevron" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M3 4.75L6 7.75L9 4.75" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
@@ -46,7 +46,7 @@
|
||||
<svg class="SectionChevron" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M4.5 2.5L8 6L4.5 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span class="SectionLabel trans" tid="orca6">Bambu Cloud</span>
|
||||
<span class="SectionLabel">Bambu Cloud</span>
|
||||
<span id="BambuStatusDot"></span>
|
||||
</div>
|
||||
|
||||
@@ -58,16 +58,16 @@
|
||||
</span>
|
||||
<span class="AccountText">
|
||||
<span id="BambuUserName" class="AccountName"></span>
|
||||
<span class="AccountSecondary trans" tid="orca8">Not signed in</span>
|
||||
<span class="AccountSecondary" data-i18n="not_signed_in">Not signed in</span>
|
||||
</span>
|
||||
<span class="AccountSignIn trans" tid="t26">Login</span>
|
||||
<span class="AccountSignIn" data-i18n="login">Login</span>
|
||||
<svg class="AccountChevron" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M3 4.75L6 7.75L9 4.75" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div id="NoPluginTip">
|
||||
<div id="NoPluginText"><a class="RedFont trans" tid="t76">Network plugin not detected. Click </a><a Class="LinkBtn trans" onClick="BeginDownloadNetworkPlugin()" tid="t77">here</a><a class="RedFont trans" tid="t78"> to install it.</a></div>
|
||||
<div id="NoPluginText" class="RedFont"><a class="LinkBtn" onClick="BeginDownloadNetworkPlugin()"></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -79,7 +79,7 @@
|
||||
<div id="AccountMenuHandle" role="presentation"></div>
|
||||
<div id="AccountMenuLogout" role="menuitem" tabindex="0" onClick="OnAccountMenuLogout()">
|
||||
<span id="AccountMenuLogoutIcon"></span>
|
||||
<span class="trans" tid="t50">Log out</span>
|
||||
<span data-i18n="log_out">Log Out</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<div id="BtnArea">
|
||||
<div menu="recent" class="BtnItem BtnItemSelected" onClick="GotoMenu('recent')">
|
||||
<div class="BtnIcon "><img class="LeftIcon" src="img/i2.png" /></div>
|
||||
<div class="trans" tid="t28">recent</div>
|
||||
<div data-i18n="recent">Recent</div>
|
||||
</div>
|
||||
<div class="BtnItem BtnShortcut" onClick="OpenUrlInLocalBrowser('https://cloud.orcaslicer.com')">
|
||||
<div class="BtnIcon "><img class="ShortcutBrandIcon" src="../image/logo.png" /></div>
|
||||
@@ -119,14 +119,14 @@
|
||||
<div class="MenuItem" onClick="OnClickNewProject()">
|
||||
<div class="MenuIcon"><img src="img/i4.png"></div>
|
||||
<div>
|
||||
<div class="trans TextS1" tid="t31">new project</div>
|
||||
<div class="MenuDesc trans" tid="t32">create new project</div>
|
||||
<div class="TextS1" data-i18n="new_project">New Project</div>
|
||||
<div class="MenuDesc" data-i18n="create_new_project">Create new project</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="MenuItem" onClick="OnClickOpenProject()">
|
||||
<div class="MenuIcon"><img src="img/i5.png"></div>
|
||||
<div>
|
||||
<div class="trans TextS1" tid="t33">open project</div>
|
||||
<div class="TextS1" data-i18n="open_project">Open Project</div>
|
||||
<div class="MenuDesc">3mf</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -135,8 +135,8 @@
|
||||
<div id="DebugText"></div>
|
||||
<div id="RecentFileArea">
|
||||
<div id="RecentTitleBlock">
|
||||
<div id="RecentTitle" class="Content-Title trans TextS1" tid="t35">recent open</div>
|
||||
<div id="RecentClearAllBtn" class="ButtonStyleRegular ButtonTypeWindow trans" tid="t12" onClick="OnDeleteAllRecentFiles()">Clear all</div>
|
||||
<div id="RecentTitle" class="Content-Title TextS1" data-i18n="recently_opened">Recently opened</div>
|
||||
<div id="RecentClearAllBtn" class="ButtonStyleRegular ButtonTypeWindow" onClick="OnDeleteAllRecentFiles()" data-i18n="clear_all">Clear all</div>
|
||||
</div>
|
||||
<div id="FileList">
|
||||
<!-- <div class="FileItem" onClick="OnOpenRecentFile('aaaa')" fpath='d:\model\11.3mf'>
|
||||
@@ -219,11 +219,11 @@
|
||||
<div id="recnet_context_menu">
|
||||
<div id="CT_Delete_Bar" class="CT_Item" onClick="OnDeleteRecentFile()">
|
||||
<div class="CT_Icon CT_Delete"></div>
|
||||
<div class="CT_Text trans" tid="t88">clear</div>
|
||||
<div class="CT_Text" data-i18n="remove">Remove</div>
|
||||
</div>
|
||||
<div id="CT_Folder_Bar" class="CT_Item " onClick="OnExploreRecentFile()" >
|
||||
<div class="CT_Icon CT_Explore"></div>
|
||||
<div class="CT_Text trans" tid="t89">open in explorer </div>
|
||||
<div class="CT_Text" data-i18n="open_containing_folder">Open Containing Folder</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ function OnInit()
|
||||
{
|
||||
//-----Official-----
|
||||
TranslatePage();
|
||||
let pluginTip = document.getElementById("NoPluginText");
|
||||
if (pluginTip)
|
||||
AppendTemplate(pluginTip, T("plugin_not_detected", "Bambu Network plug-in not detected. Click <a>here</a> to install it."),
|
||||
pluginTip.querySelector("a"));
|
||||
|
||||
SendMsg_GetLoginInfo();
|
||||
SendMsg_GetBambuLoginInfo();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Localized text for the bundled web pages. The hosting window injects the page's strings, translated
|
||||
// through the .po catalogs, as window.ORCA_UI_STRINGS before any page script runs
|
||||
// (WebViewHostDialog::localized_strings_script). The English in the markup and in the T() calls is the
|
||||
// fallback for a page opened outside the app.
|
||||
|
||||
// The string for `key`, else `fallback`. Each further argument fills the next %s.
|
||||
function T(key, fallback, ...args) {
|
||||
const strings = (typeof ORCA_UI_STRINGS !== "undefined" && ORCA_UI_STRINGS) || {};
|
||||
let text = strings[key] !== undefined ? String(strings[key]) : fallback;
|
||||
// A replacer function, so a "$" in a value (a plugin name, say) is not read as a pattern.
|
||||
args.forEach((arg) => { text = text.replace("%s", () => String(arg)); });
|
||||
return text;
|
||||
}
|
||||
|
||||
// Appends `template` to `node`, putting the nodes that follow in place of its markers, in order, so a
|
||||
// translation can move them within the sentence. %s inserts the node as it is (a search term, say);
|
||||
// <a>label</a> inserts it with the label as its text, so the translation words the link together with
|
||||
// the sentence around it.
|
||||
function AppendTemplate(node, template, ...nodes) {
|
||||
let end = 0;
|
||||
let next = 0;
|
||||
for (const marker of template.matchAll(/%s|<a>(.*?)<\/a>/g)) {
|
||||
if (marker.index > end)
|
||||
node.appendChild(document.createTextNode(template.slice(end, marker.index)));
|
||||
const inserted = nodes[next++];
|
||||
if (inserted) {
|
||||
if (marker[1] !== undefined)
|
||||
inserted.textContent = marker[1];
|
||||
node.appendChild(inserted);
|
||||
}
|
||||
end = marker.index + marker[0].length;
|
||||
}
|
||||
if (end < template.length)
|
||||
node.appendChild(document.createTextNode(template.slice(end)));
|
||||
}
|
||||
|
||||
// Translates the static markup: data-i18n names the key for an element's own text (child elements
|
||||
// such as icons and sort arrows are kept), data-i18n-title, data-i18n-placeholder and
|
||||
// data-i18n-aria-label the key for that attribute. Translating again is harmless, so a page can call
|
||||
// this after adding markup.
|
||||
function TranslatePage() {
|
||||
document.querySelectorAll("[data-i18n]").forEach((el) => {
|
||||
const textNode = Array.from(el.childNodes).find((n) => n.nodeType === Node.TEXT_NODE && n.nodeValue.trim());
|
||||
const text = T(el.dataset.i18n, textNode ? textNode.nodeValue.trim() : "");
|
||||
if (textNode)
|
||||
textNode.nodeValue = text;
|
||||
else
|
||||
el.insertBefore(document.createTextNode(text), el.firstChild);
|
||||
});
|
||||
["title", "placeholder", "aria-label"].forEach((attr) => {
|
||||
document.querySelectorAll(`[data-i18n-${attr}]`).forEach((el) => {
|
||||
el.setAttribute(attr, T(el.getAttribute(`data-i18n-${attr}`), el.getAttribute(attr) || ""));
|
||||
});
|
||||
});
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -221,15 +221,15 @@ function ShowModelInfo( pModel )
|
||||
switch(UploadType)
|
||||
{
|
||||
case 'remix':
|
||||
$('#ModelAuthorType').attr('tid','t93');
|
||||
$('#ModelAuthorType').text(T('remixed_by', 'Remixed by'));
|
||||
break;
|
||||
case 'shared':
|
||||
$('#ModelAuthorType').attr('tid','t94');
|
||||
$('#ModelAuthorType').text(T('shared_by', 'Shared by'));
|
||||
break;
|
||||
case 'origin':
|
||||
case 'profile':
|
||||
default:
|
||||
$('#ModelAuthorType').attr('tid','t92');
|
||||
$('#ModelAuthorType').text(T('created_by', 'Created by'));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>Printer Connection Required</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/home.css" />
|
||||
<link rel="stylesheet" type="text/css" href="css/dark.css" />
|
||||
<script type="text/javascript" src="../data/text.js"></script>
|
||||
<script type="text/javascript" src="../include/i18n.js"></script>
|
||||
<script type="text/javascript" src="../include/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="../homepage/js/json2.js"></script>
|
||||
<script type="text/javascript" src="../homepage/js/globalapi.js"></script>
|
||||
@@ -14,8 +14,8 @@
|
||||
</head>
|
||||
<body onLoad="OnInit()">
|
||||
<div class="container">
|
||||
<h1 class="trans">Printer Connection</h1>
|
||||
<p class="trans">Please set up your printer connection to view the device.</p>
|
||||
<h1 data-i18n="printer_connection">Printer Connection</h1>
|
||||
<p data-i18n="printer_connection_setup">Please set up your printer connection to view the device.</p>
|
||||
<img src="setup_connection.gif" alt="Printer connection setup demonstration" style="max-width: 100%; height: auto; display: block;"/>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
# Lets a pull request's macOS deps build wait for a push build of the base
|
||||
# branch that builds the same deps, from a Linux job. `check` runs after a cache
|
||||
# miss and names a queued or running base build with the same deps/ and .github/
|
||||
# to wait for, unless the pull request changes either. `wait` waits until that
|
||||
# build saves KEY. actions/cache then confirms the entry can be restored.
|
||||
#
|
||||
# Environment: GH_TOKEN, REPO, WORKFLOW_REF and BASE_REF, plus KEY_INPUTS,
|
||||
# WAIT_FOR, WAIT_SINCE, WAIT_MINUTES, KEY and ARCH for `wait`.
|
||||
# Writes key-inputs, wait-for and wait-since to $GITHUB_OUTPUT.
|
||||
set -uo pipefail
|
||||
|
||||
workflow=${WORKFLOW_REF%%@*}
|
||||
workflow=${workflow##*/}
|
||||
|
||||
# Must match the job names in build_all.yml, build_check_cache.yml and build_deps.yml.
|
||||
check_job_name="build_macos_arch (${ARCH:-}) / Check Cache"
|
||||
deps_job_name="build_macos_arch (${ARCH:-}) / Build Deps / Build Deps"
|
||||
|
||||
out() { echo "$1" >> "${GITHUB_OUTPUT:-/dev/stdout}"; }
|
||||
|
||||
# The cache key depends on deps/ and on the workflows under .github/.
|
||||
local_key_inputs() { echo "$(git rev-parse "$1:.github") $(git rev-parse "$1:deps")"; }
|
||||
remote_key_inputs() {
|
||||
gh api "repos/$REPO/git/trees/$1" < /dev/null \
|
||||
--jq '[.tree[] | select(.path == ".github" or .path == "deps") | {key: .path, value: .sha}] | from_entries | "\(.[".github"]) \(.deps)"'
|
||||
}
|
||||
|
||||
# Prints the id of a push build of the base branch that has not finished, is not
|
||||
# one of the ids in $1, and has KEY_INPUTS.
|
||||
matching_base_build() {
|
||||
local status runs id sha
|
||||
runs=$(for status in in_progress queued pending waiting requested; do
|
||||
gh api -X GET "repos/$REPO/actions/workflows/$workflow/runs" \
|
||||
-f branch="$BASE_REF" -f event=push -f status="$status" -f per_page=10 < /dev/null \
|
||||
| jq -r '.workflow_runs[] | "\(.id) \(.head_sha)"'
|
||||
done)
|
||||
while read -r id sha; do
|
||||
[ -n "$id" ] || continue
|
||||
[[ " ${1:-} " == *" $id "* ]] && continue
|
||||
if [ "$(remote_key_inputs "$sha")" = "$KEY_INPUTS" ]; then
|
||||
echo "$id"
|
||||
return
|
||||
fi
|
||||
done <<< "$runs"
|
||||
}
|
||||
|
||||
check() {
|
||||
KEY_INPUTS=$(local_key_inputs HEAD)
|
||||
out "key-inputs=$KEY_INPUTS"
|
||||
if [ "$KEY_INPUTS" != "$(local_key_inputs HEAD^1)" ]; then
|
||||
echo "This pull request changes deps/ or .github/, so it builds its own deps."
|
||||
return
|
||||
fi
|
||||
local id
|
||||
id=$(matching_base_build)
|
||||
if [ -n "$id" ]; then
|
||||
echo "Build $id of $BASE_REF has the same deps/ and .github/. Waiting for its cache."
|
||||
out "wait-for=$id"
|
||||
out "wait-since=$(date +%s)"
|
||||
else
|
||||
echo "No build of $BASE_REF is building these deps."
|
||||
fi
|
||||
}
|
||||
|
||||
key_saved() {
|
||||
gh cache list -R "$REPO" --ref "refs/heads/$BASE_REF" --key "$KEY" --json key < /dev/null \
|
||||
| jq -e --arg key "$KEY" 'any(.[]; .key == $key)' > /dev/null
|
||||
}
|
||||
|
||||
wait_for() {
|
||||
# Counts the limit from the check, including time queued behind other waiting
|
||||
# pull requests.
|
||||
local id=$WAIT_FOR seen=$WAIT_FOR deadline=$((${WAIT_SINCE:-$(date +%s)} + WAIT_MINUTES * 60)) jobs job check warned=
|
||||
while [ "$(date +%s)" -lt "$deadline" ]; do
|
||||
if key_saved; then
|
||||
echo "Found $KEY."
|
||||
return
|
||||
fi
|
||||
jobs=$(gh api -X GET "repos/$REPO/actions/runs/$id/jobs" -f filter=latest -f per_page=100 < /dev/null)
|
||||
job=$(jq -r --arg name "$deps_job_name" 'first(.jobs[] | select(.name == $name) | "\(.status) \(.conclusion)") // ""' <<< "$jobs")
|
||||
case "$job" in
|
||||
"completed success" | "completed skipped")
|
||||
# The new entry can take a moment to be listed, and a skipped job means
|
||||
# the build found the cache, possibly under the default branch.
|
||||
for _ in 1 2 3; do
|
||||
key_saved && { echo "Found $KEY."; return; }
|
||||
sleep 20
|
||||
done
|
||||
echo "Build $id of $BASE_REF finished its deps without saving $KEY."
|
||||
return ;;
|
||||
"completed cancelled") ;;
|
||||
completed*)
|
||||
echo "The deps job of build $id of $BASE_REF did not succeed."
|
||||
return ;;
|
||||
# The deps job exists only once the build's cache check has succeeded.
|
||||
"")
|
||||
case "$(gh api "repos/$REPO/actions/runs/$id" --jq '"\(.status) \(.conclusion)"' < /dev/null)" in
|
||||
"completed cancelled") ;;
|
||||
completed*)
|
||||
check=$(jq -r --arg name "$check_job_name" 'first(.jobs[] | select(.name == $name) | .conclusion) // ""' <<< "$jobs")
|
||||
if [ -z "$warned" ] && { [ -z "$check" ] || [ "$check" = success ]; }; then
|
||||
echo "::warning title=Deps wait::Build $id of $BASE_REF has no job named \"$deps_job_name\". If the build jobs were renamed, update scripts/ci_deps_cache.sh. This does not affect this pull request."
|
||||
warned=1
|
||||
fi ;;
|
||||
*)
|
||||
sleep 120
|
||||
continue ;;
|
||||
esac ;;
|
||||
*)
|
||||
sleep 120
|
||||
continue ;;
|
||||
esac
|
||||
# Follows another unfinished build with the same key inputs, such as the
|
||||
# push that replaced a cancelled one.
|
||||
id=$(matching_base_build "$seen")
|
||||
[ -n "$id" ] || break
|
||||
seen="$seen $id"
|
||||
echo "Following build $id of $BASE_REF."
|
||||
done
|
||||
echo "No build of $BASE_REF saved $KEY."
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
check) check ;;
|
||||
wait) wait_for ;;
|
||||
*) echo "usage: $0 check|wait" >&2; exit 2 ;;
|
||||
esac
|
||||
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Holds a pull request's macOS build until a hosted macOS runner is free for it.
|
||||
|
||||
Runs from a Linux job in build_check_cache.yml, once per macOS arch, in a single
|
||||
repo-wide line (a concurrency group with queue: max), so only the job at the
|
||||
front of the line polls. It lets its arch in when the runners every active Build
|
||||
all run holds or still needs, plus one for this arch, fit in MACOS_RUNNER_LIMIT:
|
||||
|
||||
- A push, nightly or manual run holds RESERVE runners until its macOS work is
|
||||
done. The jobs API lists only the jobs a run has reached, so what it still
|
||||
needs cannot be counted and is reserved instead.
|
||||
- A pull request run holds one runner for each arch let in until that arch's
|
||||
app build is done, and none after. A run holding a runner while it waits for
|
||||
its other arch would deadlock: the other arch can sit in the line behind a
|
||||
pull request that waits for that runner.
|
||||
- A run holds at least the macOS jobs it has queued or running.
|
||||
|
||||
A pull request's universal build and tests skip the line and are not held for
|
||||
in advance. They take minutes, and holding two runners for them through the
|
||||
slower arch's build left runners idle while other builds waited.
|
||||
- In the minutes around the nightly's cron time, RESERVE runners are held for it
|
||||
until its run appears.
|
||||
|
||||
A pull request labelled macos-priority when its run starts waits in a separate
|
||||
line under the same rule, and the normal line counts every priority arch still
|
||||
waiting as holding a runner, so the next free runners go to it.
|
||||
|
||||
Any API error repeated FAILURES_BEFORE_ADMIT times, and the WAIT_MINUTES limit,
|
||||
let the arch in, so a fault here never blocks pull requests.
|
||||
|
||||
Environment: GH_TOKEN, REPO, WORKFLOW_REF, GITHUB_RUN_ID, and optionally PRIORITY,
|
||||
MACOS_RUNNER_LIMIT, WAIT_MINUTES, POLL_SECONDS and GITHUB_API_URL.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
# A push, nightly or manual run builds arm64 and x86_64 at the same time, then
|
||||
# the universal build and the macOS tests at the same time.
|
||||
RESERVE = 2
|
||||
|
||||
# Must match the job names in build_all.yml and build_check_cache.yml.
|
||||
ARCH_PREFIX = "build_macos_arch ("
|
||||
GATE = "Wait for a macOS runner"
|
||||
PRIORITY_GATE = GATE + " (priority)"
|
||||
FINAL_JOBS = {"Build macOS Universal", "macOS arm64"}
|
||||
# Must match the cron in build_all.yml.
|
||||
NIGHTLY_UTC = datetime.time(2, 15)
|
||||
NIGHTLY_REPO = "OrcaSlicer/OrcaSlicer"
|
||||
NIGHTLY_LEAD = datetime.timedelta(minutes=10)
|
||||
NIGHTLY_GRACE = datetime.timedelta(minutes=45)
|
||||
|
||||
ACTIVE = {"queued", "in_progress", "waiting", "pending", "requested"}
|
||||
# Runs created this recently are listed whatever their status.
|
||||
RECENT = datetime.timedelta(hours=24)
|
||||
FAILURES_BEFORE_ADMIT = 3
|
||||
|
||||
|
||||
def is_macos(job):
|
||||
return any(label.startswith("macos-") for label in job.get("labels") or [])
|
||||
|
||||
|
||||
def macos_done(jobs):
|
||||
"""True once the universal build and the macOS tests have finished or been skipped.
|
||||
Both start together when the arch builds finish."""
|
||||
# A skipped caller job is listed under its own name, a started one as "<name> / <job>".
|
||||
final = [job for job in jobs if job["name"].split(" / ")[0] in FINAL_JOBS]
|
||||
return bool(final) and all(job["status"] == "completed" for job in final)
|
||||
|
||||
|
||||
def gates(jobs, names=(GATE, PRIORITY_GATE)):
|
||||
return [job for job in jobs if job["name"].split(" / ")[-1] in names]
|
||||
|
||||
|
||||
def arch_of(job):
|
||||
name = job["name"]
|
||||
return name[len(ARCH_PREFIX):name.find(")")] if name.startswith(ARCH_PREFIX) else None
|
||||
|
||||
|
||||
def admitted_arches(jobs):
|
||||
return {arch_of(job) for job in gates(jobs) if job["conclusion"] == "success"}
|
||||
|
||||
|
||||
def arch_built(jobs, arch):
|
||||
"""True once the arch's app build finished, or one of its jobs failed or was cancelled."""
|
||||
own = [job for job in jobs if arch_of(job) == arch]
|
||||
return any(job["conclusion"] in ("failure", "cancelled") for job in own) or \
|
||||
any(" / Build OrcaSlicer" in job["name"] and job["status"] == "completed" for job in own)
|
||||
|
||||
|
||||
def priority_waiting(jobs):
|
||||
"""How many of the run's arches wait in the priority line."""
|
||||
return sum(1 for job in gates(jobs, (PRIORITY_GATE,)) if job["status"] != "completed")
|
||||
|
||||
|
||||
def run_demand(run, jobs, yield_to_priority=False):
|
||||
"""macOS runners a run holds or still needs. With yield_to_priority, each
|
||||
priority arch still waiting counts as holding one."""
|
||||
# A run with no jobs that is pending waits behind another run of its
|
||||
# concurrency group, which holds the runners for both.
|
||||
if not jobs and run["status"] in ("pending", "waiting"):
|
||||
return 0
|
||||
active = sum(1 for job in jobs if is_macos(job) and job["status"] in ACTIVE)
|
||||
if macos_done(jobs):
|
||||
return active
|
||||
if run["event"] != "pull_request":
|
||||
return max(active, RESERVE)
|
||||
held = sum(1 for arch in admitted_arches(jobs) if not arch_built(jobs, arch))
|
||||
if yield_to_priority:
|
||||
held += priority_waiting(jobs)
|
||||
return max(active, held)
|
||||
|
||||
|
||||
def nightly_window(now):
|
||||
"""The window around today's nightly cron time, as (start, end) in UTC."""
|
||||
cron = datetime.datetime.combine(now.date(), NIGHTLY_UTC, tzinfo=datetime.timezone.utc)
|
||||
return cron - NIGHTLY_LEAD, cron + NIGHTLY_GRACE
|
||||
|
||||
|
||||
def parse_time(value):
|
||||
return datetime.datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
|
||||
|
||||
class Api:
|
||||
def __init__(self, token, url="https://api.github.com"):
|
||||
self.token = token
|
||||
self.url = url.rstrip("/")
|
||||
# The token's hourly limit is shared by every workflow in the repository,
|
||||
# so what the last response says is left shows their use too.
|
||||
self.calls = 0
|
||||
self.remaining = self.limit = "?"
|
||||
|
||||
def get(self, path, **params):
|
||||
query = urllib.parse.urlencode(params)
|
||||
request = urllib.request.Request(f"{self.url}/{path}?{query}", headers={
|
||||
"Accept": "application/vnd.github+json",
|
||||
"Authorization": f"Bearer {self.token}",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
})
|
||||
self.calls += 1
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
self.note_rate_limit(response.headers)
|
||||
return json.load(response)
|
||||
except urllib.error.HTTPError as error:
|
||||
# A rate-limited request fails, and its headers say so.
|
||||
self.note_rate_limit(error.headers)
|
||||
raise
|
||||
|
||||
def note_rate_limit(self, headers):
|
||||
self.remaining = headers.get("X-RateLimit-Remaining", "?")
|
||||
self.limit = headers.get("X-RateLimit-Limit", "?")
|
||||
|
||||
def usage(self, calls_before):
|
||||
return f" API calls this check: {self.calls - calls_before}, rate limit left: {self.remaining} of {self.limit}"
|
||||
|
||||
|
||||
def list_runs(api, repo, workflow, **params):
|
||||
runs, page = [], 1
|
||||
while True:
|
||||
body = api.get(f"repos/{repo}/actions/workflows/{workflow}/runs",
|
||||
per_page=100, page=page, **params)
|
||||
runs += body["workflow_runs"]
|
||||
if len(runs) >= body["total_count"] or not body["workflow_runs"]:
|
||||
return runs
|
||||
page += 1
|
||||
|
||||
|
||||
def latest_run(api, repo, workflow, **params):
|
||||
runs = api.get(f"repos/{repo}/actions/workflows/{workflow}/runs",
|
||||
per_page=1, **params)["workflow_runs"]
|
||||
return runs[0] if runs else None
|
||||
|
||||
|
||||
def measure(api, repo, workflow, run_id, now, priority=False):
|
||||
"""Total macOS runners held or needed, and one line per run that holds any.
|
||||
The priority line does not count the priority runs waiting behind it."""
|
||||
total, lines = 0, []
|
||||
# A run is listed as queued whenever one of its jobs waits for a runner, and
|
||||
# as pending whenever one waits in a concurrency group, so runs in any of these
|
||||
# can hold runners. A run that moves between the lists between the calls is
|
||||
# in neither, as one is whenever the job ahead of this one leaves the line, so
|
||||
# runs created recently are listed by creation time as well, and this run is
|
||||
# read directly.
|
||||
since = (now - RECENT).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
runs = {run["id"]: run for status in ("in_progress", "queued", "pending", "waiting")
|
||||
for run in list_runs(api, repo, workflow, status=status)}
|
||||
runs.update((run["id"], run) for run in list_runs(api, repo, workflow, created=f">={since}")
|
||||
if run["status"] != "completed")
|
||||
runs[run_id] = api.get(f"repos/{repo}/actions/runs/{run_id}")
|
||||
for run in runs.values():
|
||||
jobs = api.get(f"repos/{repo}/actions/runs/{run['id']}/jobs",
|
||||
filter="latest", per_page=100)["jobs"]
|
||||
own = run["id"] == run_id
|
||||
demand = run_demand(run, jobs, yield_to_priority=not priority and not own)
|
||||
if demand:
|
||||
total += demand
|
||||
waiting = ", priority, waiting" if priority_waiting(jobs) and not own else ""
|
||||
lines.append(f" {demand} run {run['id']} ({run['event']}, {run['head_branch']}{waiting})")
|
||||
|
||||
# build_all.yml runs the nightly only in the main repository.
|
||||
start, end = nightly_window(now)
|
||||
if repo == NIGHTLY_REPO and start <= now < end:
|
||||
last = latest_run(api, repo, workflow, event="schedule")
|
||||
if not last or parse_time(last["created_at"]) < start:
|
||||
total += RESERVE
|
||||
lines.append(f" {RESERVE} the nightly, due at {NIGHTLY_UTC:%H:%M} UTC")
|
||||
return total, lines
|
||||
|
||||
|
||||
def wait(measure_now, limit, wait_minutes, poll_seconds,
|
||||
clock=time.monotonic, sleep=time.sleep, log=print):
|
||||
"""Polls until this arch fits. Returns the reason it was let in."""
|
||||
deadline = clock() + wait_minutes * 60
|
||||
failures = 0
|
||||
while True:
|
||||
try:
|
||||
total, lines = measure_now()
|
||||
except (urllib.error.URLError, OSError, ValueError, KeyError, TypeError) as error:
|
||||
failures += 1
|
||||
log(f"::warning title=macOS admission::Could not read the queue ({error}).")
|
||||
if failures >= FAILURES_BEFORE_ADMIT:
|
||||
return "the queue could not be read"
|
||||
else:
|
||||
failures = 0
|
||||
log(f"{total} of {limit} macOS runners held or needed:")
|
||||
for line in lines:
|
||||
log(line)
|
||||
if total + 1 <= limit:
|
||||
return "a runner is free"
|
||||
if clock() + poll_seconds >= deadline:
|
||||
return f"it waited {wait_minutes} minutes"
|
||||
sleep(poll_seconds)
|
||||
|
||||
|
||||
def main():
|
||||
repo = os.environ["REPO"]
|
||||
workflow = os.environ["WORKFLOW_REF"].split("@")[0].rsplit("/", 1)[-1]
|
||||
api = Api(os.environ["GH_TOKEN"], os.environ.get("GITHUB_API_URL", "https://api.github.com"))
|
||||
run_id = int(os.environ["GITHUB_RUN_ID"])
|
||||
limit = int(os.environ.get("MACOS_RUNNER_LIMIT") or 5)
|
||||
|
||||
def measure_now():
|
||||
calls_before = api.calls
|
||||
try:
|
||||
total, lines = measure(api, repo, workflow, run_id, datetime.datetime.now(datetime.timezone.utc),
|
||||
priority=os.environ.get("PRIORITY") == "true")
|
||||
except Exception:
|
||||
print(api.usage(calls_before).strip())
|
||||
raise
|
||||
return total, lines + [api.usage(calls_before)]
|
||||
|
||||
reason = wait(
|
||||
measure_now,
|
||||
limit,
|
||||
wait_minutes=int(os.environ.get("WAIT_MINUTES") or 240),
|
||||
poll_seconds=int(os.environ.get("POLL_SECONDS") or 180),
|
||||
)
|
||||
print(f"Letting this macOS build in: {reason}.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user