betterdisplaycli 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.
@@ -0,0 +1,14 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: macos-latest
8
+
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+ - uses: oven-sh/setup-bun@v2
12
+ - run: brew install bettterdisplay
13
+ - run: make lint
14
+ - run: make test
@@ -0,0 +1,13 @@
1
+ name: Publish GitHub release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - v*
7
+
8
+ jobs:
9
+ Publish:
10
+ permissions:
11
+ contents: write
12
+ if: startsWith(github.ref, 'refs/tags/v')
13
+ uses: cubing/actions-workflows/.github/workflows/publish-github-release.yaml@main
package/Makefile ADDED
@@ -0,0 +1,31 @@
1
+ .PHONY: lint
2
+ lint: setup
3
+ bun x @biomejs/biome check
4
+ bun x tsc --noEmit --project .
5
+
6
+ .PHONY: format
7
+ format: setup
8
+ bun x @biomejs/biome check --write
9
+
10
+ .PHONY: test
11
+ test: setup
12
+ bun test
13
+
14
+ .PHONY: setup
15
+ setup:
16
+ bun install --frozen-lockfile
17
+
18
+ .PHONY: clean
19
+ clean:
20
+ rm -rf ./dist
21
+
22
+ .PHONY: reset
23
+ reset: clean
24
+ rm -rf ./node_modules
25
+
26
+ .PHONY: publish
27
+ publish:
28
+ npm publish
29
+
30
+ .PHONY: prepublishOnly
31
+ prepublishOnly: lint
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # `betterdisplaycli.js`
2
+
3
+ TypeScript bindings for `betterdisplaycli`.
4
+
5
+ Only a small number of bindings are currently supported. More will be added as needed.
6
+
7
+ ## Example usage
8
+
9
+ ```ts
10
+ import { getAllDevices } from "betterdisplaycli";
11
+
12
+ const numConnected = (await getAllDevices({ ignoreDisplayGroups: true })).length;
13
+ const plural = numConnected === 1 ? "" : "s";
14
+ console.log(`${numConnected} display${plural} and/or virtual screen${plural} connected.`);
15
+ ```
package/biome.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3
+ "extends": ["./node_modules/@cubing/dev-config/biome/biome.json"],
4
+ "files": {
5
+ "includes": ["**", "!dist", "!package.json"]
6
+ }
7
+ }
package/bun.lock ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "workspaces": {
4
+ "": {
5
+ "dependencies": {
6
+ "printable-shell-command": "^2.3.0",
7
+ },
8
+ "devDependencies": {
9
+ "@biomejs/biome": "^2.3.4",
10
+ "@cubing/dev-config": "^0.4.1",
11
+ },
12
+ },
13
+ },
14
+ "packages": {
15
+ "@biomejs/biome": ["@biomejs/biome@2.3.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.4", "@biomejs/cli-darwin-x64": "2.3.4", "@biomejs/cli-linux-arm64": "2.3.4", "@biomejs/cli-linux-arm64-musl": "2.3.4", "@biomejs/cli-linux-x64": "2.3.4", "@biomejs/cli-linux-x64-musl": "2.3.4", "@biomejs/cli-win32-arm64": "2.3.4", "@biomejs/cli-win32-x64": "2.3.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TU08LXjBHdy0mEY9APtEtZdNQQijXUDSXR7IK1i45wgoPD5R0muK7s61QcFir6FpOj/RP1+YkPx5QJlycXUU3w=="],
16
+
17
+ "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-w40GvlNzLaqmuWYiDU6Ys9FNhJiclngKqcGld3iJIiy2bpJ0Q+8n3haiaC81uTPY/NA0d8Q/I3Z9+ajc14102Q=="],
18
+
19
+ "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-3s7TLVtjJ7ni1xADXsS7x7GMUrLBZXg8SemXc3T0XLslzvqKj/dq1xGeBQ+pOWQzng9MaozfacIHdK2UlJ3jGA=="],
20
+
21
+ "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-y7efHyyM2gYmHy/AdWEip+VgTMe9973aP7XYKPzu/j8JxnPHuSUXftzmPhkVw0lfm4ECGbdBdGD6+rLmTgNZaA=="],
22
+
23
+ "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-IruVGQRwMURivWazchiq7gKAqZSFs5so6gi0hJyxk7x6HR+iwZbO2IxNOqyLURBvL06qkIHs7Wffl6Bw30vCbQ=="],
24
+
25
+ "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gKfjWR/6/dfIxPJCw8REdEowiXCkIpl9jycpNVHux8aX2yhWPLjydOshkDL6Y/82PcQJHn95VCj7J+BRcE5o1Q=="],
26
+
27
+ "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-mzKFFv/w66e4/jCobFmD3kymCqG+FuWE7sVa4Yjqd9v7qt2UhXo67MSZKY9Ih18V2IwPzRKQPCw6KwdZs6AXSA=="],
28
+
29
+ "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-5TJ6JfVez+yyupJ/iGUici2wzKf0RrSAxJhghQXtAEsc67OIpdwSKAQboemILrwKfHDi5s6mu7mX+VTCTUydkw=="],
30
+
31
+ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-FGCijXecmC4IedQ0esdYNlMpx0Jxgf4zceCaMu6fkjWyjgn50ZQtMiqZZQ0Q/77yqPxvtkgZAvt5uGw0gAAjig=="],
32
+
33
+ "@cubing/dev-config": ["@cubing/dev-config@0.4.1", "", { "dependencies": { "esbuild": ">=0.25.3 && <1.0.0" } }, "sha512-OPv30QNLWdTikNTugbIcCk0w2JRrrWNgxH+lYDNiG3yIOIxAq7hnvaWhiRa/x+Of6q4rMzjCxTg3cHRuCsbt1g=="],
34
+
35
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
36
+
37
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
38
+
39
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
40
+
41
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
42
+
43
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
44
+
45
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
46
+
47
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
48
+
49
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
50
+
51
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
52
+
53
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
54
+
55
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
56
+
57
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
58
+
59
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
60
+
61
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
62
+
63
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
64
+
65
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
66
+
67
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
68
+
69
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
70
+
71
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
72
+
73
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
74
+
75
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
76
+
77
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
78
+
79
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
80
+
81
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
82
+
83
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
84
+
85
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
86
+
87
+ "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
88
+
89
+ "@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
90
+
91
+ "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
92
+
93
+ "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
94
+
95
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
96
+
97
+ "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
98
+
99
+ "path-class": ["path-class@0.6.1", "", { "dependencies": { "@types/node": "^24.7.1", "xdg-basedir": "^5.1.0" } }, "sha512-38ZYmRh38kFSKivJCHk9S4ZHOK4Wf6ocxHfYBdDmVN7GWVOXrYsxwMBQpjhGyPNBdiPo4W3X91x2TzGmxHBlUg=="],
100
+
101
+ "printable-shell-command": ["printable-shell-command@2.3.0", "", { "dependencies": { "path-class": "^0.6.1", "type-fest": "^5.1.0" }, "optionalDependencies": { "@types/bun": "^1.2.20", "@types/node": "^24.2.0" } }, "sha512-qobgNOAPDXPqj6VI+RA9r6ouiJdq6hxWSPKAjm7SMdy5CplCUAsFB/1rHyURhNpK7qg6uz7iP+yuRj+NegjgOg=="],
102
+
103
+ "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
104
+
105
+ "type-fest": ["type-fest@5.2.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-xxCJm+Bckc6kQBknN7i9fnP/xobQRsRQxR01CztFkp/h++yfVxUUcmMgfR2HttJx/dpWjS9ubVuyspJv24Q9DA=="],
106
+
107
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
108
+
109
+ "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
110
+ }
111
+ }
package/bunfig.toml ADDED
@@ -0,0 +1,3 @@
1
+ [install]
2
+ auto = "disable"
3
+ linker = "isolated"
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "betterdisplaycli",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript bindings for `betterdisplaycli`.",
5
+ "author": "Lucas Garron <code@garron.net>",
6
+ "exports": {
7
+ ".": {
8
+ "default": "./src/index.ts"
9
+ }
10
+ },
11
+ "dependencies": {
12
+ "printable-shell-command": "^2.3.0"
13
+ },
14
+ "devDependencies": {
15
+ "@biomejs/biome": "^2.3.4",
16
+ "@cubing/dev-config": "^0.4.1"
17
+ },
18
+ "scripts": {
19
+ "prepublishOnly": "make prepublishOnly"
20
+ }
21
+ }
package/src/Device.ts ADDED
@@ -0,0 +1,72 @@
1
+ export type NumberString = string;
2
+
3
+ export interface DeviceInfoCommon {
4
+ deviceType: "Display" | "VirtualScreen" | "DisplayGroup";
5
+ name: string;
6
+ tagID: NumberString;
7
+ }
8
+
9
+ export interface DisplayInfo extends DeviceInfoCommon {
10
+ deviceType: "Display";
11
+ UUID: string;
12
+ alphanumericSerial: string;
13
+ displayID: NumberString;
14
+ model: string; // `NumberString`?
15
+ originalName: string;
16
+ productName: string;
17
+ registryLocation: string;
18
+ serial: string;
19
+ vendor: string;
20
+ weekOfManufacture: NumberString; // "0" if unavailable?
21
+ yearOfManufacture: NumberString; // "0" if unavailable?
22
+ }
23
+
24
+ export interface VirtualScreenInfo extends DeviceInfoCommon {
25
+ deviceType: "VirtualScreen";
26
+ }
27
+
28
+ export interface DisplayGroupInfo extends DeviceInfoCommon {
29
+ deviceType: "DisplayGroup";
30
+ }
31
+
32
+ export type DeviceInfo = DisplayInfo | VirtualScreenInfo | DisplayGroupInfo;
33
+
34
+ export class Device {
35
+ constructor(public readonly info: DeviceInfoCommon) {}
36
+ }
37
+
38
+ export class Display extends Device {
39
+ constructor(public override readonly info: DisplayInfo) {
40
+ super(info);
41
+ }
42
+ }
43
+
44
+ export class VirtualScreen extends Device {
45
+ constructor(public override readonly info: VirtualScreenInfo) {
46
+ super(info);
47
+ }
48
+ }
49
+
50
+ export class DisplayGroup extends Device {
51
+ constructor(public override readonly info: DisplayGroupInfo) {
52
+ super(info);
53
+ }
54
+ }
55
+
56
+ export function deviceFromInfo(info: DisplayInfo): Display;
57
+ export function deviceFromInfo(info: VirtualScreenInfo): VirtualScreen;
58
+ export function deviceFromInfo(info: DisplayGroupInfo): DisplayGroup;
59
+ export function deviceFromInfo(info: DeviceInfo): Device;
60
+ export function deviceFromInfo(info: DeviceInfo): Device {
61
+ switch (info.deviceType) {
62
+ case "Display": {
63
+ return new Display(info);
64
+ }
65
+ case "VirtualScreen": {
66
+ return new VirtualScreen(info);
67
+ }
68
+ case "DisplayGroup": {
69
+ return new DisplayGroup(info);
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,8 @@
1
+ import { expect, test } from "bun:test";
2
+ import { getAllDevices } from "./get";
3
+
4
+ test("getAllDevices", async () => {
5
+ const numConnected = (await getAllDevices({ ignoreDisplayGroups: true }))
6
+ .length;
7
+ expect(numConnected).toBeTypeOf("number");
8
+ });
package/src/get.ts ADDED
@@ -0,0 +1,45 @@
1
+ import type { styleText } from "node:util";
2
+ import { PrintableShellCommand } from "printable-shell-command";
3
+ import {
4
+ type Device,
5
+ type DeviceInfo,
6
+ type Display,
7
+ deviceFromInfo,
8
+ type VirtualScreen,
9
+ } from "./Device";
10
+
11
+ const styleTextFormat: Parameters<typeof styleText>[0] = [
12
+ "bold",
13
+ "gray",
14
+ ] as const;
15
+
16
+ export async function getAllDevices(options?: {
17
+ ignoreDisplayGroups: true;
18
+ }): Promise<(Display | VirtualScreen)[]>;
19
+ export async function getAllDevices(options?: {
20
+ ignoreDisplayGroups?: boolean;
21
+ }): Promise<Device[]> {
22
+ const jsonStream = await new PrintableShellCommand("betterdisplaycli", [
23
+ ["get", "--identifiers"],
24
+ ])
25
+ .print({ styleTextFormat })
26
+ .stdout()
27
+ .text();
28
+ const deviceInfos: DeviceInfo[] = JSON.parse(`[${jsonStream}]`);
29
+ return deviceInfos
30
+ .map((info) => deviceFromInfo(info))
31
+ .filter((device: Device) => {
32
+ return (
33
+ !options?.ignoreDisplayGroups ||
34
+ device.info.deviceType !== "DisplayGroup"
35
+ );
36
+ });
37
+ }
38
+
39
+ export async function connectAllDevices(): Promise<void> {
40
+ await new PrintableShellCommand("betterdisplaycli", [
41
+ ["perform", "--connectAllDisplays"],
42
+ ])
43
+ .print({ styleTextFormat })
44
+ .spawnTransparently().success;
45
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./Device";
2
+ export { connectAllDevices, getAllDevices } from "./get";
package/tsconfig.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "./node_modules/@cubing/dev-config/ts/es2022-types/tsconfig.json"
3
+ }