Commit Graph

29871 Commits

Author SHA1 Message Date
SoftFever
8d9b08fed9 Merge branch 'main' into feature/bambu_printer_housekeeping 2026-07-18 02:43:38 +08:00
mgoiogana
e86913266b Add Basque (eu) localization (#14782) 2026-07-17 15:27:44 -03:00
Andrij Mizyk
9020b61e9f Update Ukrainian translation (#14819)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-17 15:11:40 -03:00
SoftFever
ddfa961111 Merge branch 'main' into feature/bambu_printer_housekeeping
# Conflicts:
#	tests/slic3rutils/CMakeLists.txt
2026-07-18 02:08:13 +08:00
SoftFever
0b4cb89f7c Remove porting jargon and provenance notes from comments 2026-07-18 01:46:23 +08:00
SoftFever
4683c17f6f Fix device popup, mapping, and status-parse defects
- Allow mapping to EMPTY trays only from the multi-machine send page and keep
  both panels pickable in the LEFT_AND_RIGHT view
- Guard the error-dialog cloud snapshot against stale callbacks and fall back
  to the local illustration on timeout
- Parse the ipcam storage-check ack and axis/chamber pushes defensively
- Strip fan-control telemetry, initialize the upgrade error code, restore the
  .json filter in the model-id scan
- Fix best-position popup tray lookup, gradient placement, and colour-list
  ownership
- Cover switch binding sets and invalid-track transients in DevMapping tests
2026-07-18 01:46:10 +08:00
SoftFever
12720ccbd8 Gate network plugin loading to ABI-compatible series
Trim the version whitelist to the latest series plus the pinned legacy build,
and reject out-of-series configured versions at startup, compatibility check,
and load failure - falling back to the latest installed build or the clean
re-download flow so the config never keeps pointing at an unsupported build.
2026-07-18 01:45:54 +08:00
SoftFever
88fb89b5eb Plugin audit (#14821)
* Block plugins from reading or writing app config and cloud credentials

Add a denied-filename registry to the plugin audit sandbox, seeded with OrcaSlicer's config (.conf/.ini) and the cloud refresh-token file. The deny is checked above the loading-mode read exemption and the allowed roots, so a plugin cannot reach these files even though they sit inside data_dir(), which is itself an allowed root. Case-insensitive prefix matching also covers the .bak/.tmp companions that hold the same data, and os.rename/os.remove are hooked alongside open so the files cannot be deleted or clobbered either.
2026-07-18 01:24:44 +08:00
SoftFever
2717c37f63 feat: Python Plugins (#14530)
# Introducing a Python Plugin System (WIP)

This PR opens up a way to extend OrcaSlicer with **Python plugins** —
small scripts (or full wheels) that run inside an embedded CPython
interpreter, without anyone having to fork the app or touch the C++
core.

I'm putting this up **early and on purpose**. It works end-to-end today,
but it is not finished and the public surface is deliberately small.
Before we lock anything in, we want the community's opinions on the
three decisions that are hard to reverse later: **what API we expose,
which plugin types we invest in, and how the security/audit layer should
behave.** Consider this a request for comments more than a merge
candidate.

## Why

People keep wanting to bolt their own behavior onto the slicer — custom
G-code post-processing, automation, bespoke printer/host integrations,
one-off analysis. Today that means maintaining a patched fork. The goal
here is a *sanctioned* extension path: a stable, documented seam where a
plugin can hook into a specific point in OrcaSlicer's workflow, with a
clear boundary around what plugin code is allowed to do.

## What's in this PR

**An embedded Python runtime.** A single CPython interpreter is started
once (intended to be on the main thread), with proper GIL handoff so
plugin code can run from worker threads. Plugin `stderr` (including
tracebacks from threads a plugin spawns) is persisted to
`data_dir()/log/python_*.log`.

**One API module, `orca`.** This is the surface a plugin sees. It
exposes the plugin base classes, the `@orca.plugin` decorator and
`register_capability()`, a typed `ExecutionResult`, and the
`PluginType`/`PluginResult` enums, along with per-type base classes
under `orca.gcode` / `orca.script` / `orca.printer_agent`. A host
bridge, `orca.host`, provides **read-only** access to the current model
and preset/config values, plus interactive `host.plater()` and `host.ui`
helpers (messages, dialogs, windows, progress). There is deliberately no
*write* access to slicer models or config, and no general GUI/toolkit
access beyond these host helpers. The exact shape of `orca.host` is one
of the things we most want feedback on.

**Three plugin types to start:**
- `post-processing` — runs during G-code export and receives the G-code
path + output context.
- `script` — a manual "Run" action from the Plugins dialog.
- `printer-connection` — a Python "printer agent" that registers into
the network layer on load. This is still WIP, along with a printer agent
workflow that is also WIP.

(The `PluginType` enum reserves several more names — Automation,
Analysis, Importer, Exporter, Visualization — but only the three above
are wired up.)

**Two packaging forms:** a single `.py` file with [PEP
723](https://peps.python.org/pep-0723/) inline metadata, or a `.whl`
wheel (with third-party dependencies installed via a bundled `uv`).

**Discovery, install, and a Plugins dialog** — local side-loading plus a
cloud subscription service, catalog/loader lifecycle, and per-plugin
error reporting in the UI.

**Audit-hook groundwork (PEP 578).** Every C++→Python call opens a
per-call audit context, and a CPython audit hook filters filesystem
access against a write allow-list (`data_dir()`, plus scoped roots like
the current G-code folder). This is *groundwork, not a sandbox* — see
Limitations.

**Docs.** Substantially complete author and contributor guides live
under `docs/plugins/` (development guide, security/audit deep-dive,
architecture overview, worked examples); the *feature* is what's WIP,
not the docs.

## Orca Cloud integration

Plugins are **fully integrated with Orca Cloud**, distributed in a
similar way to preset bundles — so this builds directly on the cloud
foundation rather than bolting on a separate mechanism.

- **Subscribe, don't side-load.** Instead of manually copying files, you
subscribe to a plugin from the cloud and OrcaSlicer pulls it down and
loads it for you — the same one-click experience as preset bundles.
- **Tied to your account, synced across machines.** Subscribed plugins
live under your user (`orca_plugins/_subscribed/<user_id>/`) and follow
you to any machine you're signed in on, exactly like your presets. Sign
out and the cloud plugins are unloaded; sign back in and they're
restored.
- **Stays up to date.** When a new version is published, OrcaSlicer can
fetch and install the update rather than leaving you on a stale copy.
- **Managed from the Plugins dialog.** Browse, install, update, and
unsubscribe live alongside local side-loading — which still works for
development and private plugins.
- **Integrated with presets.** Plugin references travel with a preset
bundle (via the preset's `plugins` fields), so publishing a preset that
uses plugins carries those references through the existing cloud sync.
If a referenced plugin is missing when you install the bundle on another
machine, OrcaSlicer **offers to install** the missing plugins for you (a
one-click prompt), provided those plugins are on the cloud.

## Where we need feedback

Really any form of feedback would be helpful; we'd rather grow this
slowly from real use cases than expose internals we can't keep stable —
which is why the current surface is kept small. The `orca.host` API in
particular is where we'd most value opinions.

## Limitations / known gaps (it's WIP)

- **The audit hook is not a sandbox.** It currently enforces only the
`open` event's writes, and only for string paths (fd/bytes opens are not
checked). `subprocess`, sockets, `ctypes`, `os.open`, and non-`open`
filesystem mutations (`os.remove`/`rename`/`mkdir`) are **not** blocked
yet. An `Enforcing` mode is stubbed but not yet wired, so today all
calls run in the writes-only "loading" mode. More details can be found
[here](https://www.orcaslicer.com/wiki/developer_reference/plugin_development/plugin_audit_hook.html#limitations).
- **The `orca` API is unstable** and will change based on this
discussion. Don't build anything load-bearing on it yet.
- The `requires-python` field is parsed but not enforced.
- Dependency install and some of the Plugins dialog UX are functional
but still rough around the edges.

## Docs
[How to
Use](https://www.orcaslicer.com/wiki/plugins/getting_started.html)
[Developer
Reference](https://www.orcaslicer.com/wiki/developer_reference/plugin_development/plugin_system.html)

## Software Development Kit
Currently, there is a script `generate_orca_python_stubs.py` to generate
the `.pyi` files that can be used for intellisense. We will release the
stub file as an SDK in future releases, but for now, if you intend to
develop plugins, you can generate the stub files locally.

## Notes

This system was developed primarily on Windows and Linux; testing on
macOS has so far been limited. macOS-specific behavior — the bundled
Python/`uv` runtime, path handling, and the audit hook — is the most
likely to need attention, and feedback or testing from macOS users is
especially welcome.

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)

## Orca Cloud to OrcaSlicer Plugins Workflow Overview:


https://github.com/user-attachments/assets/abbd7900-3062-4e33-8f77-5d30d567be1d
2026-07-17 23:39:18 +08:00
SoftFever
3576ed6445 Merge branch 'main' into feat/plugin-feature 2026-07-17 23:38:34 +08:00
SoftFever
ee2e7e7a28 Add actions speed dial to run plugin scripts (#14697)
# Description

Use Space to trigger a new **speed dial**, which allows users to run
**app actions**. The only app actions implemented currently are python
plugin scripts.

## Notes

- Only toggleable in the Prepare (3D) view. Press Space, type to filter,
Enter or double-click to run.
- Focuses on search bar automatically
- Frecency-sorted (run count + recency) with alphabetical fallback (C++
computes)
- Pin actions as favourites and they will show on the top favourites
bar. Done through star icon on each row.
- Script plugins have no icon art yet, so tiles show a collision-aware
monogram: the capability's initial, escalating only when names collide -
prepend the package initial, then add an ordinal - so same-named actions
from different plugins stay distinguishable.
- E.g., capability Bravo from plugin Alpha normally shows just B. If
another action's name also starts with B, they disambiguate by
prepending the package initial (Alpha -> AB). If two still collide on
both initials (both AB), they become AB1 and AB2.
- "Run X?" confirm with a per-plugin "don't ask again" scope, owned
C++-side; suppression persists.
- Persistence (`speed_dial` AppConfig section): `favourite_actions`
(ordered id list) + per-action `stats` + `ask_suppressed`.

- Example of shape in data_dir:
```json
{
    "speed_dial": {
        "ask_suppressed": "[\"9b12aa079924bbc4\"]",
        "favourite_actions": "[\"ccfdf8b9e492b624\",\"9b12aa079924bbc4\",\"b7abfa67626248e4\"]",
        "stats": "{\"31d9d129a616a8b7\":{\"count\":4,\"last\":1783924989},\"53ec17d430634f62\":{\"count\":3,\"last\":1783939329},\"9b12aa079924bbc4\":{\"count\":5,\"last\":1783924981},\"9f2cb0d3ca56a87c\":{\"count\":1,\"last\":1783668370},\"b7abfa67626248e4\":{\"count\":4,\"last\":1783939325},\"f93469da14248128\":{\"count\":3,\"last\":1783939337}}"
	},
}
```

# Screenshots/Recordings/Graphs

<img width="688" height="335" alt="image"
src="https://github.com/user-attachments/assets/683efa3b-9401-4977-a347-d70193188165"
/>

<img width="681" height="172" alt="image"
src="https://github.com/user-attachments/assets/fafb4965-054b-4fd5-ad6a-03145264fbe0"
/>

<img width="683" height="335" alt="image"
src="https://github.com/user-attachments/assets/00d5bd49-95c0-4f2f-966b-6c04c14c3cf3"
/>


## Tests

- **Web layer (green):** node-vm logic test `test-speeddial-logic.js`
covers `filterActions`, `visibleFavourites` (incl. the runnable guard),
`selectedActionId`, `resultCountText`, `actionLabel`, `tileCode`,
`nextSel`, and payload seeding - pure helpers, DOM-free.
- **Backend:** `ActionRegistry` FNV-1a id golden-vector Catch2 test
(`test_speed_dial_action_id`) pins the hash; the registry was verified
by fresh-context review including a threading fix (`run()` operates on a
stack copy so a queued refresh can't reallocate the action vector
mid-run).
- **Manual (all passing):**
- Space opens the dial in Prepare only; no regression to existing
Prepare-tab keys or the Plugins dialog.
- Search auto-focuses; typing filters live; a freshly-run action rises
in the frecency order.
- Up jumps to the favourites bar, Down into the list, Alt+1..9 hits
favourites; Enter and double-click run the highlighted action.
- Star pins/unpins an action; favourites persist across an app restart.
- "Run X?" confirm with per-plugin "don't ask again" is respected on
later runs.
  - The `?` shortcuts dialog shows the Space row.
  - Verified in both light and dark themes.

## Known Issues

When there are no actions, plugins, or scripts, the search bar will show
"Search 0 actions". This is bad UX. One alternative considered was to
show a call to action, for example "Please load plugin scripts so that
they appear here".

However, this is ultimately not implemented, as eventually it is not
expected that actions will be empty. The action registry will not be
expected to be empty because we will include in-app actions such as
opening dialogues or other app actions.

<img width="722" height="117" alt="image"
src="https://github.com/user-attachments/assets/a4b9c0db-b2bf-44bc-9550-398dd8b4c7aa"
/>

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-17 23:36:34 +08:00
SoftFever
ee4fca1493 clean up 2026-07-17 23:04:42 +08:00
SoftFever
2614ef7d63 Defer speed-dial web commands off the webview callback stack 2026-07-17 22:37:56 +08:00
Ian Chua
1c82b88c0a fix: unit test shutting down pythonintepreter 2026-07-17 21:56:04 +08:00
Ian Chua
dda49a2b7b Merge branch 'feat/plugin-feature' into feature/speed-dial 2026-07-17 21:29:25 +08:00
Ian Chua
5e15a67425 fix: deprecate show_dialog api and add an option to create_window params 2026-07-17 21:12:55 +08:00
SoftFever
f0f496e9c3 Merge branch 'feat/plugin-feature' into feature/speed-dial
Adapt the speed dial's ActionRegistry to the collapsed
get_plugin_capability(PluginCapabilityId) overload, and restore the
script success/skipped status message the dialog lost when its
PluginScriptRunner refactor was superseded by ActionRegistry.
2026-07-17 19:32:44 +08:00
SoftFever
f6ff15ed99 Feat/plugin config (#14746)
# Description

This PR introduces persistent, capability-scoped configuration for plugins. Users can configure plugins through the Plugins dialog instead of manually editing the plugin’s Python source.

Configuration exists at two levels:

- **Global** — one configuration per capability, shared by every preset. Edited in the Plugins dialog’s **Config** tab.
- **Per preset** — an optional override stored on a process or printer preset, edited from that preset’s **Plugin Preferences** group. A preset that overrides a capability configures the slices it drives; presets that do not simply use the global configuration.

Plugin authors can use the built-in JSON editor or provide a custom HTML settings interface. Both levels use the same editor and the same stored shape.

## New Plugin Configuration APIs

The following APIs are available to all plugin capability types.

### Python APIs

- `get_config() -> str` — Returns a raw JSON string of the capability’s effective configuration: the active preset’s override if it has one, otherwise the global configuration, otherwise `{}`.
- `save_config(config) -> bool` — Persists JSON-compatible configuration for the capability and returns whether the write succeeded. Always writes the **global** configuration (see [Preset overrides](#preset-overrides)).
- `get_config_version() -> str` — Returns the plugin version that last saved the configuration `get_config()` returned, from that same level, or `""` if it has never been saved.
- `has_config_ui() -> bool` — Override and return `True` to use a custom configuration interface instead of the built-in JSON editor.
- `get_config_ui() -> str` — Returns the HTML used to render the custom configuration interface.
- `get_default_config() -> str` — Returns a raw JSON string of the configuration applied by **Restore defaults** in the Plugins dialog. The default implementation returns `{}`.

### Custom UI JavaScript APIs

Custom configuration interfaces receive a sandboxed `window.orca` bridge:

- `window.orca.getConfig()` — Returns the current capability configuration.
- `window.orca.saveConfig(config)` — Requests that the host persist the supplied configuration.
- `window.orca.onConfig(callback)` — Immediately invokes the callback with the current configuration and invokes it again after successful saves or restores.

`saveConfig()` is asynchronous and does not return a Promise. Custom interfaces should use `onConfig()` to observe the successfully persisted state.

## Plugins Dialog

Every activated capability appears in the Plugins dialog’s **Config** tab, where its global configuration is edited.

The editor shown for a capability is selected as follows:

- If `has_config_ui()` returns `True` and `get_config_ui()` returns valid, non-empty HTML, the dialog renders the custom interface.
- Otherwise, the dialog renders the built-in JSON editor.
- If a custom interface cannot be loaded, the dialog reports the error and falls back to the JSON editor.
- The **Restore defaults** action replaces the stored configuration with the value returned by `get_default_config()`.

Custom interfaces run in a sandboxed iframe and can access configuration only through the provided `window.orca` bridge.

## Preset overrides

Process and printer presets gain a **Plugin Preferences → Capabilities** setting (Advanced mode). Its **Configure** button opens a dialog listing the capabilities that preset actually uses — the ones its `plugins` manifest declares *and* one of its plugin-backed options points at — and edits each one’s configuration for that preset alone. The button shows the number of overrides the preset carries.

That dialog offers two actions:

- **Save** — stores the edited configuration as this preset’s override.
- **Restore defaults** — discards the preset’s override, so the capability falls back to the global configuration. A preset holding no override *is* a preset at its defaults.

### How a running capability reads its configuration

`get_config()` resolves in this order:

1. The active preset’s override for this capability, if it has one.
2. The global configuration in `config.json`.
3. `{}`.

Which preset is consulted follows from the capability’s type. A plugin-backed option declares the capability type it accepts (`ConfigOptionDef::plugin_type`) and belongs to exactly one preset type, so `slicing-pipeline` capabilities are configured by the process preset and `printer-connection` capabilities by the printer preset. Nothing is hardcoded: declaring `plugin_type` on a new option is all it takes to place a new capability type on that map.

`get_config_version()` reports the version stamp from whichever level supplied the configuration, so a plugin migrating a stale config is never handed one level’s data with another level’s version.

`save_config()` from Python always writes the global configuration, never a preset — presets are the user’s to edit, and a plugin saving from a worker thread cannot mark one dirty. A capability whose active preset overrides it will therefore keep reading that override back rather than what it saved.

### Storage

A preset’s overrides live in an ordinary string setting on the preset (`plugin_preference_overrides`), holding a JSON array of entries keyed by plugin and capability. Because it is an ordinary setting, the whole preset lifecycle carries it for free: the dirty marker, the revert arrow, inheritance, project (3MF) round-tripping, and preset sync all behave exactly as they do for every other setting. The dialog is a pure editor over that text — it never writes to the preset itself and never writes to the global config file.

## Configuration Storage

All global plugin configuration is stored in a shared file:

`data_dir()/orca_plugins/config.json`

Configuration entries are isolated by plugin and capability. The host also records the plugin version that last wrote each entry.

The configuration file is intentionally stored outside individual plugin directories. This allows settings to survive:

- Plugin upgrades and reloads
- Local plugin deletion and reinstallation
- Cloud plugin unsubscribe and resubscribe operations

Reinstalling or resubscribing to the same plugin restores access to its previously saved configuration.

## Known limitations

**Filament capabilities cannot be overridden per preset.** There is no single active filament preset — one is selected per extruder — and `get_config()` does not say which extruder the capability is running for, so a filament override could only be applied by guessing. Rather than hand a plugin another extruder's settings, filament capabilities read the global configuration.

Nothing reaches this today: no filament option declares a `plugin_type`, so no capability type maps to the filament preset. Lifting it means pushing the extruder onto the plugin call context the Python trampoline already maintains and resolving the preset from that, with the extruder optional — whole slicing steps (`posSlice`, `psGCodePostProcess`) span every extruder and have no current filament.

# Tests

`tests/slic3rutils` covers the capability config store, the Python config API, the preset override layer, the capability-type → preset-type mapping, and which capabilities a preset counts as in use.

# Screenshots/Recordings/Graphs

Custom UI
<img width="855" height="703" alt="image" src="https://github.com/user-attachments/assets/745ecb7d-9e20-4c39-b857-5aa730a27142" />

Default JSON text editor
<img width="855" height="703" alt="image" src="https://github.com/user-attachments/assets/18b7b89c-6f77-4960-a9b2-964e71f74fc3" />

Process Sidebar
<img width="717" height="360" alt="image" src="https://github.com/user-attachments/assets/8fac3e66-c06a-44e4-ad4b-4cc6c003bb2b" />

Filament dialog
<img width="1090" height="832" alt="image" src="https://github.com/user-attachments/assets/ff6a4cbe-11c0-4d04-9ecb-9a717bdeb3f4" />

Printer settings dialog
<img width="1090" height="832" alt="image" src="https://github.com/user-attachments/assets/c616afb0-4eb2-40c2-92c0-7f5edc50b4e6" />

Dialog opened from preset settings
<img width="860" height="725" alt="image" src="https://github.com/user-attachments/assets/069408a8-e94b-47e0-8e16-a81e0d58b4d4" />

# Example plugin with custom UI used in screenshot
[custom_ui_screenshot_demo.py](https://github.com/user-attachments/files/29995212/custom_ui_screenshot_demo.py)

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-17 18:45:51 +08:00
SoftFever
a152c7bcad Name the plugin capability config option consistently
The preset option was plugin_preference_overrides while the GUI field type
that renders it was GUIType::plugin_config, for one and the same thing.
Settle on "config": the store, the dialog and the Python hooks all say config
already, so renaming that way touches 4 files instead of the whole plugin
subsystem and the public plugin API.

Keep the _overrides suffix — the option is the preset's override layer over
the base PluginConfig store, a distinction EffectiveCapabilityConfig tracks.

Also wrap the printer tab's group heading in L(); it was the only one of the
three missing it, and was therefore untranslatable.
2026-07-17 18:34:15 +08:00
SoftFever
6767ab5ae0 Make the Latest and installed version labels dynamic 2026-07-17 16:25:10 +08:00
SoftFever
9d9bb9cdbd Install the OTA plugin update immediately and hot-reload it 2026-07-17 16:25:10 +08:00
SoftFever
cc471b95b6 Skip arm64 Linux and Windows builds on self-hosted runners (#14811)
There is no arm64 self-hosted build server, so when \`vars.SELF_HOSTED\`
is set the arm64 Linux and Windows legs previously fell back to
GitHub-hosted runners. Drop those legs entirely instead, along with the
unit test jobs that consume their artifacts.

**Changes:**

- **Linux / Windows builds:** matrices switch from a static \`include:\`
list to \`fromJSON(vars.SELF_HOSTED && ... || ...)\`, so self-hosted
runs build x86_64/x64 only. The Windows job's per-arch runner
conditional is gone — the runner is now baked into each matrix entry.
- **Unit tests:** \`unit_tests_linux_aarch64\` and
\`unit_tests_windows_arm64\` are gated on \`!vars.SELF_HOSTED\`; their
\`needs\` still succeed, so \`success()\` alone would not skip them.
- **Slice check:** the profile validator artifact is now named per-arch
and uploaded from the aarch64 leg normally, or x86_64 on self-hosted.
The job's runner and download name follow the same switch, keeping the
gate alive rather than failing on a missing artifact.
- **Comments:** trimmed across the touched blocks.

No change when \`SELF_HOSTED\` is unset — an unset variable is falsy, so
GitHub-hosted runs keep both arches, the same runners, and the aarch64
slice check.
nightly-builds
2026-07-17 16:09:40 +08:00
SoftFever
c2f88b17be Complete the plugin settings removal after the feat/plugin-feature merge
The merge kept this branch's PluginConfig design, which deletes
PluginDescriptor::settings, get_plugin_settings() and ctx.params, but left
references to them behind: the slic3rutils target did not build, and the
bindings test still asserted the removed ctx.params attribute.

Port the two settings tests onto PluginConfig instead of dropping them. They
guard a field bug where a cloud-metadata refresh wiped a plugin's settings and
it silently ran on its own defaults, so the equivalent properties are still
worth pinning: that a stored config survives the refresh, and that an edited
config reaches the plugin through a real dispatch.

Also defer PluginsConfigDialog's web commands off the webview script-message
callback, as PluginsDialog already does. Its remove_preset_override handler put
a modal wxMessageBox on that stack, which is the GTK crash class fixed in
b779a7bfed/f2ccbfc8b5 for the sibling dialog.
2026-07-17 16:09:19 +08:00
SoftFever
79593e07c6 List same-series OTA plugin builds in the version selector 2026-07-17 14:23:40 +08:00
SoftFever
5808ae5ab0 Fix the network plugin OTA update flow 2026-07-17 14:23:39 +08:00
Ian Chua
4c60781cf4 fix: merge branch feat/plugin-feature 2026-07-17 14:02:10 +08:00
Andrew
b4f6573753 Merge branch 'feat/plugin-feature' into feature/speed-dial
# Conflicts:
#	src/slic3r/GUI/PluginsDialog.hpp
2026-07-17 13:59:56 +08:00
Ian Chua
96182e778a fix: merge pythonjsonutils into pluginfsutils 2026-07-17 13:35:51 +08:00
SoftFever
1b722de6e7 Fix latent popup defects and mark the unwired device modules 2026-07-17 13:20:41 +08:00
SoftFever
38ab986c5f Drop the transitional device-layer shims 2026-07-17 12:46:45 +08:00
SoftFever
895a5c8d6b Skip arm64 Linux and Windows builds on self-hosted runners 2026-07-17 12:21:29 +08:00
SoftFever
37e27a24c4 Fix the pre-send advisories and port the remaining send-flow checks 2026-07-17 12:10:45 +08:00
SoftFever
b663f851fe Merge branch 'main' into feat/plugin-feature 2026-07-17 11:30:34 +08:00
SoftFever
7f8baf46d3 Port the pre-send checks and wire the AMS best-position popup 2026-07-17 11:10:21 +08:00
SoftFever
d097fbec9a Resync the device send flow 2026-07-17 10:20:24 +08:00
SoftFever
2ee0bbde54 Port the filament-change stop button, hub version row and HMS fallback 2026-07-17 09:41:01 +08:00
Rodrigo Faselli
33909a51cd Fix external outline Thickness (#14741)
* Fix external outline

* Format and values

* frag_color -> gl_FragColor

* Remove Transform3d& view_matrix

* Clarify rendering comments in 3DScene.cpp

Updated comments to clarify rendering process using stencil buffer.

---------

Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
2026-07-17 09:04:36 +08:00
SoftFever
80d3c3908d Resync the device status pages 2026-07-17 08:30:22 +08:00
SoftFever
5e95aca42d Restore the popup accent colors and improved strings 2026-07-17 07:25:02 +08:00
SoftFever
40a875800d Resync the AMS and device dialogs 2026-07-17 06:48:15 +08:00
SoftFever
50abe02f18 Make AMS trays read-only in laser mode and add the gradient fill helper 2026-07-17 05:06:09 +08:00
Alexandre Folle de Menezes
979e56f1d0 Standardize the Unicode chars for unit strings (#14631)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-16 17:37:09 -03:00
SoftFever
7265a2fbfe Resync the AMS control and item widgets 2026-07-17 04:30:45 +08:00
SoftFever
db51d8ab9b Mark the accent-color divergence in the best-position popup 2026-07-17 03:50:29 +08:00
SoftFever
d4e95e3269 Resync the device tab widgets and add the AMS best-position popup 2026-07-17 03:37:18 +08:00
SoftFever
0feca8d442 Port accessory firmware versions, cold-pull state and filament checks 2026-07-17 02:49:45 +08:00
SoftFever
1c9fda463b Fix plugin settings lost on cloud metadata refresh
Cloud catalog records never carry [tool.orcaslicer.plugin.settings], so the
metadata merge wiped the locally-parsed settings and plugins silently ran on
their built-in defaults (ctx.params arrived empty).
2026-07-17 02:09:34 +08:00
SoftFever
8d211ce878 Move axis, calib, chamber, status and upgrade handling into DeviceCore 2026-07-17 02:04:25 +08:00
SoftFever
8a6609f282 Restore the extruder name fallback and fix the loading-busy check 2026-07-17 01:16:47 +08:00
SoftFever
4bca7b3008 Resync the DeviceCore state models 2026-07-17 00:49:04 +08:00