Skip to content

feat(macro): add copy action button in macro editor - #386

Open
adaviscourt wants to merge 26 commits into
vial-kb:mainfrom
adaviscourt:macro-action-duplicate
Open

feat(macro): add copy action button in macro editor#386
adaviscourt wants to merge 26 commits into
vial-kb:mainfrom
adaviscourt:macro-action-duplicate

Conversation

@adaviscourt

@adaviscourt adaviscourt commented Apr 11, 2026

Copy link
Copy Markdown

When I am creating a macro that does a sequential down/up to simulate a multi-key press, it would be nice to just copy the down action and switch the input to up.

image image

Code written with assistance from Claude Code (Sonnet 4.6)

adaviscourt and others added 26 commits April 11, 2026 12:07
Replaces the fbs build/runtime layer, which pinned the project to
Python 3.6 / PyQt5 5.9 / PyInstaller 3.4 and forced fork-based deps.

Runtime
- New `src/main/python/app_context.py` provides `VialContext`
  (QApplication setup + resource resolution via `sys._MEIPASS` when
  frozen, source tree otherwise) and `is_frozen()`.
- `main.py` and `macro/macro_recorder_linux.py` drop `fbs_runtime`
  imports.

Dependencies
- PyQt5 5.9.2 -> 5.15.x (drops sip pin, sip6 bundled)
- PyInstaller 3.4 -> 6.x
- simpleeval pinned commit -> PyPI release
- cython-hidapi vial fork -> PyPI hidapi
- Dropped fbs, future, sip, altgraph, macholib, pefile
- Adds `pyproject.toml` with project metadata and build/test extras
- `test-requirements.txt` rewritten to layer on top of requirements

Packaging
- `vial-mac.spec`, `vial-win.spec`, `vial-linux.spec` replace the
  fbs build settings JSON. Bundles resources from
  `src/main/resources/base/` at the archive root.
- `misc/Vial.desktop` replaces fbs-generated desktop file.
- Linux Dockerfile bumped to Ubuntu 22.04 + Python 3.10, swaps
  pkg2appimage chain for appimagetool. Builder script runs
  PyInstaller and assembles AppDir directly.

Verified locally on macOS: `pyinstaller vial-mac.spec` produces a
working `Vial.app` that launches and enumerates a Vial keyboard via
PyPI hidapi. Linux/Windows build chains untested in this commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- mac: drop manual python.pkg install, use setup-python@v5 with
  Python 3.10. Replace `fbs freeze` + `hdiutil` with
  `pyinstaller vial-mac.spec` + `create-dmg`. Bump
  MACOSX_DEPLOYMENT_TARGET to 10.13 (PyInstaller 6 minimum).
  Pin runner to macos-13 for x86_64 build.
- win: bump Python 3.6 -> 3.10, setup-python@v4 -> v5. Replace
  `fbs freeze`/`fbs installer` with `pyinstaller vial-win.spec` +
  direct `makensis` invocation against existing Installer.nsi
  (copies dist\Vial into src\installer\Vial so the NSI's relative
  paths still resolve).
- linux: workflow unchanged; new Dockerfile and builder script
  from previous commit drive the Python/PyInstaller bump.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Removes files no longer referenced after the PyInstaller migration:
- src/build/settings/{base,mac,linux}.json (fbs config)
- util/setup_python36.sh (Python 3.6 source build for old Docker)
- misc/Vial.yml (pkg2appimage recipe; replaced by appimagetool flow)

