Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for URL protocol and file type association (all OS) #119

Merged
merged 84 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
bd284eb
Add first-draft swift/appkit launcher for macOS
aganders3 Feb 24, 2023
181476c
Install nested bundle on macOS
aganders3 Feb 24, 2023
87c92d8
Make outer macOS bundle background-only, preventing dock icon quirks
aganders3 Feb 24, 2023
af13f67
Copy environment when launching mainApp
aganders3 Feb 25, 2023
ca9785c
Apply swift-format
aganders3 Feb 28, 2023
f7b8329
Merge branch 'cep-devel' into swift-appkit-launcher
jaimergp Mar 3, 2023
97072ea
fix docs
jaimergp Mar 3, 2023
e331ed2
remove debug line
jaimergp Mar 3, 2023
37a313b
rename
jaimergp Mar 3, 2023
9c3675d
add tests (wip)
jaimergp Mar 3, 2023
876a94c
add tests for url_protocols; wip for extensions
jaimergp Mar 20, 2023
e352220
support custom mime types in linux too
jaimergp Mar 20, 2023
65f2160
update docs a bit
jaimergp Mar 20, 2023
78878fa
pre-commit
jaimergp Mar 20, 2023
1b39dc6
pre-commit all
jaimergp Mar 20, 2023
6465462
Merge branch 'cep-devel' of github.com:conda/menuinst into swift-appk…
jaimergp Mar 20, 2023
d5dad02
pre-commit again
jaimergp Mar 20, 2023
d80215f
amend docs about CFBundleTypeRole
jaimergp Mar 20, 2023
202f47b
amend schema
jaimergp Mar 21, 2023
0c2d6d7
pre-commit
jaimergp Mar 21, 2023
225a8de
lsregister for file types AND url protocols
jaimergp Mar 21, 2023
a0a5c8a
pass -a for now
jaimergp Mar 21, 2023
0378f7e
clean lsregister a bit
jaimergp Mar 21, 2023
3f328a4
debug a bit?
jaimergp Mar 21, 2023
198deb6
parent -> menu
jaimergp Apr 4, 2023
5a18712
change assertion
jaimergp May 22, 2023
18be68b
Merge branch 'cep-devel' of github.com:conda/menuinst into swift-appk…
jaimergp May 22, 2023
ccab728
pre-commit
jaimergp May 22, 2023
5da6845
fix commands in linux
jaimergp May 22, 2023
11a899d
always log
jaimergp May 22, 2023
ee0eb9b
add missing import
jaimergp May 22, 2023
76abd3b
configure logging
jaimergp May 22, 2023
2a5ddeb
do not log these two calls
jaimergp May 22, 2023
bf1aaac
do not log
jaimergp May 22, 2023
7cdbf95
make it str
jaimergp May 22, 2023
a72132b
de-duplicate code
jaimergp May 22, 2023
0471cf4
debug file txt
jaimergp May 22, 2023
28ec15c
ensure TMPDIR is the destination
jaimergp May 22, 2023
fb89efc
start must be called with CMD
jaimergp May 22, 2023
07d369a
fix windows
jaimergp May 22, 2023
564a64f
raw strings here too
jaimergp May 22, 2023
bcf3d35
make CMD flash faster, escape strings ourselves
jaimergp May 22, 2023
2c5ab80
pre-commit
jaimergp May 22, 2023
676920e
fix linux?
jaimergp May 23, 2023
d4323c1
pin typing_extensions (temporarily)
jaimergp May 23, 2023
ae715d6
only run update-*-database commands if available
jaimergp May 23, 2023
cecefa9
add system deps on linux
jaimergp May 23, 2023
5b22b88
do not patch on ci
jaimergp May 23, 2023
350ab6a
use libglib's gio too
jaimergp May 23, 2023
9681dd5
debug xdg
jaimergp May 23, 2023
01f060b
print mimeapps.list
jaimergp May 23, 2023
5e3d0f6
add desktop-file-utils
jaimergp May 23, 2023
3290bba
mock gnome as DE?
jaimergp May 23, 2023
6e0b8f4
always remove after
jaimergp May 23, 2023
b739100
debug urls
jaimergp May 23, 2023
3e8c28c
fix macos?
jaimergp May 23, 2023
589e729
add tmate shell (tmp)
jaimergp May 23, 2023
052fb50
only delete outside CI
jaimergp May 23, 2023
ce71bc3
leave files
jaimergp May 23, 2023
281163e
change port for one example
jaimergp May 23, 2023
a136257
do delete files
jaimergp May 23, 2023
0c85950
increase timeouts
jaimergp May 23, 2023
147216b
comment tmate out
jaimergp May 23, 2023
fb1c670
pre-commit
jaimergp May 23, 2023
15936d3
Update test output polling logic
aganders3 May 24, 2023
5f10851
Add newline in URL handler
aganders3 May 24, 2023
7f27f02
use localhost implicitly
jaimergp May 31, 2023
0ff8424
unbuffered python
jaimergp May 31, 2023
b10a301
only one version for now
jaimergp May 31, 2023
ae91706
only clean if remove_after is true
jaimergp May 31, 2023
d83afb1
fix spacing
jaimergp May 31, 2023
852131f
do not delete files for now
jaimergp May 31, 2023
49898e6
add some stderr logging too
jaimergp May 31, 2023
7c45da6
pre-commit
jaimergp May 31, 2023
1a0808e
enable debugging on macos always
jaimergp May 31, 2023
1b603ce
remove fputs again
jaimergp May 31, 2023
3a84987
revert some temporary changes
jaimergp May 31, 2023
d300789
remove some old workarounds
jaimergp Jun 12, 2023
a5f98b9
rename url_handler to event_handler because it's used for file types too
jaimergp Jun 13, 2023
f631bca
remove unnecessary check
jaimergp Jun 13, 2023
ffb8027
add news
jaimergp Jun 13, 2023
ba89871
noqa this
jaimergp Jun 13, 2023
d89914e
provide workaround for <10.15
jaimergp Jun 14, 2023
b5cbe10
declare compat with 10.6+
jaimergp Jun 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ on:
- main
- cep-devel
paths-ignore:
- 'docs/**'
- "docs/**"
pull_request:
branches:
- main
- cep-devel
paths-ignore:
- 'docs/**'
- "docs/**"

