pmptr 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/LICENSE +21 -0
- package/README.md +189 -0
- package/assets/icon.png +0 -0
- package/assets/icon.svg +10 -0
- package/assets/icons/1024x1024.png +0 -0
- package/assets/icons/128x128.png +0 -0
- package/assets/icons/16x16.png +0 -0
- package/assets/icons/256x256.png +0 -0
- package/assets/icons/32x32.png +0 -0
- package/assets/icons/512x512.png +0 -0
- package/assets/icons/64x64.png +0 -0
- package/bin/pmptr.js +12 -0
- package/package.json +72 -0
- package/src/control/control.css +317 -0
- package/src/control/control.html +260 -0
- package/src/control/control.js +313 -0
- package/src/main/main.js +223 -0
- package/src/main/preload.js +35 -0
- package/src/prompter/prompter-preload.js +17 -0
- package/src/prompter/prompter.css +233 -0
- package/src/prompter/prompter.html +44 -0
- package/src/prompter/prompter.js +198 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jatin Krmalik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# <img src="assets/icon.svg" height="40" align="center" /> pmptr
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jatinkrmalik/pmptr/releases)
|
|
4
|
+
[](https://github.com/jatinkrmalik/pmptr/actions/workflows/build.yml)
|
|
5
|
+
[](https://github.com/jatinkrmalik/pmptr/actions/workflows/release.yml)
|
|
6
|
+
[](https://github.com/jatinkrmalik/pmptr/actions/workflows/nightly.yml)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://github.com/jatinkrmalik/pmptr)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://www.electronjs.org)
|
|
11
|
+
|
|
12
|
+
> **Beta** — pmptr is in active beta. Expect occasional bugs and breaking changes.
|
|
13
|
+
> Please [report issues](https://github.com/jatinkrmalik/pmptr/issues) you encounter.
|
|
14
|
+
|
|
15
|
+
A minimal virtual teleprompter that lives as a transparent, always-on-top,
|
|
16
|
+
click-through overlay over whatever you do on your screen.
|
|
17
|
+
|
|
18
|
+
## Download
|
|
19
|
+
|
|
20
|
+
Grab the latest build from the [Releases page](https://github.com/jatinkrmalik/pmptr/releases).
|
|
21
|
+
|
|
22
|
+
| Platform | Format |
|
|
23
|
+
|----------|--------|
|
|
24
|
+
| macOS | `.dmg` |
|
|
25
|
+
| Windows | `.exe` (NSIS installer) |
|
|
26
|
+
| Linux | `.AppImage` or `.deb` |
|
|
27
|
+
|
|
28
|
+
Not code-signed — your OS may warn on first launch. That's expected during beta.
|
|
29
|
+
|
|
30
|
+
## Install from npm
|
|
31
|
+
|
|
32
|
+
If you have [Node.js](https://nodejs.org) installed, you can install `pmptr` globally and run it from your terminal:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g pmptr
|
|
36
|
+
pmptr
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires Node.js **20** or later.
|
|
40
|
+
|
|
41
|
+
## Run from source
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/jatinkrmalik/pmptr.git
|
|
45
|
+
cd pmptr
|
|
46
|
+
npm install
|
|
47
|
+
npm start
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then click **Open floating prompter** in the control window.
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Control window** — paste your script, tune speed, size, colors, opacity, mirror,
|
|
55
|
+
window dimensions, and more.
|
|
56
|
+
- **Floating prompter window** — transparent, frameless, always on top, with
|
|
57
|
+
a true OS-level click-through "lock" so you can keep working with your mouse
|
|
58
|
+
on whatever is underneath.
|
|
59
|
+
- Settings persist to disk in your Electron user-data folder.
|
|
60
|
+
- Live updates: edits in the control window apply to the prompter instantly.
|
|
61
|
+
|
|
62
|
+
## Shortcuts (in the floating window)
|
|
63
|
+
|
|
64
|
+
| Key | Action |
|
|
65
|
+
| --------- | --------------------------------------- |
|
|
66
|
+
| `Space` | Play / pause |
|
|
67
|
+
| `R` | Reset scroll to the top |
|
|
68
|
+
| `↑` / `↓` | Speed ± 5 px/s |
|
|
69
|
+
| `L` | Toggle click-through (lock / unlock) |
|
|
70
|
+
| `Esc` | Close the prompter |
|
|
71
|
+
|
|
72
|
+
You can also use the small HUD in the bottom-right of the floating window
|
|
73
|
+
(mouse over it to reveal it).
|
|
74
|
+
|
|
75
|
+
## Architecture
|
|
76
|
+
|
|
77
|
+
```mermaid
|
|
78
|
+
graph TB
|
|
79
|
+
subgraph "Electron Main Process"
|
|
80
|
+
M[main.js]
|
|
81
|
+
SETTINGS[(settings.json)]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
subgraph "Control Window"
|
|
85
|
+
CH[control.html]
|
|
86
|
+
CJ[control.js]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
subgraph "Prompter Window"
|
|
90
|
+
PH[prompter.html]
|
|
91
|
+
PJ[prompter.js]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
M -->|spawns| CH
|
|
95
|
+
M -->|spawns| PH
|
|
96
|
+
M <-->|save/load| SETTINGS
|
|
97
|
+
CJ -->|IPC: settings changed| M
|
|
98
|
+
M -->|IPC: settings| PH
|
|
99
|
+
PH -->|IPC: state| M
|
|
100
|
+
M -->|IPC: state| CJ
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The app runs in two Electron `BrowserWindow` instances that communicate
|
|
104
|
+
through IPC handlers in the main process. The control window is where you
|
|
105
|
+
paste your script and adjust settings; the prompter window is the
|
|
106
|
+
transparent, always-on-top overlay that scrolls the text. Settings are
|
|
107
|
+
persisted to `settings.json` in the Electron user-data directory.
|
|
108
|
+
|
|
109
|
+
## How the click-through works
|
|
110
|
+
|
|
111
|
+
The prompter is a separate `BrowserWindow` with `transparent: true`, `frame: false`,
|
|
112
|
+
and `alwaysOnTop: true`. When you toggle "click-through" (the lock), the main
|
|
113
|
+
process calls `win.setIgnoreMouseEvents(true, { forward: true })` — clicks
|
|
114
|
+
and wheel events fall straight through to whatever app is behind, while the
|
|
115
|
+
window stays visible and keeps scrolling. The HUD itself is hidden while
|
|
116
|
+
locked, so nothing on the prompter intercepts your pointer.
|
|
117
|
+
|
|
118
|
+
## Tweaking transparency
|
|
119
|
+
|
|
120
|
+
The window background is a CSS `rgba()` color set on `.frame`. Move the
|
|
121
|
+
**Background opacity** slider to 0 for fully see-through, or use
|
|
122
|
+
**Background dim** to keep it readable on bright content underneath. The text
|
|
123
|
+
itself stays opaque.
|
|
124
|
+
|
|
125
|
+
## Project layout
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
src/
|
|
129
|
+
├── main/
|
|
130
|
+
│ ├── main.js Electron main: creates both windows, handles IPC,
|
|
131
|
+
│ │ click-through, always-on-top, position presets.
|
|
132
|
+
│ └── preload.js contextBridge for the control window.
|
|
133
|
+
├── control/
|
|
134
|
+
│ ├── control.html The control panel UI.
|
|
135
|
+
│ ├── control.js Control panel logic.
|
|
136
|
+
│ └── control.css Control panel styles.
|
|
137
|
+
└── prompter/
|
|
138
|
+
├── prompter.html The floating teleprompter overlay.
|
|
139
|
+
├── prompter.js Prompter logic (scroll, shortcuts, HUD).
|
|
140
|
+
├── prompter.css Prompter styles.
|
|
141
|
+
└── prompter-preload.js contextBridge for the prompter window.
|
|
142
|
+
|
|
143
|
+
assets/
|
|
144
|
+
└── icon.svg Application icon.
|
|
145
|
+
|
|
146
|
+
.github/workflows/
|
|
147
|
+
── build.yml CI build for Linux, macOS, Windows.
|
|
148
|
+
├── release.yml Tag-triggered release pipeline.
|
|
149
|
+
├── nightly.yml Daily scheduled builds.
|
|
150
|
+
└── pr-build.yml Comment-triggered PR artifact builds.
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Install dependencies
|
|
157
|
+
npm install
|
|
158
|
+
|
|
159
|
+
# Run the app
|
|
160
|
+
npm start
|
|
161
|
+
|
|
162
|
+
# Lint code
|
|
163
|
+
npm run lint
|
|
164
|
+
|
|
165
|
+
# Run tests (currently just lint)
|
|
166
|
+
npm test
|
|
167
|
+
|
|
168
|
+
# Build for distribution
|
|
169
|
+
npm run build
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Known limitations
|
|
173
|
+
|
|
174
|
+
- Wayland compositors vary in their support for `setIgnoreMouseEvents` and
|
|
175
|
+
`setAlwaysOnTop` (the underlying APIs Electron uses). X11 (Xorg) and recent
|
|
176
|
+
KDE / GNOME Wayland work fine; some lighter Wayland compositors may ignore
|
|
177
|
+
these hints. If click-through or always-on-top does not work, the prompter
|
|
178
|
+
is still useful — just drag it to a corner.
|
|
179
|
+
- On macOS you may need to grant Accessibility / Screen Recording permissions
|
|
180
|
+
to the app for click-through to behave predictably across all apps.
|
|
181
|
+
- The app is not code-signed. Your OS may warn on first launch.
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT — see [LICENSE](LICENSE) for details.
|
package/assets/icon.png
ADDED
|
Binary file
|
package/assets/icon.svg
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
|
2
|
+
<!-- Dark background -->
|
|
3
|
+
<rect width="512" height="512" rx="64" fill="#0f1115"/>
|
|
4
|
+
<!-- Screen -->
|
|
5
|
+
<rect x="96" y="144" width="320" height="224" rx="16" stroke="#ffffff" stroke-width="12"/>
|
|
6
|
+
<!-- Text lines -->
|
|
7
|
+
<path d="M144 208h224M144 256h224M144 304h224" stroke="#ffffff" stroke-width="8" stroke-linecap="round"/>
|
|
8
|
+
<!-- Play arrow -->
|
|
9
|
+
<path d="M336 344l-40 24v-48z" fill="#ffd84d"/>
|
|
10
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/pmptr.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const electron = require('electron');
|
|
6
|
+
|
|
7
|
+
const appPath = path.join(__dirname, '..');
|
|
8
|
+
const child = spawn(electron, [appPath], { stdio: 'inherit' });
|
|
9
|
+
|
|
10
|
+
child.on('close', (code) => {
|
|
11
|
+
process.exit(code ?? 0);
|
|
12
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pmptr",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A minimal virtual teleprompter that lives as a transparent, always-on-top, click-through overlay.",
|
|
5
|
+
"main": "src/main/main.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pmptr": "bin/pmptr.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/**/*",
|
|
11
|
+
"src/**/*",
|
|
12
|
+
"assets/**/*"
|
|
13
|
+
],
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Jatin Malik",
|
|
16
|
+
"email": "jatinkrmalik@gmail.com"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/jatinkrmalik/pmptr.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/jatinkrmalik/pmptr/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/jatinkrmalik/pmptr",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"start": "electron .",
|
|
28
|
+
"lint": "eslint src/",
|
|
29
|
+
"test": "npm run lint",
|
|
30
|
+
"build": "electron-builder"
|
|
31
|
+
},
|
|
32
|
+
"build": {
|
|
33
|
+
"appId": "com.pmptr.app",
|
|
34
|
+
"productName": "pmptr",
|
|
35
|
+
"directories": {
|
|
36
|
+
"output": "dist"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"bin/**/*",
|
|
40
|
+
"src/**/*",
|
|
41
|
+
"assets/**/*",
|
|
42
|
+
"package.json"
|
|
43
|
+
],
|
|
44
|
+
"mac": {
|
|
45
|
+
"target": "dmg",
|
|
46
|
+
"icon": "assets/icon.png"
|
|
47
|
+
},
|
|
48
|
+
"win": {
|
|
49
|
+
"target": "nsis",
|
|
50
|
+
"icon": "assets/icon.png"
|
|
51
|
+
},
|
|
52
|
+
"linux": {
|
|
53
|
+
"target": [
|
|
54
|
+
"AppImage",
|
|
55
|
+
"deb"
|
|
56
|
+
],
|
|
57
|
+
"icon": "assets/icon.png",
|
|
58
|
+
"category": "Utility"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"electron": "^31.0.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"electron-builder": "^24.13.3",
|
|
66
|
+
"eslint": "^8.57.0"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=20.0.0"
|
|
70
|
+
},
|
|
71
|
+
"license": "MIT"
|
|
72
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg: #0f1115;
|
|
3
|
+
--bg-2: #15181f;
|
|
4
|
+
--bg-3: #1c2029;
|
|
5
|
+
--fg: #e7eaf0;
|
|
6
|
+
--muted: #8a93a6;
|
|
7
|
+
--line: #262b36;
|
|
8
|
+
--accent: #7aa2ff;
|
|
9
|
+
--accent-2: #ffd84d;
|
|
10
|
+
--danger: #ff6b6b;
|
|
11
|
+
--ok: #6bd58a;
|
|
12
|
+
--radius: 10px;
|
|
13
|
+
--radius-sm: 6px;
|
|
14
|
+
--shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 6px 20px rgba(0, 0, 0, 0.35);
|
|
15
|
+
color-scheme: dark;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
* {
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
html,
|
|
23
|
+
body {
|
|
24
|
+
margin: 0;
|
|
25
|
+
padding: 0;
|
|
26
|
+
background: var(--bg);
|
|
27
|
+
color: var(--fg);
|
|
28
|
+
font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
29
|
+
"Helvetica Neue", Arial, sans-serif;
|
|
30
|
+
min-height: 100vh;
|
|
31
|
+
-webkit-font-smoothing: antialiased;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.topbar {
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: baseline;
|
|
37
|
+
gap: 12px;
|
|
38
|
+
padding: 14px 20px 6px;
|
|
39
|
+
border-bottom: 1px solid var(--line);
|
|
40
|
+
}
|
|
41
|
+
.brand {
|
|
42
|
+
font-weight: 700;
|
|
43
|
+
letter-spacing: 0.5px;
|
|
44
|
+
}
|
|
45
|
+
.sub {
|
|
46
|
+
color: var(--muted);
|
|
47
|
+
font-size: 12px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.layout {
|
|
51
|
+
padding: 16px 20px 28px;
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: 14px;
|
|
55
|
+
max-width: 720px;
|
|
56
|
+
margin: 0 auto;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.card {
|
|
60
|
+
background: var(--bg-2);
|
|
61
|
+
border: 1px solid var(--line);
|
|
62
|
+
border-radius: var(--radius);
|
|
63
|
+
padding: 14px 16px 16px;
|
|
64
|
+
box-shadow: var(--shadow);
|
|
65
|
+
}
|
|
66
|
+
.card h2 {
|
|
67
|
+
margin: 0 0 10px;
|
|
68
|
+
font-size: 12px;
|
|
69
|
+
text-transform: uppercase;
|
|
70
|
+
letter-spacing: 1.2px;
|
|
71
|
+
color: var(--muted);
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
}
|
|
74
|
+
.card-head {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
margin-bottom: 8px;
|
|
79
|
+
}
|
|
80
|
+
.card-head h2 {
|
|
81
|
+
margin: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
textarea#script {
|
|
85
|
+
width: 100%;
|
|
86
|
+
min-height: 180px;
|
|
87
|
+
max-height: 50vh;
|
|
88
|
+
resize: vertical;
|
|
89
|
+
background: var(--bg-3);
|
|
90
|
+
color: var(--fg);
|
|
91
|
+
border: 1px solid var(--line);
|
|
92
|
+
border-radius: var(--radius-sm);
|
|
93
|
+
padding: 10px 12px;
|
|
94
|
+
font: 14px/1.5 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
95
|
+
outline: none;
|
|
96
|
+
}
|
|
97
|
+
textarea#script:focus {
|
|
98
|
+
border-color: var(--accent);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.hint {
|
|
102
|
+
font-size: 12px;
|
|
103
|
+
color: var(--muted);
|
|
104
|
+
margin-top: 6px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.grid {
|
|
108
|
+
display: grid;
|
|
109
|
+
grid-template-columns: 1fr 1fr;
|
|
110
|
+
gap: 12px 16px;
|
|
111
|
+
}
|
|
112
|
+
@media (max-width: 560px) {
|
|
113
|
+
.grid {
|
|
114
|
+
grid-template-columns: 1fr;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.field {
|
|
119
|
+
display: flex;
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
gap: 6px;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
}
|
|
124
|
+
.field.wide {
|
|
125
|
+
grid-column: 1 / -1;
|
|
126
|
+
}
|
|
127
|
+
.field .lbl {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: baseline;
|
|
130
|
+
gap: 6px;
|
|
131
|
+
font-size: 12px;
|
|
132
|
+
color: var(--muted);
|
|
133
|
+
}
|
|
134
|
+
.field output {
|
|
135
|
+
color: var(--fg);
|
|
136
|
+
font-variant-numeric: tabular-nums;
|
|
137
|
+
font-weight: 600;
|
|
138
|
+
margin-left: auto;
|
|
139
|
+
}
|
|
140
|
+
.field .unit {
|
|
141
|
+
color: var(--muted);
|
|
142
|
+
font-weight: 400;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
input[type="range"] {
|
|
146
|
+
-webkit-appearance: none;
|
|
147
|
+
appearance: none;
|
|
148
|
+
width: 100%;
|
|
149
|
+
height: 18px;
|
|
150
|
+
background: transparent;
|
|
151
|
+
margin: 0;
|
|
152
|
+
}
|
|
153
|
+
input[type="range"]::-webkit-slider-runnable-track {
|
|
154
|
+
height: 4px;
|
|
155
|
+
background: var(--bg-3);
|
|
156
|
+
border-radius: 999px;
|
|
157
|
+
}
|
|
158
|
+
input[type="range"]::-moz-range-track {
|
|
159
|
+
height: 4px;
|
|
160
|
+
background: var(--bg-3);
|
|
161
|
+
border-radius: 999px;
|
|
162
|
+
}
|
|
163
|
+
input[type="range"]::-webkit-slider-thumb {
|
|
164
|
+
-webkit-appearance: none;
|
|
165
|
+
appearance: none;
|
|
166
|
+
width: 14px;
|
|
167
|
+
height: 14px;
|
|
168
|
+
margin-top: -5px;
|
|
169
|
+
border-radius: 50%;
|
|
170
|
+
background: var(--accent);
|
|
171
|
+
border: 0;
|
|
172
|
+
cursor: pointer;
|
|
173
|
+
box-shadow: 0 0 0 3px rgba(122, 162, 255, 0.15);
|
|
174
|
+
}
|
|
175
|
+
input[type="range"]::-moz-range-thumb {
|
|
176
|
+
width: 14px;
|
|
177
|
+
height: 14px;
|
|
178
|
+
border-radius: 50%;
|
|
179
|
+
background: var(--accent);
|
|
180
|
+
border: 0;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
input[type="color"] {
|
|
185
|
+
width: 100%;
|
|
186
|
+
height: 30px;
|
|
187
|
+
background: var(--bg-3);
|
|
188
|
+
border: 1px solid var(--line);
|
|
189
|
+
border-radius: var(--radius-sm);
|
|
190
|
+
padding: 2px;
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
select {
|
|
195
|
+
width: 100%;
|
|
196
|
+
height: 30px;
|
|
197
|
+
background: var(--bg-3);
|
|
198
|
+
color: var(--fg);
|
|
199
|
+
border: 1px solid var(--line);
|
|
200
|
+
border-radius: var(--radius-sm);
|
|
201
|
+
padding: 0 8px;
|
|
202
|
+
outline: none;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.row {
|
|
206
|
+
display: flex;
|
|
207
|
+
flex-wrap: wrap;
|
|
208
|
+
gap: 14px;
|
|
209
|
+
align-items: center;
|
|
210
|
+
margin-top: 12px;
|
|
211
|
+
}
|
|
212
|
+
.row.tight {
|
|
213
|
+
gap: 8px;
|
|
214
|
+
margin-top: 10px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.check {
|
|
218
|
+
display: inline-flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
gap: 6px;
|
|
221
|
+
font-size: 13px;
|
|
222
|
+
color: var(--fg);
|
|
223
|
+
user-select: none;
|
|
224
|
+
cursor: pointer;
|
|
225
|
+
}
|
|
226
|
+
.check input {
|
|
227
|
+
margin: 0;
|
|
228
|
+
accent-color: var(--accent);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
button {
|
|
232
|
+
appearance: none;
|
|
233
|
+
border: 1px solid var(--line);
|
|
234
|
+
background: var(--bg-3);
|
|
235
|
+
color: var(--fg);
|
|
236
|
+
padding: 8px 14px;
|
|
237
|
+
border-radius: var(--radius-sm);
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
font: inherit;
|
|
240
|
+
transition: background 80ms ease, border-color 80ms ease, transform 80ms ease;
|
|
241
|
+
}
|
|
242
|
+
button:hover {
|
|
243
|
+
background: #232834;
|
|
244
|
+
}
|
|
245
|
+
button:active {
|
|
246
|
+
transform: translateY(1px);
|
|
247
|
+
}
|
|
248
|
+
button:disabled {
|
|
249
|
+
opacity: 0.5;
|
|
250
|
+
cursor: not-allowed;
|
|
251
|
+
}
|
|
252
|
+
button.primary {
|
|
253
|
+
background: var(--accent);
|
|
254
|
+
color: #0b0d12;
|
|
255
|
+
border-color: transparent;
|
|
256
|
+
font-weight: 600;
|
|
257
|
+
}
|
|
258
|
+
button.primary:hover {
|
|
259
|
+
background: #95b5ff;
|
|
260
|
+
}
|
|
261
|
+
button.ghost {
|
|
262
|
+
background: transparent;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.actions {
|
|
266
|
+
display: flex;
|
|
267
|
+
align-items: center;
|
|
268
|
+
gap: 10px;
|
|
269
|
+
flex-wrap: wrap;
|
|
270
|
+
}
|
|
271
|
+
.actions .state {
|
|
272
|
+
margin-left: auto;
|
|
273
|
+
color: var(--muted);
|
|
274
|
+
font-size: 12px;
|
|
275
|
+
font-variant-numeric: tabular-nums;
|
|
276
|
+
}
|
|
277
|
+
.actions .state.ok {
|
|
278
|
+
color: var(--ok);
|
|
279
|
+
}
|
|
280
|
+
.actions .state.warn {
|
|
281
|
+
color: var(--accent-2);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
details.hint-card summary {
|
|
285
|
+
cursor: pointer;
|
|
286
|
+
color: var(--muted);
|
|
287
|
+
font-size: 13px;
|
|
288
|
+
list-style: none;
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
gap: 6px;
|
|
292
|
+
}
|
|
293
|
+
details.hint-card summary::before {
|
|
294
|
+
content: "▸";
|
|
295
|
+
display: inline-block;
|
|
296
|
+
transition: transform 120ms ease;
|
|
297
|
+
color: var(--muted);
|
|
298
|
+
}
|
|
299
|
+
details.hint-card[open] summary::before {
|
|
300
|
+
transform: rotate(90deg);
|
|
301
|
+
}
|
|
302
|
+
details.hint-card ul {
|
|
303
|
+
margin: 10px 0 0;
|
|
304
|
+
padding-left: 20px;
|
|
305
|
+
color: var(--fg);
|
|
306
|
+
}
|
|
307
|
+
details.hint-card li {
|
|
308
|
+
margin: 4px 0;
|
|
309
|
+
}
|
|
310
|
+
kbd {
|
|
311
|
+
background: var(--bg-3);
|
|
312
|
+
border: 1px solid var(--line);
|
|
313
|
+
border-bottom-width: 2px;
|
|
314
|
+
border-radius: 4px;
|
|
315
|
+
padding: 0 5px;
|
|
316
|
+
font: 11px/1.6 ui-monospace, "SF Mono", Menlo, monospace;
|
|
317
|
+
}
|