Updates README with current Python 3.10+ requirement, plain
`python main.py` dev command, and PyInstaller spec invocations.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The button relied on the third-party ShellExecAsUser NSIS plugin DLL,
which fbs used to bundle automatically. Without that plugin the
button silently no-ops. Removing the finish-page launch (and its
LaunchLink function) keeps the installer working without committing
a binary plugin to the repo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- pyproject.toml: requires-python = >=3.13. Add [tool.uv] package =
  false (we run from source, don't install vial-gui as a package).
- .python-version: 3.6.0 -> 3.13.
- uv.lock: new — pins all transitives for reproducible installs.
- README: document `uv sync` / `uv run` as the primary dev path,
  retain the plain-pip path as a fallback.
- CI mac/win jobs: drop setup-python, use astral-sh/setup-uv@v5
  with cache. Replace `pip install` with `uv sync --extra build
  --frozen` and `pyinstaller` with `uv run pyinstaller`.
- Linux Dockerfile: drop apt python3.10 install, install uv via the
  official script. Builder script swaps pip+venv for `uv sync`.

uv resolves and installs in seconds vs. pip's minutes for the
PyQt5 + pyobjc dependency tail; the lockfile makes builds
reproducible across the Linux/mac/Windows CI legs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`multiprocessing.RLock()` in AutorefreshThread caused an infinite
process cascade in PyInstaller-frozen builds on macOS:
multiprocessing lazily spawns a `resource_tracker` subprocess on
first primitive creation, which (under the spawn start method,
without `freeze_support()`) re-execs the frozen binary; that boots a
new QApplication and AutorefreshThread, which creates another
RLock, which spawns another tracker, and so on.

AutorefreshThread is a QThread — single-process — so the lock only
needs to coordinate threads, not processes. Switching to
`threading.RLock` removes the multiprocessing import entirely.

Also call `multiprocessing.freeze_support()` early in the frozen
entry point as defense-in-depth against any future multiprocessing
usage (PyInstaller best practice).

Pre-existing bug exposed by the Python 3.13 / PyInstaller 6 upgrade.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add tool.uv.required-environments so uv lock resolves PyQt5 wheels
  for Windows (pyqt5-qt5 5.15.18 had no win_amd64 wheel).
- Rewrite test.yml for uv toolchain; the prior version called the
  deleted util/setup_python36.sh.
- Add hidraw as a Linux-only test extra (test_gui.py imports it).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The PyPI hidraw 0.1 package has a circular import on Python 3.13,
but the test suite only needs hidraw to exist as a module so it
can monkeypatch hid.enumerate / hid.device. Stub it via a top-level
test conftest instead of installing the broken package.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PyQt5 5.15.18 no longer implicitly converts float to int in QPoint
constructors. bbox[0].x()/.y() return floats (QPointF), so wrap in
int() to match QPoint(int, int) signature.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
macos-13 free-tier runners are slow to assign; arm64 runners are
plentiful. Building both produces separate vial-mac-x86_64.dmg and
vial-mac-arm64.dmg artifacts so PR CI is no longer gated on the
single Intel runner.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Apple Silicon-only artifact. Intel users can run from source
(uv sync && uv run) or build locally; not worth the macos-13
runner queue wait for the shrinking Intel user base.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tag push (v*) triggers build + release. Release job downloads the
build artifacts, renames them to Vial-<tag>.* and attaches them to
a GitHub Release. Targets a 'release' environment so required
reviewers can gate the upload.

Setup step: configure repo Settings -> Environments -> 'release'
with required reviewers = adaviscourt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rename main.yml -> build.yml. build.yml exposes workflow_call so
release.yml can invoke it on tag push. Keeps build logic in one
place while separating CI and release triggers/concerns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
test.yml exposes workflow_call. release.yml now chains
test -> build -> release so a tagged release can't publish
with red tests. CI path (push/PR) still runs test.yml and
build.yml in parallel for fast feedback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a verify-version job that fails the release pipeline if the
pushed tag doesn't match the version in pyproject.toml. Catches
forgotten version bumps before tests/build burn runner minutes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove fbs, modernize Python toolchain (PyInstaller + uv + Python 3.13)
Generate Icon.icns from existing PNG sources and wire it into
vial-mac.spec so the macOS .app and DMG show the Vial icon instead
of the default PyInstaller floppy/Python placeholder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
build(macos): bundle app icon in .app
Limit the push trigger to the main branch so feature branches with an
open PR don't run build/test twice (once for push, once for the
pull_request synchronize event). PRs still get a full run on every
new commit via the pull_request trigger.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tests already mock hidapi via conftest and use no platform-specific
paths, so they run unchanged on mac and Windows runners. Adding both
catches PyQt5 wheel breakage and Python install regressions on the
platforms we ship.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
prepare() patched hidraw directly, which only matches the platform
import on linux (hidproxy aliases hidraw -> hid there). On macOS and
Windows hidproxy imports real hidapi as hid, so the mock_enumerate /
MockDevice patches landed on an unrelated module and device discovery
failed. Going through hidproxy.hid gives a single patch site that
covers every platform.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
RegisterClass uses a fixed class name (VIAL_DEVICE_DETECTION) and the
registration persists for the process lifetime. In tests that build
multiple MainWindow instances, the second registration raises
ERROR_CLASS_ALREADY_EXISTS (1410). Swallow that specific error since
CreateWindowEx only needs the class to exist by name.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant