@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/src/theme-host.js
CHANGED
|
@@ -11,7 +11,10 @@ function agyThemeHost(bundle) {
|
|
|
11
11
|
// Bump when this file's code changes. A same-version host only takes the
|
|
12
12
|
// new bundle; an older one would keep running its old code through
|
|
13
13
|
// update(), so it is disposed and replaced (same task: no flash).
|
|
14
|
-
const HOST_VERSION =
|
|
14
|
+
const HOST_VERSION = 31; // also bump when a module it starts (context-card, turn-tokens, loading-/progress-indicator, quota rings, code-tools) changes: the host starts them once
|
|
15
|
+
// Sidecar frames (plugin://) have their own host, frame-host.js: this one
|
|
16
|
+
// is the window's
|
|
17
|
+
if (location.protocol === 'plugin:') return null;
|
|
15
18
|
const existing = window.__agyThemeHost;
|
|
16
19
|
if (existing?.version === HOST_VERSION) return existing.update(bundle);
|
|
17
20
|
existing?.dispose();
|
|
@@ -36,14 +39,22 @@ function agyThemeHost(bundle) {
|
|
|
36
39
|
// Fonts come as bytes and are registered with the FontFace API: no
|
|
37
40
|
// network fetch, no CSP font-src, and no 300 KB data URI in the CSS text.
|
|
38
41
|
const loadedFonts = new Set();
|
|
42
|
+
const fontBytes = new Map(); // base64 -> ArrayBuffer, decoded once (the frames ask for copies)
|
|
43
|
+
function fontBuffer(f) {
|
|
44
|
+
let buffer = fontBytes.get(f.data);
|
|
45
|
+
if (!buffer) {
|
|
46
|
+
buffer = Uint8Array.from(atob(f.data), (c) => c.charCodeAt(0)).buffer;
|
|
47
|
+
fontBytes.set(f.data, buffer);
|
|
48
|
+
}
|
|
49
|
+
return buffer;
|
|
50
|
+
}
|
|
39
51
|
function ensureFonts() {
|
|
40
52
|
for (const f of bundle.fonts?.[current.theme] ?? []) {
|
|
41
53
|
const key = `${f.family}|${f.weight}|${f.stretch}`;
|
|
42
54
|
if (loadedFonts.has(key) || !document.fonts) continue;
|
|
43
55
|
loadedFonts.add(key);
|
|
44
56
|
try {
|
|
45
|
-
const
|
|
46
|
-
const face = new FontFace(f.family, bytes.buffer, { weight: f.weight, stretch: f.stretch, style: 'normal', display: 'swap' });
|
|
57
|
+
const face = new FontFace(f.family, fontBuffer(f), { weight: f.weight, stretch: f.stretch, style: 'normal', display: 'swap' });
|
|
47
58
|
document.fonts.add(face);
|
|
48
59
|
face.load().catch(() => {});
|
|
49
60
|
} catch {
|
|
@@ -74,6 +85,7 @@ function agyThemeHost(bundle) {
|
|
|
74
85
|
watchMode();
|
|
75
86
|
if (!syncMode()) return;
|
|
76
87
|
announceTheme();
|
|
88
|
+
syncFrames();
|
|
77
89
|
// The app re-sends its own (mis-parsed) overlay color on theme changes
|
|
78
90
|
requestAnimationFrame(() => syncTitleBarOverlay());
|
|
79
91
|
setTimeout(syncTitleBarOverlay, 300);
|
|
@@ -81,6 +93,57 @@ function agyThemeHost(bundle) {
|
|
|
81
93
|
// The vendored md3e-web components cache their colors; the package's own
|
|
82
94
|
// window event makes them read the tokens again (the app uses no such event)
|
|
83
95
|
const announceTheme = () => window.dispatchEvent(new Event('theme-change'));
|
|
96
|
+
|
|
97
|
+
// ---- Sidecar frames --------------------------------------------------------
|
|
98
|
+
// A sidecar pane (Swarm) is a cross-origin plugin:// iframe with its own
|
|
99
|
+
// host, frame-host.js. This side tells it the theme, seed and mode: when a
|
|
100
|
+
// frame asks (it does on load) and to every frame when they change. It also
|
|
101
|
+
// repeats the app's own "theme-change" message, the tokens the app computes
|
|
102
|
+
// from this document and gives a sidecar once, on load: after a theme switch
|
|
103
|
+
// they would be the previous theme's.
|
|
104
|
+
const APP_TOKENS = '--background --foreground --primary --primary-foreground --secondary --secondary-foreground --muted --muted-foreground --placeholder --sidebar --sidebar-secondary --sidebar-muted --content --card --card-border --border --accent --code-foreground --error --editor-font-family'.split(' ');
|
|
105
|
+
const currentMode = () => document.documentElement?.getAttribute('data-agy-mode') || 'dark';
|
|
106
|
+
const themeMessage = () => ({ type: 'agy-theme-change', theme: current.theme, seed: current.seed ?? null, key: current.key, mode: currentMode() });
|
|
107
|
+
function tokensMessage() {
|
|
108
|
+
const cs = getComputedStyle(document.documentElement);
|
|
109
|
+
const cssVariables = {};
|
|
110
|
+
for (const name of APP_TOKENS) {
|
|
111
|
+
const value = cs.getPropertyValue(name).trim();
|
|
112
|
+
if (value) cssVariables[name] = value;
|
|
113
|
+
}
|
|
114
|
+
if (!cssVariables['--accent'] && cssVariables['--primary']) cssVariables['--accent'] = cssVariables['--primary'];
|
|
115
|
+
return { type: 'theme-change', payload: { themeType: currentMode(), cssVariables } };
|
|
116
|
+
}
|
|
117
|
+
function syncFrames() {
|
|
118
|
+
if (!document.documentElement || !current) return;
|
|
119
|
+
const frames = document.querySelectorAll('iframe[src^="plugin:"]');
|
|
120
|
+
if (!frames.length) return;
|
|
121
|
+
const theme = themeMessage(), tokens = tokensMessage();
|
|
122
|
+
for (const frame of frames) {
|
|
123
|
+
try {
|
|
124
|
+
frame.contentWindow?.postMessage(theme, '*');
|
|
125
|
+
frame.contentWindow?.postMessage(tokens, '*');
|
|
126
|
+
} catch {
|
|
127
|
+
// a frame between documents: it asks again when it loads
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function onFrameMessage(e) {
|
|
132
|
+
if (e.data?.type !== 'agy-theme-query' || !current) return;
|
|
133
|
+
const frame = e.source;
|
|
134
|
+
try {
|
|
135
|
+
if (!frame || frame === window || frame.parent !== window) return;
|
|
136
|
+
frame.postMessage(themeMessage(), '*');
|
|
137
|
+
const fonts = e.data.fonts === true ? bundle.fonts?.[current.theme] : null;
|
|
138
|
+
if (fonts?.length) {
|
|
139
|
+
const copies = fonts.map((f) => ({ family: f.family, weight: f.weight, stretch: f.stretch, buffer: fontBuffer(f).slice(0) }));
|
|
140
|
+
frame.postMessage({ type: 'agy-theme-fonts', theme: current.theme, fonts: copies }, '*', copies.map((f) => f.buffer));
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
// the frame went away
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
84
147
|
let modeTarget = null;
|
|
85
148
|
function watchMode() {
|
|
86
149
|
const target = document.body ?? document.documentElement;
|
|
@@ -118,6 +181,7 @@ function agyThemeHost(bundle) {
|
|
|
118
181
|
if (style.parentNode !== parent || style !== parent.lastElementChild) parent.appendChild(style);
|
|
119
182
|
}
|
|
120
183
|
announceTheme();
|
|
184
|
+
syncFrames();
|
|
121
185
|
renderMenu();
|
|
122
186
|
if (appMenuBuilt) buildAppMenu(false);
|
|
123
187
|
if (current.theme !== 'md3e') closeAppMenu();
|
|
@@ -195,7 +259,9 @@ function agyThemeHost(bundle) {
|
|
|
195
259
|
display: flex; align-items: center; padding: 0 8px; border: 0; border-radius: 6px;
|
|
196
260
|
background: transparent; color: var(--muted-foreground, #999); font: inherit; cursor: default;
|
|
197
261
|
}
|
|
198
|
-
|
|
262
|
+
/* hover and open: a state layer of the text color, so it shows on any
|
|
263
|
+
theme's surface (Nordic's --secondary is its card color) */
|
|
264
|
+
.trigger:hover, .trigger[aria-expanded="true"] { background: color-mix(in srgb, var(--foreground, #eee) 10%, transparent); color: var(--foreground, #eee); }
|
|
199
265
|
.menu {
|
|
200
266
|
position: absolute; top: calc(100% + 6px); left: 0; min-width: 230px; padding: 6px;
|
|
201
267
|
border: 1px solid var(--border, #333); border-radius: 12px;
|
|
@@ -208,7 +274,7 @@ function agyThemeHost(bundle) {
|
|
|
208
274
|
border-radius: 8px; font: inherit; color: inherit; cursor: default; outline: none;
|
|
209
275
|
}
|
|
210
276
|
[role="menuitemradio"]:hover, [role="menuitemradio"]:focus-visible,
|
|
211
|
-
.menu-sub-trigger:hover, .menu-sub-wrapper:hover .menu-sub-trigger { background: var(--
|
|
277
|
+
.menu-sub-trigger:hover, .menu-sub-wrapper:hover .menu-sub-trigger { background: color-mix(in srgb, var(--foreground, #eee) 10%, transparent); }
|
|
212
278
|
.swatch { width: 12px; height: 12px; border-radius: 999px; box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.2); }
|
|
213
279
|
.name { flex: 1; }
|
|
214
280
|
.check { visibility: hidden; }
|
|
@@ -503,15 +569,12 @@ function agyThemeHost(bundle) {
|
|
|
503
569
|
const forced = force === true;
|
|
504
570
|
if (!appMenu || (!forced && appMenu.open)) return;
|
|
505
571
|
if (appMenuBuilt && !forced) {
|
|
506
|
-
window.__buildAppMenuHitCache = (window.__buildAppMenuHitCache || 0) + 1;
|
|
507
572
|
for (const item of appMenu.querySelectorAll('md-menu-item[value^="theme:"]')) {
|
|
508
573
|
const val = item.getAttribute('value') || '';
|
|
509
574
|
item.selected = (val === `theme:${current?.key}`);
|
|
510
575
|
}
|
|
511
|
-
fixMenuStyle(appMenu);
|
|
512
576
|
return;
|
|
513
577
|
}
|
|
514
|
-
window.__buildAppMenuFullBuild = (window.__buildAppMenuFullBuild || 0) + 1;
|
|
515
578
|
appMenus = readAppMenus();
|
|
516
579
|
if (!appMenus.length && !forced) return;
|
|
517
580
|
const items = appMenus.map((m, i) => {
|
|
@@ -655,7 +718,7 @@ function agyThemeHost(bundle) {
|
|
|
655
718
|
if (!(bundle.ripple ?? []).includes(current?.theme) || e.button !== 0 || reducedMotion?.matches) return;
|
|
656
719
|
if (hostEl && e.composedPath().includes(hostEl)) return;
|
|
657
720
|
const target = e.target instanceof Element ? e.target.closest(RIPPLE_TARGETS) : null;
|
|
658
|
-
if (!target || target.isContentEditable) return;
|
|
721
|
+
if (!target || target.isContentEditable || target.matches('[role="menu"] [role^="menuitem"]') || target.closest('[role="menu"]')) return;
|
|
659
722
|
const x = e.clientX, y = e.clientY, start = performance.now();
|
|
660
723
|
let released = false, onRelease = null;
|
|
661
724
|
const release = () => {
|
|
@@ -667,14 +730,12 @@ function agyThemeHost(bundle) {
|
|
|
667
730
|
window.addEventListener('pointerup', release, true);
|
|
668
731
|
window.addEventListener('pointercancel', release, true);
|
|
669
732
|
requestAnimationFrame(() => {
|
|
733
|
+
if (!target.isConnected) return;
|
|
670
734
|
const box = target.getBoundingClientRect();
|
|
671
735
|
if (box.width < 8 || box.height < 8) return;
|
|
672
736
|
const cs = getComputedStyle(target);
|
|
673
|
-
|
|
674
|
-
const
|
|
675
|
-
const inset = shape?.content && shape.content !== 'none' ? parseFloat(shape.left) || 0 : 0;
|
|
676
|
-
const r = { left: box.left + inset, top: box.top, width: box.width - inset * 2, height: box.height };
|
|
677
|
-
const radius = inset ? shape.borderRadius : cs.borderRadius;
|
|
737
|
+
const r = { left: box.left, top: box.top, width: box.width, height: box.height };
|
|
738
|
+
const radius = cs.borderRadius;
|
|
678
739
|
if (!rippleLayer?.isConnected) {
|
|
679
740
|
rippleLayer = document.createElement('div');
|
|
680
741
|
rippleLayer.id = 'agy-ripple-layer';
|
|
@@ -683,7 +744,7 @@ function agyThemeHost(bundle) {
|
|
|
683
744
|
}
|
|
684
745
|
const clip = document.createElement('div');
|
|
685
746
|
clip.style.cssText = `position:absolute;left:${r.left}px;top:${r.top}px;width:${r.width}px;height:${r.height}px;border-radius:${radius};overflow:hidden;`;
|
|
686
|
-
const endR = Math.hypot(r.width, r.height) / 2 +
|
|
747
|
+
const endR = Math.hypot(r.width, r.height) / 2 + 16;
|
|
687
748
|
const startScale = (Math.max(r.width, r.height) * 0.3) / endR;
|
|
688
749
|
const cx = r.width / 2, cy = r.height / 2;
|
|
689
750
|
const ink = document.createElement('div');
|
|
@@ -705,6 +766,21 @@ function agyThemeHost(bundle) {
|
|
|
705
766
|
};
|
|
706
767
|
if (released) fadeOut();
|
|
707
768
|
else onRelease = fadeOut;
|
|
769
|
+
// The target may morph while the ripple plays (corners closing on
|
|
770
|
+
// press, a selection springing round): the clip follows its box and
|
|
771
|
+
// corners each frame until the ripple is gone, so the ink never shows
|
|
772
|
+
// outside the shape.
|
|
773
|
+
const follow = () => {
|
|
774
|
+
if (!clip.isConnected || !target.isConnected) return;
|
|
775
|
+
const b = target.getBoundingClientRect();
|
|
776
|
+
clip.style.left = b.left + 'px';
|
|
777
|
+
clip.style.top = b.top + 'px';
|
|
778
|
+
clip.style.width = b.width + 'px';
|
|
779
|
+
clip.style.height = b.height + 'px';
|
|
780
|
+
clip.style.borderRadius = getComputedStyle(target).borderRadius;
|
|
781
|
+
requestAnimationFrame(follow);
|
|
782
|
+
};
|
|
783
|
+
requestAnimationFrame(follow);
|
|
708
784
|
});
|
|
709
785
|
}
|
|
710
786
|
|
|
@@ -769,8 +845,6 @@ function agyThemeHost(bundle) {
|
|
|
769
845
|
function onPointerDown(e) {
|
|
770
846
|
startRipple(e);
|
|
771
847
|
if (hostEl && !e.composedPath().includes(hostEl)) closeMenu();
|
|
772
|
-
const transcript = document.querySelector('[data-testid="autoscroll-viewport"]');
|
|
773
|
-
if (transcript && !transcript.classList.contains('no-focus-agent-input')) transcript.classList.add('no-focus-agent-input');
|
|
774
848
|
}
|
|
775
849
|
function cycleTheme() {
|
|
776
850
|
const keys = [...options.keys()];
|
|
@@ -789,47 +863,7 @@ function agyThemeHost(bundle) {
|
|
|
789
863
|
window.addEventListener('focus', syncTitleBarOverlay);
|
|
790
864
|
document.fonts?.addEventListener('loadingdone', position);
|
|
791
865
|
|
|
792
|
-
|
|
793
|
-
// Antigravity virtualizes the sidebar and auxiliary panels with TanStack
|
|
794
|
-
// Virtual. On mounting long lists (e.g. 299 diffs in Review or 95 sidebar
|
|
795
|
-
// items), TanStack Virtual synchronously reads offsetHeight on every
|
|
796
|
-
// [data-index] element while dirtying container styles between each item.
|
|
797
|
-
// In a large conversation, each forced reflow costs 80-100ms, causing a 2s+
|
|
798
|
-
// freeze upon entry. This guard serves known fixed heights or cached values
|
|
799
|
-
// for virtual rows, preventing layout thrashing while letting ResizeObserver
|
|
800
|
-
// handle real dynamic size changes asynchronously on idle frames.
|
|
801
|
-
const origOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight')?.get;
|
|
802
|
-
const virtualHeightCache = new WeakMap();
|
|
803
|
-
if (origOffsetHeight) {
|
|
804
|
-
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
|
|
805
|
-
get() {
|
|
806
|
-
if (this.hasAttribute('data-index')) {
|
|
807
|
-
const cached = virtualHeightCache.get(this);
|
|
808
|
-
if (cached !== undefined) return cached;
|
|
809
|
-
if (this.firstElementChild?.classList.contains('sticky')) {
|
|
810
|
-
virtualHeightCache.set(this, 33);
|
|
811
|
-
return 33;
|
|
812
|
-
}
|
|
813
|
-
if (this.classList.contains('bg-sidebar') || this.querySelector('h2')) {
|
|
814
|
-
virtualHeightCache.set(this, 24);
|
|
815
|
-
return 24;
|
|
816
|
-
}
|
|
817
|
-
if (this.querySelector('.agy-sidebar-row') || this.classList.contains('pb-[1px]')) {
|
|
818
|
-
virtualHeightCache.set(this, 33);
|
|
819
|
-
return 33;
|
|
820
|
-
}
|
|
821
|
-
if (!this.textContent.trim()) {
|
|
822
|
-
virtualHeightCache.set(this, 16);
|
|
823
|
-
return 16;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
const val = origOffsetHeight.call(this);
|
|
827
|
-
if (this.hasAttribute('data-index')) virtualHeightCache.set(this, val);
|
|
828
|
-
return val;
|
|
829
|
-
},
|
|
830
|
-
configurable: true,
|
|
831
|
-
});
|
|
832
|
-
}
|
|
866
|
+
window.addEventListener('message', onFrameMessage);
|
|
833
867
|
|
|
834
868
|
// Start the Overview panel's Context card; every theme gets it
|
|
835
869
|
const contextCard = typeof agyContextCard === 'function' ? agyContextCard() : null;
|
|
@@ -839,6 +873,8 @@ function agyThemeHost(bundle) {
|
|
|
839
873
|
const loadingIndicator = typeof agyLoadingIndicator === 'function' ? agyLoadingIndicator() : null;
|
|
840
874
|
// ...and wavy progress bars on running background work
|
|
841
875
|
const progressIndicators = typeof agyProgressIndicators === 'function' ? agyProgressIndicators() : null;
|
|
876
|
+
// ...and the M3 Expressive gap in the usage rings
|
|
877
|
+
const quotaRings = typeof agyQuotaRings === 'function' ? agyQuotaRings() : null;
|
|
842
878
|
// ...and Wrap lines / Line numbers in every code block's header
|
|
843
879
|
const codeTools = typeof agyCodeTools === 'function' ? agyCodeTools() : null;
|
|
844
880
|
|
|
@@ -848,6 +884,7 @@ function agyThemeHost(bundle) {
|
|
|
848
884
|
window.removeEventListener('keydown', onKeyDown, true);
|
|
849
885
|
window.removeEventListener('resize', position);
|
|
850
886
|
window.removeEventListener('focus', syncTitleBarOverlay);
|
|
887
|
+
window.removeEventListener('message', onFrameMessage);
|
|
851
888
|
document.fonts?.removeEventListener('loadingdone', position);
|
|
852
889
|
hostEl?.remove();
|
|
853
890
|
document.documentElement?.removeAttribute('data-agy-appmenu');
|
|
@@ -856,24 +893,18 @@ function agyThemeHost(bundle) {
|
|
|
856
893
|
turnTokens?.dispose();
|
|
857
894
|
loadingIndicator?.dispose();
|
|
858
895
|
progressIndicators?.dispose();
|
|
896
|
+
quotaRings?.dispose();
|
|
859
897
|
codeTools?.dispose();
|
|
860
|
-
if (origOffsetHeight) {
|
|
861
|
-
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
|
|
862
|
-
get: origOffsetHeight,
|
|
863
|
-
configurable: true,
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
898
|
document.querySelectorAll('[data-agy-ghost]').forEach((g) => g.remove());
|
|
867
899
|
document.getElementById('agy-theme-css')?.remove();
|
|
868
900
|
document.documentElement?.removeAttribute('data-agy-theme');
|
|
869
901
|
document.documentElement?.removeAttribute('data-agy-seed');
|
|
870
902
|
modeObserver.disconnect();
|
|
871
903
|
document.documentElement?.removeAttribute('data-agy-mode');
|
|
872
|
-
document.querySelectorAll('[data-testid="autoscroll-viewport"].no-focus-agent-input')
|
|
873
|
-
.forEach((el) => el.classList.remove('no-focus-agent-input'));
|
|
874
904
|
syncTitleBarOverlay();
|
|
875
905
|
delete window.__agyThemeHost;
|
|
876
906
|
delete window.agyTheme;
|
|
907
|
+
delete window.__agyPageScript; // the page script may run here again
|
|
877
908
|
}
|
|
878
909
|
|
|
879
910
|
const api = {
|
package/src/turn-tokens.js
CHANGED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
AGY "Matte" — sidecar frames (the Swarm pane)
|
|
3
|
+
|
|
4
|
+
Loaded by frame-host.js inside the plugin:// frame, after matte-tokens.css.
|
|
5
|
+
Swarm's own design is already Matte's: monochrome, quiet, no outlines on
|
|
6
|
+
the rows. So this is mostly the token map; the few rules below bring it in
|
|
7
|
+
line with the window: raised things carry the 1px hairline, radii follow
|
|
8
|
+
the panel's, and nothing loops.
|
|
9
|
+
========================================================================== */
|
|
10
|
+
|
|
11
|
+
:root[data-agy-theme="matte"] {
|
|
12
|
+
color-scheme: dark;
|
|
13
|
+
|
|
14
|
+
/* The app's tokens. It sends the window's values (--background there is
|
|
15
|
+
the window, not this panel), and the sidecar SDK writes them inline on
|
|
16
|
+
<html>: hence !important. This frame sits in the panel. */
|
|
17
|
+
--background: var(--m-panel) !important;
|
|
18
|
+
--foreground: var(--m-text) !important;
|
|
19
|
+
--primary: var(--m-primary) !important;
|
|
20
|
+
--primary-foreground: var(--m-primary-ink) !important;
|
|
21
|
+
--secondary: var(--m-high) !important;
|
|
22
|
+
--secondary-foreground: var(--m-text-2) !important;
|
|
23
|
+
--muted: var(--m-raised) !important;
|
|
24
|
+
--muted-foreground: var(--m-text-3) !important;
|
|
25
|
+
--placeholder: var(--m-text-3) !important;
|
|
26
|
+
--card: var(--m-raised) !important;
|
|
27
|
+
--card-border: var(--m-line-2) !important;
|
|
28
|
+
--border: var(--m-line) !important;
|
|
29
|
+
--accent: var(--m-primary) !important;
|
|
30
|
+
--error: var(--m-error) !important;
|
|
31
|
+
|
|
32
|
+
/* Swarm's own tokens */
|
|
33
|
+
--sw-bg: var(--m-panel);
|
|
34
|
+
--sw-card: var(--m-raised);
|
|
35
|
+
--sw-secondary: var(--m-high);
|
|
36
|
+
--sw-muted: var(--m-inset);
|
|
37
|
+
--sw-fg: var(--m-text);
|
|
38
|
+
--sw-fg-2: var(--m-text-2);
|
|
39
|
+
--sw-fg-muted: var(--m-text-3);
|
|
40
|
+
--sw-fg-faint: var(--m-text-3);
|
|
41
|
+
--sw-border: var(--m-line-2);
|
|
42
|
+
--sw-card-border: var(--m-line);
|
|
43
|
+
--sw-primary: var(--m-primary);
|
|
44
|
+
--sw-primary-fg: var(--m-primary-ink);
|
|
45
|
+
--sw-error: var(--m-error);
|
|
46
|
+
--sw-success: var(--m-success);
|
|
47
|
+
--sw-warning: var(--m-warning);
|
|
48
|
+
--sw-hover: var(--m-hover);
|
|
49
|
+
--sw-press: var(--m-select);
|
|
50
|
+
--sw-ring: var(--m-line-3);
|
|
51
|
+
--sw-line: var(--m-line);
|
|
52
|
+
--sw-fill: var(--m-select);
|
|
53
|
+
--sw-field: var(--m-raised);
|
|
54
|
+
--r-sm: 5px;
|
|
55
|
+
--r-md: 8px;
|
|
56
|
+
--r-lg: 10px;
|
|
57
|
+
}
|
|
58
|
+
:root[data-agy-theme="matte"][data-agy-mode="light"] {
|
|
59
|
+
color-scheme: light;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:root[data-agy-theme="matte"] {
|
|
63
|
+
/* Type stays Swarm's own (Google Sans Flex, the app's face): Matte leaves
|
|
64
|
+
the window's UI font alone too */
|
|
65
|
+
/* Scrollbars: Swarm styles a slim thumb (::-webkit-scrollbar), but its
|
|
66
|
+
scrollbar-width: thin on every element switches Chromium to the native
|
|
67
|
+
bar, arrow buttons and all, and those rules never apply */
|
|
68
|
+
&,
|
|
69
|
+
* {
|
|
70
|
+
scrollbar-width: auto;
|
|
71
|
+
scrollbar-color: auto;
|
|
72
|
+
}
|
|
73
|
+
body {
|
|
74
|
+
background: var(--sw-bg) !important; /* the SDK's inline body background is the window's */
|
|
75
|
+
}
|
|
76
|
+
.swarm-title {
|
|
77
|
+
color: var(--m-text-max);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Nothing loops: a status dot is a dot, the skeleton a still placeholder */
|
|
81
|
+
.live-dot::after {
|
|
82
|
+
content: none;
|
|
83
|
+
}
|
|
84
|
+
.skel {
|
|
85
|
+
animation: none;
|
|
86
|
+
background: var(--m-select);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Raised things carry the hairline: the fields and the floating button */
|
|
90
|
+
.search,
|
|
91
|
+
.pane .o2-msearch {
|
|
92
|
+
border-color: var(--m-line);
|
|
93
|
+
background: var(--m-raised);
|
|
94
|
+
}
|
|
95
|
+
.search:focus-within,
|
|
96
|
+
.pane .o2-msearch:focus-within {
|
|
97
|
+
border-color: var(--m-line-3);
|
|
98
|
+
background: var(--m-raised);
|
|
99
|
+
}
|
|
100
|
+
.jump {
|
|
101
|
+
border-color: var(--m-line-2);
|
|
102
|
+
background: var(--m-raised);
|
|
103
|
+
box-shadow: 0 6px 18px -8px var(--m-shadow);
|
|
104
|
+
}
|
|
105
|
+
.jump:hover {
|
|
106
|
+
background: var(--m-high);
|
|
107
|
+
}
|
|
108
|
+
.toast {
|
|
109
|
+
border: 1px solid var(--m-line-2);
|
|
110
|
+
background: var(--m-high);
|
|
111
|
+
box-shadow: 0 10px 28px -12px var(--m-shadow-menu);
|
|
112
|
+
color: var(--m-text);
|
|
113
|
+
}
|
|
114
|
+
/* The list: Chromium's customizable select (appearance: base-select)
|
|
115
|
+
draws the picker in the page, so it follows the theme (a raised panel with a hairline, rows 30px) */
|
|
116
|
+
.select select,
|
|
117
|
+
.select select::picker(select) {
|
|
118
|
+
appearance: base-select;
|
|
119
|
+
}
|
|
120
|
+
.select select::picker-icon {
|
|
121
|
+
display: none; /* the wrapper draws the arrow */
|
|
122
|
+
}
|
|
123
|
+
.select select {
|
|
124
|
+
align-items: center; /* a base-select is a flex box: its label would sit at the top */
|
|
125
|
+
}
|
|
126
|
+
.select select::picker(select) {
|
|
127
|
+
margin-block: 4px;
|
|
128
|
+
padding: 4px;
|
|
129
|
+
border: 1px solid var(--m-line-2);
|
|
130
|
+
border-radius: 10px;
|
|
131
|
+
background: var(--m-raised);
|
|
132
|
+
box-shadow: 0 16px 40px -12px var(--m-shadow-menu);
|
|
133
|
+
color: var(--m-text);
|
|
134
|
+
}
|
|
135
|
+
.select select option {
|
|
136
|
+
display: flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
gap: 6px;
|
|
139
|
+
min-height: 30px;
|
|
140
|
+
padding: 0 10px;
|
|
141
|
+
border-radius: 6px;
|
|
142
|
+
background: transparent;
|
|
143
|
+
color: var(--m-text);
|
|
144
|
+
font-size: 13px;
|
|
145
|
+
}
|
|
146
|
+
.select select option:is(:hover, :focus-visible) {
|
|
147
|
+
background: var(--m-hover);
|
|
148
|
+
outline: none;
|
|
149
|
+
}
|
|
150
|
+
.select select option:checked {
|
|
151
|
+
background: var(--m-select);
|
|
152
|
+
color: var(--m-text-max);
|
|
153
|
+
}
|
|
154
|
+
.select select option::checkmark {
|
|
155
|
+
order: 1;
|
|
156
|
+
margin-inline-start: auto;
|
|
157
|
+
font-size: 12px;
|
|
158
|
+
}
|
|
159
|
+
.select select option:not(:checked)::checkmark {
|
|
160
|
+
display: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* The one bright thing stays the window's send button: the unread tint
|
|
164
|
+
and the "new" divider are neutral here */
|
|
165
|
+
.feed .msg.unread-by-me {
|
|
166
|
+
background: var(--m-select);
|
|
167
|
+
}
|
|
168
|
+
.divider {
|
|
169
|
+
color: var(--m-text-2);
|
|
170
|
+
}
|
|
171
|
+
.divider::after {
|
|
172
|
+
background: var(--m-line-2);
|
|
173
|
+
}
|
|
174
|
+
.msg-channel {
|
|
175
|
+
background: var(--m-select);
|
|
176
|
+
}
|
|
177
|
+
.msg-channel:hover {
|
|
178
|
+
background: var(--m-high);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* hive.css cards: raised, hairline, the window's 12px */
|
|
182
|
+
.pane .o2 {
|
|
183
|
+
--o2-card: var(--m-raised);
|
|
184
|
+
--o2-border: var(--m-line);
|
|
185
|
+
--o2-info: var(--m-blue);
|
|
186
|
+
}
|
|
187
|
+
.pane .o2-lead,
|
|
188
|
+
.pane .o2-card,
|
|
189
|
+
.pane .o2-goal,
|
|
190
|
+
.pane .o2-run,
|
|
191
|
+
.pane .o2-prog,
|
|
192
|
+
.pane .o2-th,
|
|
193
|
+
.pane .o2-reel {
|
|
194
|
+
border-radius: 12px;
|
|
195
|
+
}
|
|
196
|
+
.pane .o2-rundot {
|
|
197
|
+
box-shadow: none;
|
|
198
|
+
}
|
|
199
|
+
.pane .o2-mrow.bees {
|
|
200
|
+
border-radius: 10px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Graph: nodes are raised chips on the panel */
|
|
204
|
+
.graph .node-box {
|
|
205
|
+
fill: var(--m-raised);
|
|
206
|
+
stroke: var(--m-line-2);
|
|
207
|
+
}
|
|
208
|
+
.graph .node:hover .node-box {
|
|
209
|
+
stroke: var(--m-line-3);
|
|
210
|
+
}
|
|
211
|
+
}
|