@materialwebunofficial/antigravity-themes 1.0.4 → 1.0.13
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/README.md +173 -34
- package/bin/cli.mjs +91 -47
- package/lib/asar.mjs +163 -0
- package/lib/bundle.mjs +70 -17
- package/lib/injector.mjs +39 -6
- package/lib/keeper.mjs +73 -0
- package/lib/native.mjs +165 -0
- package/lib/paths.mjs +3 -2
- package/lib/plugin.mjs +98 -0
- package/package.json +1 -1
- package/src/code-tools.js +1 -1
- package/src/context-card.js +1 -1
- package/src/frame-host.js +282 -0
- package/src/loading-indicator.js +1 -1
- package/src/progress-indicators.js +49 -1
- package/src/theme-host.js +97 -66
- package/src/turn-tokens.js +1 -1
- package/themes/frames/matte.css +211 -0
- package/themes/frames/md3e.css +1410 -0
- package/themes/frames/nordic.css +252 -0
- package/themes/frames/paper.css +298 -0
- package/themes/index.json +18 -12
- package/themes/matte-tokens.css +98 -0
- package/themes/matte.css +465 -135
- package/themes/md3e.css +1124 -164
- package/themes/nordic-tokens.css +60 -0
- package/themes/nordic.css +457 -67
- package/themes/paper-tokens.css +54 -0
- package/themes/paper.css +508 -68
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# antigravity-themes
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
(in five colors).
|
|
3
|
+
Four themes for Antigravity Desktop: **Matte**, **Nordic Frost**, **E-Ink /
|
|
4
|
+
Paper** and **Material 3 Expressive** (in five colors). Each one restyles the
|
|
5
|
+
whole app in its own structure, Settings, menus, dialogs and sidecar panes
|
|
6
|
+
such as Swarm included, rather than recoloring Antigravity's default look.
|
|
5
7
|
|
|
6
8
|
This is an unofficial project. It is not affiliated with, endorsed by or
|
|
7
9
|
sponsored by Google.
|
|
@@ -9,12 +11,18 @@ sponsored by Google.
|
|
|
9
11
|
## Install
|
|
10
12
|
|
|
11
13
|
```sh
|
|
12
|
-
npx -y @materialwebunofficial/antigravity-themes install
|
|
14
|
+
npx -y @materialwebunofficial/antigravity-themes install --native
|
|
13
15
|
```
|
|
14
16
|
|
|
15
17
|
That is all. It takes a few seconds, needs no administrator rights, and it is
|
|
16
|
-
permanent: the themes
|
|
17
|
-
|
|
18
|
+
permanent: the themes stay after closing Antigravity, after restarting the
|
|
19
|
+
computer and after Antigravity updates itself, with nothing running in the
|
|
20
|
+
background and nothing to reinstall. It needs [Node.js](https://nodejs.org) 22
|
|
21
|
+
or newer (only to run the command; the themes do not need it afterwards).
|
|
22
|
+
|
|
23
|
+
Without `--native`, `install` leaves Antigravity's files untouched and applies
|
|
24
|
+
the themes from a small background process instead (see
|
|
25
|
+
[What install does](#what-install-does)).
|
|
18
26
|
|
|
19
27
|
On Windows, if PowerShell says running scripts is disabled, use `npx.cmd` in
|
|
20
28
|
place of `npx`.
|
|
@@ -50,12 +58,44 @@ code 0 means installed and permanent.
|
|
|
50
58
|
|
|
51
59
|
## Requirements
|
|
52
60
|
|
|
53
|
-
- Antigravity Desktop. Tested with
|
|
61
|
+
- Antigravity Desktop. Tested with versions 2.31.0 and 2.31.2 on Windows 11
|
|
62
|
+
(the keeper carried the themes across that update on its own).
|
|
54
63
|
macOS and Linux are supported in the code but untested.
|
|
55
64
|
- [Node.js](https://nodejs.org) 22 or newer. Check with `node --version`.
|
|
56
65
|
|
|
57
66
|
## What install does
|
|
58
67
|
|
|
68
|
+
### `install --native` (recommended)
|
|
69
|
+
|
|
70
|
+
1. Stops a background process from an earlier `install`, if any, and removes
|
|
71
|
+
its logon entry and its folder, so an older version cannot replace the
|
|
72
|
+
themes.
|
|
73
|
+
2. Writes the themes into Antigravity's own files. Antigravity loads
|
|
74
|
+
`resources/app` before its `app.asar` archive, so the app is extracted
|
|
75
|
+
there once and two of its files are patched: the window's preload
|
|
76
|
+
(`dist/preload.js`) runs the theme script, and the main process
|
|
77
|
+
(`dist/main.js`) runs the frame script in each sidecar pane (Swarm), which
|
|
78
|
+
a preload cannot reach. A stamp, `resources/app/agy-themes.json`, records
|
|
79
|
+
which build of the themes is in place.
|
|
80
|
+
3. Adds a small Antigravity plugin, `~/.gemini/config/plugins/antigravity-themes`,
|
|
81
|
+
holding the **keeper**. Antigravity itself starts the keeper (as a plugin
|
|
82
|
+
sidecar, with its own built-in Node) every time it launches:
|
|
83
|
+
- when the themes are in place, the keeper exits at once;
|
|
84
|
+
- after an Antigravity update, which replaces `resources`, it extracts and
|
|
85
|
+
patches the app again (about a second) and themes the open windows right
|
|
86
|
+
away, so the update shows the themes on its first start.
|
|
87
|
+
4. Applies the themes to the open windows and panes now.
|
|
88
|
+
|
|
89
|
+
While Antigravity runs, Windows keeps its `app.asar` locked; if an earlier
|
|
90
|
+
version of this tool had patched that archive, `install --native` says so.
|
|
91
|
+
It is harmless (Antigravity loads `resources/app`); run the command again with
|
|
92
|
+
Antigravity closed to restore the original archive.
|
|
93
|
+
|
|
94
|
+
Turning the plugin off in Antigravity stops the keeper: the themes then last
|
|
95
|
+
until the next Antigravity update.
|
|
96
|
+
|
|
97
|
+
### `install` (background process)
|
|
98
|
+
|
|
59
99
|
1. Copies this package to a folder in your user profile:
|
|
60
100
|
|
|
61
101
|
| System | Folder |
|
|
@@ -79,9 +119,6 @@ code 0 means installed and permanent.
|
|
|
79
119
|
To skip this, use `npx -y @materialwebunofficial/antigravity-themes install --no-autostart` and
|
|
80
120
|
run `npx -y @materialwebunofficial/antigravity-themes start` when you want the themes.
|
|
81
121
|
|
|
82
|
-
**Direct / Native Install (Zero background processes):**
|
|
83
|
-
Use `npx -y @materialwebunofficial/antigravity-themes install --native` to inject the themes directly into Antigravity's Electron resources. This runs with ZERO background processes, no listening ports, and no startup entries.
|
|
84
|
-
|
|
85
122
|
3. Starts a small background Node.js process. It applies the theme to every
|
|
86
123
|
Antigravity window, keeps it applied when a window reloads, and waits
|
|
87
124
|
quietly while Antigravity is closed.
|
|
@@ -91,7 +128,8 @@ code 0 means installed and permanent.
|
|
|
91
128
|
|
|
92
129
|
## Choosing a theme
|
|
93
130
|
|
|
94
|
-
- **Matte**: open the **Theme** menu in
|
|
131
|
+
- **Matte**, **Nordic Frost**, **E-Ink / Paper**: open the **Theme** menu in
|
|
132
|
+
the title bar.
|
|
95
133
|
- **Material 3 Expressive**: the title bar menus (Antigravity, Workspace,
|
|
96
134
|
View, Window, Theme) are gathered into one menu button at the top left.
|
|
97
135
|
Open it, then **Theme**.
|
|
@@ -102,7 +140,10 @@ code 0 means installed and permanent.
|
|
|
102
140
|
|
|
103
141
|
| Key | Theme |
|
|
104
142
|
| ------------- | ----- |
|
|
143
|
+
| `antigravity` | Antigravity's own look (the themes off) |
|
|
105
144
|
| `matte` | Matte (the default) |
|
|
145
|
+
| `nordic` | Nordic Frost |
|
|
146
|
+
| `paper` | E-Ink / Paper |
|
|
106
147
|
| `md3e-teal` | Material 3 Expressive, Teal |
|
|
107
148
|
| `md3e-ocean` | Material 3 Expressive, Ocean |
|
|
108
149
|
| `md3e-forest` | Material 3 Expressive, Forest |
|
|
@@ -110,42 +151,102 @@ code 0 means installed and permanent.
|
|
|
110
151
|
| `md3e-coral` | Material 3 Expressive, Coral |
|
|
111
152
|
|
|
112
153
|
Your choice is remembered. Light and dark follow Antigravity's own
|
|
113
|
-
appearance setting;
|
|
154
|
+
appearance setting; every theme has a light and a dark version.
|
|
114
155
|
|
|
115
156
|
## What the themes change
|
|
116
157
|
|
|
158
|
+
Each theme has its own layout language, carried through the Settings dialog
|
|
159
|
+
(navigation, page titles, groups, rows, switches, selects, buttons, segmented
|
|
160
|
+
controls, sliders, fields, option cards, key caps, usage rings and token
|
|
161
|
+
bars), dialogs, menus, the "Restart to Update" button and the Swarm pane.
|
|
162
|
+
None of them loops an animation (the app's own pulsing update button is
|
|
163
|
+
stilled), and hovers keep text at WCAG AA contrast or better.
|
|
164
|
+
|
|
117
165
|
**Matte** is quiet and fast: a near-black window holding one rounded panel,
|
|
118
166
|
small neutral steps between surfaces, 1px lines for structure, and no
|
|
119
|
-
gradients, blur
|
|
167
|
+
gradients, blur or glow. Settings reads like an inspector: a darker index
|
|
168
|
+
column with a bright bar on the current page, plain rows ruled by hairlines
|
|
169
|
+
under small uppercase labels, compact 28px controls in raised gray, mono key
|
|
170
|
+
caps, and a switch that is the one bright thing when on.
|
|
171
|
+
|
|
172
|
+
**Nordic Frost** uses the [Nord](https://www.nordtheme.com) palette: Polar
|
|
173
|
+
Night surfaces, Snow Storm text, Frost accents, Aurora for status. Settings
|
|
174
|
+
is a preferences window: a raised index column with Frost group headings,
|
|
175
|
+
every group a solid tile with grooves of the base color between its rows,
|
|
176
|
+
flat filled controls with no strokes, and Frost marking what is on or chosen.
|
|
177
|
+
|
|
178
|
+
**E-Ink / Paper** is strict monochrome for reading: ink on paper (or paper
|
|
179
|
+
on ink), a serif for headings and prose, square corners, rules instead of
|
|
180
|
+
shadows, and no in-between animation frames. Settings is a printed form: a
|
|
181
|
+
serif index with the current page printed in reverse, small-capital group
|
|
182
|
+
titles under a heavy rule, ruled boxes with dashed lines between entries,
|
|
183
|
+
square ink controls that print in reverse when chosen, and fields drawn as
|
|
184
|
+
lines to write on.
|
|
120
185
|
|
|
121
186
|
**Material 3 Expressive** maps the Material 3 Expressive design system onto
|
|
122
187
|
the app: its color schemes, the Roboto type scale, shapes, state layers,
|
|
123
188
|
ripples and spring motion; Material Symbols Rounded icons; a navigation
|
|
124
|
-
drawer style sidebar
|
|
189
|
+
drawer style sidebar (title-small group headlines, dividers between the
|
|
190
|
+
destinations and every group); the app menu button; a shape-morphing loading
|
|
125
191
|
indicator while the agent works; and wavy progress bars for running
|
|
126
192
|
background commands and tasks. The menu and the indicators are the
|
|
127
|
-
[md3e-web](https://materialwebunofficial.github.io) components.
|
|
128
|
-
|
|
129
|
-
|
|
193
|
+
[md3e-web](https://materialwebunofficial.github.io) components. In detail:
|
|
194
|
+
|
|
195
|
+
- **Settings** follows M3's list-detail layout: a navigation drawer beside
|
|
196
|
+
the page, which is a pane with large corners; settings are M3 Expressive
|
|
197
|
+
segmented lists (items 2px apart, 4px corners inside a group, 20px
|
|
198
|
+
outside) under title-small primary category headers; M3 switches (the
|
|
199
|
+
handle springs on the compositor), sliders, connected button groups (the
|
|
200
|
+
selection fades and morphs, the pressed button widens), filled text
|
|
201
|
+
fields, outlined selects whose lists are M3 menus, and filled or tonal
|
|
202
|
+
buttons whose corners close when pressed. Shape morphs run between real
|
|
203
|
+
radii, never from the 9999px "full" token, so the spring's overshoot
|
|
204
|
+
cannot flash square corners, and a ripple follows its button's box and
|
|
205
|
+
corners while it plays.
|
|
206
|
+
- **Usage** (the model menu's View Usage, and Settings > Models & Usage):
|
|
207
|
+
M3 circular progress indicators, with the gap between the arc and its
|
|
208
|
+
track; the menu's usage view is a list of two-line menu items.
|
|
209
|
+
- **Dialogs** are M3 dialogs, **artifact cards** (plans, reports) are filled
|
|
210
|
+
cards with a filled Proceed and a tonal split Open button, and **Restart to
|
|
211
|
+
Update** is an extra-small filled button with a refresh icon.
|
|
212
|
+
|
|
213
|
+
Every theme also adds:
|
|
130
214
|
|
|
131
215
|
- **Context** at the top of the Overview panel: how full the conversation's
|
|
132
216
|
context window is and what takes it up (system prompt, tools, rules, MCP
|
|
133
217
|
tools, skills and so on), with a warning at 80% and at 95%. The app does
|
|
134
218
|
not report context window sizes, so they are assumed by model family:
|
|
135
|
-
Gemini
|
|
219
|
+
Gemini 256k tokens, Claude 200k, GPT-OSS 128k.
|
|
136
220
|
- **Token counts** under each finished agent turn, for example
|
|
137
221
|
`2.5k in · 358 out · 142k cache`.
|
|
138
222
|
- **Wrap lines** and **Line numbers** buttons beside "Copy code" in chat
|
|
139
223
|
code blocks. Line numbers are not copied.
|
|
140
224
|
|
|
225
|
+
Sidecar panes such as **Swarm** follow the theme too. Each is a separate page
|
|
226
|
+
(a `plugin://` frame) with its own styles, so every theme has a frame style
|
|
227
|
+
sheet for it (`themes/frames/`), loaded inside the frame by a small frame
|
|
228
|
+
script. In Material 3 Expressive the Swarm board is a list of tonal rows with
|
|
229
|
+
primary tabs, a search bar, filter chips and an M3 switch and slider, and its
|
|
230
|
+
Live status is the md3e-web loading indicator: still while the board is
|
|
231
|
+
connected, morphing when new posts arrive, running while it reconnects.
|
|
232
|
+
Swarm's dropdowns use Chromium's customizable select (`appearance:
|
|
233
|
+
base-select`), so their lists are drawn in the page and follow each theme: an
|
|
234
|
+
M3 menu in Material 3 Expressive, a hairline panel in Matte, a Polar Night
|
|
235
|
+
tile in Nordic Frost, a ruled box in E-Ink / Paper.
|
|
236
|
+
|
|
237
|
+
Chips inside a sent message (quotes, files, terminal output) take the
|
|
238
|
+
message bubble's own ink in every theme, so a quote stays readable on any
|
|
239
|
+
bubble color.
|
|
240
|
+
|
|
141
241
|
## Commands
|
|
142
242
|
|
|
143
243
|
Run each as `npx -y @materialwebunofficial/antigravity-themes <command>`.
|
|
144
244
|
|
|
145
245
|
| Command | What it does |
|
|
146
246
|
| ------- | ------------ |
|
|
247
|
+
| `install --native` | Write the themes into Antigravity and add the keeper plugin: no background process, kept across restarts and updates (see above) |
|
|
147
248
|
| `install [--no-autostart]` | Install, add the logon entry, and start the background process (see above) |
|
|
148
|
-
| `uninstall` | Remove everything
|
|
249
|
+
| `uninstall` | Remove everything either install added (see below) |
|
|
149
250
|
| `start` | Start the background process |
|
|
150
251
|
| `stop` | Stop it. Open windows keep the theme until they reload |
|
|
151
252
|
| `on` | Apply the themes to the open windows once, without the background process |
|
|
@@ -157,11 +258,18 @@ Run each as `npx -y @materialwebunofficial/antigravity-themes <command>`.
|
|
|
157
258
|
|
|
158
259
|
## Update
|
|
159
260
|
|
|
261
|
+
Antigravity updates need nothing: with the native install, the keeper puts
|
|
262
|
+
the themes back at the first start after the update.
|
|
263
|
+
|
|
264
|
+
To get a newer version of the themes:
|
|
265
|
+
|
|
160
266
|
```sh
|
|
161
|
-
npx -y @materialwebunofficial/antigravity-themes@latest install
|
|
267
|
+
npx -y @materialwebunofficial/antigravity-themes@latest install --native
|
|
162
268
|
```
|
|
163
269
|
|
|
164
|
-
This
|
|
270
|
+
This patches the app with the new build, replaces the keeper's copy and
|
|
271
|
+
applies the new themes to the open windows. (With the background install,
|
|
272
|
+
`install` without `--native` replaces its copy and restarts the process.)
|
|
165
273
|
|
|
166
274
|
## Uninstall
|
|
167
275
|
|
|
@@ -169,15 +277,34 @@ This replaces the installed copy and restarts the background process.
|
|
|
169
277
|
npx -y @materialwebunofficial/antigravity-themes uninstall
|
|
170
278
|
```
|
|
171
279
|
|
|
172
|
-
This
|
|
173
|
-
|
|
280
|
+
This removes the keeper plugin, takes the themes out of Antigravity's files
|
|
281
|
+
(and, with Antigravity closed, deletes the extracted `resources/app` so the
|
|
282
|
+
app runs from its own `app.asar` again), restores the app's own look in the
|
|
283
|
+
open windows, and stops a background process and deletes its logon entry and
|
|
284
|
+
install folder if there are any.
|
|
174
285
|
|
|
175
286
|
Three small cookies stay in Antigravity's own storage (see Privacy). They
|
|
176
287
|
have no effect without this tool.
|
|
177
288
|
|
|
178
289
|
## How it works
|
|
179
290
|
|
|
180
|
-
|
|
291
|
+
**Native install.** Electron loads an app folder, `resources/app`, before the
|
|
292
|
+
`app.asar` archive. The installer extracts the archive there and adds the
|
|
293
|
+
scripts to two of its files: the window's preload runs the theme script in
|
|
294
|
+
every window as it loads, and the main process runs the frame script in each
|
|
295
|
+
sidecar frame (`plugin://`, such as Swarm) as it navigates. Nothing listens
|
|
296
|
+
on a port and nothing stays running.
|
|
297
|
+
|
|
298
|
+
An update replaces `resources`, so the themes would be gone after it. The
|
|
299
|
+
keeper plugin covers that: Antigravity discovers plugins in
|
|
300
|
+
`~/.gemini/config/plugins/`, outside its install folder, and starts their
|
|
301
|
+
sidecars itself at every launch. The keeper compares the stamp in
|
|
302
|
+
`resources/app` with its own build; when they differ (or the folder is gone)
|
|
303
|
+
it extracts and patches the app from its own copy of this package, then
|
|
304
|
+
applies the themes to the windows that are already open over the app's local
|
|
305
|
+
debugging port, and exits.
|
|
306
|
+
|
|
307
|
+
**Background install.** Antigravity starts with the Chrome DevTools Protocol enabled on a local
|
|
181
308
|
port, on every launch, and writes that port number to a file named
|
|
182
309
|
`DevToolsActivePort` in its user data folder (`%APPDATA%\Antigravity` on
|
|
183
310
|
Windows, `~/Library/Application Support/Antigravity` on macOS,
|
|
@@ -191,6 +318,11 @@ each window:
|
|
|
191
318
|
(the menu, the code block buttons, the indicators), and
|
|
192
319
|
- registers the same script to run again whenever the window reloads.
|
|
193
320
|
|
|
321
|
+
In each sidecar frame (`plugin://`, such as Swarm) it runs the frame script
|
|
322
|
+
instead: only the frame host and the themes' frame style sheets. The frame
|
|
323
|
+
asks the window which theme, seed and light or dark mode is on, and the window
|
|
324
|
+
tells every frame when that changes. No other frame is touched.
|
|
325
|
+
|
|
194
326
|
To find and stop the background process, the other commands talk to it on
|
|
195
327
|
`127.0.0.1` through a random port and a random token that it writes to
|
|
196
328
|
`run.json` in the install folder while it runs.
|
|
@@ -219,17 +351,23 @@ Things to know:
|
|
|
219
351
|
| `agy-code` | The Wrap lines and Line numbers settings |
|
|
220
352
|
| `agy-usage` | Running token totals for your last 20 conversations, by conversation id |
|
|
221
353
|
|
|
222
|
-
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
354
|
+
- Native install: `resources/app/agy-themes.json` (the build in place and
|
|
355
|
+
the Antigravity version), and `keeper.log` in the data folder Antigravity
|
|
356
|
+
gives the keeper (what the keeper did at each start; started anew at
|
|
357
|
+
256 KB).
|
|
358
|
+
- Background install, in the install folder: `run.json` (process id, control
|
|
359
|
+
port, token and version, while running), `run.log` (when windows were found
|
|
360
|
+
and themed, with their internal addresses; started anew when it reaches
|
|
361
|
+
512 KB) and, on Windows, `start.vbs` (starts the background process with no
|
|
362
|
+
window).
|
|
226
363
|
|
|
227
364
|
## Compatibility and known limits
|
|
228
365
|
|
|
229
|
-
- The themes are written against Antigravity 2.
|
|
366
|
+
- The themes are written against Antigravity 2.31's interface (its class
|
|
230
367
|
names, element ids and component data). An app update can break parts of
|
|
231
368
|
them until this package is updated. If that happens,
|
|
232
|
-
`npx -y @materialwebunofficial/antigravity-themes
|
|
369
|
+
`npx -y @materialwebunofficial/antigravity-themes theme antigravity`
|
|
370
|
+
restores the app's own look at once, and `uninstall` removes everything.
|
|
233
371
|
- Windows 11 is tested. macOS and Linux follow the platforms' usual folders
|
|
234
372
|
and logon entries but have not been tested.
|
|
235
373
|
- The context window sizes are assumptions (see above).
|
|
@@ -241,8 +379,9 @@ Things to know:
|
|
|
241
379
|
- `npx -y @materialwebunofficial/antigravity-themes status` shows whether the themes are installed,
|
|
242
380
|
whether the background process runs, and which Antigravity windows are
|
|
243
381
|
themed.
|
|
244
|
-
- The log
|
|
245
|
-
|
|
382
|
+
- The logs are `keeper.log` (native install) and `run.log` in the install
|
|
383
|
+
folder (background install).
|
|
384
|
+
- Antigravity looks wrong after an update: run `npx -y @materialwebunofficial/antigravity-themes theme antigravity`
|
|
246
385
|
and [open an issue](https://github.com/materialwebunofficial/antigravity-themes/issues) with your Antigravity version.
|
|
247
386
|
|
|
248
387
|
## Development
|
|
@@ -262,9 +401,9 @@ at a time.
|
|
|
262
401
|
| Path | Contents |
|
|
263
402
|
| ---- | -------- |
|
|
264
403
|
| `bin/cli.mjs` | The commands |
|
|
265
|
-
| `lib/` | Finding Antigravity, the DevTools connection, logon entries |
|
|
266
|
-
| `src/` | The scripts that run in the app: the theme host (menus, theme switching, ripples) and the context, token, code block and indicator features |
|
|
267
|
-
| `themes/` | `index.json` (the theme list) and
|
|
404
|
+
| `lib/` | Finding Antigravity, the DevTools connection, logon entries, the native install (`native.mjs`, with `asar.mjs` for the archive), the keeper (`keeper.mjs`) and its plugin (`plugin.mjs`) |
|
|
405
|
+
| `src/` | The scripts that run in the app: the theme host (menus, theme switching, ripples), the frame host for sidecar panes, and the context, token, code block and indicator features |
|
|
406
|
+
| `themes/` | `index.json` (the theme list), each theme's tokens and CSS, and `frames/`: each theme's style sheet for sidecar panes |
|
|
268
407
|
| `vendor/md3e/` | The md3e-web components, the Roboto font, and their licenses |
|
|
269
408
|
| `tools/` | Generators for files in `themes/` and `vendor/` (below) |
|
|
270
409
|
|
package/bin/cli.mjs
CHANGED
|
@@ -4,10 +4,13 @@ import { randomUUID } from 'node:crypto';
|
|
|
4
4
|
import { appendFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, watch as watchFiles, writeFileSync } from 'node:fs';
|
|
5
5
|
import http from 'node:http';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import { ROOT, pageScript, REMOVE_SCRIPT, PROBE_SCRIPT, selectScript } from '../lib/bundle.mjs';
|
|
8
|
-
import { readPort, evaluateEverywhere, watch } from '../lib/injector.mjs';
|
|
7
|
+
import { ROOT, pageScript, frameScript, REMOVE_SCRIPT, PROBE_SCRIPT, selectScript } from '../lib/bundle.mjs';
|
|
8
|
+
import { readPort, evaluateEverywhere, watch, scriptFor } from '../lib/injector.mjs';
|
|
9
9
|
import { installDir, stateFile, logFile, portFile, appResourcesDir } from '../lib/paths.mjs';
|
|
10
10
|
import { launch, writeAutostart, removeAutostart, hasAutostart, autostartFile } from '../lib/autostart.mjs';
|
|
11
|
+
import { appState, buildId, ensureApp, patchApp, removeLegacyArchive, restoreArchive, unpatchApp } from '../lib/native.mjs';
|
|
12
|
+
import { installPlugin, pluginDir, removePlugin } from '../lib/plugin.mjs';
|
|
13
|
+
import { removeTree } from '../lib/asar.mjs';
|
|
11
14
|
|
|
12
15
|
const NAME = 'antigravity-themes';
|
|
13
16
|
const pkg = JSON.parse(readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
@@ -101,7 +104,7 @@ async function run() {
|
|
|
101
104
|
process.exit(1);
|
|
102
105
|
});
|
|
103
106
|
log(`${NAME} ${pkg.version} running from ${ROOT}`);
|
|
104
|
-
const watcher = watch({ getSource: () => pageScript(ROOT), log });
|
|
107
|
+
const watcher = watch({ getSource: () => scriptFor({ page: pageScript(ROOT), frame: frameScript(ROOT) }), log });
|
|
105
108
|
if (has('--dev')) {
|
|
106
109
|
let timer;
|
|
107
110
|
for (const dir of ['src', 'themes', 'vendor']) {
|
|
@@ -118,50 +121,66 @@ async function run() {
|
|
|
118
121
|
const RUNTIME = ['bin', 'lib', 'src', 'themes', 'vendor', 'package.json', 'README.md', 'LICENSE', 'NOTICE'];
|
|
119
122
|
|
|
120
123
|
const isThemed = (result) => Boolean(result.value && JSON.parse(result.value));
|
|
124
|
+
// Only the targets the themes go to: windows and sidecar frames
|
|
125
|
+
const PROBE = scriptFor({ page: PROBE_SCRIPT, frame: PROBE_SCRIPT });
|
|
121
126
|
|
|
122
127
|
// Waits for the background process to reach the open windows, so that install
|
|
123
128
|
// can report the result itself. Throws when Antigravity is not open.
|
|
124
129
|
async function themedWindows() {
|
|
125
130
|
let results = [];
|
|
126
131
|
for (let i = 0; i < 40; i++) {
|
|
127
|
-
results = await evaluateEverywhere(
|
|
132
|
+
results = await evaluateEverywhere(PROBE);
|
|
128
133
|
if (results.length && results.every(isThemed)) break;
|
|
129
134
|
await sleep(200);
|
|
130
135
|
}
|
|
131
136
|
return { themed: results.filter(isThemed).length, total: results.length };
|
|
132
137
|
}
|
|
133
138
|
|
|
139
|
+
// The permanent install: the scripts go into Antigravity's own files
|
|
140
|
+
// (lib/native.mjs), and the keeper plugin (lib/plugin.mjs, lib/keeper.mjs)
|
|
141
|
+
// puts them back after an Antigravity update. Nothing of ours keeps running.
|
|
134
142
|
async function installNative() {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const appDir = path.join(resDir, 'app');
|
|
139
|
-
if (!existsSync(asarPath) && !existsSync(appDir)) {
|
|
140
|
-
fail(`Could not find Antigravity resources at ${resDir}. Is Antigravity installed?`);
|
|
143
|
+
const resources = appResourcesDir();
|
|
144
|
+
if (!existsSync(path.join(resources, 'app.asar')) && !existsSync(path.join(resources, 'app'))) {
|
|
145
|
+
fail(`Could not find Antigravity resources at ${resources}. Is Antigravity installed?`);
|
|
141
146
|
}
|
|
142
|
-
await stopBackground();
|
|
143
|
-
removeAutostart();
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
if (await stopBackground()) say('Stopped the background process of an earlier install.');
|
|
148
|
+
const entry = removeAutostart();
|
|
149
|
+
if (entry) say(`Removed its logon entry: ${entry}`);
|
|
150
|
+
// the background install's own copy of the package is no longer used
|
|
151
|
+
const stale = installDir();
|
|
152
|
+
const background = ['bin', 'start.vbs', 'run.json'].some((f) => existsSync(path.join(stale, f)));
|
|
153
|
+
if (path.resolve(stale) !== path.resolve(ROOT) && background) {
|
|
154
|
+
try {
|
|
155
|
+
removeTree(stale);
|
|
156
|
+
say(`Removed the earlier background install: ${stale}`);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
say(`Could not remove the earlier background install at ${stale} (${err.code || err.message}); it is unused.`);
|
|
159
|
+
}
|
|
148
160
|
}
|
|
149
161
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const marker = '// Antigravity Native Theme Injection (Zero background processes)';
|
|
157
|
-
if (!preloadContent.includes(marker)) {
|
|
158
|
-
const patch = `\n\n${marker}\n(function() {\n try {\n const electron = require('electron');\n const path = require('path');\n const fs = require('fs');\n const b = path.join(__dirname, 'theme-bundle.js');\n if (fs.existsSync(b)) {\n const c = fs.readFileSync(b, 'utf8');\n if (electron.webFrame) electron.webFrame.executeJavaScript(c);\n }\n } catch (e) {}\n})();\n`;
|
|
159
|
-
appendFileSync(preloadPath, patch, 'utf8');
|
|
162
|
+
const page = pageScript(ROOT), frame = frameScript(ROOT), build = buildId(page, frame);
|
|
163
|
+
try {
|
|
164
|
+
if (restoreArchive(resources)) say("Restored the app's own app.asar (an earlier version had patched it).");
|
|
165
|
+
} catch (err) {
|
|
166
|
+
say(`app.asar still holds an earlier patch: it is in use while Antigravity runs (${err.code || err.message}).`);
|
|
167
|
+
say(' Harmless, Antigravity loads resources/app; run install again with Antigravity closed to restore it.');
|
|
160
168
|
}
|
|
169
|
+
if (removeLegacyArchive(resources)) say('Removed app.asar.patched, left by an earlier build.');
|
|
170
|
+
if (ensureApp(resources)) say(`Extracted the app to ${path.join(resources, 'app')}.`);
|
|
171
|
+
const { appVersion } = patchApp(path.join(resources, 'app'), { page, frame, build });
|
|
172
|
+
say(`Patched Antigravity ${appVersion}: the themes load with every start.`);
|
|
173
|
+
say(`Keeper plugin: ${installPlugin(ROOT, { build, version: pkg.version })}`);
|
|
174
|
+
say(' Antigravity runs it at each start; after an update it puts the themes back.');
|
|
161
175
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
176
|
+
try {
|
|
177
|
+
const results = await evaluateEverywhere(scriptFor({ page, frame }));
|
|
178
|
+
const applied = results.filter((r) => !r.error).length;
|
|
179
|
+
say(applied ? `Applied to ${applied} open window(s) and pane(s) now.` : 'Antigravity is open: the themes appear in new windows.');
|
|
180
|
+
} catch {
|
|
181
|
+
say('Antigravity is not open: the themes appear when it starts.');
|
|
182
|
+
}
|
|
183
|
+
say('Done. Permanent, across restarts and updates, with no background process and no logon entry.');
|
|
165
184
|
}
|
|
166
185
|
|
|
167
186
|
async function install() {
|
|
@@ -206,9 +225,13 @@ async function install() {
|
|
|
206
225
|
}
|
|
207
226
|
|
|
208
227
|
async function uninstall() {
|
|
228
|
+
const plugin = removePlugin();
|
|
229
|
+
if (plugin) say(`Removed the keeper plugin: ${plugin}`);
|
|
209
230
|
if (await stopBackground()) say('Stopped the background process.');
|
|
231
|
+
let open = false;
|
|
210
232
|
try {
|
|
211
233
|
const results = await evaluateEverywhere(REMOVE_SCRIPT);
|
|
234
|
+
open = true;
|
|
212
235
|
say(`Removed the theme from ${results.filter((r) => !r.error).length} open window(s).`);
|
|
213
236
|
} catch {
|
|
214
237
|
say('Antigravity is not running: nothing to remove from its windows.');
|
|
@@ -217,19 +240,32 @@ async function uninstall() {
|
|
|
217
240
|
if (entry) say(`Removed the logon entry: ${entry}`);
|
|
218
241
|
const dir = installDir();
|
|
219
242
|
if (path.basename(dir) === NAME && existsSync(dir)) {
|
|
220
|
-
|
|
243
|
+
removeTree(dir);
|
|
221
244
|
say(`Deleted ${dir}`);
|
|
222
245
|
}
|
|
223
|
-
const
|
|
246
|
+
const resources = appResourcesDir();
|
|
247
|
+
try {
|
|
248
|
+
if (restoreArchive(resources)) say("Restored the app's own app.asar.");
|
|
249
|
+
} catch (err) {
|
|
250
|
+
say(`Could not restore app.asar now (${err.code || err.message}): close Antigravity and run uninstall again.`);
|
|
251
|
+
}
|
|
252
|
+
removeLegacyArchive(resources);
|
|
253
|
+
const appDir = path.join(resources, 'app');
|
|
224
254
|
if (existsSync(appDir)) {
|
|
225
|
-
|
|
226
|
-
|
|
255
|
+
if (open) {
|
|
256
|
+
// the running app loads its windows from this folder: leave it, unpatched
|
|
257
|
+
unpatchApp(appDir);
|
|
258
|
+
say(`Took the themes out of ${appDir}. Close Antigravity and run uninstall again to delete that copy of the app.`);
|
|
259
|
+
} else {
|
|
260
|
+
removeTree(appDir);
|
|
261
|
+
say(`Removed ${appDir}: Antigravity runs from its own app.asar again.`);
|
|
262
|
+
}
|
|
227
263
|
}
|
|
228
264
|
say('Done. Antigravity keeps your last theme choice in its own storage; it has no effect without this tool.');
|
|
229
265
|
}
|
|
230
266
|
|
|
231
267
|
async function on() {
|
|
232
|
-
const results = await evaluateEverywhere(pageScript(ROOT));
|
|
268
|
+
const results = await evaluateEverywhere(scriptFor({ page: pageScript(ROOT), frame: frameScript(ROOT) }));
|
|
233
269
|
const ok = results.filter((r) => !r.error).length;
|
|
234
270
|
say(`Applied to ${ok} window(s).${await running() ? '' : ' Without the background process it lasts until a window reloads; "install" makes it permanent.'}`);
|
|
235
271
|
}
|
|
@@ -257,17 +293,22 @@ async function theme() {
|
|
|
257
293
|
}
|
|
258
294
|
|
|
259
295
|
async function status() {
|
|
296
|
+
const resources = appResourcesDir();
|
|
297
|
+
const app = appState(resources);
|
|
298
|
+
const native = app.stamp && app.extractedVersion === app.appVersion;
|
|
299
|
+
const keeper = existsSync(path.join(pluginDir(), 'sidecars', 'keeper', 'sidecar.json'));
|
|
300
|
+
say(`Native: ${native ? `patched (build ${app.stamp.build}, Antigravity ${app.appVersion})` : app.stamp ? `outdated (made for ${app.extractedVersion}, Antigravity is ${app.appVersion})` : 'no'}`);
|
|
301
|
+
say(`Keeper: ${keeper ? pluginDir() : 'no'}`);
|
|
260
302
|
const dir = installDir();
|
|
261
303
|
let installed = null;
|
|
262
304
|
try { installed = JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8')).version; } catch { /* not installed */ }
|
|
263
|
-
say(`
|
|
264
|
-
say(`At logon: ${hasAutostart() ? autostartFile() : 'no'}`);
|
|
305
|
+
say(`Background: ${installed ? `${installed} in ${dir}` : 'not installed'}${hasAutostart() ? `, at logon (${autostartFile()})` : ''}`);
|
|
265
306
|
const state = await running();
|
|
266
|
-
|
|
307
|
+
if (state) say(` running (process ${state.pid}, version ${state.version})`);
|
|
267
308
|
try {
|
|
268
309
|
const port = readPort();
|
|
269
|
-
const results = await evaluateEverywhere(
|
|
270
|
-
say(`Antigravity: running, port ${port}, ${results.length} window(s)`);
|
|
310
|
+
const results = await evaluateEverywhere(PROBE, port);
|
|
311
|
+
say(`Antigravity: running, port ${port}, ${results.length} window(s) and pane(s)`);
|
|
271
312
|
for (const r of results) {
|
|
272
313
|
const host = r.value ? JSON.parse(r.value) : null;
|
|
273
314
|
say(` ${host ? `themed (${host.theme})` : 'not themed'} ${r.url}`);
|
|
@@ -276,10 +317,12 @@ async function status() {
|
|
|
276
317
|
say(`Antigravity: ${existsSync(portFile()) ? err.message : `not running (no ${portFile()})`}`);
|
|
277
318
|
}
|
|
278
319
|
say();
|
|
279
|
-
if (
|
|
280
|
-
say('OK: installed
|
|
320
|
+
if (native && keeper) {
|
|
321
|
+
say('OK: installed natively, kept across Antigravity updates.');
|
|
322
|
+
} else if (installed && hasAutostart() && state) {
|
|
323
|
+
say('OK: installed with the background process.');
|
|
281
324
|
} else {
|
|
282
|
-
say(`Not permanent. Fix it with: ${NPX} install`);
|
|
325
|
+
say(`Not permanent. Fix it with: ${NPX} install --native`);
|
|
283
326
|
process.exitCode = 1;
|
|
284
327
|
}
|
|
285
328
|
}
|
|
@@ -289,16 +332,17 @@ function help() {
|
|
|
289
332
|
|
|
290
333
|
Usage: ${NPX} <command>
|
|
291
334
|
|
|
292
|
-
install
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
335
|
+
install --native Install the themes permanently, into Antigravity's own files: no background
|
|
336
|
+
process, no logon entry. A small plugin puts them back after an Antigravity update.
|
|
337
|
+
install The older way: copies the themes to ${installDir()} and themes Antigravity
|
|
338
|
+
from a background process started at logon.
|
|
339
|
+
uninstall Remove the themes, the plugin, the background process and its logon entry
|
|
296
340
|
start Start the background process that themes every window (not permanent)
|
|
297
341
|
stop Stop it (open windows keep the theme until they reload)
|
|
298
342
|
on Apply the themes to the open windows once (not permanent)
|
|
299
343
|
off Stop the background process and remove the themes from the open windows
|
|
300
344
|
theme [key] List the themes, or switch every window to one
|
|
301
|
-
status Show what is installed and running; exit code 1 if not permanent
|
|
345
|
+
status Show what is installed and running; exit code 1 if it is not permanent
|
|
302
346
|
run [--dev] Run the background process in this terminal (--dev reloads on file changes)`);
|
|
303
347
|
}
|
|
304
348
|
|