@materialwebunofficial/antigravity-themes 0.0.0-stage → 1.0.4

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/lib/paths.mjs ADDED
@@ -0,0 +1,35 @@
1
+ // Where Antigravity writes its debug port, and where this tool installs
2
+ // itself, per platform. Windows is tested; macOS and Linux follow Electron's
3
+ // conventions for an app named "Antigravity".
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+
7
+ const home = os.homedir();
8
+
9
+ // Electron's userData folder for Antigravity
10
+ export function appDataDir() {
11
+ if (process.platform === 'win32') return path.join(process.env.APPDATA ?? path.join(home, 'AppData', 'Roaming'), 'Antigravity');
12
+ if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'Antigravity');
13
+ return path.join(process.env.XDG_CONFIG_HOME ?? path.join(home, '.config'), 'Antigravity');
14
+ }
15
+
16
+ // Antigravity starts with --remote-debugging-port=0 and writes the port it
17
+ // got here (first line), anew on every launch
18
+ export const portFile = () => path.join(appDataDir(), 'DevToolsActivePort');
19
+
20
+ export function installDir() {
21
+ if (process.platform === 'win32') return path.join(process.env.LOCALAPPDATA ?? path.join(home, 'AppData', 'Local'), 'antigravity-themes');
22
+ if (process.platform === 'darwin') return path.join(home, 'Library', 'Application Support', 'antigravity-themes');
23
+ return path.join(process.env.XDG_DATA_HOME ?? path.join(home, '.local', 'share'), 'antigravity-themes');
24
+ }
25
+
26
+ // Where Antigravity's Electron application resources reside
27
+ export function appResourcesDir() {
28
+ if (process.platform === 'win32') return path.join(process.env.LOCALAPPDATA ?? path.join(home, 'AppData', 'Local'), 'Programs', 'Antigravity', 'resources');
29
+ if (process.platform === 'darwin') return '/Applications/Antigravity.app/Contents/Resources';
30
+ return '/opt/Antigravity/resources';
31
+ }
32
+
33
+ // The background process writes how to reach it here while it runs
34
+ export const stateFile = () => path.join(installDir(), 'run.json');
35
+ export const logFile = () => path.join(installDir(), 'run.log');
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@materialwebunofficial/antigravity-themes",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.4",
4
+ "description": "Matte, Paper, Nordic, and Material 3 Expressive themes for Antigravity Desktop, applied without changing the app's files.",
5
+ "homepage": "https://github.com/materialwebunofficial/antigravity-themes#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/materialwebunofficial/antigravity-themes.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/materialwebunofficial/antigravity-themes/issues"
12
+ },
13
+ "type": "module",
14
+ "bin": {
15
+ "antigravity-themes": "bin/cli.mjs"
16
+ },
17
+ "files": [
18
+ "bin/",
19
+ "lib/",
20
+ "src/",
21
+ "themes/",
22
+ "vendor/",
23
+ "LICENSE",
24
+ "NOTICE",
25
+ "README.md"
26
+ ],
27
+ "engines": {
28
+ "node": ">=22"
29
+ },
30
+ "keywords": [
31
+ "antigravity",
32
+ "theme",
33
+ "material-design",
34
+ "material-3",
35
+ "material-3-expressive",
36
+ "md3e"
37
+ ],
38
+ "license": "Apache-2.0",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }
@@ -0,0 +1,137 @@
1
+ // AGY code tools: two toggles in every chat code block's header, beside
2
+ // "Copy code": Wrap lines (the app's default; off scrolls long lines
3
+ // sideways) and Line numbers. One choice for all code blocks, kept in the
4
+ // agy-code cookie like the theme, and applied as attributes on <html>.
5
+ //
6
+ // The buttons are the app's own header buttons (its classes, a Material
7
+ // Symbols glyph at 14px; Outlined like the app's icons, md3e.css swaps in
8
+ // Rounded), so each theme styles them as it styles Copy code. Line numbers
9
+ // are a CSS counter on the app's .code-line rows (::before): nothing is
10
+ // added to the rows React renders, and the numbers stay out of selections
11
+ // and of Copy code. Themes color them through --agy-lineno.
12
+ function agyCodeTools() {
13
+ const COOKIE = 'agy-code';
14
+ const COPY = 'button[aria-label="Copy code"], button[aria-label="Copied"]';
15
+ const BUTTON_CLASSES = 'appearance-none bg-transparent border-0 p-0 cursor-pointer text-secondary-foreground hover:text-foreground transition-colors';
16
+ const TOOLS = [
17
+ { key: 'wrap', label: 'Wrap lines', on: (p) => !p.nowrap, // wrap_text
18
+ icon: 'M570 -192 432 -330 570 -468 621 -417 569 -366H672Q702 -366 723 -387Q744 -408 744 -438Q744 -468 723 -489Q702 -510 672 -510H192V-582H672Q732 -582 774 -540Q816 -498 816 -438Q816 -378 774 -336Q732 -294 672 -294H570L621 -243ZM192 -294V-366H384V-294ZM192 -726V-798H768V-726Z' },
19
+ { key: 'lines', label: 'Line numbers', on: (p) => p.lines, // format_list_numbered
20
+ icon: 'M144 -144V-192H240V-216H192V-264H240V-288H144V-336H264Q274 -336 281 -329Q288 -322 288 -312V-264Q288 -254 281 -247Q274 -240 264 -240Q274 -240 281 -233Q288 -226 288 -216V-168Q288 -158 281 -151Q274 -144 264 -144ZM144 -384V-480Q144 -490 151 -497Q158 -504 168 -504H240V-528H144V-576H264Q274 -576 281 -569Q288 -562 288 -552V-480Q288 -470 281 -463Q274 -456 264 -456H192V-432H288V-384ZM192 -624V-768H144V-816H240V-624ZM360 -240V-312H816V-240ZM360 -444V-516H816V-444ZM360 -648V-720H816V-648Z' },
21
+ ];
22
+
23
+ const style = document.createElement('style');
24
+ style.id = 'agy-code-css';
25
+ style.textContent = `
26
+ .agy-code-tool { display: inline-flex; align-items: center; justify-content: center; }
27
+ .agy-code-tool[aria-pressed="true"] { color: var(--foreground); }
28
+ :root[data-agy-code-lines] .agy-code { counter-reset: agy-line; }
29
+ :root[data-agy-code-lines] .agy-code .code-line { counter-increment: agy-line; }
30
+ :root[data-agy-code-lines] .agy-code .code-line::before {
31
+ content: counter(agy-line);
32
+ flex: none; min-width: 2ch; margin-right: 16px; text-align: right;
33
+ color: var(--agy-lineno, var(--editor-line-number-foreground, rgb(128 128 128 / 0.5)));
34
+ font-variant-numeric: tabular-nums; user-select: none;
35
+ }
36
+ :root[data-agy-code-nowrap] .agy-code { white-space: pre; word-break: normal; }
37
+ :root[data-agy-code-nowrap] .agy-code > :last-child { overflow-x: auto; }
38
+ :root[data-agy-code-nowrap] .agy-code .line-content { white-space: pre !important; word-break: normal; } /* inline pre-wrap */`;
39
+ (document.head || document.documentElement).appendChild(style);
40
+
41
+ // ---- The choice ---------------------------------------------------------------
42
+ const readPref = () => {
43
+ try {
44
+ const flags = ((document.cookie.match(/(?:^|;\s*)agy-code=([^;]*)/) || [])[1] || '').split('.');
45
+ return { nowrap: flags.includes('nowrap'), lines: flags.includes('lines') };
46
+ } catch {
47
+ return { nowrap: false, lines: false };
48
+ }
49
+ };
50
+ let pref = readPref();
51
+ function apply() {
52
+ const root = document.documentElement;
53
+ root.toggleAttribute('data-agy-code-nowrap', pref.nowrap);
54
+ root.toggleAttribute('data-agy-code-lines', pref.lines);
55
+ for (const b of document.querySelectorAll('.agy-code-tool')) {
56
+ const tool = TOOLS.find((t) => t.key === b.dataset.tool);
57
+ b.setAttribute('aria-pressed', String(tool.on(pref)));
58
+ }
59
+ }
60
+ function toggle(key) {
61
+ if (key === 'wrap') pref.nowrap = !pref.nowrap;
62
+ else pref.lines = !pref.lines;
63
+ const value = [pref.nowrap && 'nowrap', pref.lines && 'lines'].filter(Boolean).join('.');
64
+ try {
65
+ document.cookie = `${COOKIE}=${value}; Max-Age=31536000; Path=/; SameSite=Strict; Secure`;
66
+ } catch {}
67
+ apply();
68
+ }
69
+
70
+ // ---- The buttons --------------------------------------------------------------
71
+ // Header markup: block > [header > [language, buttons…], body]
72
+ function decorate(copy) {
73
+ const group = copy.parentElement, header = group?.parentElement, block = header?.parentElement;
74
+ if (!block || !header.nextElementSibling || group.querySelector(':scope > .agy-code-tool')) return;
75
+ block.classList.add('agy-code');
76
+ for (const tool of TOOLS) {
77
+ const b = document.createElement('button');
78
+ b.type = 'button';
79
+ b.className = `${BUTTON_CLASSES} agy-code-tool`;
80
+ b.dataset.tool = tool.key;
81
+ b.setAttribute('aria-label', tool.label);
82
+ b.setAttribute('aria-pressed', String(tool.on(pref)));
83
+ b.innerHTML = `<svg viewBox="0 -960 960 960" fill="currentColor" class="h-3.5 w-3.5" aria-hidden="true"><path d="${tool.icon}"></path></svg>`;
84
+ b.addEventListener('click', (e) => {
85
+ e.stopPropagation();
86
+ toggle(tool.key);
87
+ });
88
+ group.insertBefore(b, copy);
89
+ }
90
+ }
91
+
92
+ // Code blocks mount with their message: look inside what the transcript
93
+ // adds, and rescan it every 2s for a replaced viewport
94
+ let viewport = null, observer = null;
95
+ function onMutations(records) {
96
+ try {
97
+ for (const r of records) {
98
+ for (const n of r.addedNodes) {
99
+ if (n.nodeType !== 1 || n.classList.contains('agy-code-tool')) continue;
100
+ if (n.matches(COPY)) decorate(n);
101
+ else n.querySelectorAll(COPY).forEach(decorate);
102
+ }
103
+ }
104
+ } catch {
105
+ // app internals changed: leave the code blocks as they are
106
+ }
107
+ }
108
+ function scan() {
109
+ if (document.hidden) return;
110
+ const vp = document.querySelector('[data-testid="autoscroll-viewport"]');
111
+ if (vp !== viewport) {
112
+ observer?.disconnect();
113
+ viewport = vp;
114
+ if (vp) {
115
+ observer = new MutationObserver(onMutations);
116
+ observer.observe(vp, { childList: true, subtree: true });
117
+ }
118
+ }
119
+ viewport?.querySelectorAll(COPY).forEach(decorate);
120
+ }
121
+
122
+ apply();
123
+ const timer = setInterval(scan, 2000);
124
+ scan();
125
+
126
+ return {
127
+ dispose() {
128
+ clearInterval(timer);
129
+ observer?.disconnect();
130
+ document.querySelectorAll('.agy-code-tool').forEach((b) => b.remove());
131
+ document.querySelectorAll('.agy-code').forEach((b) => b.classList.remove('agy-code'));
132
+ document.documentElement.removeAttribute('data-agy-code-nowrap');
133
+ document.documentElement.removeAttribute('data-agy-code-lines');
134
+ style.remove();
135
+ },
136
+ };
137
+ }