Commit Graph

29816 Commits

Author SHA1 Message Date
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
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
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
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
b663f851fe Merge branch 'main' into feat/plugin-feature 2026-07-17 11:30:34 +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
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
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
f7145c9b37 Update Maschine G-Code for X2D [Update Bambu Lab X2D 0.4 nozzle.json] (#14778)
Update Maschine G-Code according to latest Bambu Studio Version X2D
filament_change gcode: 2026/07/01
X2D layer_change gcode: 2026/07/01
X2D start gcode: 2026/06/05
X2D timelapse gcode: 2026/06/03

# Description

<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
  > * What issue does this PR address or fix?
  > * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> 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-16 23:43:59 +08:00
SoftFever
a4d69962fc Merge branch 'main' into patch-4 2026-07-16 23:19:58 +08:00
SoftFever
0a0d59b76b Fix plugins dialog hiding behind the main window on macOS 2026-07-16 22:36:31 +08:00
SoftFever
6f30d03e28 fix test errors 2026-07-16 21:40:21 +08:00
SoftFever
f2ccbfc8b5 Open the plugin terminal off the webview callback stack
open_terminal_dialog is reached from the plugins dialog's webview
command, and TerminalDialog hosts a webview of its own — same class as
the plugin-window crash. Defer the window work via CallAfter, guard the
re-front Show() per #13657, and drop the redundant Raise() on creation.
2026-07-16 21:25:30 +08:00
Ian Chua
d9f8460a4e fix: merge PythonFileUtils into PluginFsUtils 2026-07-16 20:40:32 +08:00
Heiko Liebscher
671277b381 Update German (de) translation (#14577) 2026-07-16 09:11:09 -03:00
Mqrius
6d1974a662 Restore flow calibration special order, overridable via fill order (#14786) 2026-07-16 09:10:15 -03:00
SoftFever
b779a7bfed Create plugin windows off the webview callback stack
wx 3.3.2 delivers webview script messages synchronously inside the native
callback on GTK and macOS, so script plugins run with the plugins-dialog
webview's signal/delegate frame on the stack. Creating and presenting the
orca.host.ui window from there crashed on Linux at Raise() --
gtk_window_present while GTK's deferred show was still in flight.

Defer the whole window creation to a CallAfter with a pre-bound registry
handle (post/close stay FIFO-safe, teardown races become a no-op), and
drop Raise() plus the show_modeless_dialog wrapper: Show() already
activates and fronts a new window on every platform.
2026-07-16 20:07:41 +08:00
Ian Chua
a86202b45b fix: change speed dial from popup to dialog for consistent behavior on linux 2026-07-16 19:47:26 +08:00
Ian Chua
f142716e51 fix: change popup to dialog with popup behavior 2026-07-16 18:29:40 +08:00
Ian Chua
74ffc46bbc fix: syntax 2026-07-16 18:22:55 +08:00
Ian Chua
f4414dd72b fix: collapse plugincapabilityid 2026-07-16 18:18:07 +08:00
Andrew
30170839c1 Merge branch 'feat/plugin-feature' into feature/speed-dial 2026-07-16 17:15:24 +08:00
Andrew
1c33983edf Confine plugin lifecycle callbacks to UI thread
Drop the orphaned PluginCallbackList (dead
after the PluginManager migration) and hop
run_on_*_callbacks onto the UI thread via
CallAfter, snapshotting under the mutex on the
worker first. Keeps wx subscribers off the
detached load/unload workers.
2026-07-16 17:08:03 +08:00
Andrew
13575601ff Give speed-dial popup GUI_App ownership 2026-07-16 16:09:30 +08:00
Andrew
13f5c777cc Rename SpeedDialDialog files to SpeedDialPopup 2026-07-16 16:09:30 +08:00
Ian Chua
49396699e7 fix: initialize embedded python from bundled runtime 2026-07-16 15:40:13 +08:00
Andrew
9279f86858 Migrate from PluginLoader to PluginManager
Refactor to streamline plugin management within
ActionRegistry by consolidating interface usage.
2026-07-16 13:32:40 +08:00
Andrew
c816e48b78 refactor: collapse speed-dial action source abstraction
Only one action source ever existed, so the
IActionSource interface and ScriptActionSource
are gone. ActionRegistry now subscribes to the
plugin loader and enumerates actions directly
in init() - no polymorphism for one impl.

Replace the opaque FNV-hash SpeedDialActionId
with a readable composed id of the form
prefix:title:source_key. Split AppAction's
single source field into source_key (stable
identity, e.g. plugin_key) and source_name
(display), so identity and display no longer
share one field.
2026-07-16 13:08:48 +08:00
Ian Chua
b6f98d9592 fix: load default config on initial load 2026-07-16 12:57:59 +08:00
Ian Chua
a15dcdfb3f move configure to plugin preferences in printer settings dialog 2026-07-16 12:16:22 +08:00
Ian Chua
525a16177c fix: bundle python runtime for macos plugin tests 2026-07-16 12:06:45 +08:00
Noisyfox
5e7ad1ad70 Fix OpenGL framebuffer object support on macOS (#14769)
* Fix OpenGL framebuffer object support on macOS
this enables the outline on macOS

* Revert "Fix OpenGL framebuffer object support on macOS"

This reverts commit 9872708954.

* Enable ARB framebuffer if OpenGL >= 3.0 (OrcaSlicer/OrcaSlicer#14770)

* FIX: plater toolbar image update

jira: STUDIO-12457

Change-Id: Ia855b4bd9bec884d52202f5d8f5cfad9efce0f2f
(cherry picked from commit 474691813b497654ef701fe7073992db366e81a9)

* FIX: thumbinal regen

jira: STUDIO-16732

Change-Id: I4cc4e76b36caf1a41793c1f84ee5e88a44ebdd42
(cherry picked from commit 53f33b7db260005e477842d6459b1b28aa8aa0d8)

* FIX: force update thumbinal once dirty

jira: STUDIO-13041

Change-Id: I770743b53c51351cb3b8a7133140974c75c05364
(cherry picked from commit 96198a12712f21d2b4521b6e3d1caff50bed21ff)

* FIX:thumbnail update

jira: STUDIO-13041 / STUDIO-13304

Change-Id: I9dec25f12454fc8485195cf5dc3feb421b6347c4
(cherry picked from commit cf778dc90f805d3f46e28f8f1d1d5814d3fb4d51)

---------

Co-authored-by: jun.zhang <jun.zhang@bambulab.com>
2026-07-16 11:02:55 +08:00
SoftFever
8d8e4c2835 fix profiles errors spotted with new changes 2026-07-16 00:33:14 +08:00
SoftFever
e5106a23b6 feat: AMS filament drying control (#14637)
## What this does
Ports the AMS filament drying control feature from BambuStudio. Most
work was done by Claude Code with deepseek-v4-pro. Thanks Bambu & CC &
DeepSeek :P


Allows users to start, monitor, and stop AMS-based filament drying
directly from the OrcaSlicer UI for N3F (AMS 2 Pro) and N3S (AMS HT) AMS
units.


Mostly from
c8f70c6ca7

Part of #12091

## Screenshots
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/24f579cb-c67c-4d6e-bf77-c31e018f2f70"
/>
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/74f628aa-6f5f-4150-b2e9-082e4ffc3527"
/>
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/d2f26412-a054-4085-9236-5074a030b001"
/>
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/7beef770-8fbc-4375-b244-e0de44b8db2f"
/>

## Changes
- **Data model:** Added drying status enums (`DryStatus`,
`DrySubStatus`, `CannotDryReason`, etc.), `DrySettings` struct,
`DevFilamentDryingPreset` struct to `DevAms`/`DevFilaSystem`
- **Promoted `DevAmsType`** to a global enum (`EXT_SPOOL=0, AMS=1,
AMS_LITE=2, N3F=3, N3S=4`), renamed `DUMMY` → `EXT_SPOOL`
- **JSON parsing:** Extended `DevFilaSystemParser` to parse drying
status fields from printer status messages
- **Commands:** Added `CtrlAmsStartDryingHour()` and
`CtrlAmsStopDrying()` sending `"ams_filament_drying"` JSON via MQTT
- **Backend utility:** New `DevUtilBackend` class with
`GetFilamentDryingPreset()` for reading filament drying config keys
- **UI dialog:** New `AMSDryControl` dialog with three pages
(status/control, guide, progress) matching BambuStudio behavior
- **Integration:** Wired AMS humidity indicator click to open the drying
dialog for N3F/N3S AMS types
- **Assets:** 12 new drying-related images from BambuStudio
- **Firmware parsing:** Added `is_support_remote_dry` flag parsed from
`fun2` bit 5

## Constraints
- N3F/N3S only — standard AMS and AMS Lite continue to use the existing
humidity popup
- Backward compatible — existing `command_ams_drying_stop()` preserved


<!--
> 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-16 00:04:59 +08:00
SoftFever
c7558e978b Merge branch 'main' into dev/ams-heat 2026-07-15 23:33:12 +08:00
SoftFever
a34310cbda Assign setting_ids to the profiles added for slice validation
The eight presets added in 0545750c1b never got ids: the six iQ processes
had none, and K1 SE 0.8 / V-Core 4 0.8 carried ids copied from the presets
they were duplicated from — K1 SE 0.8 still shared K1C 0.8's id. Regenerated
with scripts/assign_vendor_setting_ids.py; none of the eight have shipped in
a release, so no existing id changes meaning.
2026-07-15 23:16:33 +08:00
Ian Chua
8ba478913f fix merge conflicts 2026-07-15 22:09:47 +08:00
Ian Chua
e21123d8e9 fix: trim configs 2026-07-15 21:43:13 +08:00
Ian Chua
d2b5008156 fix: test_slicing_pipeline_hook 2026-07-15 21:39:55 +08:00
Ian Chua
9cdd399fdd fix: test slicing pipeline hook 2026-07-15 21:32:37 +08:00
Ian Chua
cd8c42b125 fix merge conflicts 2026-07-15 21:04:42 +08:00
Ian Chua
62afea225a Fix/impl refactor (#14776)
* fix: impl refactor

* fix: unload/load python module, race conditions, freezes

* remove dead code

* remove extra hook

* remove more dead code

* fix gil run script
2026-07-15 20:12:51 +08:00
SoftFever
0849c44ae0 Merge branch 'main' into dev/ams-heat 2026-07-15 19:45:27 +08:00