concurrency:
# Concurrency group that uses the workflow name and PR number if available
Expand All @@ -27,20 +27,28 @@ jobs:
run_test_suite:
name: ${{ matrix.os }}-py${{ matrix.python-version }}
runs-on: ${{ matrix.os }}-latest
timeout-minutes: 30
timeout-minutes: 60
env:
CONDA_NUMBER_CHANNEL_NOTICES: 0
PYTHONUNBUFFERED: 1
strategy:
fail-fast: false
matrix:
os: [windows, ubuntu, macos]
python-version: ["3.8", "3.9", "3.10",] # "3.11"]
python-version: ["3.8", "3.9", "3.10"] # "3.11"]

steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Add Linux dependencies
if: matrix.os == 'ubuntu'
run: |
sudo apt-get install libfile-mimeinfo-perl desktop-file-utils
echo "XDG_UTILS_DEBUG_LEVEL=2" >> $GITHUB_ENV
echo "XDG_CURRENT_DESKTOP=GNOME" >> $GITHUB_ENV

- name: Install dependencies
uses: mamba-org/provision-with-micromamba@main
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ cython_debug/

# MacOS
.DS_Store
.build

# setuptools-scm
menuinst/_version.py
215 changes: 123 additions & 92 deletions docs/source/defining-shortcuts.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Define your own shortcuts

`menuinst` takes JSON configuration files as its input.
The minimal structure is a dictionary with four keys
`menuinst` takes JSON configuration files as its input. The minimal structure is a dictionary with
four keys

- `$schema`: The JSON-schema standard version
- `$id`: The version of the menuinst configuration, as a JSON schema URL
- `menu_name`: The name for this group of menu items
- `menu_items`: A list of dictionaries, each defining the settings for one shortcut / menu item. Each menu item must define, at least, the following keys (see {class}`MenuItem schema <menuinst._schema.MenuItem>` for more details):
- `menu_items`: A list of dictionaries, each defining the settings for one shortcut / menu item.
Each menu item must define, at least, the following keys (see {class}`MenuItem schema <menuinst._schema.MenuItem>`
for more details):
- `name`: The name for this specific shortcut.
- `command`: A list of strings detailing how to launch the application.
- `platforms`: A dictionary with up to three keys. All of them are optional but you must at least define one. The presence of a key with a non-`null` value enables the shortcut for that platform. If you don't include any, shortcuts will not be created. Available keys are:
- `platforms`: A dictionary with up to three keys. All of them are optional but you must at
least define one. The presence of a key with a non-`null` value enables the shortcut for that
platform. If you don't include any, shortcuts will not be created. Available keys are:
- `linux` - see {class}`Linux schema <menuinst._schema.Linux>`
- `osx`- see {class}`MacOS schema <menuinst._schema.MacOS>`
- `win`- see {class}`Windows schema <menuinst._schema.Windows>`
Expand All @@ -25,20 +29,20 @@ A minimal example to launch Python's `turtle` module would be:

