replicas-computer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/assets/desktop/brand-wallpaper.svg +54 -0
- package/assets/desktop/geist-mono.woff2 +0 -0
- package/assets/desktop/gtk-settings.ini +11 -0
- package/assets/desktop/novnc-index.html +282 -0
- package/assets/desktop/openbox-rc.xml +79 -0
- package/assets/desktop/openbox-theme/close.xbm +7 -0
- package/assets/desktop/openbox-theme/close_hover.xbm +7 -0
- package/assets/desktop/openbox-theme/close_pressed.xbm +7 -0
- package/assets/desktop/openbox-theme/iconify.xbm +7 -0
- package/assets/desktop/openbox-theme/iconify_hover.xbm +7 -0
- package/assets/desktop/openbox-theme/iconify_pressed.xbm +7 -0
- package/assets/desktop/openbox-theme/max.xbm +7 -0
- package/assets/desktop/openbox-theme/max_hover.xbm +7 -0
- package/assets/desktop/openbox-theme/max_pressed.xbm +7 -0
- package/assets/desktop/openbox-theme/max_toggled.xbm +7 -0
- package/assets/desktop/openbox-theme/max_toggled_hover.xbm +7 -0
- package/assets/desktop/openbox-theme/max_toggled_pressed.xbm +7 -0
- package/assets/desktop/openbox-theme/themerc +66 -0
- package/assets/desktop/picom.conf +44 -0
- package/assets/desktop/replicas-chrome +52 -0
- package/assets/desktop/replicas-chrome.desktop +21 -0
- package/assets/desktop/start-services.sh +383 -0
- package/assets/desktop/tint2rc +91 -0
- package/dist/index.js +3287 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# replicas-computer
|
|
2
|
+
|
|
3
|
+
The versioned computer-use runtime for Replicas workspace images. It owns the desktop daemon, CLI commands, Chrome wrapper, and desktop assets.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
replicas-computer setup --check
|
|
7
|
+
sudo replicas-computer setup
|
|
8
|
+
replicas-computer daemon
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`setup --check` reports missing OS dependencies without changing the machine. `setup` installs package-owned assets and the `replicas-computer.service` systemd unit. Operational commands use the daemon over a user-only Unix socket.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<svg width="1920" height="1080" viewBox="0 0 910 1010" preserveAspectRatio="none" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_brand_wallpaper)">
|
|
3
|
+
<g clip-path="url(#clip1_brand_wallpaper)">
|
|
4
|
+
<g filter="url(#filter0_n_brand_wallpaper)">
|
|
5
|
+
<path d="M905 5H5V1005H905V5Z" fill="url(#paint0_linear_brand_wallpaper)"/>
|
|
6
|
+
</g>
|
|
7
|
+
<g filter="url(#filter1_f_brand_wallpaper)">
|
|
8
|
+
<path d="M-2.98651 642.026C-2.98651 642.026 -141.133 716.811 -223.859 615.965C-306.584 515.119 -212.815 254.222 -157.739 166.382C-102.663 78.5416 289.934 113.449 372.659 214.295C455.385 315.141 -2.98651 642.026 -2.98651 642.026Z" fill="#082C25" fill-opacity="0.75"/>
|
|
9
|
+
</g>
|
|
10
|
+
<g filter="url(#filter2_f_brand_wallpaper)">
|
|
11
|
+
<path d="M835.074 696.861C835.074 696.861 907.89 736.28 951.494 683.124C995.099 629.969 945.674 492.451 916.643 446.151C887.613 399.851 680.677 418.25 637.072 471.405C593.468 524.561 835.074 696.861 835.074 696.861Z" fill="#082C25" fill-opacity="0.7"/>
|
|
12
|
+
</g>
|
|
13
|
+
</g>
|
|
14
|
+
</g>
|
|
15
|
+
<defs>
|
|
16
|
+
<filter id="filter0_n_brand_wallpaper" x="5" y="5" width="900" height="1000" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
17
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
18
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
19
|
+
<feTurbulence type="fractalNoise" baseFrequency="1.0779999494552612 1.0779999494552612" stitchTiles="stitch" numOctaves="3" result="noise" seed="4356" />
|
|
20
|
+
<feColorMatrix in="noise" type="luminanceToAlpha" result="alphaNoise" />
|
|
21
|
+
<feComponentTransfer in="alphaNoise" result="coloredNoise1">
|
|
22
|
+
<feFuncA type="discrete" tableValues="1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "/>
|
|
23
|
+
</feComponentTransfer>
|
|
24
|
+
<feComposite operator="in" in2="shape" in="coloredNoise1" result="noise1Clipped" />
|
|
25
|
+
<feFlood flood-color="rgba(0, 0, 0, 0.2)" result="color1Flood" />
|
|
26
|
+
<feComposite operator="in" in2="noise1Clipped" in="color1Flood" result="color1" />
|
|
27
|
+
<feMerge result="effect1_noise_brand_wallpaper">
|
|
28
|
+
<feMergeNode in="shape" />
|
|
29
|
+
<feMergeNode in="color1" />
|
|
30
|
+
</feMerge>
|
|
31
|
+
</filter>
|
|
32
|
+
<filter id="filter1_f_brand_wallpaper" x="-496.13" y="-123.322" width="1118.34" height="1031.59" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
33
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
34
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
35
|
+
<feGaussianBlur stdDeviation="119.783" result="effect1_foregroundBlur_brand_wallpaper"/>
|
|
36
|
+
</filter>
|
|
37
|
+
<filter id="filter2_f_brand_wallpaper" x="505.533" y="293.449" width="589.474" height="543.749" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
38
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
39
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
40
|
+
<feGaussianBlur stdDeviation="63.1371" result="effect1_foregroundBlur_brand_wallpaper"/>
|
|
41
|
+
</filter>
|
|
42
|
+
<linearGradient id="paint0_linear_brand_wallpaper" x1="-5.01855" y1="5" x2="999.36" y2="898.989" gradientUnits="userSpaceOnUse">
|
|
43
|
+
<stop stop-color="#1B9179"/>
|
|
44
|
+
<stop offset="0.128829" stop-color="#1D8570"/>
|
|
45
|
+
<stop offset="1" stop-color="#082B24"/>
|
|
46
|
+
</linearGradient>
|
|
47
|
+
<clipPath id="clip0_brand_wallpaper">
|
|
48
|
+
<rect x="5" y="5" width="900" height="1000" fill="white"/>
|
|
49
|
+
</clipPath>
|
|
50
|
+
<clipPath id="clip1_brand_wallpaper">
|
|
51
|
+
<rect width="900" height="1000" fill="white" transform="translate(5 5)"/>
|
|
52
|
+
</clipPath>
|
|
53
|
+
</defs>
|
|
54
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[Settings]
|
|
2
|
+
gtk-theme-name=Yaru-dark
|
|
3
|
+
gtk-icon-theme-name=Yaru
|
|
4
|
+
gtk-cursor-theme-name=Bibata-Modern-Ice
|
|
5
|
+
gtk-cursor-theme-size=22
|
|
6
|
+
gtk-font-name=Cantarell 11
|
|
7
|
+
gtk-application-prefer-dark-theme=1
|
|
8
|
+
gtk-xft-antialias=1
|
|
9
|
+
gtk-xft-hinting=1
|
|
10
|
+
gtk-xft-hintstyle=hintslight
|
|
11
|
+
gtk-xft-rgba=rgb
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
|
+
<title>Replicas Desktop</title>
|
|
7
|
+
<style>
|
|
8
|
+
@font-face {
|
|
9
|
+
font-family: "Geist Mono";
|
|
10
|
+
font-style: normal;
|
|
11
|
+
font-weight: 400 600;
|
|
12
|
+
font-display: swap;
|
|
13
|
+
src: url("./geist-mono.woff2") format("woff2");
|
|
14
|
+
}
|
|
15
|
+
html, body {
|
|
16
|
+
margin: 0;
|
|
17
|
+
padding: 0;
|
|
18
|
+
height: 100%;
|
|
19
|
+
width: 100%;
|
|
20
|
+
background:
|
|
21
|
+
radial-gradient(circle at 18% 20%, rgba(62, 235, 163, 0.16), transparent 34%),
|
|
22
|
+
radial-gradient(circle at 84% 78%, rgba(8, 44, 37, 0.85), transparent 42%),
|
|
23
|
+
linear-gradient(135deg, #1B9179 0%, #1D8570 13%, #082B24 100%);
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
26
|
+
color: rgba(255, 255, 255, 0.72);
|
|
27
|
+
}
|
|
28
|
+
#screen {
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
cursor: none;
|
|
32
|
+
}
|
|
33
|
+
#screen > div {
|
|
34
|
+
background: transparent !important;
|
|
35
|
+
overflow: hidden !important;
|
|
36
|
+
}
|
|
37
|
+
#status {
|
|
38
|
+
position: fixed;
|
|
39
|
+
inset: 0;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
gap: 16px;
|
|
44
|
+
pointer-events: none;
|
|
45
|
+
opacity: 0;
|
|
46
|
+
transition: opacity 240ms ease-out;
|
|
47
|
+
}
|
|
48
|
+
#status.visible { opacity: 1; }
|
|
49
|
+
#status .logo {
|
|
50
|
+
width: 52px;
|
|
51
|
+
height: 52px;
|
|
52
|
+
flex: none;
|
|
53
|
+
filter: drop-shadow(0 2px 16px rgba(62, 235, 163, 0.5));
|
|
54
|
+
}
|
|
55
|
+
#status-text {
|
|
56
|
+
font-size: 24px;
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
letter-spacing: -0.01em;
|
|
59
|
+
color: rgba(255, 255, 255, 0.92);
|
|
60
|
+
text-shadow: 0 1px 24px rgba(0, 0, 0, 0.5);
|
|
61
|
+
}
|
|
62
|
+
#hint {
|
|
63
|
+
position: fixed;
|
|
64
|
+
left: 50%;
|
|
65
|
+
bottom: 20px;
|
|
66
|
+
transform: translateX(-50%) translateY(6px);
|
|
67
|
+
padding: 7px 12px;
|
|
68
|
+
background: rgba(13, 17, 23, 0.92);
|
|
69
|
+
border: 1px solid rgba(255, 255, 255, 0.16);
|
|
70
|
+
color: rgba(255, 255, 255, 0.85);
|
|
71
|
+
font-size: 12px;
|
|
72
|
+
font-weight: 500;
|
|
73
|
+
white-space: nowrap;
|
|
74
|
+
pointer-events: none;
|
|
75
|
+
opacity: 0;
|
|
76
|
+
transition: opacity 200ms ease-out, transform 200ms ease-out;
|
|
77
|
+
}
|
|
78
|
+
#hint.visible { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
79
|
+
</style>
|
|
80
|
+
</head>
|
|
81
|
+
<body>
|
|
82
|
+
<div id="screen"></div>
|
|
83
|
+
<div id="status">
|
|
84
|
+
<svg class="logo" viewBox="0 0 360 360" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
85
|
+
<g transform="translate(67.5 30)" fill="#3eeba3">
|
|
86
|
+
<path d="M0 0H75V75H0V0Z"/>
|
|
87
|
+
<path d="M75 0H150V75H75V0Z"/>
|
|
88
|
+
<path d="M150 75H225V150H150V75Z"/>
|
|
89
|
+
<path d="M0 150H75V225H0V150Z"/>
|
|
90
|
+
<path d="M75 150H150V225H75V150Z"/>
|
|
91
|
+
<path d="M0 225H75V300H0V225Z"/>
|
|
92
|
+
<path d="M150 225H225V300H150V225Z"/>
|
|
93
|
+
</g>
|
|
94
|
+
</svg>
|
|
95
|
+
<span id="status-text">connecting…</span>
|
|
96
|
+
</div>
|
|
97
|
+
<div id="hint">This desktop runs Linux — ⌘ shortcuts won't work here. Use Ctrl instead (e.g. Ctrl+C / Ctrl+V).</div>
|
|
98
|
+
<script>
|
|
99
|
+
// Serialized from the status logo so the mark is defined once, and set outside the module below so a failed noVNC import still leaves the tab branded.
|
|
100
|
+
const icon = document.createElement('link');
|
|
101
|
+
icon.rel = 'icon';
|
|
102
|
+
icon.type = 'image/svg+xml';
|
|
103
|
+
icon.href = `data:image/svg+xml,${encodeURIComponent(new XMLSerializer().serializeToString(document.querySelector('#status .logo')))}`;
|
|
104
|
+
document.head.append(icon);
|
|
105
|
+
</script>
|
|
106
|
+
<script type="module">
|
|
107
|
+
import RFB from './core/rfb.js';
|
|
108
|
+
import { encodings } from './core/encodings.js';
|
|
109
|
+
|
|
110
|
+
const screen = document.getElementById('screen');
|
|
111
|
+
const status = document.getElementById('status');
|
|
112
|
+
const statusText = document.getElementById('status-text');
|
|
113
|
+
const showStatus = (msg) => {
|
|
114
|
+
statusText.textContent = msg;
|
|
115
|
+
status.classList.add('visible');
|
|
116
|
+
};
|
|
117
|
+
const hideStatus = () => status.classList.remove('visible');
|
|
118
|
+
const modernCursor = 'url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M5 3.5l12.2 11.4-5.8.7 3.2 5.3-2.6 1.5-3.2-5.3-4.1 4.3z%27 fill=%27white%27 stroke=%27%2310161f%27 stroke-width=%272%27 stroke-linejoin=%27round%27/%3E%3Cpath d=%27M5 3.5l12.2 11.4-5.8.7 3.2 5.3-2.6 1.5-3.2-5.3-4.1 4.3z%27 fill=%27%23f8fafc%27/%3E%3C/svg%3E") 5 4, default';
|
|
119
|
+
const applyModernCursor = () => {
|
|
120
|
+
const canvas = screen.querySelector('canvas');
|
|
121
|
+
if (screen.style.cursor !== modernCursor) screen.style.cursor = modernCursor;
|
|
122
|
+
if (canvas && canvas.style.cursor !== modernCursor) canvas.style.cursor = modernCursor;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
|
126
|
+
const wsUrl = `${proto}://${window.location.host}/websockify`;
|
|
127
|
+
const params = new URLSearchParams(window.location.search);
|
|
128
|
+
const intParam = (name, fallback, min = 0, max = 9) => {
|
|
129
|
+
const value = Number.parseInt(params.get(name) || '', 10);
|
|
130
|
+
return Number.isInteger(value) && value >= min && value <= max ? value : fallback;
|
|
131
|
+
};
|
|
132
|
+
const requestedProfile = params.get('profile');
|
|
133
|
+
const streamProfile = requestedProfile === 'raw' || requestedProfile === 'hextile' ? requestedProfile : 'tight';
|
|
134
|
+
const requestFps = intParam('requestFps', streamProfile === 'tight' ? 120 : 0, 0, 240);
|
|
135
|
+
const requestBatch = intParam('requestBatch', 1, 1, 8);
|
|
136
|
+
const inputRefresh = params.get('inputRefresh') === '1' || streamProfile === 'tight';
|
|
137
|
+
const optimisticScroll = params.get('optimisticScroll') === '1';
|
|
138
|
+
const optimisticScrollMax = intParam('optimisticScrollMax', 220, 0, 720);
|
|
139
|
+
const sendRawEncodings = (sock) => {
|
|
140
|
+
const nextEncodings = [
|
|
141
|
+
encodings.encodingCopyRect,
|
|
142
|
+
encodings.encodingRaw,
|
|
143
|
+
encodings.pseudoEncodingDesktopSize,
|
|
144
|
+
encodings.pseudoEncodingLastRect,
|
|
145
|
+
encodings.pseudoEncodingQEMUExtendedKeyEvent,
|
|
146
|
+
encodings.pseudoEncodingExtendedDesktopSize,
|
|
147
|
+
encodings.pseudoEncodingXvp,
|
|
148
|
+
encodings.pseudoEncodingFence,
|
|
149
|
+
encodings.pseudoEncodingContinuousUpdates,
|
|
150
|
+
encodings.pseudoEncodingDesktopName,
|
|
151
|
+
encodings.pseudoEncodingExtendedClipboard,
|
|
152
|
+
encodings.pseudoEncodingVMwareCursor,
|
|
153
|
+
encodings.pseudoEncodingCursor,
|
|
154
|
+
];
|
|
155
|
+
RFB.messages.clientEncodings(sock, nextEncodings);
|
|
156
|
+
};
|
|
157
|
+
const sendHextileEncodings = (sock) => {
|
|
158
|
+
const nextEncodings = [
|
|
159
|
+
encodings.encodingCopyRect,
|
|
160
|
+
encodings.encodingHextile,
|
|
161
|
+
encodings.encodingRaw,
|
|
162
|
+
encodings.pseudoEncodingDesktopSize,
|
|
163
|
+
encodings.pseudoEncodingLastRect,
|
|
164
|
+
encodings.pseudoEncodingQEMUExtendedKeyEvent,
|
|
165
|
+
encodings.pseudoEncodingExtendedDesktopSize,
|
|
166
|
+
encodings.pseudoEncodingXvp,
|
|
167
|
+
encodings.pseudoEncodingFence,
|
|
168
|
+
encodings.pseudoEncodingContinuousUpdates,
|
|
169
|
+
encodings.pseudoEncodingDesktopName,
|
|
170
|
+
encodings.pseudoEncodingExtendedClipboard,
|
|
171
|
+
encodings.pseudoEncodingVMwareCursor,
|
|
172
|
+
encodings.pseudoEncodingCursor,
|
|
173
|
+
];
|
|
174
|
+
RFB.messages.clientEncodings(sock, nextEncodings);
|
|
175
|
+
};
|
|
176
|
+
if (streamProfile !== 'tight') {
|
|
177
|
+
const sendDefaultEncodings = RFB.prototype._sendEncodings;
|
|
178
|
+
RFB.prototype._sendEncodings = function () {
|
|
179
|
+
if (this._fbDepth !== 24) {
|
|
180
|
+
sendDefaultEncodings.call(this);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (streamProfile === 'hextile') sendHextileEncodings(this._sock);
|
|
184
|
+
else sendRawEncodings(this._sock);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
showStatus('connecting…');
|
|
189
|
+
const rfb = new RFB(screen, wsUrl);
|
|
190
|
+
rfb.scaleViewport = true;
|
|
191
|
+
rfb.resizeSession = false;
|
|
192
|
+
rfb.background = '#000';
|
|
193
|
+
rfb.showDotCursor = false;
|
|
194
|
+
if (streamProfile === 'tight') {
|
|
195
|
+
rfb.compressionLevel = intParam('compression', 2);
|
|
196
|
+
rfb.qualityLevel = intParam('quality', 5);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let updatePumpId;
|
|
200
|
+
const requestFramebufferUpdate = () => {
|
|
201
|
+
if (rfb._rfbConnectionState !== 'connected' || rfb._enabledContinuousUpdates) return;
|
|
202
|
+
if (!rfb._sock || !rfb._fbWidth || !rfb._fbHeight) return;
|
|
203
|
+
for (let i = 0; i < requestBatch; i += 1) {
|
|
204
|
+
RFB.messages.fbUpdateRequest(rfb._sock, true, 0, 0, rfb._fbWidth, rfb._fbHeight);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const startUpdatePump = () => {
|
|
208
|
+
clearInterval(updatePumpId);
|
|
209
|
+
if (!requestFps) return;
|
|
210
|
+
updatePumpId = setInterval(requestFramebufferUpdate, Math.max(8, Math.round(1000 / requestFps)));
|
|
211
|
+
};
|
|
212
|
+
const stopUpdatePump = () => clearInterval(updatePumpId);
|
|
213
|
+
|
|
214
|
+
let lastInputRefreshAt = 0;
|
|
215
|
+
const queueInputRefresh = () => {
|
|
216
|
+
if (!inputRefresh) return;
|
|
217
|
+
const now = performance.now();
|
|
218
|
+
if (now - lastInputRefreshAt < 16) return;
|
|
219
|
+
lastInputRefreshAt = now;
|
|
220
|
+
requestFramebufferUpdate();
|
|
221
|
+
for (const delay of [8, 16, 32, 48, 64, 96]) {
|
|
222
|
+
setTimeout(requestFramebufferUpdate, delay);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const shiftCanvasForWheel = (event) => {
|
|
226
|
+
if (!optimisticScroll || event.ctrlKey || event.metaKey || Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return;
|
|
227
|
+
const canvas = screen.querySelector('canvas');
|
|
228
|
+
const context = canvas?.getContext?.('2d');
|
|
229
|
+
if (!canvas || !context || !canvas.width || !canvas.height) return;
|
|
230
|
+
|
|
231
|
+
const maxOffset = Math.max(0, Math.min(optimisticScrollMax, canvas.height - 1));
|
|
232
|
+
const deltaScale = event.deltaMode === WheelEvent.DOM_DELTA_LINE ? 32 : event.deltaMode === WheelEvent.DOM_DELTA_PAGE ? canvas.height : 1;
|
|
233
|
+
const offset = Math.trunc(Math.max(-maxOffset, Math.min(maxOffset, event.deltaY * deltaScale)));
|
|
234
|
+
if (!offset) return;
|
|
235
|
+
|
|
236
|
+
if (offset > 0) {
|
|
237
|
+
context.drawImage(canvas, 0, offset, canvas.width, canvas.height - offset, 0, 0, canvas.width, canvas.height - offset);
|
|
238
|
+
context.drawImage(canvas, 0, canvas.height - offset - 1, canvas.width, 1, 0, canvas.height - offset, canvas.width, offset);
|
|
239
|
+
} else {
|
|
240
|
+
const size = -offset;
|
|
241
|
+
context.drawImage(canvas, 0, 0, canvas.width, canvas.height - size, 0, size, canvas.width, canvas.height - size);
|
|
242
|
+
context.drawImage(canvas, 0, size, canvas.width, 1, 0, 0, canvas.width, size);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const handleWheelInput = (event) => {
|
|
246
|
+
shiftCanvasForWheel(event);
|
|
247
|
+
queueInputRefresh();
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const hint = document.getElementById('hint');
|
|
251
|
+
const isMac = /Mac|iPhone|iPad/.test(navigator.userAgentData?.platform || navigator.platform || '');
|
|
252
|
+
let hintTimer;
|
|
253
|
+
window.addEventListener('keydown', (e) => {
|
|
254
|
+
if (!isMac || !e.metaKey || e.key === 'Meta') return;
|
|
255
|
+
hint.classList.add('visible');
|
|
256
|
+
clearTimeout(hintTimer);
|
|
257
|
+
hintTimer = setTimeout(() => hint.classList.remove('visible'), 4000);
|
|
258
|
+
}, { capture: true, passive: true });
|
|
259
|
+
|
|
260
|
+
const cursorObserver = new MutationObserver(applyModernCursor);
|
|
261
|
+
cursorObserver.observe(screen, { attributes: true, childList: true, subtree: true, attributeFilter: ['style'] });
|
|
262
|
+
window.addEventListener('wheel', handleWheelInput, { capture: true, passive: true });
|
|
263
|
+
for (const eventName of ['pointerdown', 'pointerup', 'pointermove', 'keydown', 'keyup']) {
|
|
264
|
+
window.addEventListener(eventName, queueInputRefresh, { passive: true });
|
|
265
|
+
}
|
|
266
|
+
rfb.addEventListener('connect', () => {
|
|
267
|
+
hideStatus();
|
|
268
|
+
applyModernCursor();
|
|
269
|
+
startUpdatePump();
|
|
270
|
+
});
|
|
271
|
+
rfb.addEventListener('disconnect', (e) => {
|
|
272
|
+
stopUpdatePump();
|
|
273
|
+
showStatus(e.detail.clean ? 'disconnected' : 'connecting…');
|
|
274
|
+
if (!e.detail.clean) setTimeout(() => window.location.reload(), 1500);
|
|
275
|
+
});
|
|
276
|
+
rfb.addEventListener('securityfailure', (e) => {
|
|
277
|
+
showStatus(`auth failed: ${e.detail.reason || 'unknown'}`);
|
|
278
|
+
});
|
|
279
|
+
rfb.addEventListener('bell', (e) => e.preventDefault?.());
|
|
280
|
+
</script>
|
|
281
|
+
</body>
|
|
282
|
+
</html>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<openbox_config xmlns="http://openbox.org/3.4/rc">
|
|
3
|
+
<resistance>
|
|
4
|
+
<strength>10</strength>
|
|
5
|
+
<screen_edge_strength>20</screen_edge_strength>
|
|
6
|
+
</resistance>
|
|
7
|
+
<focus>
|
|
8
|
+
<focusNew>yes</focusNew>
|
|
9
|
+
<followMouse>no</followMouse>
|
|
10
|
+
<focusLast>yes</focusLast>
|
|
11
|
+
<underMouse>no</underMouse>
|
|
12
|
+
<focusDelay>200</focusDelay>
|
|
13
|
+
<raiseOnFocus>no</raiseOnFocus>
|
|
14
|
+
</focus>
|
|
15
|
+
<placement>
|
|
16
|
+
<policy>Smart</policy>
|
|
17
|
+
<center>yes</center>
|
|
18
|
+
<monitor>Active</monitor>
|
|
19
|
+
<primaryMonitor>1</primaryMonitor>
|
|
20
|
+
</placement>
|
|
21
|
+
<theme>
|
|
22
|
+
<name>Replicas-modern</name>
|
|
23
|
+
<titleLayout>NLIMC</titleLayout>
|
|
24
|
+
<keepBorder>yes</keepBorder>
|
|
25
|
+
<animateIconify>yes</animateIconify>
|
|
26
|
+
<font place="ActiveWindow">
|
|
27
|
+
<name>Cantarell</name>
|
|
28
|
+
<size>11</size>
|
|
29
|
+
<weight>bold</weight>
|
|
30
|
+
<slant>normal</slant>
|
|
31
|
+
</font>
|
|
32
|
+
<font place="InactiveWindow">
|
|
33
|
+
<name>Cantarell</name>
|
|
34
|
+
<size>11</size>
|
|
35
|
+
<weight>bold</weight>
|
|
36
|
+
<slant>normal</slant>
|
|
37
|
+
</font>
|
|
38
|
+
<font place="MenuHeader">
|
|
39
|
+
<name>Cantarell</name>
|
|
40
|
+
<size>11</size>
|
|
41
|
+
<weight>normal</weight>
|
|
42
|
+
<slant>normal</slant>
|
|
43
|
+
</font>
|
|
44
|
+
<font place="MenuItem">
|
|
45
|
+
<name>Cantarell</name>
|
|
46
|
+
<size>11</size>
|
|
47
|
+
<weight>normal</weight>
|
|
48
|
+
<slant>normal</slant>
|
|
49
|
+
</font>
|
|
50
|
+
</theme>
|
|
51
|
+
<desktops>
|
|
52
|
+
<number>1</number>
|
|
53
|
+
<firstdesk>1</firstdesk>
|
|
54
|
+
<names>
|
|
55
|
+
<name>Desktop</name>
|
|
56
|
+
</names>
|
|
57
|
+
<popupTime>0</popupTime>
|
|
58
|
+
</desktops>
|
|
59
|
+
<resize>
|
|
60
|
+
<drawContents>yes</drawContents>
|
|
61
|
+
<popupShow>Nonpixel</popupShow>
|
|
62
|
+
<popupPosition>Center</popupPosition>
|
|
63
|
+
</resize>
|
|
64
|
+
<margins>
|
|
65
|
+
<top>0</top>
|
|
66
|
+
<bottom>96</bottom>
|
|
67
|
+
<left>0</left>
|
|
68
|
+
<right>0</right>
|
|
69
|
+
</margins>
|
|
70
|
+
<dock>
|
|
71
|
+
<position>Bottom</position>
|
|
72
|
+
<stacking>Above</stacking>
|
|
73
|
+
<direction>Vertical</direction>
|
|
74
|
+
<autoHide>no</autoHide>
|
|
75
|
+
<hideDelay>300</hideDelay>
|
|
76
|
+
<showDelay>300</showDelay>
|
|
77
|
+
</dock>
|
|
78
|
+
<applications/>
|
|
79
|
+
</openbox_config>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#define iconify_width 14
|
|
2
|
+
#define iconify_height 14
|
|
3
|
+
static unsigned char iconify_bits[] = {
|
|
4
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
|
|
5
|
+
0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
6
|
+
0x00, 0x00, 0x00, 0x00
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#define iconify_width 14
|
|
2
|
+
#define iconify_height 14
|
|
3
|
+
static unsigned char iconify_bits[] = {
|
|
4
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
|
|
5
|
+
0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
6
|
+
0x00, 0x00, 0x00, 0x00
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#define iconify_width 14
|
|
2
|
+
#define iconify_height 14
|
|
3
|
+
static unsigned char iconify_bits[] = {
|
|
4
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
|
|
5
|
+
0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
6
|
+
0x00, 0x00, 0x00, 0x00
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#define max_toggled_width 14
|
|
2
|
+
#define max_toggled_height 14
|
|
3
|
+
static unsigned char max_toggled_bits[] = {
|
|
4
|
+
0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, 0x30, 0x03, 0x3c, 0x0f, 0x3c, 0x0f,
|
|
5
|
+
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00,
|
|
6
|
+
0x00, 0x00, 0x00, 0x00
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#define max_toggled_width 14
|
|
2
|
+
#define max_toggled_height 14
|
|
3
|
+
static unsigned char max_toggled_bits[] = {
|
|
4
|
+
0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, 0x30, 0x03, 0x3c, 0x0f, 0x3c, 0x0f,
|
|
5
|
+
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00,
|
|
6
|
+
0x00, 0x00, 0x00, 0x00
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#define max_toggled_width 14
|
|
2
|
+
#define max_toggled_height 14
|
|
3
|
+
static unsigned char max_toggled_bits[] = {
|
|
4
|
+
0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, 0x30, 0x03, 0x3c, 0x0f, 0x3c, 0x0f,
|
|
5
|
+
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00,
|
|
6
|
+
0x00, 0x00, 0x00, 0x00
|
|
7
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
border.width: 1
|
|
2
|
+
padding.width: 5
|
|
3
|
+
padding.height: 8
|
|
4
|
+
window.client.padding.width: 1
|
|
5
|
+
window.client.padding.height: 1
|
|
6
|
+
window.handle.width: 0
|
|
7
|
+
window.label.text.justify: center
|
|
8
|
+
|
|
9
|
+
window.active.border.color: #111827
|
|
10
|
+
window.inactive.border.color: #1f2937
|
|
11
|
+
window.*.client.color: #ffffff
|
|
12
|
+
window.*.title.bg: flat
|
|
13
|
+
window.active.title.bg.color: #111827
|
|
14
|
+
window.inactive.title.bg.color: #273142
|
|
15
|
+
window.active.label.bg: parentrelative
|
|
16
|
+
window.inactive.label.bg: parentrelative
|
|
17
|
+
window.active.button.*.bg: flat
|
|
18
|
+
window.active.button.*.bg.color: #111827
|
|
19
|
+
window.inactive.button.*.bg: flat
|
|
20
|
+
window.inactive.button.*.bg.color: #273142
|
|
21
|
+
window.active.button.*.image.color: #f8fafc
|
|
22
|
+
window.inactive.button.*.image.color: #cbd5e1
|
|
23
|
+
window.active.button.*.hover.bg: flat
|
|
24
|
+
window.active.button.*.hover.bg.color: #334155
|
|
25
|
+
window.inactive.button.*.hover.bg: flat
|
|
26
|
+
window.inactive.button.*.hover.bg.color: #334155
|
|
27
|
+
window.active.button.*.hover.image.color: #ffffff
|
|
28
|
+
window.inactive.button.*.hover.image.color: #ffffff
|
|
29
|
+
window.active.button.*.pressed.image.color: #38bdf8
|
|
30
|
+
window.inactive.button.*.pressed.image.color: #38bdf8
|
|
31
|
+
window.active.label.text.color: #f8fafc
|
|
32
|
+
window.inactive.label.text.color: #d1d5db
|
|
33
|
+
window.active.title.separator.color: #111827
|
|
34
|
+
window.inactive.title.separator.color: #273142
|
|
35
|
+
|
|
36
|
+
menu.border.width: 1
|
|
37
|
+
menu.separator.width: 1
|
|
38
|
+
menu.separator.padding.width: 6
|
|
39
|
+
menu.separator.padding.height: 4
|
|
40
|
+
menu.overlap.x: 0
|
|
41
|
+
menu.overlap.y: 0
|
|
42
|
+
menu.border.color: #1f2937
|
|
43
|
+
menu.separator.color: #334155
|
|
44
|
+
menu.items.text.color: #e5e7eb
|
|
45
|
+
menu.items.disabled.text.color: #64748b
|
|
46
|
+
menu.items.active.text.color: #ffffff
|
|
47
|
+
menu.items.active.disabled.text.color: #94a3b8
|
|
48
|
+
menu.items.bg: flat
|
|
49
|
+
menu.items.bg.color: #111827
|
|
50
|
+
menu.items.active.bg: flat
|
|
51
|
+
menu.items.active.bg.color: #2563eb
|
|
52
|
+
menu.title.text.color: #f8fafc
|
|
53
|
+
menu.title.bg: flat
|
|
54
|
+
menu.title.bg.color: #0f172a
|
|
55
|
+
|
|
56
|
+
osd.border.width: 1
|
|
57
|
+
osd.border.color: #334155
|
|
58
|
+
osd.bg: flat
|
|
59
|
+
osd.bg.color: #111827
|
|
60
|
+
osd.hilight.bg: flat
|
|
61
|
+
osd.hilight.bg.color: #2563eb
|
|
62
|
+
osd.unhilight.bg: flat
|
|
63
|
+
osd.unhilight.bg.color: #1f2937
|
|
64
|
+
osd.label.bg: flat
|
|
65
|
+
osd.label.bg.color: #111827
|
|
66
|
+
osd.label.text.color: #f8fafc
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
backend = "xrender";
|
|
2
|
+
vsync = false;
|
|
3
|
+
detect-rounded-corners = true;
|
|
4
|
+
detect-client-opacity = true;
|
|
5
|
+
detect-transient = true;
|
|
6
|
+
|
|
7
|
+
shadow = true;
|
|
8
|
+
shadow-radius = 16;
|
|
9
|
+
shadow-opacity = 0.28;
|
|
10
|
+
shadow-offset-x = -8;
|
|
11
|
+
shadow-offset-y = -8;
|
|
12
|
+
shadow-color = "#000000";
|
|
13
|
+
shadow-exclude = [
|
|
14
|
+
"class_g = 'tint2'",
|
|
15
|
+
"class_g = 'Conky'",
|
|
16
|
+
"name = 'Notification'",
|
|
17
|
+
"_GTK_FRAME_EXTENTS@:c",
|
|
18
|
+
"window_type = 'dock'",
|
|
19
|
+
"window_type = 'desktop'"
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
fading = false;
|
|
23
|
+
|
|
24
|
+
active-opacity = 1.0;
|
|
25
|
+
inactive-opacity = 1.0;
|
|
26
|
+
frame-opacity = 1.0;
|
|
27
|
+
inactive-opacity-override = false;
|
|
28
|
+
|
|
29
|
+
corner-radius = 10;
|
|
30
|
+
rounded-corners-exclude = [
|
|
31
|
+
"class_g = 'tint2'",
|
|
32
|
+
"window_type = 'dock'",
|
|
33
|
+
"window_type = 'desktop'"
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
wintypes:
|
|
37
|
+
{
|
|
38
|
+
tooltip = { fade = false; shadow = true; opacity = 0.95; focus = true; };
|
|
39
|
+
dock = { shadow = false; };
|
|
40
|
+
dnd = { shadow = false; };
|
|
41
|
+
popup_menu = { opacity = 0.97; };
|
|
42
|
+
dropdown_menu = { opacity = 0.97; };
|
|
43
|
+
menu = { opacity = 0.97; };
|
|
44
|
+
};
|