@deltaroboticsinc/protoboard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +69 -0
- package/assets/PB Logo N-200.svg +1 -0
- package/assets/ProtoboardFile.ico +0 -0
- package/assets/icons/1024.png +0 -0
- package/assets/icons/128.png +0 -0
- package/assets/icons/16.png +0 -0
- package/assets/icons/256.png +0 -0
- package/assets/icons/32.png +0 -0
- package/assets/icons/48.png +0 -0
- package/assets/icons/512.png +0 -0
- package/assets/icons/64.png +0 -0
- package/cli.js +78 -0
- package/installers/linux/install.sh +53 -0
- package/installers/linux/protoboard-open +37 -0
- package/installers/linux/protoboard-open.desktop +10 -0
- package/installers/linux/protoboard.xml +9 -0
- package/installers/linux/uninstall.sh +22 -0
- package/installers/macos/Protoboard.app/Contents/Info.plist +72 -0
- package/installers/macos/Protoboard.app/Contents/MacOS/protoboard-open +37 -0
- package/installers/macos/install.sh +69 -0
- package/installers/macos/uninstall.sh +15 -0
- package/installers/windows/Open-Protoboard.ps1 +38 -0
- package/installers/windows/install.ps1 +85 -0
- package/installers/windows/uninstall.ps1 +19 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026 Delta Robotics Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software is the proprietary property of Delta Robotics Inc.
|
|
4
|
+
Use, copying, modification, distribution, sublicensing, or any other
|
|
5
|
+
exploitation of this software, in whole or in part, is prohibited
|
|
6
|
+
without the prior written permission of Delta Robotics Inc.
|
|
7
|
+
|
|
8
|
+
This software is published to the npm registry solely to make
|
|
9
|
+
installation convenient for Delta Robotics employees and explicitly
|
|
10
|
+
authorized collaborators. Public availability on the registry does
|
|
11
|
+
not constitute a grant of any rights under this notice.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
14
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
15
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
16
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
17
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
18
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
19
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# protoboard-desktop
|
|
2
|
+
|
|
3
|
+
Downloadable, self-hostable build of Protoboard for desktop or sandbox use.
|
|
4
|
+
|
|
5
|
+
Until the full desktop build lands, this repo ships **`@deltaroboticsinc/protoboard`** — a one-command installer that registers the `.protoboard` file type with its logo and a double-click handler on Windows, macOS, and Linux.
|
|
6
|
+
|
|
7
|
+
## Quickstart
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx @deltaroboticsinc/protoboard install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That's it. Save a `.protoboard` file anywhere (Desktop, Downloads, wherever) and it will show the Protoboard mark. Double-clicking opens the board in your default browser at `https://alpha.protoboard.xyz/open?boardId=...`.
|
|
14
|
+
|
|
15
|
+
To remove:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npx @deltaroboticsinc/protoboard uninstall
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
No admin/root required. All registration is per-user.
|
|
22
|
+
|
|
23
|
+
## How it works
|
|
24
|
+
|
|
25
|
+
- **Windows** — writes per-user keys under `HKCU:\Software\Classes`; copies `ProtoboardFile.ico` and a launcher `Open-Protoboard.ps1` into `%LOCALAPPDATA%\Protoboard\`. Double-click runs the launcher, which reads `board.metadata.id` from the JSON and opens the browser.
|
|
26
|
+
- **macOS** — installs a tiny `Protoboard.app` bundle into `~/Applications/` declaring the `xyz.protoboard.file` UTI; `iconutil` assembles `.icns` from the bundled PNGs; LaunchServices picks it up via `lsregister`. If `duti` is installed, also sets as the default handler.
|
|
27
|
+
- **Linux** — installs an XDG MIME definition, a `.desktop` entry, and mimetype icons via `xdg-icon-resource`. `xdg-mime default` makes it the default handler.
|
|
28
|
+
|
|
29
|
+
The `.protoboard` format itself doesn't change — it's still plain JSON with a custom extension.
|
|
30
|
+
|
|
31
|
+
## For maintainers
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pnpm install
|
|
35
|
+
pnpm run generate-icons # rebuilds .ico + PNGs from assets/PB Logo N-200.svg
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Generated icons (`assets/ProtoboardFile.ico`, `assets/icons/*.png`) are checked into the repo so install-time has no native build step. `.icns` is assembled on the user's mac at install time via Apple's `iconutil`, so it doesn't need to be checked in.
|
|
39
|
+
|
|
40
|
+
### Publishing
|
|
41
|
+
|
|
42
|
+
Tag a version and push:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
git tag v0.1.0 && git push origin v0.1.0
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `Publish to npm` workflow uses the repo's `NPM_TOKEN` secret to publish to `@deltaroboticsinc/protoboard` on npmjs.com. First-time setup:
|
|
49
|
+
|
|
50
|
+
1. NPM org `deltaroboticsinc` already exists.
|
|
51
|
+
2. Generate an automation token at npmjs.com and add it as `NPM_TOKEN` in this repo's GitHub secrets.
|
|
52
|
+
|
|
53
|
+
### Repo layout
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
assets/ # SVG source + generated icons (committed)
|
|
57
|
+
installers/
|
|
58
|
+
windows/ # install.ps1, uninstall.ps1, Open-Protoboard.ps1
|
|
59
|
+
macos/ # install.sh, uninstall.sh, Protoboard.app/
|
|
60
|
+
linux/ # install.sh, uninstall.sh, .xml, .desktop, launcher
|
|
61
|
+
scripts/generate-icons.mjs
|
|
62
|
+
cli.js # the published bin: detects OS and dispatches
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Troubleshooting
|
|
66
|
+
|
|
67
|
+
- **Windows: icon doesn't update in Explorer.** Sign out/in, or run `ie4uinit.exe -show`. The installer triggers `SHChangeNotify` but Explorer occasionally caches aggressively.
|
|
68
|
+
- **macOS: Finder still shows the JSON icon.** Right-click any `.protoboard` file → Get Info → Open with → Protoboard → Change All. Or install `duti` (`brew install duti`) and re-run the installer.
|
|
69
|
+
- **Linux: handler doesn't open.** Confirm `~/.local/bin` is on `PATH`, or check `xdg-mime query default application/x-protoboard+json` returns `protoboard-open.desktop`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" zoomAndPan="magnify" viewBox="0 0 375 374.999991" height="500" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><g/><clipPath id="9012e781b9"><rect x="0" width="303" y="0" height="375"/></clipPath></defs><g transform="matrix(1, 0, 0, 1, 36, 0)"><g clip-path="url(#9012e781b9)"><g fill="#e5e5e5" fill-opacity="1"><g transform="translate(0.238764, 349.173976)"><g><path d="M 93.671875 -137.90625 L 204.75 -249 C 206 -250.25 206.625 -251.816406 206.625 -253.703125 C 206.625 -255.585938 206 -257.15625 204.75 -258.40625 L 180.75 -281.9375 C 177.601562 -285.082031 173.679688 -286.65625 168.984375 -286.65625 L 132.734375 -286.65625 C 128.023438 -286.65625 124.101562 -285.082031 120.96875 -281.9375 L 93.671875 -254.640625 C 90.523438 -251.503906 88.953125 -247.582031 88.953125 -242.875 L 88.953125 -140.265625 C 88.953125 -137.753906 89.582031 -136.5 90.84375 -136.5 C 91.78125 -136.5 92.722656 -136.96875 93.671875 -137.90625 Z M 132.734375 -42.828125 L 168.984375 -42.828125 C 173.679688 -42.828125 177.601562 -44.398438 180.75 -47.546875 L 208.046875 -74.84375 C 211.179688 -77.976562 212.75 -81.898438 212.75 -86.609375 L 212.75 -189.21875 C 212.75 -191.726562 212.125 -192.984375 210.875 -192.984375 C 209.925781 -192.984375 208.984375 -192.507812 208.046875 -191.5625 L 96.96875 -80.484375 C 95.394531 -78.921875 94.609375 -77.351562 94.609375 -75.78125 C 94.609375 -74.207031 95.394531 -72.640625 96.96875 -71.078125 L 120.96875 -47.546875 C 124.101562 -44.398438 128.023438 -42.828125 132.734375 -42.828125 Z M 52.25 -273.9375 L 101.671875 -322.890625 C 105.429688 -327.285156 110.453125 -329.484375 116.734375 -329.484375 L 184.984375 -329.484375 C 190.941406 -329.484375 195.960938 -327.441406 200.046875 -323.359375 L 249 -273.9375 C 253.382812 -270.175781 255.578125 -265.15625 255.578125 -258.875 L 255.578125 -70.609375 C 255.578125 -64.640625 253.382812 -59.460938 249 -55.078125 L 200.046875 -6.125 C 195.960938 -2.039062 190.941406 0 184.984375 0 L 116.734375 0 C 110.453125 0 105.429688 -2.195312 101.671875 -6.59375 L 52.25 -55.546875 C 48.164062 -59.617188 46.125 -64.640625 46.125 -70.609375 L 46.125 -258.875 C 46.125 -264.84375 48.164062 -269.863281 52.25 -273.9375 Z M 52.25 -273.9375 "/></g></g></g></g></g></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/cli.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('node:child_process');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
|
|
6
|
+
const VALID = new Set(['install', 'uninstall']);
|
|
7
|
+
const HELP = `@deltaroboticsinc/protoboard
|
|
8
|
+
|
|
9
|
+
Register the .protoboard file type with its logo and a double-click
|
|
10
|
+
handler that opens the board in your default browser.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
npx @deltaroboticsinc/protoboard <command>
|
|
14
|
+
|
|
15
|
+
Commands:
|
|
16
|
+
install Register .protoboard icon and open handler for the current user.
|
|
17
|
+
uninstall Remove the registration.
|
|
18
|
+
--help Show this message.
|
|
19
|
+
--version Print the package version.
|
|
20
|
+
|
|
21
|
+
Per-user install; no admin/root required.`;
|
|
22
|
+
|
|
23
|
+
function pkgVersion() {
|
|
24
|
+
return require('./package.json').version;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pickInstaller(cmd) {
|
|
28
|
+
const root = __dirname;
|
|
29
|
+
if (process.platform === 'win32') {
|
|
30
|
+
const script = path.join(root, 'installers', 'windows', cmd === 'install' ? 'install.ps1' : 'uninstall.ps1');
|
|
31
|
+
return {
|
|
32
|
+
exe: 'powershell.exe',
|
|
33
|
+
args: ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', script],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (process.platform === 'darwin') {
|
|
37
|
+
const script = path.join(root, 'installers', 'macos', cmd === 'install' ? 'install.sh' : 'uninstall.sh');
|
|
38
|
+
return { exe: 'bash', args: [script] };
|
|
39
|
+
}
|
|
40
|
+
if (process.platform === 'linux') {
|
|
41
|
+
const script = path.join(root, 'installers', 'linux', cmd === 'install' ? 'install.sh' : 'uninstall.sh');
|
|
42
|
+
return { exe: 'bash', args: [script] };
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function main() {
|
|
48
|
+
const arg = process.argv[2];
|
|
49
|
+
if (!arg || arg === '--help' || arg === '-h' || arg === 'help') {
|
|
50
|
+
console.log(HELP);
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
if (arg === '--version' || arg === '-v') {
|
|
54
|
+
console.log(pkgVersion());
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
if (!VALID.has(arg)) {
|
|
58
|
+
console.error(`Unknown command: ${arg}\n\n${HELP}`);
|
|
59
|
+
process.exit(2);
|
|
60
|
+
}
|
|
61
|
+
const picked = pickInstaller(arg);
|
|
62
|
+
if (!picked) {
|
|
63
|
+
console.error(`Unsupported platform: ${process.platform}. Supported: win32, darwin, linux.`);
|
|
64
|
+
process.exit(2);
|
|
65
|
+
}
|
|
66
|
+
if (!fs.existsSync(picked.args[picked.args.length - 1])) {
|
|
67
|
+
console.error(`Installer script missing: ${picked.args[picked.args.length - 1]}`);
|
|
68
|
+
process.exit(2);
|
|
69
|
+
}
|
|
70
|
+
const child = spawn(picked.exe, picked.args, { stdio: 'inherit' });
|
|
71
|
+
child.on('exit', (code) => process.exit(code ?? 1));
|
|
72
|
+
child.on('error', (err) => {
|
|
73
|
+
console.error(`Failed to launch installer: ${err.message}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
main();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Register the .protoboard file type for the current user on Linux.
|
|
3
|
+
# Per-user XDG paths; no sudo. Uses shared-mime-info + .desktop + xdg-icon-resource.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
repo_root="$(cd "$script_dir/../.." && pwd)"
|
|
9
|
+
icons_src="$repo_root/assets/icons"
|
|
10
|
+
|
|
11
|
+
if [[ ! -f "$icons_src/256.png" ]]; then
|
|
12
|
+
echo "Missing PNGs in $icons_src. Run 'pnpm run generate-icons' first." >&2
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
mime_dir="$HOME/.local/share/mime/packages"
|
|
17
|
+
apps_dir="$HOME/.local/share/applications"
|
|
18
|
+
bin_dir="$HOME/.local/bin"
|
|
19
|
+
|
|
20
|
+
mkdir -p "$mime_dir" "$apps_dir" "$bin_dir"
|
|
21
|
+
|
|
22
|
+
# 1. MIME definition (links .protoboard glob to application/x-protoboard+json).
|
|
23
|
+
cp "$script_dir/protoboard.xml" "$mime_dir/protoboard.xml"
|
|
24
|
+
|
|
25
|
+
# 2. .desktop entry pointing at protoboard-open.
|
|
26
|
+
cp "$script_dir/protoboard-open.desktop" "$apps_dir/protoboard-open.desktop"
|
|
27
|
+
|
|
28
|
+
# 3. Launcher script.
|
|
29
|
+
cp "$script_dir/protoboard-open" "$bin_dir/protoboard-open"
|
|
30
|
+
chmod +x "$bin_dir/protoboard-open"
|
|
31
|
+
|
|
32
|
+
# 4. Install mimetype icons at each size.
|
|
33
|
+
for size in 16 32 48 64 128 256 512; do
|
|
34
|
+
png="$icons_src/$size.png"
|
|
35
|
+
if [[ -f "$png" ]]; then
|
|
36
|
+
xdg-icon-resource install --noupdate --context mimetypes --size "$size" "$png" application-x-protoboard+json
|
|
37
|
+
fi
|
|
38
|
+
done
|
|
39
|
+
xdg-icon-resource forceupdate
|
|
40
|
+
|
|
41
|
+
# 5. Refresh MIME and desktop databases.
|
|
42
|
+
update-mime-database "$HOME/.local/share/mime"
|
|
43
|
+
update-desktop-database "$apps_dir" 2>/dev/null || true
|
|
44
|
+
xdg-mime default protoboard-open.desktop application/x-protoboard+json
|
|
45
|
+
|
|
46
|
+
case ":$PATH:" in
|
|
47
|
+
*":$bin_dir:"*) ;;
|
|
48
|
+
*) echo "Note: $bin_dir is not on PATH. The .desktop entry uses an absolute path so the handler will still fire, but you may want to add it for terminal use." ;;
|
|
49
|
+
esac
|
|
50
|
+
|
|
51
|
+
echo "Protoboard file association installed."
|
|
52
|
+
echo " Handler: $bin_dir/protoboard-open"
|
|
53
|
+
echo " MIME: application/x-protoboard+json (glob *.protoboard)"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Launcher invoked when a .protoboard file is opened on Linux.
|
|
3
|
+
# Builds https://alpha.protoboard.xyz/board/<userId>/<boardId> when both
|
|
4
|
+
# ids are present; otherwise falls back to https://alpha.protoboard.xyz/.
|
|
5
|
+
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
APP_ROOT="https://alpha.protoboard.xyz"
|
|
9
|
+
|
|
10
|
+
file="${1:-}"
|
|
11
|
+
if [[ -z "$file" || ! -f "$file" ]]; then
|
|
12
|
+
xdg-open "$APP_ROOT" >/dev/null 2>&1 &
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
read -r board_id user_id <<EOF
|
|
17
|
+
$(python3 - "$file" <<'PY' 2>/dev/null || true
|
|
18
|
+
import json, sys
|
|
19
|
+
try:
|
|
20
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
21
|
+
d = json.load(fh)
|
|
22
|
+
board = d.get("board") or {}
|
|
23
|
+
md = board.get("metadata") or {}
|
|
24
|
+
bid = md.get("id") or ""
|
|
25
|
+
uid = md.get("userId") or board.get("userId") or ""
|
|
26
|
+
print(bid, uid)
|
|
27
|
+
except Exception:
|
|
28
|
+
print("", "")
|
|
29
|
+
PY
|
|
30
|
+
)
|
|
31
|
+
EOF
|
|
32
|
+
|
|
33
|
+
if [[ -n "${board_id:-}" && -n "${user_id:-}" ]]; then
|
|
34
|
+
xdg-open "$APP_ROOT/board/$user_id/$board_id" >/dev/null 2>&1 &
|
|
35
|
+
else
|
|
36
|
+
xdg-open "$APP_ROOT" >/dev/null 2>&1 &
|
|
37
|
+
fi
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[Desktop Entry]
|
|
2
|
+
Type=Application
|
|
3
|
+
Name=Protoboard
|
|
4
|
+
Comment=Open Protoboard files in the browser
|
|
5
|
+
Exec=protoboard-open %f
|
|
6
|
+
Terminal=false
|
|
7
|
+
NoDisplay=true
|
|
8
|
+
MimeType=application/x-protoboard+json;
|
|
9
|
+
Icon=application-x-protoboard+json
|
|
10
|
+
Categories=Development;Engineering;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
|
3
|
+
<mime-type type="application/x-protoboard+json">
|
|
4
|
+
<comment>Protoboard File</comment>
|
|
5
|
+
<sub-class-of type="application/json"/>
|
|
6
|
+
<glob pattern="*.protoboard"/>
|
|
7
|
+
<icon name="application-x-protoboard+json"/>
|
|
8
|
+
</mime-type>
|
|
9
|
+
</mime-info>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Remove the .protoboard registration for the current user on Linux.
|
|
3
|
+
|
|
4
|
+
set -u
|
|
5
|
+
|
|
6
|
+
mime_file="$HOME/.local/share/mime/packages/protoboard.xml"
|
|
7
|
+
desktop_file="$HOME/.local/share/applications/protoboard-open.desktop"
|
|
8
|
+
bin_file="$HOME/.local/bin/protoboard-open"
|
|
9
|
+
|
|
10
|
+
[[ -f "$mime_file" ]] && rm -f "$mime_file"
|
|
11
|
+
[[ -f "$desktop_file" ]] && rm -f "$desktop_file"
|
|
12
|
+
[[ -f "$bin_file" ]] && rm -f "$bin_file"
|
|
13
|
+
|
|
14
|
+
for size in 16 32 48 64 128 256 512; do
|
|
15
|
+
xdg-icon-resource uninstall --noupdate --context mimetypes --size "$size" application-x-protoboard+json 2>/dev/null || true
|
|
16
|
+
done
|
|
17
|
+
xdg-icon-resource forceupdate 2>/dev/null || true
|
|
18
|
+
|
|
19
|
+
update-mime-database "$HOME/.local/share/mime" 2>/dev/null || true
|
|
20
|
+
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
|
|
21
|
+
|
|
22
|
+
echo "Protoboard file association removed."
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>CFBundleIdentifier</key>
|
|
6
|
+
<string>xyz.protoboard.opener</string>
|
|
7
|
+
<key>CFBundleName</key>
|
|
8
|
+
<string>Protoboard</string>
|
|
9
|
+
<key>CFBundleDisplayName</key>
|
|
10
|
+
<string>Protoboard</string>
|
|
11
|
+
<key>CFBundleVersion</key>
|
|
12
|
+
<string>0.1.0</string>
|
|
13
|
+
<key>CFBundleShortVersionString</key>
|
|
14
|
+
<string>0.1.0</string>
|
|
15
|
+
<key>CFBundlePackageType</key>
|
|
16
|
+
<string>APPL</string>
|
|
17
|
+
<key>CFBundleSignature</key>
|
|
18
|
+
<string>????</string>
|
|
19
|
+
<key>CFBundleExecutable</key>
|
|
20
|
+
<string>protoboard-open</string>
|
|
21
|
+
<key>CFBundleIconFile</key>
|
|
22
|
+
<string>ProtoboardFile</string>
|
|
23
|
+
<key>LSMinimumSystemVersion</key>
|
|
24
|
+
<string>10.13</string>
|
|
25
|
+
<key>LSUIElement</key>
|
|
26
|
+
<true/>
|
|
27
|
+
<key>CFBundleDocumentTypes</key>
|
|
28
|
+
<array>
|
|
29
|
+
<dict>
|
|
30
|
+
<key>CFBundleTypeName</key>
|
|
31
|
+
<string>Protoboard File</string>
|
|
32
|
+
<key>CFBundleTypeRole</key>
|
|
33
|
+
<string>Viewer</string>
|
|
34
|
+
<key>CFBundleTypeIconFile</key>
|
|
35
|
+
<string>ProtoboardFile</string>
|
|
36
|
+
<key>LSItemContentTypes</key>
|
|
37
|
+
<array>
|
|
38
|
+
<string>xyz.protoboard.file</string>
|
|
39
|
+
</array>
|
|
40
|
+
<key>LSHandlerRank</key>
|
|
41
|
+
<string>Owner</string>
|
|
42
|
+
</dict>
|
|
43
|
+
</array>
|
|
44
|
+
<key>UTExportedTypeDeclarations</key>
|
|
45
|
+
<array>
|
|
46
|
+
<dict>
|
|
47
|
+
<key>UTTypeIdentifier</key>
|
|
48
|
+
<string>xyz.protoboard.file</string>
|
|
49
|
+
<key>UTTypeDescription</key>
|
|
50
|
+
<string>Protoboard File</string>
|
|
51
|
+
<key>UTTypeConformsTo</key>
|
|
52
|
+
<array>
|
|
53
|
+
<string>public.json</string>
|
|
54
|
+
<string>public.data</string>
|
|
55
|
+
</array>
|
|
56
|
+
<key>UTTypeIconFile</key>
|
|
57
|
+
<string>ProtoboardFile</string>
|
|
58
|
+
<key>UTTypeTagSpecification</key>
|
|
59
|
+
<dict>
|
|
60
|
+
<key>public.filename-extension</key>
|
|
61
|
+
<array>
|
|
62
|
+
<string>protoboard</string>
|
|
63
|
+
</array>
|
|
64
|
+
<key>public.mime-type</key>
|
|
65
|
+
<array>
|
|
66
|
+
<string>application/x-protoboard+json</string>
|
|
67
|
+
</array>
|
|
68
|
+
</dict>
|
|
69
|
+
</dict>
|
|
70
|
+
</array>
|
|
71
|
+
</dict>
|
|
72
|
+
</plist>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Launcher invoked by LaunchServices when a .protoboard file is opened.
|
|
3
|
+
# Builds https://alpha.protoboard.xyz/board/<userId>/<boardId> when both
|
|
4
|
+
# ids are present; otherwise falls back to https://alpha.protoboard.xyz/.
|
|
5
|
+
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
APP_ROOT="https://alpha.protoboard.xyz"
|
|
9
|
+
|
|
10
|
+
file="${1:-}"
|
|
11
|
+
if [[ -z "$file" || ! -f "$file" ]]; then
|
|
12
|
+
open "$APP_ROOT"
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
read -r board_id user_id <<EOF
|
|
17
|
+
$(python3 - "$file" <<'PY' 2>/dev/null || true
|
|
18
|
+
import json, sys
|
|
19
|
+
try:
|
|
20
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
21
|
+
d = json.load(fh)
|
|
22
|
+
board = d.get("board") or {}
|
|
23
|
+
md = board.get("metadata") or {}
|
|
24
|
+
bid = md.get("id") or ""
|
|
25
|
+
uid = md.get("userId") or board.get("userId") or ""
|
|
26
|
+
print(bid, uid)
|
|
27
|
+
except Exception:
|
|
28
|
+
print("", "")
|
|
29
|
+
PY
|
|
30
|
+
)
|
|
31
|
+
EOF
|
|
32
|
+
|
|
33
|
+
if [[ -n "${board_id:-}" && -n "${user_id:-}" ]]; then
|
|
34
|
+
open "$APP_ROOT/board/$user_id/$board_id"
|
|
35
|
+
else
|
|
36
|
+
open "$APP_ROOT"
|
|
37
|
+
fi
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Install the Protoboard.app bundle into ~/Applications and register the
|
|
3
|
+
# .protoboard UTI + icon with LaunchServices. Per-user; no sudo.
|
|
4
|
+
#
|
|
5
|
+
# The .icns icon is assembled here (not pre-built) using Apple's native
|
|
6
|
+
# iconutil, so the repo doesn't need to ship a binary .icns blob.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
repo_root="$(cd "$script_dir/../.." && pwd)"
|
|
12
|
+
src_app="$script_dir/Protoboard.app"
|
|
13
|
+
dst_app="$HOME/Applications/Protoboard.app"
|
|
14
|
+
|
|
15
|
+
if [[ ! -d "$src_app" ]]; then
|
|
16
|
+
echo "Missing app bundle: $src_app" >&2
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# 1. Build ProtoboardFile.icns from the PNGs in assets/icons/.
|
|
21
|
+
icons_src="$repo_root/assets/icons"
|
|
22
|
+
if [[ ! -f "$icons_src/512.png" ]]; then
|
|
23
|
+
echo "Missing PNGs in $icons_src. Run 'pnpm run generate-icons' first." >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
tmp_iconset="$(mktemp -d)/ProtoboardFile.iconset"
|
|
27
|
+
mkdir -p "$tmp_iconset"
|
|
28
|
+
cp "$icons_src/16.png" "$tmp_iconset/icon_16x16.png"
|
|
29
|
+
cp "$icons_src/32.png" "$tmp_iconset/icon_16x16@2x.png"
|
|
30
|
+
cp "$icons_src/32.png" "$tmp_iconset/icon_32x32.png"
|
|
31
|
+
cp "$icons_src/64.png" "$tmp_iconset/icon_32x32@2x.png"
|
|
32
|
+
cp "$icons_src/128.png" "$tmp_iconset/icon_128x128.png"
|
|
33
|
+
cp "$icons_src/256.png" "$tmp_iconset/icon_128x128@2x.png"
|
|
34
|
+
cp "$icons_src/256.png" "$tmp_iconset/icon_256x256.png"
|
|
35
|
+
cp "$icons_src/512.png" "$tmp_iconset/icon_256x256@2x.png"
|
|
36
|
+
cp "$icons_src/512.png" "$tmp_iconset/icon_512x512.png"
|
|
37
|
+
if [[ -f "$icons_src/1024.png" ]]; then
|
|
38
|
+
cp "$icons_src/1024.png" "$tmp_iconset/icon_512x512@2x.png"
|
|
39
|
+
fi
|
|
40
|
+
icns_out="$src_app/Contents/Resources/ProtoboardFile.icns"
|
|
41
|
+
mkdir -p "$(dirname "$icns_out")"
|
|
42
|
+
iconutil -c icns "$tmp_iconset" -o "$icns_out"
|
|
43
|
+
rm -rf "$(dirname "$tmp_iconset")"
|
|
44
|
+
|
|
45
|
+
# 2. Make the launcher executable and copy the bundle into ~/Applications.
|
|
46
|
+
chmod +x "$src_app/Contents/MacOS/protoboard-open"
|
|
47
|
+
mkdir -p "$HOME/Applications"
|
|
48
|
+
rm -rf "$dst_app"
|
|
49
|
+
cp -R "$src_app" "$dst_app"
|
|
50
|
+
|
|
51
|
+
# 3. Register with LaunchServices so the UTI and icon are picked up.
|
|
52
|
+
lsregister="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister"
|
|
53
|
+
"$lsregister" -f "$dst_app"
|
|
54
|
+
|
|
55
|
+
# 4. Try to set as default handler. duti is optional.
|
|
56
|
+
if command -v duti >/dev/null 2>&1; then
|
|
57
|
+
duti -s xyz.protoboard.file all || true
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# 5. Nudge Finder to refresh icon cache.
|
|
61
|
+
killall Finder 2>/dev/null || true
|
|
62
|
+
|
|
63
|
+
echo "Protoboard file association installed."
|
|
64
|
+
echo " App: $dst_app"
|
|
65
|
+
echo " UTI: xyz.protoboard.file"
|
|
66
|
+
echo
|
|
67
|
+
echo "If Finder still shows the default JSON icon: right-click any .protoboard file"
|
|
68
|
+
echo "→ Get Info → Open with → Protoboard → Change All. Or install 'duti' (brew install duti)"
|
|
69
|
+
echo "and re-run this installer."
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Remove the Protoboard.app bundle and unregister the UTI.
|
|
3
|
+
|
|
4
|
+
set -u
|
|
5
|
+
|
|
6
|
+
dst_app="$HOME/Applications/Protoboard.app"
|
|
7
|
+
lsregister="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister"
|
|
8
|
+
|
|
9
|
+
if [[ -d "$dst_app" ]]; then
|
|
10
|
+
"$lsregister" -u "$dst_app" 2>/dev/null || true
|
|
11
|
+
rm -rf "$dst_app"
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
killall Finder 2>/dev/null || true
|
|
15
|
+
echo "Protoboard file association removed."
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Launcher invoked by Explorer when a .protoboard file is double-clicked.
|
|
2
|
+
# Builds https://alpha.protoboard.xyz/board/<userId>/<boardId> when both
|
|
3
|
+
# ids are present; otherwise falls back to https://alpha.protoboard.xyz/.
|
|
4
|
+
|
|
5
|
+
param([Parameter(Mandatory = $true)][string]$Path)
|
|
6
|
+
|
|
7
|
+
$AppRoot = 'https://alpha.protoboard.xyz'
|
|
8
|
+
|
|
9
|
+
function Open-Url([string]$Url) {
|
|
10
|
+
Start-Process $Url | Out-Null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
if (-not (Test-Path -LiteralPath $Path)) {
|
|
15
|
+
Open-Url $AppRoot
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
$json = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
19
|
+
|
|
20
|
+
$boardId = $null
|
|
21
|
+
$userId = $null
|
|
22
|
+
if ($json.board) {
|
|
23
|
+
if ($json.board.metadata) {
|
|
24
|
+
$boardId = $json.board.metadata.id
|
|
25
|
+
if (-not $userId) { $userId = $json.board.metadata.userId }
|
|
26
|
+
}
|
|
27
|
+
if (-not $userId) { $userId = $json.board.userId }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ($boardId -and $userId) {
|
|
31
|
+
Open-Url "$AppRoot/board/$userId/$boardId"
|
|
32
|
+
} else {
|
|
33
|
+
Open-Url $AppRoot
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
Open-Url $AppRoot
|
|
38
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Register the .protoboard file type for the current user.
|
|
2
|
+
# - Per-user HKCU registry (no admin required)
|
|
3
|
+
# - Icon: ProtoboardFile.ico copied to %LOCALAPPDATA%\Protoboard\
|
|
4
|
+
# - Open handler: Open-Protoboard.ps1 reads board id from the JSON
|
|
5
|
+
# and launches https://alpha.protoboard.xyz/open?boardId=...
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
|
|
9
|
+
$installDir = Join-Path $env:LOCALAPPDATA 'Protoboard'
|
|
10
|
+
$scriptDir = $PSScriptRoot
|
|
11
|
+
$repoRoot = Resolve-Path (Join-Path $scriptDir '..\..')
|
|
12
|
+
$icoSource = Join-Path $repoRoot 'assets\ProtoboardFile.ico'
|
|
13
|
+
$launcherSource = Join-Path $scriptDir 'Open-Protoboard.ps1'
|
|
14
|
+
|
|
15
|
+
if (-not (Test-Path $icoSource)) { throw "Missing icon: $icoSource. Run 'pnpm run generate-icons' first." }
|
|
16
|
+
if (-not (Test-Path $launcherSource)) { throw "Missing launcher: $launcherSource" }
|
|
17
|
+
|
|
18
|
+
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
|
|
19
|
+
Copy-Item $icoSource (Join-Path $installDir 'ProtoboardFile.ico') -Force
|
|
20
|
+
Copy-Item $launcherSource (Join-Path $installDir 'Open-Protoboard.ps1') -Force
|
|
21
|
+
|
|
22
|
+
$icoPath = Join-Path $installDir 'ProtoboardFile.ico'
|
|
23
|
+
$launcherPath = Join-Path $installDir 'Open-Protoboard.ps1'
|
|
24
|
+
|
|
25
|
+
$extKey = 'HKCU:\Software\Classes\.protoboard'
|
|
26
|
+
$progKey = 'HKCU:\Software\Classes\Protoboard.File'
|
|
27
|
+
$iconKey = Join-Path $progKey 'DefaultIcon'
|
|
28
|
+
$shellKey = Join-Path $progKey 'shell\open\command'
|
|
29
|
+
|
|
30
|
+
New-Item -Path $extKey -Force | Out-Null
|
|
31
|
+
New-ItemProperty -Path $extKey -Name '(default)' -Value 'Protoboard.File' -PropertyType String -Force | Out-Null
|
|
32
|
+
New-ItemProperty -Path $extKey -Name 'Content Type' -Value 'application/x-protoboard+json' -PropertyType String -Force | Out-Null
|
|
33
|
+
New-ItemProperty -Path $extKey -Name 'PerceivedType' -Value 'text' -PropertyType String -Force | Out-Null
|
|
34
|
+
|
|
35
|
+
New-Item -Path $progKey -Force | Out-Null
|
|
36
|
+
New-ItemProperty -Path $progKey -Name '(default)' -Value 'Protoboard File' -PropertyType String -Force | Out-Null
|
|
37
|
+
New-ItemProperty -Path $progKey -Name 'FriendlyTypeName' -Value 'Protoboard File' -PropertyType String -Force | Out-Null
|
|
38
|
+
|
|
39
|
+
New-Item -Path $iconKey -Force | Out-Null
|
|
40
|
+
New-ItemProperty -Path $iconKey -Name '(default)' -Value "`"$icoPath`",0" -PropertyType String -Force | Out-Null
|
|
41
|
+
|
|
42
|
+
New-Item -Path $shellKey -Force | Out-Null
|
|
43
|
+
$openCmd = "powershell.exe -NoProfile -ExecutionPolicy Bypass -File `"$launcherPath`" `"%1`""
|
|
44
|
+
New-ItemProperty -Path $shellKey -Name '(default)' -Value $openCmd -PropertyType String -Force | Out-Null
|
|
45
|
+
|
|
46
|
+
# If the user has ever right-clicked → Open with on a .protoboard file,
|
|
47
|
+
# Windows wrote a UserChoice override under FileExts that hides our icon.
|
|
48
|
+
# Clear it so DefaultIcon wins. (Re-set as Open-with is one-click to recover.)
|
|
49
|
+
$fileExts = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.protoboard'
|
|
50
|
+
if (Test-Path $fileExts) {
|
|
51
|
+
Remove-Item -Path $fileExts -Recurse -Force -ErrorAction SilentlyContinue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Wipe the per-user icon cache so Explorer re-reads DefaultIcon.
|
|
55
|
+
$explorerWasRunning = $false
|
|
56
|
+
$explorer = Get-Process explorer -ErrorAction SilentlyContinue
|
|
57
|
+
if ($explorer) {
|
|
58
|
+
$explorerWasRunning = $true
|
|
59
|
+
Stop-Process -Name explorer -Force -ErrorAction SilentlyContinue
|
|
60
|
+
Start-Sleep -Milliseconds 600
|
|
61
|
+
}
|
|
62
|
+
$cacheDir = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows\Explorer'
|
|
63
|
+
if (Test-Path $cacheDir) {
|
|
64
|
+
Get-ChildItem -Path $cacheDir -Filter 'iconcache_*.db' -Force -ErrorAction SilentlyContinue |
|
|
65
|
+
Remove-Item -Force -ErrorAction SilentlyContinue
|
|
66
|
+
Get-ChildItem -Path $cacheDir -Filter 'thumbcache_*.db' -Force -ErrorAction SilentlyContinue |
|
|
67
|
+
Remove-Item -Force -ErrorAction SilentlyContinue
|
|
68
|
+
}
|
|
69
|
+
$legacyCache = Join-Path $env:LOCALAPPDATA 'IconCache.db'
|
|
70
|
+
if (Test-Path $legacyCache) { Remove-Item -Path $legacyCache -Force -ErrorAction SilentlyContinue }
|
|
71
|
+
|
|
72
|
+
if ($explorerWasRunning) { Start-Process explorer.exe | Out-Null }
|
|
73
|
+
|
|
74
|
+
# Force Explorer to pick up the new icon/handler immediately.
|
|
75
|
+
Add-Type -Namespace Win32 -Name Shell32 -MemberDefinition @'
|
|
76
|
+
[System.Runtime.InteropServices.DllImport("shell32.dll")]
|
|
77
|
+
public static extern void SHChangeNotify(int wEventId, int uFlags, System.IntPtr dwItem1, System.IntPtr dwItem2);
|
|
78
|
+
'@ | Out-Null
|
|
79
|
+
[Win32.Shell32]::SHChangeNotify(0x08000000, 0, [System.IntPtr]::Zero, [System.IntPtr]::Zero)
|
|
80
|
+
|
|
81
|
+
Write-Host "Protoboard file association installed." -ForegroundColor Green
|
|
82
|
+
Write-Host " Icon: $icoPath"
|
|
83
|
+
Write-Host " Handler: $launcherPath"
|
|
84
|
+
Write-Host ""
|
|
85
|
+
Write-Host "Save a .protoboard file anywhere to see the icon. Double-click opens the board in your browser."
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Remove the .protoboard file-type registration for the current user.
|
|
2
|
+
|
|
3
|
+
$ErrorActionPreference = 'Continue'
|
|
4
|
+
|
|
5
|
+
$installDir = Join-Path $env:LOCALAPPDATA 'Protoboard'
|
|
6
|
+
$extKey = 'HKCU:\Software\Classes\.protoboard'
|
|
7
|
+
$progKey = 'HKCU:\Software\Classes\Protoboard.File'
|
|
8
|
+
|
|
9
|
+
if (Test-Path $extKey) { Remove-Item -Path $extKey -Recurse -Force }
|
|
10
|
+
if (Test-Path $progKey) { Remove-Item -Path $progKey -Recurse -Force }
|
|
11
|
+
if (Test-Path $installDir) { Remove-Item -Path $installDir -Recurse -Force }
|
|
12
|
+
|
|
13
|
+
Add-Type -Namespace Win32 -Name Shell32 -MemberDefinition @'
|
|
14
|
+
[System.Runtime.InteropServices.DllImport("shell32.dll")]
|
|
15
|
+
public static extern void SHChangeNotify(int wEventId, int uFlags, System.IntPtr dwItem1, System.IntPtr dwItem2);
|
|
16
|
+
'@ | Out-Null
|
|
17
|
+
[Win32.Shell32]::SHChangeNotify(0x08000000, 0, [System.IntPtr]::Zero, [System.IntPtr]::Zero)
|
|
18
|
+
|
|
19
|
+
Write-Host "Protoboard file association removed." -ForegroundColor Green
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deltaroboticsinc/protoboard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Register the .protoboard file type with its logo and a double-click handler across Windows, macOS, and Linux.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"author": "Delta Robotics Inc",
|
|
7
|
+
"homepage": "https://github.com/Delta-Robotics-Inc/protoboard-desktop",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Delta-Robotics-Inc/protoboard-desktop.git"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"protoboard": "./cli.js"
|
|
14
|
+
},
|
|
15
|
+
"main": "cli.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"cli.js",
|
|
18
|
+
"assets/",
|
|
19
|
+
"installers/"
|
|
20
|
+
],
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"png-to-ico": "^2.1.8",
|
|
23
|
+
"sharp": "^0.34.1"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"os": [
|
|
29
|
+
"darwin",
|
|
30
|
+
"linux",
|
|
31
|
+
"win32"
|
|
32
|
+
],
|
|
33
|
+
"keywords": [
|
|
34
|
+
"protoboard",
|
|
35
|
+
"file-association",
|
|
36
|
+
"file-icon",
|
|
37
|
+
"windows",
|
|
38
|
+
"macos",
|
|
39
|
+
"linux"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"generate-icons": "node scripts/generate-icons.mjs"
|
|
46
|
+
}
|
|
47
|
+
}
|