```json
{
"$schema": "https://json-schema.org/draft-07/schema",
"$id": "https://schemas.conda.io/menuinst-1.schema.json",
"menu_name": "Python {{ PY_VER }}",
"menu_items": [
{
"name": "Launch Turtle",
"command": ["python", "-m", "turtle"],
"platforms": {
"linux": {},
"osx": {},
"win": {},
}
}
]
"$schema": "https://json-schema.org/draft-07/schema",
"$id": "https://schemas.conda.io/menuinst-1.schema.json",
"menu_name": "Python {{ PY_VER }}",
"menu_items": [
{
"name": "Launch Turtle",
"command": ["python", "-m", "turtle"],
"platforms": {
"linux": {},
"osx": {},
"win": {}
}
}
]
}
```

Expand All @@ -48,8 +52,8 @@ Note how the `menu_name` is using a placeholder `{{ PY_VER }}`.
The full list of available placeholders is available at {ref}`placeholders`.
```

This is not using any customization options or advanced features.
It's the bare minimum to make it work: a name, the command, and the target platforms.
This is not using any customization options or advanced features. It's the bare minimum to make it
work: a name, the command, and the target platforms.

## Associate your shortcut with file types and URL protocols

Expand All @@ -58,98 +62,125 @@ It's the bare minimum to make it work: a name, the command, and the target platf
Each operating system has a slightly different way of associating a file type to a given shortcut.
Unix systems have the notion of MIME types, while Windows relies more on file name extensions.

* On Linux, use the `MimeType` option.
Remember to add the `%f` (single file) or `%F` (several files) placeholders to your command
so the URLs are passed adequately.
* On macOS, use `CFBundleDocumentTypes`. Requires no placeholder. The opened document will be automatically passed as an argument.
* On Windows, use `file_extensions`. Remember to add the `%1` or `%*` placeholders to your command
- On Linux, use the `MimeType` option. Remember to add the `%f` (single file) or `%F` (several
files) placeholders to your command so the paths are passed adequately. If you are defining a new
MIME type, you must fill the `glob_patterns` field by mapping the new MIME type to the file
extensions you want to associate with it.
- On macOS, use `CFBundleDocumentTypes`. Requires no placeholder. The opened document will be
automatically passed as a regular command-line argument. The file will be dispatched via events.
You need to define the `event_handler` field to define a logic that will forward the caught files
to your application (via sockets, API calls, inotify or any other inter-process communication
mechanism) The association happens via UTI strings (Uniform Type Identifiers). If you need UTIs
not defined by Apple, use the `UTImportedTypeDeclarations` field if they are provided by other
apps, or `UTExportedTypeDeclarations` if you are defining them yourself.
- On Windows, use `file_extensions`. Remember to add the `%1` or `%*` placeholders to your command
so the path of the opened file(s) is passed adequately.


A multi-platform example:

```json
{
"$schema": "https://json-schema.org/draft-07/schema",
"$id": "https://schemas.conda.io/menuinst-1.schema.json",
"menu_name": "File type handler example",
"menu_items": [
{
"name": "My CSV Reader",
"activate": true,
"command": ["{{ PREFIX }}/bin/my_csv_reader.py"],
"icon": "{{ MENU_DIR }}/my_csv_reader.{{ ICON_EXT }}",
"platforms": {
"linux": {
"command": ["{{ PREFIX }}/bin/my_csv_reader.py", "%f"],
"MimeType": ["text/csv"]
},
"macos": {
"CFBundleDocumentTypes": [
{
"CFBundleTypeIconFile": "{{ MENU_DIR }}/my_csv_reader",
"CFBundleTypeName": "my-csv-reader.csv",
"CFBundleTypeRole": "Viewer",
"LSItemContentTypes": ["public.comma-separated-values-text"],
"LSHandlerRank": "Default"
}
]
},
"windows": {
"command": ["{{ SCRIPTS_DIR }}/my_csv_reader.py", "%1"],
"file_extensions": [".csv"]
"$schema": "https://json-schema.org/draft-07/schema",
"$id": "https://schemas.conda.io/menuinst-1.schema.json",
"menu_name": "File type handler example",
"menu_items": [
{
"name": "My CSV Reader",
"activate": true,
"command": ["{{ PREFIX }}/bin/open_menuinst_files.py"],
"icon": "{{ MENU_DIR }}/open_menuinst_files.{{ ICON_EXT }}",
"platforms": {
"linux": {
"command": ["{{ PREFIX }}/bin/open_menuinst_files.py", "%f"],
"MimeType": ["application/x-menuinst"],
"glob_patterns": {
"application/x-menuinst": ["*.menuinst"]
}
},
"osx": {
"CFBundleDocumentTypes": [
{
"CFBundleTypeName": "org.conda.menuinst.opener",
"CFBundleTypeRole": "Viewer",
"LSItemContentTypes": ["org.conda.menuinst.main-file-uti"],
"LSHandlerRank": "Default"
}
],
"UTExportedTypeDeclarations": [
{
"UTTypeConformsTo": ["public.data", "public.content"],
"UTTypeIdentifier": "org.conda.menuinst.main-file-uti",
"UTTypeTagSpecification": [
{
"public.filename-extension": ["menuinst"]
}
]
}
]
},
"windows": {
"command": ["{{ SCRIPTS_DIR }}/open_menuinst_files.py", "%1"],
"file_extensions": [".csv"]
}
]
}
}
]
}
```

### URL protocols

Each operating system has a slightly different way of associating a URL protocol to a given shortcut.
Each operating system has a slightly different way of associating a URL protocol to a given
shortcut.

* On Linux, you must use the `MimeType` option too.
Use the `x-scheme-handler/your-protocol-here` syntax.
Remember to add the `%u` (single URL) or `%U` (several URLs) placeholders to your command
so the URLs are passed adequately.
* On macOS, use `CFBundleURLTypes`. Requires no placeholder. The URL will be dispatched via events. Right now, the .app launcher/forwarder doesn't know about the Apple events involved in this, so **it will not work**. WIP.
* On Windows, use `url_protocols`. Remember to add the `%1` or `%*` placeholders to your command
- On Linux, you must use the `MimeType` option too. Use the `x-scheme-handler/your-protocol-here`
syntax. Remember to add the `%u` (single URL) or `%U` (several URLs) placeholders to your command
so the URLs are passed adequately.

- On Windows, use `url_protocols`. Remember to add the `%1` or `%*` placeholders to your command so
the URLs are passed adequately.
- On macOS, use `CFBundleURLTypes`. Requires no placeholders. The URL will be dispatched via
events. You need to define the `event_handler` field to define a logic that will forward the
caught URLs to your application (via sockets, API calls, inotify or any other inter-process
communication mechanism). Note that setting `CFBundleTypeRole` will make the wrapper blip in the
dock when the URL is opened. If you don't want that, do not set it.

```json
{
"$schema": "https://json-schema.org/draft-07/schema",
"$id": "https://schemas.conda.io/menuinst-1.schema.json",
"menu_name": "Protocol handler example",
"menu_items": [
{
"name": "My custom menuinst:// handler",
"activate": true,
"command": ["{{ PREFIX }}/bin/my_protocol_handler.py"],
"icon": "{{ MENU_DIR }}/my_protocol_handler.{{ ICON_EXT }}",
"platforms": {
"linux": {
"command": ["{{ PREFIX }}/bin/my_protocol_handler.py", "%u"],
"MimeType": ["x-scheme-handler/menuinst"]
},
"macos": {
"CFBundleURLTypes": [
{
"CFBundleURLIconFile": "{{ MENU_DIR }}/my_protocol_handler",
"CFBundleURLName": "my-protocol-handler.menuinst.does-not-work-yet",
"CFBundleTypeRole": "Viewer",
"CFBundleURLSchemes": ["menuinst"]
}
]
},
"windows": {
"command": ["{{ SCRIPTS_DIR }}/my_protocol_handler.py", "%1"],
"url_protocols": ["menuinst"]
}
"$schema": "https://json-schema.org/draft-07/schema",
"$id": "https://schemas.conda.io/menuinst-1.schema.json",
"menu_name": "Protocol handler example",
"menu_items": [
{
"name": "My custom menuinst:// handler",
"activate": true,
"command": ["{{ PREFIX }}/bin/my_protocol_handler.py"],
"icon": "{{ MENU_DIR }}/my_protocol_handler.{{ ICON_EXT }}",
"platforms": {
"linux": {
"command": ["{{ PREFIX }}/bin/my_protocol_handler.py", "%u"],
"MimeType": ["x-scheme-handler/menuinst"]
},
"osx": {
"command": [
"{{ PREFIX }}/bin/my_protocol_handler.py",
"--listen",
"4444"
],
"CFBundleURLTypes": [
{
"CFBundleURLIconFile": "{{ MENU_DIR }}/my_protocol_handler",
"CFBundleURLName": "my-protocol-handler.menuinst.does-not-work-yet",
"CFBundleURLSchemes": ["menuinst"]
}
],
"event_handler": "for i in 1 2 3 4 5; do echo \"$*\" | nc localhost 4444 && break || sleep 1; done"
},
"windows": {
"command": ["{{ SCRIPTS_DIR }}/my_protocol_handler.py", "%1"],
"url_protocols": ["menuinst"]
}
]
}
}
]
}
```
Loading